/* ================================================================
   GLOBAL RESET & BASE
   ================================================================ */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{font-family:'Inter',system-ui,-apple-system,sans-serif;font-feature-settings:'cv01','cv03','cv04','cv11';
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;line-height:1.5;scroll-behavior:smooth;
  overflow:clip;overscroll-behavior:none}
body{background:var(--sys-bg);color:var(--sys-text);font-size:var(--ref-font-base);
  font-weight:var(--sys-font-wt);overflow:clip;overscroll-behavior:none;
  min-height:100vh;min-height:100dvh;
  -webkit-tap-highlight-color:transparent;user-select:none;
  touch-action:pan-y pinch-zoom;
  /* v2.20.0 r5: Changed from 100vw to 100%. On mobile browsers, 100vw
     includes the scrollbar gutter width, making the body wider than the
     visible viewport. This causes the entire layout to shift right — the
     left edge gets proper padding but the right column clips past the
     screen edge. 100% constrains to the actual visible width. */
  max-width:100%;
  position:relative}

/* v2.20.0 r5: Hard constraint on html to prevent ANY horizontal overflow.
   The body's overflow:clip should handle this, but iOS Safari PWA sometimes
   ignores it when the html element itself is wider. Belt and suspenders. */
html {
  overflow-x:hidden;
  width:100%;
  max-width:100%;
}
/* v2.15.0: visibility:hidden moved to inline skeleton CSS in header.php.
   Body is visible from the start; the skeleton overlay covers content
   until ts-ready fires. The old 3s CSS fallback is replaced by the
   skeleton's own failsafe animation. */
body.ts-ready{}
button{border:none;background:none;font:inherit;color:inherit;cursor:pointer;
  outline:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
button:focus-visible{box-shadow:var(--sys-focus);border-radius:var(--ref-radius-sm)}
input,textarea,select{font:inherit;color:inherit;border:none;outline:none;
  background:none;-webkit-tap-highlight-color:transparent}
input:focus-visible,textarea:focus-visible,select:focus-visible{box-shadow:var(--sys-focus)}
a{color:var(--sys-brand);text-decoration:none;touch-action:manipulation}
h1,h2,h3,h4,h5,h6{font-weight:var(--sys-font-wt-sb);line-height:1.25}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
/* v2.14.3: Utility classes consumed by app.js settings templates */
.mt-4{margin-top:var(--ref-space-4)!important}
.w-full{width:100%!important}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;
  animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}

/* ---- CSS fallback if JS fails ---- */
/* v2.15.0: If JS never boots (no ts-ready), the skeleton auto-hides after 4s
   and content becomes visible via the inline <style> failsafe in header.php.
   The old visibility:hidden + 3s unhide animation is removed — the skeleton
   approach gives users immediate visual feedback instead of a blank screen. */

/* ================================================================
   LAYOUT — Views
   ================================================================ */
.view{display:none;width:100%;min-height:100vh;min-height:100dvh}
.view.active{display:flex;flex-direction:column;
  /* v2.10.0 fix: Explicit height creates a "definite main size" so flex:1
     children (sub-views) resolve their height correctly. Without this,
     iOS Safari treats the available space as infinite (WebKit Bug #137730)
     and overflow-y:auto on sub-views never triggers a scrollbar.
     min-height retained as fallback for edge cases. */
  height:100vh;height:100dvh}
#view-login.active{display:flex;align-items:center;justify-content:center}
#view-main{padding-bottom:calc(49px + env(safe-area-inset-bottom,0px));
  overscroll-behavior:contain;
  transition:transform .35s cubic-bezier(.32,.72,0,1),opacity .35s ease}
.sub-view{display:none;flex:1;overflow-y:auto;overflow-x:clip;
  /* v2.21.0: contain overscroll so iOS rubber-banding does not reveal the
     darker page void behind the surface (the "ugly black section" on scroll). */
  overscroll-behavior:contain;
  padding:var(--ref-space-4);padding-top:calc(env(safe-area-inset-top,0px) + var(--ref-space-3));
  max-width:100%;width:100%}
.sub-view.active{display:block}
/* v2.14.0: Enforce the visibility contract with !important so that plugin CSS
   using ID selectors (e.g. #sv-team { display:flex } in nav-inject.css) cannot
   accidentally override the base .sub-view { display:none }. Without this,
   ID-specificity (1,0,0) beats the class rule (0,1,0) and the non-active sub-view
   renders its content below the active view — visually leaking skeleton/loading
   states at the bottom of the page. When .active IS present, plugins' own display
   value (flex/grid/etc.) still wins normally since this :not(.active) guard won't
   match. */
.sub-view:not(.active){display:none !important}

/* v2.14.3.1: Pull-to-refresh indicator — inserted at the top of the active
   sub-view by initPullToRefresh() in app.js. Consistent with the TSA/TSIM
   plugin-level PTR pattern but at the theme shell level. */
.ts-pull-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 0;
  overflow: hidden;
  font-size: var(--ref-font-sm, 15px);
  color: var(--sys-text-ter, #94A3B8);
  background: var(--sys-bg-alt, var(--sys-surface-raised, #F1F5F9));
  transition: opacity 0.15s ease;
  border-radius: var(--ref-radius-md, 8px);
  margin-bottom: var(--ref-space-1, 4px);
}

/* ================================================================
   LOGIN
   ================================================================ */
#view-login{background:linear-gradient(145deg,var(--ref-brand-600) 0%,var(--ref-brand-900) 50%,var(--ref-brand-950) 100%);
  padding:var(--ref-space-4)}
.login-card{background:var(--sys-surface);border-radius:var(--ref-radius-2xl);
  padding:var(--ref-space-8) var(--ref-space-6);width:100%;max-width:380px;
  box-shadow:var(--sys-shadow-xl)}
.login-logo{text-align:center;margin-bottom:var(--ref-space-6)}
.login-logo .logo-mark{width:56px;height:56px;background:var(--ref-brand-500);
  border-radius:var(--ref-radius-lg);display:inline-flex;align-items:center;
  justify-content:center;color:#fff;margin-bottom:var(--ref-space-3)}
.login-logo h1{font-size:var(--ref-font-xl);font-weight:var(--sys-font-wt-b);letter-spacing:-.02em}
.login-logo p{font-size:var(--ref-font-sm);color:var(--sys-text-sec);margin-top:var(--ref-space-1)}
.login-version{font-size:var(--ref-font-xs);color:var(--sys-text-ter);text-align:center;margin-top:var(--ref-space-4)}
.fg{margin-bottom:var(--ref-space-4)}
.fg label{display:block;font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  color:var(--sys-text-sec);margin-bottom:var(--ref-space-1)}
.fg input,.fg select{width:100%;padding:var(--ref-space-3) var(--ref-space-4);
  border:1.5px solid var(--sys-border);border-radius:var(--ref-radius-md);
  background:var(--sys-bg);font-size:var(--ref-font-base);min-height:52px;
  transition:border-color var(--ref-dur-fast) var(--ref-ease)}
.fg input:focus,.fg select:focus{border-color:var(--sys-brand)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--ref-space-2);
  padding:var(--ref-space-3) var(--ref-space-5);border-radius:var(--ref-radius-md);
  font-size:var(--ref-font-base);font-weight:var(--sys-font-wt-md);min-height:52px;
  transition:all var(--ref-dur-fast) var(--ref-ease);cursor:pointer}
.btn-brand{background:var(--sys-brand);color:var(--sys-text-brand);width:100%}
.btn-brand:hover{background:var(--sys-brand-hover)}
.btn-brand:active{background:var(--sys-brand-dark);transform:scale(.98)}
.btn-outline{border:1.5px solid var(--sys-border);color:var(--sys-text);background:var(--sys-surface)}
.btn-outline:hover{border-color:var(--sys-brand);color:var(--sys-brand)}
.btn-sm{min-height:36px;padding:var(--ref-space-2) var(--ref-space-3);font-size:var(--ref-font-sm);
  border-radius:var(--ref-radius-sm)}
.btn-icon{width:48px;height:48px;min-height:48px;border-radius:var(--ref-radius-md);
  display:inline-flex;align-items:center;justify-content:center}
.btn-icon:hover{background:var(--sys-brand-light)}

/* ================================================================
   v2.21.0: NO-SUBMIT-SHIFT HOOKS
   Shared affordances for the "stay on the button, show inline status,
   never reflow" submit pattern. Plugins opt in (Surveys, Leads,
   Commissions, Estimates) so every widget behaves identically.
   See NO-SUBMIT-SHIFT-CONTRACT-v1.md. CLS-correct by construction:
   the busy button keeps its box dimensions, the status slot reserves
   its space when empty, and everything animates with opacity only.
   ================================================================ */
/* Busy button: keep EXACT dimensions while working — never let the label
   swap change width/height (that is the layout shift). Spinner replaces the
   label in place; pointer events disabled; announced to AT via aria-busy. */
.ts-btn-busy{position:relative;pointer-events:none;color:transparent !important}
.ts-btn-busy > *{visibility:hidden}
.ts-btn-busy::after{
  content:'';position:absolute;top:50%;left:50%;
  width:1.1em;height:1.1em;margin:-.55em 0 0 -.55em;
  border:2px solid currentColor;border-top-color:transparent;border-radius:50%;
  /* currentColor is transparent above, so paint the spinner from the busy
     button's intended ink: brand-text for filled buttons, text otherwise. */
  color:var(--sys-text-brand);
  animation:appSpin .7s linear infinite;visibility:visible}
.btn-outline.ts-btn-busy::after,.ts-btn-busy.ts-btn-busy-ink-text::after{color:var(--sys-text)}
/* v2.20.3-patched-9: defer to plugin-supplied busy contents.
   The hook above is for a PLAIN button — the theme hides the label and paints
   its own ::after spinner. But some plugins (e.g. Surveys) ALSO inject their
   own spinner + "Running…/Syncing…" label INTO the button and then add
   .ts-btn-busy as the contract instructs. Without this guard the theme's
   `color:transparent` + `> * {visibility:hidden}` + ::after would erase the
   plugin's label and double the spinner. When the busy button contains a
   plugin-injected spinner/label child, step back entirely: don't blank the
   text, don't hide children, and suppress the theme ::after — let the plugin's
   own busy UI show. Plugins that inject nothing (e.g. Leads) are unaffected and
   still get the theme spinner. (:has() is already used elsewhere in this file;
   Safari 17+/target devices support it.) */
.ts-btn-busy:has(> [class*="spinner"]),
.ts-btn-busy:has(> [class*="busy-label"]){
  color:inherit !important;
}
.ts-btn-busy:has(> [class*="spinner"]) > *,
.ts-btn-busy:has(> [class*="busy-label"]) > *{
  visibility:visible;
}
.ts-btn-busy:has(> [class*="spinner"])::after,
.ts-btn-busy:has(> [class*="busy-label"])::after{
  content:none;
}

/* Inline status slot: ALWAYS occupies space (reserved min-height) so showing
   a message never pushes content. Fades in via opacity. Place directly above
   or below the action button. Opaque surface so it reads over anything. */
.ts-inline-status{
  min-height:var(--ref-space-6);
  display:flex;align-items:center;gap:var(--ref-space-2);
  padding:var(--ref-space-2) var(--ref-space-3);
  font-size:var(--ref-font-sm);color:var(--sys-text-sec);
  background:var(--sys-surface);border-radius:var(--ref-radius-md);
  opacity:0;transition:opacity var(--ref-dur-norm) var(--ref-ease)}
.ts-inline-status.is-visible{opacity:1}
.ts-inline-status .ts-inline-spinner{
  width:1em;height:1em;flex-shrink:0;
  border:2px solid var(--sys-border);border-top-color:var(--sys-brand);
  border-radius:50%;animation:appSpin .7s linear infinite}
.ts-inline-status.is-success{color:var(--sys-success)}
.ts-inline-status.is-error{color:var(--sys-error)}
[data-theme="sunlight"] .ts-inline-status{border:2px solid #000}

/* ================================================================
   GREETING ROW
   ================================================================ */
.greeting-row{padding:var(--ref-space-2) 0 var(--ref-space-1);
  display:flex;align-items:center;justify-content:space-between;
  /* v2.21.0: reserve the refresh button's full height so the row cannot
     vertically collapse into the search trigger below it. */
  min-height:40px}
.greeting-row h2{font-size:var(--ref-font-xl);font-weight:var(--sys-font-wt-b);
  letter-spacing:-.02em;color:var(--sys-text);margin:0;line-height:1.2}
/* v2.14.4 A1: Visible refresh button */
.ts-refresh-btn{width:40px;height:40px;min-height:40px;border-radius:var(--ref-radius-md,12px);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  color:var(--sys-text-sec);background:var(--sys-surface);
  border:1px solid var(--sys-border);
  /* v2.21.0: keep the refresh button's tap region self-contained so it can
     never share/overlap a hit-rect with the search trigger on large phones.
     position:relative + z-index keeps it above the adjacent search button;
     touch-action prevents gesture bleed during the compact-bar transition. */
  position:relative;z-index:2;touch-action:manipulation;
  transition:all var(--ref-dur-fast) var(--ref-ease);cursor:pointer}
.ts-refresh-btn:hover{color:var(--sys-brand);border-color:var(--sys-brand);
  background:var(--sys-brand-light)}
.ts-refresh-btn:active{transform:scale(.9)}
[data-theme="sunlight"] .ts-refresh-btn{border:2px solid #000;color:#000}
/* v2.16.0 T2: Inner wrapper for greeting + refresh alignment */
.greeting-row-inner{display:flex;align-items:center;justify-content:space-between;width:100%}

/* ================================================================
   DASHBOARD SEARCH TRIGGER
   ================================================================ */
.dash-search-trigger{display:flex;align-items:center;gap:var(--ref-space-2);
  width:100%;padding:var(--ref-space-3) var(--ref-space-4);
  background:var(--sys-surface);border:1.5px solid var(--sys-border);
  /* v2.21.0: rounded rectangle (was --ref-radius-full pill — the only oval in
     the UI). Matches the dock tiles / cards so the search bar is visually
     consistent in both its full and compact states. */
  border-radius:var(--ref-radius-lg);min-height:48px;
  color:var(--sys-text-ter);font-size:var(--ref-font-base);
  /* v2.21.0: explicit top gap guarantees vertical separation from the
     greeting/refresh row above; touch-action keeps the tap region clean. */
  margin-top:var(--ref-space-2);margin-bottom:var(--ref-space-3);
  touch-action:manipulation;
  transition:border-color var(--ref-dur-fast) var(--ref-ease),
  box-shadow var(--ref-dur-fast) var(--ref-ease)}
.dash-search-trigger:hover{border-color:var(--sys-brand);box-shadow:var(--sys-shadow-sm)}
.dash-search-trigger:active{transform:scale(.99)}
.dash-search-trigger svg{flex-shrink:0}
.dash-search-trigger span{flex:1;text-align:left}

/* ================================================================
   v2.14.4.3 A4: APP DOCK — 2-column big-touch grid
   Full-column rounded squares, large icons + labels, portrait-first.
   ================================================================ */
/* v2.14.5: bigger tiles — more padding, min-height, and label text for
   mobile readability. Icons unchanged per design spec. */
.app-dock{
  /* v2.20.1: minmax(0, 1fr) instead of bare 1fr — Safari WebKit can
     resolve 1fr to fractional px that exceeds the container when combined
     with gap on high-DPI devices. minmax(0, 1fr) constrains the minimum
     and prevents sub-pixel overflow on the right column. */
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--ref-space-3);
  padding:var(--ref-space-3) 0 var(--ref-space-4);
  max-width:100%;box-sizing:border-box}
.dock-app{
  position:relative;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--ref-space-3);
  padding:var(--ref-space-5) var(--ref-space-4);
  background:var(--sys-surface);
  border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-xl);
  box-shadow:var(--sys-shadow-sm);
  cursor:pointer;
  min-height:130px;
  transition:all var(--ref-dur-fast) var(--ref-ease);
  -webkit-tap-highlight-color:transparent;
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
  /* Stagger entrance animation */
  opacity:0;animation:fadeSlideUp var(--ref-dur-norm) var(--ref-ease-out) forwards}
.dock-app:nth-child(1){animation-delay:30ms}
.dock-app:nth-child(2){animation-delay:70ms}
.dock-app:nth-child(3){animation-delay:110ms}
.dock-app:nth-child(4){animation-delay:150ms}
.dock-app:nth-child(5){animation-delay:190ms}
.dock-app:nth-child(6){animation-delay:230ms}
.dock-app:nth-child(7){animation-delay:270ms}
.dock-app:nth-child(8){animation-delay:310ms}
/* Desktop hover — subtle lift + glow */
@media(hover:hover){
  .dock-app:hover{box-shadow:var(--sys-shadow-md);
    border-color:var(--sys-brand);transform:translateY(-2px)}
}
/* Press / tap — darken + scale down, visible feedback */
.dock-app:active{
  transform:scale(.93);
  box-shadow:none;
  background:var(--sys-surface-subtle);
  border-color:var(--sys-brand);
  transition-duration:50ms}
.dock-icon{
  width:72px;height:72px;border-radius:var(--ref-radius-lg);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0;
  box-shadow:0 0 8px rgba(0,0,0,.18);margin:0 auto}
.dock-icon svg{width:34px;height:34px;stroke-width:1.75}
.dock-label{
  font-size:var(--ref-font-lg);font-weight:var(--sys-font-wt-sb);
  color:var(--sys-text);text-align:center;line-height:1.25;
  max-width:100%;word-wrap:break-word;
  /* v2.21.0: reserve a fixed two-line band so a 1- vs 2-line label (e.g.
     "Commissions" wrapping) can never change tile height — eliminates the
     per-tile cumulative layout shift. Labels longer than two lines clamp
     with an ellipsis rather than growing the tile. */
  min-height:calc(1.25em * 2);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden}
/* Description hidden by default — too cluttered on phone */
.dock-desc{display:none}
/* v2.21.0: long-label fit. Carleton asked that only the long dock names
   ("Commissions", "Knowledge") be slightly smaller so they sit on ONE line —
   not a global type change. app.js tags labels over a length threshold with
   .is-long; these rules shrink ONLY those and force a single line. Short labels
   are untouched. Applies to both the tile-grid dock (.dock-label) and the
   springboard grid (.app-ic .nm). */
.dock-label.is-long{
  font-size:var(--ref-font-md);
  -webkit-line-clamp:1;line-clamp:1;
  min-height:1.25em;
}
.app-ic .nm.is-long{
  font-size:var(--ref-font-xs);
}
/* Tablet+ (600px): bigger icons, more padding */
@media(min-width:600px){
  .dock-app{min-height:150px;padding:var(--ref-space-6) var(--ref-space-5)}
  .dock-icon{width:84px;height:84px;border-radius:var(--ref-radius-xl)}
  .dock-icon svg{width:40px;height:40px}
  .dock-label{font-size:var(--ref-font-xl)}
  /* Show description only on tablet where there's room */
  .dock-desc{display:-webkit-box;font-size:var(--ref-font-xs);color:var(--sys-text-sec);
    text-align:center;line-height:1.3;max-width:100%;
    -webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
}
/* Wide tablet / desktop: 3-col if many apps */
@media(min-width:900px){
  .app-dock.dock-many{grid-template-columns:repeat(3,minmax(0,1fr))}
}
[data-theme="sunlight"] .dock-app{border:2.5px solid #000;box-shadow:none}
[data-theme="sunlight"] .dock-icon{border:2px solid #000}
/* v2.16.0 T11: Light mode dock-app surface contrast */
:root[data-theme="light"] .dock-app{background:var(--ref-gray-0);border-color:var(--ref-gray-200)}

/* ================================================================
   v2.14.4 C2: NOTIFICATION BADGES (on nav items)
   Non-red, subtle brand-accent badge. Plugins inject badge elements;
   this provides the styling contract.
   ================================================================ */
.ni-badge{position:absolute;top:2px;right:50%;transform:translateX(calc(50% + 14px));
  min-width:18px;height:18px;border-radius:var(--ref-radius-full);
  background:var(--sys-brand);color:var(--sys-text-brand);
  font-size:11px;font-weight:var(--sys-font-wt-sb);
  display:flex;align-items:center;justify-content:center;
  padding:0 5px;line-height:1;pointer-events:none;
  box-shadow:0 1px 3px rgba(0,0,0,.2)}
.ni-badge:empty{width:10px;height:10px;min-width:10px;padding:0}
[data-theme="sunlight"] .ni-badge{background:#000;color:#fff;border:1.5px solid #fff}

/* ================================================================
   v2.14.4 D1: ADD-TO-HOMESCREEN INSTALL BANNER
   ================================================================ */
.ts-install-banner{position:fixed;bottom:calc(49px + env(safe-area-inset-bottom,0px) + 8px);
  left:16px;right:16px;z-index:140;
  background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-lg);box-shadow:var(--sys-shadow-lg);
  padding:var(--ref-space-3) var(--ref-space-4);
  display:flex;align-items:center;gap:var(--ref-space-3);
  animation:toastIn var(--ref-dur-norm) var(--ref-ease) forwards}
.ts-install-text{flex:1;font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  color:var(--sys-text);line-height:1.3}
.ts-install-how-btn{padding:var(--ref-space-2) var(--ref-space-4);
  background:var(--sys-brand);color:var(--sys-text-brand);
  border-radius:var(--ref-radius-full);font-size:var(--ref-font-sm);
  font-weight:var(--sys-font-wt-sb);white-space:nowrap;min-height:36px;
  transition:background var(--ref-dur-fast) var(--ref-ease)}
.ts-install-how-btn:hover{background:var(--sys-brand-hover)}
.ts-install-dismiss-btn{width:32px;height:32px;display:flex;align-items:center;
  justify-content:center;color:var(--sys-text-ter);border-radius:var(--ref-radius-sm);
  flex-shrink:0}
.ts-install-dismiss-btn:hover{background:var(--sys-bg-alt)}
@media(min-width:820px){
  .ts-install-banner{left:96px;max-width:480px}
}
[data-theme="sunlight"] .ts-install-banner{border:2px solid #000}
[data-theme="sunlight"] .ts-install-how-btn{background:#000}

/* v2.17.2: iOS Add-to-Home-Screen visual guide overlay */
.ts-ios-guide{
  display:none;position:fixed;inset:0;z-index:200;
  align-items:flex-start;justify-content:center;
  padding:calc(env(safe-area-inset-top,0px) + 16px) 16px 0;
}
.ts-ios-guide-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.55);}
.ts-ios-guide-card{
  position:relative;z-index:1;
  background:var(--sys-surface,#1e293b);border:1px solid var(--sys-border,#334155);
  border-radius:20px;padding:24px 20px 20px;
  width:100%;max-width:380px;
  box-shadow:0 8px 32px rgba(0,0,0,.4);
  animation:toastIn 250ms ease forwards;
}
.ts-ios-guide-close{
  position:absolute;top:12px;right:12px;width:32px;height:32px;
  display:flex;align-items:center;justify-content:center;
  border-radius:50%;background:var(--sys-bg-alt,#374151);color:var(--sys-text-sec,#9ca3af);
  font-size:16px;cursor:pointer;border:none;
}
.ts-ios-guide-title{
  font-size:18px;font-weight:700;color:var(--sys-text,#fff);margin:0 0 4px;
}
.ts-ios-guide-sub{
  font-size:13px;color:var(--sys-text-sec,#9ca3af);margin:0 0 16px;
}
.ts-ios-guide-steps{display:flex;flex-direction:column;gap:14px;}
.ts-ios-guide-step{display:flex;align-items:flex-start;gap:12px;}
.ts-ios-guide-num{
  flex-shrink:0;width:28px;height:28px;
  display:flex;align-items:center;justify-content:center;
  background:var(--sys-brand,#2C5F8A);color:#fff;
  border-radius:50%;font-size:14px;font-weight:700;
}
.ts-ios-guide-content{
  display:flex;align-items:center;gap:10px;
  font-size:14px;color:var(--sys-text,#e2e8f0);line-height:1.4;
  padding-top:3px;
}
.ts-ios-guide-content strong{color:var(--sys-brand,#60a5fa);font-weight:600;}
.ts-ios-guide-icon{
  flex-shrink:0;width:36px;height:36px;
  display:flex;align-items:center;justify-content:center;
  background:var(--sys-bg-alt,#374151);border-radius:8px;
  color:var(--sys-brand,#60a5fa);
}
.ts-ios-guide-arrow{
  display:flex;justify-content:flex-end;margin-top:16px;padding-right:8px;
  color:var(--sys-text-sec,#9ca3af);
}
.ts-ios-guide-arrow.ts-arrow-bounce{
  animation:ts-bounce-arrow 1.2s ease-in-out infinite;
}
@keyframes ts-bounce-arrow{
  0%,100%{transform:translateY(0);opacity:.6}
  50%{transform:translateY(8px);opacity:1}
}

/* ================================================================
   QUICK STATS ROW — v2.14.4.3: larger touch-friendly stat cards
   ================================================================ */
.quick-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--ref-space-3);padding:var(--ref-space-3) 0}
.stat-pill{display:flex;align-items:center;gap:var(--ref-space-3);
  padding:var(--ref-space-4);background:var(--sys-surface);
  border:1px solid var(--sys-border);border-radius:var(--ref-radius-lg);
  box-shadow:var(--sys-shadow-sm);min-height:64px;min-width:0;
  cursor:pointer;transition:all var(--ref-dur-fast) var(--ref-ease)}
@media(hover:hover){
  .stat-pill:hover{box-shadow:var(--sys-shadow-md);border-color:var(--sys-brand)}
}
.stat-pill:active{transform:scale(.95);box-shadow:none;
  background:var(--sys-surface-subtle);border-color:var(--sys-brand);
  transition-duration:50ms}
.stat-pill .stat-icon{width:36px;height:36px;border-radius:var(--ref-radius-md);display:flex;
  align-items:center;justify-content:center;flex-shrink:0}
.stat-pill .stat-icon svg{width:20px;height:20px}
.stat-pill .stat-val{font-size:var(--ref-font-md);font-weight:var(--sys-font-wt-sb)}
.stat-pill .stat-label{font-size:var(--ref-font-sm);color:var(--sys-text-sec)}
/* When 3 pills, let the third span full width */
.quick-stats .stat-pill:nth-child(3):last-child{grid-column:1 / -1}

/* ================================================================
   SEARCH BAR (inline — hidden by default, kept for compatibility)
   ================================================================ */
.search-wrap{position:relative;margin-bottom:var(--ref-space-3)}
.search-bar{display:flex;align-items:center;gap:var(--ref-space-2);
  background:var(--sys-surface);border:1.5px solid var(--sys-border);
  /* v2.21.0: match .dash-search-trigger — rounded rectangle, not pill. */
  border-radius:var(--ref-radius-lg);padding:0 var(--ref-space-4);
  min-height:52px;transition:border-color var(--ref-dur-fast) var(--ref-ease),
  box-shadow var(--ref-dur-fast) var(--ref-ease)}
.search-bar:focus-within{border-color:var(--sys-brand);box-shadow:var(--sys-shadow-md)}
.search-bar svg{color:var(--sys-text-ter);flex-shrink:0;width:18px;height:18px}
.search-bar input{flex:1;background:none;font-size:var(--ref-font-base);
  color:var(--sys-text);min-height:50px;user-select:text}
.search-bar input::placeholder{color:var(--sys-text-ter)}
.search-results{position:absolute;top:calc(100% + 4px);left:0;right:0;
  background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-lg);box-shadow:var(--sys-shadow-lg);
  max-height:320px;overflow-y:auto;z-index:90;display:none}
.search-results.show{display:block}
.search-item{display:flex;align-items:center;gap:var(--ref-space-3);
  padding:var(--ref-space-3) var(--ref-space-4);cursor:pointer;
  transition:background var(--ref-dur-fast) var(--ref-ease)}
.search-item:hover{background:var(--sys-bg-alt)}
.search-item .si-icon{width:36px;height:36px;border-radius:var(--ref-radius-sm);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.search-item .si-text{flex:1;min-width:0}
.search-item .si-name{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md)}
.search-item .si-hint{font-size:var(--ref-font-xs);color:var(--sys-text-ter);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.search-item .si-slash{font-size:var(--ref-font-xs);color:var(--sys-text-ter);
  font-family:monospace;background:var(--sys-bg-alt);padding:2px 6px;
  border-radius:var(--ref-radius-xs);flex-shrink:0}

/* ================================================================
   RECENTLY USED
   ================================================================ */
.section-label{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-sb);
  color:var(--sys-text-ter);text-transform:uppercase;letter-spacing:.06em;
  margin-bottom:var(--ref-space-2);padding-left:var(--ref-space-1)}
.recent-row{display:flex;flex-wrap:wrap;gap:var(--ref-space-3);padding-bottom:var(--ref-space-3)}
.recent-chip{flex:0 0 auto;display:flex;align-items:center;gap:var(--ref-space-2);
  padding:var(--ref-space-2) var(--ref-space-4) var(--ref-space-2) var(--ref-space-2);
  background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-full);cursor:pointer;min-height:48px;
  transition:all var(--ref-dur-fast) var(--ref-ease)}
.recent-chip:hover{border-color:var(--sys-brand);background:var(--sys-brand-light)}
.recent-chip:active{transform:scale(.97)}
.recent-chip .rc-icon{width:32px;height:32px;border-radius:50%;display:flex;
  align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.recent-chip .rc-icon svg{width:16px;height:16px}
.recent-chip .rc-name{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  white-space:nowrap}

/* ================================================================
   APP GRID
   ================================================================ */
.cat-section{margin-bottom:var(--ref-space-5)}
.cat-label{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-sb);
  color:var(--sys-text-ter);text-transform:uppercase;letter-spacing:.06em;
  margin-bottom:var(--ref-space-3);padding-left:var(--ref-space-1);
  display:flex;align-items:center;gap:var(--ref-space-2)}
.cat-label .cat-dot{width:8px;height:8px;border-radius:50%}
.app-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--ref-space-4)}
@media(min-width:400px){.app-grid{grid-template-columns:repeat(4,1fr)}}
@media(min-width:600px){.app-grid{grid-template-columns:repeat(5,1fr)}}
@media(min-width:900px){.app-grid{grid-template-columns:repeat(6,1fr)}}
.app-ic{display:flex;flex-direction:column;align-items:center;gap:var(--ref-space-2);
  padding:var(--ref-space-2);cursor:pointer;border-radius:var(--ref-radius-md);
  transition:all var(--ref-dur-fast) var(--ref-ease)}
@media(hover:hover){
  .app-ic:hover{transform:translateY(-2px)}
  .app-ic:hover .ic{box-shadow:var(--sys-shadow-md)}
}
.app-ic:active{transform:scale(.90);transition-duration:50ms}
.app-ic:active .ic{box-shadow:none;filter:brightness(.85)}
.app-ic .ic{width:72px;height:72px;border-radius:var(--ref-radius-lg);
  display:flex;align-items:center;justify-content:center;color:#fff;
  box-shadow:var(--sys-shadow-sm);transition:box-shadow var(--ref-dur-fast) var(--ref-ease)}
.app-ic:hover .ic{box-shadow:var(--sys-shadow-md)}
.app-ic .ic svg{width:32px;height:32px;stroke-width:var(--sys-icon-wt)}
.app-ic .nm{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  text-align:center;color:var(--sys-text-sec);line-height:1.2;
  max-width:90px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- Staggered App Grid Animations ---- */
@keyframes fadeSlideUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
.app-ic{opacity:0;animation:fadeSlideUp var(--ref-dur-norm) var(--ref-ease-out) forwards}
.app-ic:nth-child(1){animation-delay:30ms}.app-ic:nth-child(2){animation-delay:60ms}
.app-ic:nth-child(3){animation-delay:90ms}.app-ic:nth-child(4){animation-delay:120ms}
.app-ic:nth-child(5){animation-delay:150ms}.app-ic:nth-child(6){animation-delay:180ms}
.app-ic:nth-child(7){animation-delay:210ms}.app-ic:nth-child(8){animation-delay:240ms}

/* ---- Large Action Cards (role-adaptive ≤4 apps) ---- */
.app-grid-hero{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--ref-space-4);
  margin-bottom:var(--ref-space-5)}
.action-card{display:flex;align-items:center;gap:var(--ref-space-4);
  padding:var(--ref-space-5);background:var(--sys-surface);
  border:1px solid var(--sys-border);border-radius:var(--ref-radius-xl);
  cursor:pointer;min-height:88px;
  box-shadow:var(--sys-shadow-sm);
  transition:all var(--ref-dur-fast) var(--ref-ease);
  opacity:0;animation:fadeSlideUp var(--ref-dur-norm) var(--ref-ease-out) forwards}
@media(hover:hover){
  .action-card:hover{box-shadow:var(--sys-shadow-md);transform:translateY(-2px);
    border-color:var(--sys-brand)}
}
.action-card:active{transform:scale(.95);box-shadow:none;
  background:var(--sys-surface-subtle);border-color:var(--sys-brand);
  transition-duration:50ms}
.action-card:nth-child(1){animation-delay:30ms}.action-card:nth-child(2){animation-delay:90ms}
.action-card:nth-child(3){animation-delay:150ms}.action-card:nth-child(4){animation-delay:210ms}
.action-card .ac-icon{width:60px;height:60px;border-radius:var(--ref-radius-lg);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.action-card .ac-icon svg{width:30px;height:30px;stroke-width:var(--sys-icon-wt)}
.action-card .ac-text{flex:1;min-width:0}
.action-card .ac-name{font-size:var(--ref-font-md);font-weight:var(--sys-font-wt-sb)}
.action-card .ac-desc{font-size:var(--ref-font-sm);color:var(--sys-text-sec);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}

/* ---- View Fade Animation ---- */
@keyframes viewFadeIn{from{opacity:0}to{opacity:1}}
.sub-view.active{animation:viewFadeIn var(--ref-dur-norm) var(--ref-ease)}

/* ================================================================
   KPI METRIC GRID (v2.8.0 — Owner / Admin / Mfg views)
   ================================================================ */
.kpi-grid{
  /* v2.21.0: minmax(0,1fr) (was bare 1fr) — same Safari/high-DPI sub-pixel
     fix the app dock uses. Bare 1fr + gap can resolve to fractional px that
     exceeds the container, clipping the right column. */
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--ref-space-3);padding:var(--ref-space-2) 0;margin-bottom:var(--ref-space-3)}
.kpi-card{
  background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-lg);padding:var(--ref-space-3) var(--ref-space-4);
  display:flex;flex-direction:column;gap:2px;min-height:72px;
  box-shadow:var(--sys-shadow-sm);transition:all var(--ref-dur-fast) var(--ref-ease)}
@media(hover:hover){.kpi-card:hover{box-shadow:var(--sys-shadow-md)}}
.kpi-card.kpi-primary{
  padding:var(--ref-space-4) var(--ref-space-5);min-height:96px}
.kpi-card.kpi-action{cursor:pointer}
@media(hover:hover){
  .kpi-card.kpi-action:hover{border-color:var(--sys-brand);background:var(--sys-brand-light)}
}
.kpi-card.kpi-action:active{transform:scale(.95);box-shadow:none;
  background:var(--sys-surface-subtle);border-color:var(--sys-brand);
  transition-duration:50ms}
.kpi-label{
  font-size:var(--ref-font-xs);font-weight:var(--sys-font-wt-md);
  color:var(--sys-text-sec);text-transform:uppercase;letter-spacing:.04em}
.kpi-value{
  font-size:var(--ref-font-xl);font-weight:var(--sys-font-wt-b);
  color:var(--sys-text);line-height:1.1}
.kpi-card.kpi-primary .kpi-value{font-size:var(--ref-font-2xl)}
.kpi-sub{font-size:var(--ref-font-xs);color:var(--sys-text-ter);margin-top:2px}
.kpi-icon{display:flex;align-items:center;margin-bottom:2px}
.kpi-icon svg{width:20px;height:20px;stroke-width:var(--sys-icon-wt)}
/* KPI loading shimmer & loaded flash */
.kpi-loading{
  background:linear-gradient(90deg,var(--sys-bg-alt) 25%,var(--sys-border) 50%,var(--sys-bg-alt) 75%);
  background-size:200% 100%;animation:shimmer 1.5s infinite;
  border-radius:var(--ref-radius-xs);color:transparent!important;
  min-width:40px;min-height:1.1em}
@keyframes kpiFadeIn{from{opacity:.3}to{opacity:1}}
.kpi-loaded{animation:kpiFadeIn .3s ease forwards}
/* Responsive: 4-col on wider screens, primary cards span 2 */
@media(min-width:600px){
  .kpi-grid{grid-template-columns:repeat(4,1fr)}
  .kpi-card.kpi-primary{grid-column:span 2}}
@media(min-width:900px){
  .kpi-grid:not(.kpi-grid-compact){grid-template-columns:repeat(4,1fr)}}

/* ---- Sunlight overrides ---- */
[data-theme="sunlight"] .action-card{border:2.5px solid #000;box-shadow:none}
[data-theme="sunlight"] .app-ic .ic{border:2.5px solid #000;box-shadow:none}
[data-theme="sunlight"] .stat-pill{border:2px solid #000;box-shadow:none}
[data-theme="sunlight"] .kpi-card{border:2.5px solid #000;box-shadow:none}
[data-theme="sunlight"] .kpi-card.kpi-primary{border-width:3px}
[data-theme="sunlight"] .recent-chip{border:2px solid #000}
[data-theme="sunlight"] .search-bar{border:2px solid #000}
[data-theme="sunlight"] .dash-search-trigger{border:2px solid #000}
[data-theme="sunlight"] .bnav{border-top:2px solid #000}

/* v2.14.4 A6: Bigger bottom nav on tablet (before sidebar breakpoint) */
@media(min-width:600px) and (max-width:819px){
  .bnav{height:56px}
  .ni{min-height:52px}
  .ni svg{width:26px;height:26px}
  .ni .ni-label{font-size:15px}
}

/* ================================================================
   BOTTOM SHEET (App Interaction Panel — legacy, kept for plugins)
   ================================================================ */
.sheet-overlay{position:fixed;inset:0;background:var(--sys-overlay);z-index:200;
  opacity:0;pointer-events:none;transition:opacity var(--ref-dur-norm) var(--ref-ease)}
.sheet-overlay.show{opacity:1;pointer-events:auto}
.bottom-sheet{position:fixed;bottom:0;left:0;right:0;z-index:210;
  background:var(--sys-surface);border-radius:var(--ref-radius-2xl) var(--ref-radius-2xl) 0 0;
  box-shadow:var(--sys-shadow-xl);transform:translateY(100%);
  transition:transform var(--ref-dur-slow) var(--ref-ease-spring);
  max-height:92dvh;display:flex;flex-direction:column;
  padding-bottom:env(safe-area-inset-bottom,0)}
.bottom-sheet.show{transform:translateY(0)}
.sheet-handle{width:36px;height:4px;background:var(--sys-border-strong);
  border-radius:var(--ref-radius-full);margin:var(--ref-space-3) auto var(--ref-space-2);flex-shrink:0}
.sheet-header{display:flex;align-items:center;gap:var(--ref-space-3);
  padding:0 var(--ref-space-5) var(--ref-space-4);border-bottom:1px solid var(--sys-border)}
.sheet-header .sh-icon{width:44px;height:44px;border-radius:var(--ref-radius-md);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.sheet-header .sh-icon svg{width:22px;height:22px;stroke-width:var(--sys-icon-wt)}
.sheet-header .sh-info{flex:1;min-width:0}
.sheet-header .sh-title{font-size:var(--ref-font-lg);font-weight:var(--sys-font-wt-sb)}
.sheet-header .sh-desc{font-size:var(--ref-font-sm);color:var(--sys-text-sec)}
.sheet-header .sh-close{color:var(--sys-text-ter)}
.sheet-body{flex:1;overflow-y:auto;padding:var(--ref-space-4) var(--ref-space-5);
  -webkit-overflow-scrolling:touch}
.sheet-body .fg{margin-bottom:var(--ref-space-4)}
.sheet-body .fg label{display:block;font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  color:var(--sys-text-sec);margin-bottom:var(--ref-space-1)}
.sheet-body .fg input,.sheet-body .fg select,.sheet-body .fg textarea{width:100%;
  padding:var(--ref-space-3) var(--ref-space-4);border:1.5px solid var(--sys-border);
  border-radius:var(--ref-radius-md);background:var(--sys-bg);font-size:var(--ref-font-base);
  min-height:52px;transition:border-color var(--ref-dur-fast) var(--ref-ease)}
.sheet-body .fg input:focus,.sheet-body .fg select:focus,.sheet-body .fg textarea:focus{
  border-color:var(--sys-brand)}
.sheet-body .fg textarea{min-height:80px;resize:vertical}
.sheet-footer{padding:var(--ref-space-4) var(--ref-space-5);border-top:1px solid var(--sys-border);
  display:flex;gap:var(--ref-space-3)}
.sheet-footer .btn{flex:1}

/* ---- Output area ---- */
.output-area{margin-top:var(--ref-space-4);padding:var(--ref-space-4);
  background:var(--sys-bg-alt);border-radius:var(--ref-radius-md);
  border-left:3px solid var(--sys-success);font-size:var(--ref-font-sm);
  font-family:'Inter',monospace;line-height:1.65;white-space:pre-wrap;
  word-break:break-word;display:none;color:var(--sys-text)}
.output-area.visible{display:block}
.output-area.loading{border-left-color:var(--sys-brand)}

/* Shimmer */
.shimmer{background:linear-gradient(90deg,var(--sys-bg-alt) 25%,var(--sys-border) 50%,var(--sys-bg-alt) 75%);
  background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:var(--ref-radius-sm);
  height:14px;margin:var(--ref-space-2) 0}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* ================================================================
   BOTTOM NAVIGATION (3-item: Dashboard | Logo | Chat)
   ================================================================ */
.bnav{position:fixed;bottom:0;left:0;right:0;z-index:150;
  background-color:var(--sys-surface);
  background:var(--sys-surface-blur);backdrop-filter:blur(16px);
  -webkit-backdrop-filter:blur(16px);
  border-top:1px solid var(--sys-border);
  padding-bottom:env(safe-area-inset-bottom,0);
  display:flex;align-items:center;justify-content:center;min-height:49px;
  transition:transform .35s cubic-bezier(.32,.72,0,1)}
/* v2.16.0 T9: Dark mode solid fallback for nav bar */
[data-theme="dark"] .bnav{background-color:var(--ref-gray-800)}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .bnav{background-color:var(--ref-gray-800)}
}

/* Left + Right nav items (Apps, Chat) */
.ni{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;flex:1;min-height:44px;color:var(--sys-text-ter);
  transition:color var(--ref-dur-fast) var(--ref-ease);position:relative;padding:4px 0}
.ni svg{width:22px;height:22px;stroke-width:var(--sys-icon-wt);
  transition:all var(--ref-dur-fast) var(--ref-ease)}
.ni .ni-label{font-size:var(--ref-font-xs);font-weight:var(--sys-font-wt-md)}
.ni.active{color:var(--sys-brand)}
.ni.active svg{stroke-width:calc(var(--sys-icon-wt) + .5)}
.ni.active::before{content:'';position:absolute;top:4px;width:36px;height:28px;
  background:var(--sys-brand-light);border-radius:var(--ref-radius-full);z-index:-1}

/* Center logo button */
.bnav-logo{flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  padding:4px var(--ref-space-4);height:100%;cursor:pointer;
  transition:opacity var(--ref-dur-fast) var(--ref-ease)}
.bnav-logo:hover{opacity:.8}
.bnav-logo:active{transform:scale(.95)}
.bnav-logo img{max-width:100px;max-height:36px;width:auto;height:auto;object-fit:contain}
.bnav-logo-text{font-size:var(--ref-font-lg);font-weight:var(--sys-font-wt-b);
  color:var(--sys-brand);letter-spacing:-.02em}
/* v2.21.0: logo contrast safety net.
   The correct fix is uploading a dark-inked light-mode logo to ts_logo_light
   (see DATA-THEME-CONTRACT). But if that asset is missing/misconfigured, the
   dark-mode (light-inked) wordmark renders on a near-white bar and becomes
   invisible. As a defensive backstop in the light surfaces ONLY, paint a
   subtle neutral chip behind the logo so a light-inked wordmark still reads.
   Scoped to light/sunlight; dark mode (where the light-inked logo is correct)
   is untouched. Kept subtle so it's invisible when the right asset IS used. */
:root[data-theme="light"] .bnav-logo,
:root[data-theme="sunlight"] .bnav-logo{
  background:var(--sys-surface-subtle);border-radius:var(--ref-radius-md)}

/* Settings-active state: highlight logo when settings view is open */
.bnav-logo.settings-active{opacity:1;position:relative}
.bnav-logo.settings-active::after{content:'';position:absolute;bottom:2px;
  width:24px;height:3px;background:var(--sys-brand);border-radius:var(--ref-radius-full)}

/* FAB (Floating Action Button) — REMOVED v2.6.3 */

/* ================================================================
   SETTINGS
   ================================================================ */
.profile-card{background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-lg);padding:var(--ref-space-5);
  display:flex;align-items:center;gap:var(--ref-space-4);margin-bottom:var(--ref-space-5)}
.profile-avatar{width:54px;height:54px;border-radius:50%;background:var(--sys-brand);
  display:flex;align-items:center;justify-content:center;color:var(--sys-text-brand);
  font-size:var(--ref-font-xl);font-weight:var(--sys-font-wt-b);flex-shrink:0}
.profile-info h3{font-size:var(--ref-font-lg);font-weight:var(--sys-font-wt-sb)}
.profile-info p{font-size:var(--ref-font-sm);color:var(--sys-text-sec)}
.settings-section{margin-bottom:var(--ref-space-5)}
.settings-section h4{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-sb);
  color:var(--sys-text-ter);text-transform:uppercase;letter-spacing:.06em;
  margin-bottom:var(--ref-space-3)}
.theme-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--ref-space-3)}
.theme-btn{padding:var(--ref-space-3);border:2px solid var(--sys-border);
  border-radius:var(--ref-radius-md);text-align:center;min-height:56px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  transition:all var(--ref-dur-fast) var(--ref-ease);font-size:var(--ref-font-xs);
  font-weight:var(--sys-font-wt-md)}
.theme-btn:hover{border-color:var(--sys-brand)}
.theme-btn.active{border-color:var(--sys-brand);background:var(--sys-brand-light);
  color:var(--sys-brand)}
.theme-btn .tb-preview{width:28px;height:28px;border-radius:50%;border:2px solid var(--sys-border)}
.settings-row{display:flex;align-items:center;justify-content:space-between;
  padding:var(--ref-space-4);background:var(--sys-surface);
  border:1px solid var(--sys-border);border-radius:var(--ref-radius-md);
  margin-bottom:var(--ref-space-2);min-height:56px}
.settings-row .sr-left{display:flex;align-items:center;gap:var(--ref-space-3)}
.settings-row .sr-left svg{color:var(--sys-text-sec);width:20px;height:20px}
.settings-row .sr-label{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md)}
.settings-row .sr-right{font-size:var(--ref-font-sm);color:var(--sys-text-sec)}
.status-badge{display:inline-flex;align-items:center;gap:var(--ref-space-1);
  padding:var(--ref-space-1) var(--ref-space-3);border-radius:var(--ref-radius-full);
  font-size:var(--ref-font-xs);font-weight:var(--sys-font-wt-md)}
.status-badge.online{background:rgba(16,185,129,.12);color:var(--ref-green-600)}
.status-badge.offline{background:rgba(239,68,68,.12);color:var(--ref-red-600)}
.status-dot{width:6px;height:6px;border-radius:50%;background:currentColor}

/* ================================================================
   TOAST
   ================================================================ */
.toast-container{position:fixed;top:calc(env(safe-area-inset-top,0) + 16px);
  left:50%;transform:translateX(-50%);z-index:300;display:flex;flex-direction:column;
  gap:var(--ref-space-2);pointer-events:none;width:calc(100% - 32px);max-width:400px}
.toast{background:var(--sys-surface-raised);color:var(--sys-text);
  padding:var(--ref-space-3) var(--ref-space-4);border-radius:var(--ref-radius-md);
  box-shadow:var(--sys-shadow-lg);border:1px solid var(--sys-border);
  font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md);
  display:flex;align-items:center;gap:var(--ref-space-2);
  transform:translateY(-20px);opacity:0;pointer-events:auto;
  animation:toastIn var(--ref-dur-norm) var(--ref-ease) forwards}
@keyframes toastIn{to{transform:translateY(0);opacity:1}}
.toast.out{animation:toastOut var(--ref-dur-norm) var(--ref-ease) forwards}
@keyframes toastOut{to{transform:translateY(-20px);opacity:0}}

/* ================================================================
   COMMAND PALETTE (Spotlight)
   ================================================================ */
.cmd-overlay{position:fixed;inset:0;background:var(--sys-overlay);z-index:250;
  display:none;align-items:flex-start;justify-content:center;padding-top:80px}
.cmd-overlay.show{display:flex}
.cmd-box{background:var(--sys-surface);border-radius:var(--ref-radius-xl);
  box-shadow:var(--sys-shadow-xl);width:calc(100% - 32px);max-width:480px;
  overflow:hidden}
.cmd-input-wrap{display:flex;align-items:center;gap:var(--ref-space-3);
  padding:var(--ref-space-4);border-bottom:1px solid var(--sys-border)}
.cmd-input-wrap svg{color:var(--sys-text-ter);flex-shrink:0}
.cmd-input-wrap input{flex:1;font-size:var(--ref-font-lg);background:none;user-select:text}
.cmd-results{max-height:360px;overflow-y:auto}
.cmd-item{display:flex;align-items:center;gap:var(--ref-space-3);
  padding:var(--ref-space-3) var(--ref-space-4);cursor:pointer;
  transition:background var(--ref-dur-fast) var(--ref-ease)}
.cmd-item:hover,.cmd-item.focused{background:var(--sys-brand-light)}
.cmd-item .ci-icon{width:36px;height:36px;border-radius:var(--ref-radius-sm);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.cmd-item .ci-icon svg{width:18px;height:18px}
.cmd-item .ci-text{flex:1}
.cmd-item .ci-name{font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md)}
.cmd-item .ci-desc{font-size:var(--ref-font-xs);color:var(--sys-text-ter)}
.cmd-item .ci-shortcut{font-size:var(--ref-font-xs);color:var(--sys-text-ter);
  font-family:monospace;background:var(--sys-bg-alt);padding:2px 8px;
  border-radius:var(--ref-radius-xs);flex-shrink:0}
.cmd-item-analytics{border-top:1px solid var(--sys-border)}
.cmd-item-analytics .ci-desc{font-style:italic}
.cmd-empty{padding:var(--ref-space-8);text-align:center;color:var(--sys-text-ter);
  font-size:var(--ref-font-sm)}

/* ================================================================
   BUG REPORT — Moved to bug-reporter.css (v2.7.0)
   ================================================================ */

/* ================================================================
   SUNLIGHT MODE SPECIFIC
   ================================================================ */
[data-theme="sunlight"] .bottom-sheet{border:2px solid #000;border-bottom:none}
[data-theme="sunlight"] .profile-card{border:2px solid #000}
[data-theme="sunlight"] .settings-row{border:2px solid #000}
[data-theme="sunlight"] .theme-btn{border:2px solid #000}
[data-theme="sunlight"] .toast{border:2px solid #000}

/* ================================================================
   DARK MODE CONTRAST (v2.14.4 A7)
   Increased border visibility and card elevation for dark/system.
   ================================================================ */
[data-theme="dark"] .kpi-card,
[data-theme="dark"] .dash-widget-container,
[data-theme="dark"] .stat-pill{
  border-color:var(--ref-gray-600);
  box-shadow:0 2px 8px rgba(0,0,0,.35)}
[data-theme="dark"] .dash-widget-header{
  background:rgba(255,255,255,.04);border-bottom-color:var(--ref-gray-600)}
[data-theme="dark"] .kpi-label{color:var(--ref-gray-300)}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .kpi-card,
  [data-theme="system"] .dash-widget-container,
  [data-theme="system"] .stat-pill{
    border-color:var(--ref-gray-600);
    box-shadow:0 2px 8px rgba(0,0,0,.35)}
  [data-theme="system"] .dash-widget-header{
    background:rgba(255,255,255,.04);border-bottom-color:var(--ref-gray-600)}
  [data-theme="system"] .kpi-label{color:var(--ref-gray-300)}
}


/* ================================================================
   DASHBOARD WIDGETS (v2.0)
   ================================================================ */
.dash-widget-zone{
  display:flex;flex-direction:column;gap:var(--ref-space-4);
  margin-bottom:var(--ref-space-5);
}
.dash-widget-container{
  background:var(--sys-surface);border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-xl);overflow:hidden;
  box-shadow:var(--sys-shadow-sm);
}
.dash-widget-header{
  display:flex;align-items:center;gap:var(--ref-space-3);
  padding:var(--ref-space-3) var(--ref-space-4);
  border-bottom:1px solid var(--sys-border);background:var(--sys-bg-alt);
}
.dash-widget-header .dw-icon{
  width:32px;height:32px;border-radius:var(--ref-radius-sm);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0;
}
.dash-widget-header .dw-icon svg{width:18px;height:18px}
.dash-widget-header .dw-title{
  font-size:var(--ref-font-md);font-weight:var(--sys-font-wt-sb);margin:0;
}
/* v2.14.3.1: Widget reorder controls — up/down arrows in the header */
.dw-reorder{
  margin-left:auto;display:flex;gap:2px;flex-shrink:0;
}
.dw-reorder-btn{
  width:32px;height:28px;border:1px solid var(--sys-border, #e2e8f0);border-radius:var(--ref-radius-sm, 6px);
  background:var(--sys-surface, #fff);color:var(--sys-text, #1e293b);cursor:pointer;
  display:flex;align-items:center;justify-content:center;transition:all 0.15s ease;
  padding:0;-webkit-tap-highlight-color:transparent;touch-action:manipulation;
}
.dw-reorder-btn:active:not(:disabled){background:var(--sys-brand, #2C5F8A);color:#fff;transform:scale(0.92);}
.dw-reorder-btn:disabled{opacity:0.25;cursor:not-allowed;}
.dw-reorder-btn svg{pointer-events:none;}
/* v2.20.3-patched-8: dark-mode reorder chevron contrast. The currentColor SVG
   arrows were nearly invisible on the dark widget surface when inking from
   --sys-text-sec. Pin the glyph to full --sys-text and give the tap target a
   faint surface lift + stronger border so it reads on the shop floor. Light
   and sunlight are unchanged. */
[data-theme="dark"] .dw-reorder-btn{
  color:var(--sys-text, #f1f5f9);
  background:rgba(255,255,255,.06);
  border-color:var(--ref-gray-600);
}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .dw-reorder-btn{
    color:var(--sys-text, #f1f5f9);
    background:rgba(255,255,255,.06);
    border-color:var(--ref-gray-600);
  }
}
.dash-widget-body{padding:var(--ref-space-4)}

/* ================================================================
   v2.21.0: GAME CONTAINER BUDGET (wide screens / landscape)
   The game shown during AI waits is "tiny in the middle" on desktop/
   wide-tablet. The THEME owns the container height; the ts-game PLUGIN
   scales its canvas (letterbox, no resolution change) to fill it.
   Contract: a game surface may grow to --ts-game-max-h. On phones the
   budget is unchanged; on >=900px and in landscape it is larger so the
   plugin has vertical room to scale into. Target the game widget body
   or any element the plugin tags with [data-ts-game-surface].
   ================================================================ */
:root{ --ts-game-max-h: 420px; }
@media (min-width: 900px){ :root{ --ts-game-max-h: 640px; } }
@media (orientation: landscape) and (min-width: 740px){ :root{ --ts-game-max-h: 70vh; } }
.dash-widget-container[data-app-id="ts-game"] .dash-widget-body,
[data-ts-game-surface]{
  max-height:var(--ts-game-max-h);
}

/* Sunlight overrides for widgets */
[data-theme="sunlight"] .dash-widget-container{
  border:2px solid #000;box-shadow:none;
}
[data-theme="sunlight"] .dash-widget-header{
  border-bottom:2px solid #000;background:#fff;
}

/* ================================================================
   FULL-SCREEN APP VIEWPORT
   ================================================================ */
/* v2.16.0 T5: Instant jump on mobile (no slide animation) */
.app-viewport{position:fixed;inset:0;z-index:200;background:var(--sys-bg);
  display:flex;flex-direction:column;
  transform:translateY(100%);
  transition:none;
  will-change:transform;
  pointer-events:none}
.app-viewport.active{transform:translateY(0);pointer-events:auto}

/* ---- App Header (compact) ---- */
/* v2.14.5: position:relative + z-index creates a stacking context so scrolled
   content in .app-body never composites above the header. box-shadow masks
   any sub-pixel edge bleed at the header/body boundary. */
.app-header{display:flex;align-items:center;gap:var(--ref-space-2);
  padding:env(safe-area-inset-top,0) var(--ref-space-3) 0;
  min-height:56px;background:var(--sys-surface);
  border-bottom:1px solid var(--sys-border);flex-shrink:0;
  position:relative;z-index:10;
  box-shadow:0 2px 6px -2px rgba(0,0,0,.1)}
.app-back{width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  border-radius:var(--ref-radius-md);color:var(--sys-brand)}
.app-back:hover{background:var(--sys-brand-light)}
.app-header-info{display:flex;align-items:center;gap:var(--ref-space-2);flex:1;min-width:0}
.app-header-icon{width:28px;height:28px;border-radius:var(--ref-radius-sm);
  display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}
.app-header-icon svg{width:16px;height:16px}
.app-header-title{font-size:var(--ref-font-md);font-weight:var(--sys-font-wt-sb);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.app-header-actions{display:flex;align-items:center;gap:var(--ref-space-1)}

/* ---- App Body ---- */
/* v2.21.0: surface background (not the darker --sys-bg page void) so short
   ajax_html content doesn't flash a black band, and contain overscroll so
   rubber-banding past the content reveals the surface, not the void. */
.app-body{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;
  background:var(--sys-surface);overscroll-behavior:contain}
.app-body.has-iframe{overflow:hidden}
.app-body iframe{width:100%;height:100%;border:none;display:block}

/* ---- Loading & Error states ---- */
.app-loading{display:flex;align-items:center;justify-content:center;height:200px;
  color:var(--sys-text-sec)}
.app-loading-spinner{width:28px;height:28px;border:3px solid var(--sys-border);
  border-top-color:var(--sys-brand);border-radius:50%;animation:appSpin .7s linear infinite}
@keyframes appSpin{to{transform:rotate(360deg)}}
.app-error{padding:var(--ref-space-6);text-align:center;color:var(--sys-text-sec);
  font-size:var(--ref-font-sm)}

/* ---- Chrome transitions when app is active ---- */
body.app-active .bnav{transform:translateY(100%);pointer-events:none}
body.app-active #view-main{pointer-events:none}

/* ---- Sunlight overrides for viewport ---- */
[data-theme="sunlight"] .app-header{border-bottom:2px solid #000}
/* v2.14.5: stronger shadow in dark themes for header edge masking */
[data-theme="dark"] .app-header{box-shadow:0 2px 8px -2px rgba(0,0,0,.35)}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .app-header{box-shadow:0 2px 8px -2px rgba(0,0,0,.35)}
}

/* ================================================================
   DESKTOP RESPONSIVE BREAKPOINTS
   ================================================================ */
@media(min-width: 820px) {
  /* Bottom nav becomes left sidebar */
  .bnav {
    top: 0;
    bottom: 0;
    left: 0;
    right: auto;            /* ← UNSET mobile right:0 */
    width: 88px;            /* v2.14.4 A6: wider sidebar for bigger touch targets */
    height: 100%;           /* ← Explicit full height (not auto) */
    height: 100dvh;         /* ← Modern override for dynamic viewport */
    flex-direction: column;
    justify-content: flex-start;
    padding-top: var(--ref-space-4);
    padding-bottom: var(--ref-space-2);
    border-top: none;
    border-right: 1px solid var(--sys-border);
    transition: transform .35s cubic-bezier(.32,.72,0,1);
    overflow-y: auto;       /* ← Scrollable if content overflows */
    overflow-x: hidden;
  }
  .ni {
    flex: none;
    width: 100%;
    min-height: 76px;       /* v2.14.4 A6: taller touch targets */
  }
  .ni svg {
    width: 32px;            /* v2.16.0 T14: larger sidebar icons */
    height: 32px;
  }
  .ni .ni-label {
    font-size: 16px;        /* v2.16.0 T14: larger sidebar label text */
  }
  /* Logo in sidebar: sits between Dashboard and Chat */
  .bnav-logo {
    width: 100%;
    padding: var(--ref-space-4) var(--ref-space-2);
    justify-content: center;
  }
  .bnav-logo img {
    max-width: 64px;
    max-height: 32px;
  }
  /* v2.14.4 A5: Vertical logo gets more height in the sidebar */
  .bnav-logo img.logo-vertical {
    max-width: 56px;
    max-height: 64px;
    object-fit: contain;
  }
  .bnav-logo.settings-active::after {
    bottom: auto;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 24px;
  }

  #view-main {
    padding-bottom: 0;
    padding-left: 88px;     /* v2.14.4 A6: match wider sidebar */
  }

  /* v2.16.0 T5: Fast 0.1s transition on desktop */
  .app-viewport {
    transform: scale(0.96);
    opacity: 0;
    transition: transform .1s cubic-bezier(.32,.72,0,1), opacity .1s ease;
  }
  .app-viewport.active {
    transform: scale(1);
    opacity: 1;
  }

  /* Hide sidebar when app is active (slide left on desktop) */
  body.app-active .bnav {
    transform: translateX(-100%);
  }
}

@media(min-width: 1024px) {
  .app-grid {
    grid-template-columns: repeat(8, 1fr);
  }
  .cmd-overlay {
    padding-top: 120px;
  }
}

/* v2.13.0: App Authorizations + Nutshell modal (company-wide) */
.ts-app-auth-card { margin-bottom: var(--ref-space-4, 16px); }
.ts-app-auth-card .ts-auth-hint {
  font-size: var(--ref-font-xs, 13px);
  color: var(--sys-text-sec);
  margin: var(--ref-space-1, 4px) 0 var(--ref-space-3, 12px);
  line-height: 1.45;
}
.ts-app-auth-card .btn.is-loading { opacity: .7; cursor: wait; }
.ts-app-auth-card .ts-auth-divider {
  height: 1px;
  background: var(--sys-border);
  margin: var(--ref-space-4, 16px) 0;
}
.ts-app-auth-card .ts-auth-scope {
  font-size: var(--ref-font-xs, 12px);
  font-weight: 400;
  color: var(--sys-text-sec);
  margin-left: var(--ref-space-1, 4px);
}

/* v2.20.2: Field Preferences card */
.ts-field-prefs-card { margin-bottom: var(--ref-space-4, 16px); }
.ts-fp-header {
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; padding: var(--ref-space-3, 12px) 0;
  -webkit-tap-highlight-color: transparent;
}
.ts-fp-header-left {
  display: flex; align-items: center; gap: var(--ref-space-2, 8px);
}
.ts-fp-header-left svg {
  width: 18px; height: 18px; color: var(--sys-text-sec);
}
.ts-fp-header-left h4 {
  margin: 0; font-size: var(--ref-font-sm, 14px);
  font-weight: var(--sys-font-wt-sb, 600);
  color: var(--sys-text-ter); text-transform: uppercase; letter-spacing: .06em;
}
.ts-fp-chevron {
  width: 18px; height: 18px; color: var(--sys-text-sec);
  transition: transform var(--ref-dur-fast, 150ms) var(--ref-ease);
  flex-shrink: 0;
}
.ts-fp-body { padding-bottom: var(--ref-space-3, 12px); }
.ts-fp-hint {
  font-size: var(--ref-font-xs, 13px); color: var(--sys-text-sec);
  line-height: 1.45; margin: 0 0 var(--ref-space-4, 16px);
}
.ts-fp-field { margin-bottom: var(--ref-space-4, 16px); }
.ts-fp-label {
  display: block; font-size: var(--ref-font-sm, 14px);
  font-weight: var(--sys-font-wt-md, 500); color: var(--sys-text);
  margin-bottom: var(--ref-space-1, 4px);
}
.ts-fp-field-hint {
  font-size: var(--ref-font-xs, 12px); color: var(--sys-text-ter);
  line-height: 1.4; margin: 0 0 var(--ref-space-2, 8px);
}
.ts-fp-textarea {
  width: 100%; box-sizing: border-box;
  padding: var(--ref-space-3, 12px);
  font-family: inherit; font-size: var(--ref-font-sm, 14px);
  line-height: 1.5; color: var(--sys-text);
  background: var(--sys-surface); border: 1px solid var(--sys-border);
  border-radius: var(--ref-radius-md, 8px);
  resize: vertical; min-height: 56px;
  transition: border-color var(--ref-dur-fast, 150ms) var(--ref-ease);
}
.ts-fp-textarea:focus {
  outline: none; border-color: var(--sys-brand);
  box-shadow: 0 0 0 3px var(--sys-brand-light, rgba(44,95,138,.15));
}
.ts-fp-textarea::placeholder { color: var(--sys-text-ter); }
.ts-fp-actions {
  padding-top: var(--ref-space-3, 12px);
}
.ts-fp-actions .btn { display: flex; align-items: center; justify-content: center; gap: var(--ref-space-2, 8px); }
.ts-fp-actions .btn svg { width: 16px; height: 16px; }
.ts-fp-loading, .ts-fp-error {
  font-size: var(--ref-font-sm, 14px); color: var(--sys-text-sec);
  padding: var(--ref-space-4, 16px) 0; text-align: center;
}
.ts-fp-error { color: var(--ref-red-600, #DC2626); }
.ts-fp-migration-banner {
  display: flex; align-items: flex-start; gap: var(--ref-space-2, 8px);
  background: var(--sys-brand-light, rgba(44,95,138,.08));
  border: 1px solid var(--sys-brand, #2C5F8A);
  border-radius: var(--ref-radius-md, 8px);
  padding: var(--ref-space-3, 12px);
  font-size: var(--ref-font-xs, 13px); color: var(--sys-text);
  line-height: 1.45; margin-bottom: var(--ref-space-4, 16px);
}
.ts-fp-migration-banner svg { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; color: var(--sys-brand); }

/* Nutshell modal */
.ts-ns-overlay {
  position: fixed; inset: 0;
  background: var(--sys-overlay, rgba(0,0,0,.45));
  display: flex; align-items: center; justify-content: center;
  padding: var(--ref-space-4, 16px);
  z-index: 9998;
  opacity: 0;
  transition: opacity var(--ref-dur-norm, 250ms) var(--ref-ease, cubic-bezier(.4,0,.2,1));
}
.ts-ns-overlay.show { opacity: 1; }
.ts-ns-modal {
  background: var(--sys-surface);
  color: var(--sys-text);
  border: 1px solid var(--sys-border);
  border-radius: var(--ref-radius-lg, 16px);
  box-shadow: var(--sys-shadow-xl);
  width: 100%;
  max-width: 440px;
  max-height: 90vh;
  overflow-y: auto;
}
.ts-ns-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--ref-space-4, 16px);
  border-bottom: 1px solid var(--sys-border);
}
.ts-ns-header h3 {
  margin: 0;
  font-size: var(--ref-font-md, 19px);
  font-weight: var(--sys-font-wt-sb, 600);
  display: flex; align-items: center; gap: var(--ref-space-2, 8px);
}
.ts-ns-body {
  padding: var(--ref-space-4, 16px);
  display: flex; flex-direction: column; gap: var(--ref-space-3, 12px);
}
.ts-ns-label {
  display: flex; flex-direction: column; gap: var(--ref-space-1, 4px);
  font-size: var(--ref-font-sm, 15px);
  font-weight: var(--sys-font-wt-md, 500);
}
.ts-ns-label input {
  width: 100%;
  padding: var(--ref-space-2, 8px) var(--ref-space-3, 12px);
  border: 1px solid var(--sys-border-strong);
  border-radius: var(--ref-radius-sm, 8px);
  background: var(--sys-surface);
  color: var(--sys-text);
  font-size: var(--ref-font-base, 17px);
  box-sizing: border-box;
}
.ts-ns-label input:focus {
  outline: none;
  box-shadow: var(--sys-focus);
  border-color: var(--sys-brand);
}
.ts-ns-hint {
  font-size: var(--ref-font-xs, 13px);
  color: var(--sys-text-sec);
  margin: 0;
  line-height: 1.45;
}
.ts-ns-error {
  background: rgba(239,68,68,.1);
  color: var(--sys-error);
  border: 1px solid var(--sys-error);
  border-radius: var(--ref-radius-sm, 8px);
  padding: var(--ref-space-2, 8px) var(--ref-space-3, 12px);
  font-size: var(--ref-font-xs, 13px);
}
.ts-ns-footer {
  padding: var(--ref-space-4, 16px);
  border-top: 1px solid var(--sys-border);
  display: flex; justify-content: flex-end; gap: var(--ref-space-2, 8px);
}
.ts-ns-footer .btn.is-loading { opacity: .7; cursor: wait; }

/* === LAYOUT v2.12.5 — sidebar preserved on desktop, sticky headers === */

/* 1. SINGLE-COLUMN DASHBOARD — widget zone and grid container stack vertically.
      The .app-grid itself keeps its responsive grid columns (repeat(3-6,1fr))
      defined above. Only the outer containers stack. */
.dash-widget-zone,
#app-grid-container {
  display: flex !important;
  flex-direction: column !important;
  gap: var(--ref-space-4, 16px);
  width: 100%;
  max-width: 100%;
}
.dash-widget-container,
.dash-widget-zone > *,
.app-card {
  width: 100% !important;
  max-width: 100% !important;
  min-width: 0 !important;
  margin-inline: 0 !important;
}

/* Wrap rows — no horizontal scrolling anywhere (v2.14.1) */
.recent-row,
.app-chips-row,
.greeting-row {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: wrap !important;
  overflow-x: visible !important;
  gap: var(--ref-space-2, 8px);
}
/* Quick stats: grid layout, prevent horizontal overflow */
.quick-stats {
  overflow-x: hidden !important;
  max-width: 100%;
}

/* 2. SCROLL OWNERSHIP — .sub-view owns vertical scroll */
.view.active {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: clip;
  max-width: 100%; /* v2.20.0 r5: was 100vw which includes scrollbar gutter */
}
.sub-view {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: clip;
  overscroll-behavior-x: none;
  overscroll-behavior-y: none;
  max-width: 100%;
}
/* v2.14.4.3: Hard clip — prevent ANY horizontal shift */
#view-main, #sv-dash, .app-viewport {
  max-width: 100%;
  overflow-x: hidden; /* r5: changed from clip to hidden — iOS WebKit converts
                         clip to auto when paired with overflow-y:auto */
  overscroll-behavior-x: none;
}
/* v2.20.0 r5: The right-side layout shift is caused by the sub-view's
   scroll container being wider than the viewport. Force explicit width
   and symmetric padding. */
#sv-dash.sub-view {
  width: 100%;
  box-sizing: border-box;
  padding-left: var(--ref-space-4);
  padding-right: var(--ref-space-4);
}
/* Force all direct children of the dash sub-view to stay within bounds */
#sv-dash > * {
  max-width: 100%;
  box-sizing: border-box;
}
/* v2.21.0: when a widget is launched from the dock, bridge.js scrolls it to the
   top of #sv-dash via scrollIntoView({block:'start'}). scroll-margin-top reserves
   space above the target equal to the sticky dash chrome (top bar + sticky icon
   strip) plus a small gap, so the launched widget's header lands just below the
   chrome instead of under it. Declarative offset — no JS geometry. */
.dash-widget-container {
  scroll-margin-top: calc(var(--dash-top-h, 0px) + var(--dash-sticky-h, 0px) + 8px);
}
.app-body { min-height: 0; }

/* 3. STICKY WIDGET HEADERS — handled in the consolidated v2.14.1 block below.
      Container setup (overflow, isolation) remains here. */
@media (max-width: 1199.98px) {
  .dash-widget-container {
    position: relative;
    isolation: isolate;
    overflow: clip;
    overflow-clip-margin: 0;
  }
}

/* 4. BOTTOM NAV — ONLY phones + iPad mini (<820px).
      At ≥820px the theme's original left-sidebar rules take over untouched. */
@media (max-width: 819.98px) {
  .bnav {
    position: fixed !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    top: auto !important;
    width: 100% !important;
    height: auto !important;
    min-height: 49px;
    flex-direction: row !important;
    z-index: 200;
    padding-bottom: env(safe-area-inset-bottom, 0);
    transform: translateZ(0);
    backface-visibility: hidden;
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    backdrop-filter:         saturate(180%) blur(12px);
    background: color-mix(in srgb, var(--sys-surface) 92%, transparent);
    border-top: 1px solid var(--sys-border, rgba(0,0,0,.08));
    border-right: none !important;
    /* v2.14.4.3: Ensure bottom nav never clips off-screen */
    overflow: visible;
  }
  /* v2.14.4.3: Padding ONLY on .sub-view (not #view-main or .view.active)
     to avoid stacking padding that pushes content past viewport edge */
  #view-main,
  .view.active {
    margin-left: 0 !important;
    padding-left: 0;
    padding-right: 0;
  }
  .sub-view {
    margin-left: 0 !important;
    padding-left: var(--ref-space-4, 16px);
    padding-right: var(--ref-space-4, 16px);
  }
  /* Clear space below so content doesn't hide behind bnav */
  #view-main {
    padding-bottom: calc(
      56px +
      env(safe-area-inset-bottom, 0px) +
      12px
    );
  }
}

/* 5. TABLET-PORTRAIT-AND-UP SCROLL FIX (≥600px, doesn't touch nav) */
@media (min-width: 600px) {
  .view.active { overflow: clip; }
}

/* 6. Desktop reading-width cap (sidebar preserved by theme default) */
@media (min-width: 1280px) {
  .sub-view {
    max-width: 1200px;
    margin: 0 auto;
    padding-inline: var(--ref-space-6);
  }
}
@media (min-width: 1680px) {
  .sub-view { max-width: 1400px; }
}

/* 6b. Full-width opt-out (v2.14.0) — chat-style sub-views need to fill the
   viewport on desktop. iMessage/Slack on macOS are full-width by design;
   the messaging plugin's Team tab and any future chat-shaped plugin should
   match that expectation. Plugins opt in by adding `.ts-fullwidth` to
   their .sub-view element. The TS Internal Messaging plugin's #sv-team is
   wired in here directly so it works without any plugin-side change.
   Cap-overrides repeat at every cap breakpoint so a more-specific media
   rule above can't sneak the cap back in. The !important on the base rule
   matches what the messaging plugin previously shipped in nav-inject.css —
   migrating the responsibility to the theme keeps that contract.

   Note: this co-exists fine with the messaging plugin's own #sv-team rule
   in nav-inject.css (v1.0.13). When that plugin drops its workaround in a
   future release, this is the one source of truth. */
.sub-view.ts-fullwidth,
#sv-team {
  max-width: none !important;
  padding-inline: 0 !important;
}
@media (min-width: 1280px) {
  .sub-view.ts-fullwidth,
  #sv-team {
    max-width: none;
    padding-inline: 0;
  }
}
@media (min-width: 1680px) {
  .sub-view.ts-fullwidth,
  #sv-team { max-width: none; }
}

/* 7. App viewport cleanup */
.app-viewport:not(.active) { visibility: hidden; pointer-events: none; }
/* === END LAYOUT v2.12.5 === */
/* === STICKY HEADER AESTHETIC FIX v2.14.1 === */
/* Sticky widget headers need:
   1. Explicit opaque background (not inherit — that resolves to transparent
      when the parent chain has no solid bg, causing content peek-through)
   2. Rounded top corners matching the card container
   3. Soft bottom shadow instead of hard edge
   4. top: var(--dash-top-h) so they stick BELOW the dash-top, not behind it
   overflow:clip on containers clips at the border-radius without creating a
   scrolling context, so sticky children still work. */

/* v2.17.1: --dash-top-h is set by JS after measuring .dash-top height.
   Widget headers use it to stick right below the greeting/search bar,
   eliminating the 20-80px transparent gap where content was peeking through. */
:root { --dash-top-h: 0px; --dash-sticky-h: 0px; }

@media (max-width: 1199.98px) {
  .widget-card {
    overflow: clip;
    overflow-clip-margin: 0;
  }

  .dash-widget-container > *:first-child,
  .dash-widget-container > div > *:first-child:not(div),
  .dash-widget-container .widget-header,
  .dash-widget-container .card-header,
  .dash-widget-container > header,
  .dash-widget-container > h2:first-child,
  .dash-widget-container > h3:first-child,
  .widget-card > .widget-header,
  .widget-card > header,
  .widget-card > h3:first-child,
  /* v2.14.5: cover plugin headers rendered inside app-body scroll context */
  .app-body .widget-header,
  .app-body .card-header,
  .app-body > div > header:first-child,
  .app-body [data-sticky-header] {
    position: sticky !important;
    /* v2.20.0 r4: Widget headers stick below BOTH the greeting bar AND the
       sticky icon bar. Previously they stuck at --dash-top-h only, which
       is the same position as the icon bar — so headers slid behind it.
       Adding --dash-sticky-h pushes widget headers below the icon bar. */
    top: calc(var(--dash-top-h, 0px) + var(--dash-sticky-h, 0px)) !important;
    z-index: 20 !important;

    /* Explicit opaque surface — NEVER inherit or transparent. This is the
       fix for the "content peeking under header" bug. */
    background: var(--sys-surface, #fff) !important;
    background-clip: padding-box !important;

    border-top-left-radius:  var(--ref-radius-lg, 16px) !important;
    border-top-right-radius: var(--ref-radius-lg, 16px) !important;
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;

    box-shadow: 0 6px 8px -6px rgba(0, 0, 0, 0.12) !important;

    padding-block: var(--ref-space-3, 12px);
    padding-inline: var(--ref-space-4, 16px);
  }
}
/* === END STICKY HEADER AESTHETIC FIX === */

/* ================================================================
   v2.16.0 T15: DASH-TOP — sticky greeting + search
   ================================================================ */
.dash-top{
  position:sticky;top:0;z-index:30;
  background:var(--sys-bg);
  /* v2.17.1: Extend into safe area so content never peeks above the greeting */
  padding-top:calc(env(safe-area-inset-top, 0px) + var(--ref-space-3));
  padding-bottom:var(--ref-space-2);
  /* v2.21.0: shadow removed at rest — over a near-white light-mode bg the
     hairline read as a horizontal "cut off" seam (dark/sun never showed it
     because they force a matched background below). A subtle separation is
     reapplied only in the compact/scrolled state (.dash-compact), when the
     bar is genuinely floating over scrolled content. */
}
/* v2.17.1: The dashboard sub-view must have NO padding-top — the sticky
   dash-top covers the full top area (including safe-area on notched phones).
   Without this, the sub-view's padding creates a gap above the stuck header
   where content scrolls through and is visible as a "bar". */
#sv-dash.sub-view{ padding-top:0 }
/* v2.17.1: Greeting — shrink text to fit instead of truncating with "..." */
.greeting-row h2,.greeting-row-inner h2{
  white-space:nowrap;overflow:hidden;
  max-width:calc(100% - 56px);
  /* Scale from 18px (cramped) to full xl size, fitting the viewport */
  font-size:clamp(18px, 5.5vw, var(--ref-font-xl));
}
/* v2.17.1: pseudo-element covers any sub-pixel gap at the bottom edge
   so scrolling content never peeks through below the sticky bar */
.dash-top::after{
  content:'';display:block;position:absolute;
  left:0;right:0;bottom:-2px;height:2px;
  background:var(--sys-bg);z-index:30;
}
[data-theme="dark"] .dash-top{background:var(--ref-gray-950)}
[data-theme="dark"] .dash-top::after{background:var(--ref-gray-950)}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .dash-top{background:var(--ref-gray-950)}
  [data-theme="system"] .dash-top::after{background:var(--ref-gray-950)}
}
[data-theme="sunlight"] .dash-top{background:#FFFFFF;box-shadow:0 4px 6px -4px rgba(0,0,0,.15)}
[data-theme="sunlight"] .dash-top::after{background:#FFFFFF}
/* v2.21.0: explicit light-mode parity. Light previously fell through to
   --sys-bg with no matched cover, which (with the old hairline shadow) showed
   a seam under the greeting/search zone. Match .dash-top and its ::after
   cover to the page background so the sticky header blends seamlessly, the
   same way dark/system/sunlight already do. */
:root[data-theme="light"] .dash-top{background:var(--sys-bg)}
:root[data-theme="light"] .dash-top::after{background:var(--sys-bg)}
/* v2.21.0: reintroduce a subtle separation ONLY when scrolled/compact, so the
   floating bar reads as elevated over content without showing a seam at rest. */
.dash-top.dash-compact{box-shadow:0 4px 6px -4px rgba(0,0,0,.12)}

/* ================================================================
   v2.16.0 T4: DASH-STICKY — compact app bar (visible on scroll)
   v2.17.1: top uses --dash-top-h so it sits below dash-top, not behind it
   ================================================================ */
.dash-sticky{
  position:fixed;left:0;right:0;
  /* v2.20.1: Removed env(safe-area-inset-top) — it was double-counted.
     --dash-top-h is measured from getBoundingClientRect().height on .dash-top,
     which ALREADY includes the safe-area padding. Adding safe-area again
     pushed the bar below the search bar by ~47px on notched iPhones in PWA. */
  top:var(--dash-top-h,0px);
  z-index:35;
  background:var(--sys-bg);
  border-bottom:1px solid var(--sys-border);
  padding:var(--ref-space-1) 0;
  /* Hidden by default — slides in when sentinel scrolls out of view */
  transform:translateY(-110%);
  opacity:0;pointer-events:none;
  transition:transform 200ms ease-out, opacity 150ms ease-out;
  will-change:transform,opacity;
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
}
.dash-sticky.visible{
  transform:translateY(0);
  opacity:1;pointer-events:auto;
}
.sticky-app-strip{
  display:flex;gap:var(--ref-space-2);
  overflow-x:auto;
  padding:0 var(--ref-space-3);
  scrollbar-width:none;
  mask-image:linear-gradient(to right, #000 82%, transparent 100%);
  -webkit-mask-image:linear-gradient(to right, #000 82%, transparent 100%);
}
.sticky-app-strip::-webkit-scrollbar{display:none}
/* v2.17.2: Desktop — center the strip when there's extra space */
@media(min-width:900px){
  .sticky-app-strip{justify-content:center;padding:0 var(--ref-space-5);}
}
.sticky-app-btn{
  position:relative;
  flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:var(--ref-space-1);border-radius:var(--ref-radius-lg);
  transition:transform var(--ref-dur-fast) var(--ref-ease);
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
}
.sticky-app-btn:active{transform:scale(.88)}
/* v2.17.2: Sticky bar icons — large enough to be tappable */
.sticky-app-icon{
  width:52px;height:52px;border-radius:var(--ref-radius-lg);
  display:flex;align-items:center;justify-content:center;color:#fff;
  box-shadow:0 1px 4px rgba(0,0,0,.12);
}
.sticky-app-icon svg{width:24px;height:24px;stroke-width:1.75}
/* v2.17.2: Compact labels */
.sticky-app-label{
  font-size:10px;font-weight:600;color:var(--sys-text-sec);
  text-align:center;line-height:1.1;max-width:62px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
[data-theme="dark"] .dash-sticky{background:var(--ref-gray-950);border-bottom-color:var(--ref-gray-700)}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .dash-sticky{background:var(--ref-gray-950);border-bottom-color:var(--ref-gray-700)}
}
[data-theme="sunlight"] .dash-sticky{background:#FFFFFF;border-bottom:2px solid #000}
[data-theme="sunlight"] .sticky-app-icon{border:2px solid #000;box-shadow:none}

/* ================================================================
   v2.16.0 T10: SUNLIGHT MODE — additional overrides
   ================================================================ */
[data-theme="sunlight"] .dock-icon{box-shadow:none}
[data-theme="sunlight"] .kpi-value{color:#000}
[data-theme="sunlight"] .kpi-label{color:#000}
[data-theme="sunlight"] .btn-brand{background:#000;color:#fff}
[data-theme="sunlight"] .btn-brand:hover{background:#222}
[data-theme="sunlight"] .btn-brand:active{background:#000}

/* ================================================================
   v2.16.0 T5: WIDGET HIGHLIGHT — pulse animation for deep-link
   ================================================================ */
@keyframes widgetHighlight{
  0%{box-shadow:0 0 0 0 rgba(44,95,138,.4)}
  50%{box-shadow:0 0 0 8px rgba(44,95,138,.15)}
  100%{box-shadow:0 0 0 0 rgba(44,95,138,0)}
}
.widget-highlight{animation:widgetHighlight .8s ease 2}

/* ── v2.17.3: Dock & Sticky Bar Edit Mode (unified reorder) ── */
@keyframes dock-shake {
  0%,100% { transform:rotate(0deg); }
  25% { transform:rotate(-1.5deg); }
  75% { transform:rotate(1.5deg); }
}
.dock-edit-mode [data-app-id] {
  animation:dock-shake 0.3s ease-in-out infinite;
  cursor:default;
}
.dock-edit-mode [data-app-id]:nth-child(odd) { animation-delay:0.05s; }
.dock-edit-mode [data-app-id]:nth-child(even) { animation-delay:0.15s; }
@keyframes dock-swap { 0%{opacity:.5;transform:scale(.9)} 100%{opacity:1;transform:scale(1)} }
.dock-swapping { animation:dock-swap 200ms ease-out !important; }

/* Chevron arrows — hidden until edit mode */
.dock-reorder-arrows, .sticky-reorder-arrows {
  display:none !important;position:absolute;z-index:5;overflow:hidden;
  width:0;height:0;opacity:0;pointer-events:none;
}
/* Show only in edit mode */
.dock-edit-mode .dock-reorder-arrows {
  display:flex !important;position:static;width:auto;height:auto;opacity:1;pointer-events:auto;
  flex-direction:row;gap:8px;justify-content:center;margin-top:4px;
}
.dock-edit-mode .sticky-reorder-arrows {
  display:flex !important;position:static;width:auto;height:auto;opacity:1;pointer-events:auto;
  flex-direction:row;gap:4px;justify-content:center;margin-top:2px;
}

/* Arrow buttons — shared */
.dock-arr, .sticky-arr {
  display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:50%;
  background:var(--sys-surface,#fff);border:1.5px solid var(--sys-border,#E2E8F0);
  color:var(--sys-text,#0F172A);cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.12);
  transition:all 100ms ease;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;padding:0;
}
.dock-arr:active, .sticky-arr:active {
  transform:scale(.85);background:var(--sys-brand,#2C5F8A);color:#fff;border-color:var(--sys-brand,#2C5F8A);
}
/* Disable arrows at edges using data-pos attribute */
.dock-edit-mode [data-pos="first"] .dock-arr-left,
.dock-edit-mode [data-pos="first"] .sticky-arr-left { opacity:0.2;pointer-events:none; }
.dock-edit-mode [data-pos="last"] .dock-arr-right,
.dock-edit-mode [data-pos="last"] .sticky-arr-right { opacity:0.2;pointer-events:none; }

/* Sticky bar arrows are smaller */
.sticky-arr { width:28px;height:28px; }
.sticky-arr svg { width:12px;height:12px; }

/* Done button */
.dock-edit-done {
  display:block;width:auto;margin:12px auto 0;padding:12px 36px;
  font-size:16px;font-weight:600;color:#FFF;
  background:var(--sys-brand,#2C5F8A);border:none;border-radius:12px;
  cursor:pointer;touch-action:manipulation;
  transition:filter 150ms;
  box-shadow:0 2px 8px rgba(44,95,138,0.3);
}
.dock-edit-done:hover { filter:brightness(1.1); }
.dock-edit-done:active { transform:scale(.95); }
/* Compact Done button inside sticky bar */
.dock-edit-done-sticky {
  display:block;width:auto;margin:6px auto 4px;padding:8px 24px;
  font-size:13px;border-radius:8px;
}

/* Pin sticky bar visible during edit mode */
.dash-sticky.edit-pinned { transform:translateY(0);opacity:1;pointer-events:auto; }

/* Dark mode adjustments */
[data-theme="dark"] .dock-arr, [data-theme="dark"] .sticky-arr {
  background:var(--ref-gray-800,#1e293b);border-color:var(--ref-gray-600,#475569);color:#e2e8f0;
}
/* Sunlight mode adjustments */
[data-theme="sunlight"] .dock-arr, [data-theme="sunlight"] .sticky-arr {
  background:#fff;border:2px solid #000;color:#000;
}
[data-theme="sunlight"] .dock-edit-done { background:#000; }

/* ================================================================
   v2.20.0 — THEME IMPROVEMENT BATCH
   ================================================================ */

/* ── Light mode contrast improvements ─────────────────────────── */
/* CRITICAL: These rules are scoped to [data-theme="light"] ONLY.
   Using :root or unscopied selectors breaks dark mode by forcing
   white backgrounds on every card.
   
   Strategy: subtle box-shadow instead of borders. Shadows respect
   the existing background color (no forced white), and they're
   softer than the 1px border approach which created "big white box"
   artifacts the user didn't like. */

[data-theme="light"] .kpi-card,
[data-theme="light"] .stat-pill {
  box-shadow:0 1px 3px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
}
[data-theme="light"] .app-tile {
  box-shadow:0 1px 3px rgba(0,0,0,.05), 0 0 0 1px rgba(0,0,0,.03);
}
[data-theme="light"] #profile-card-area > div {
  box-shadow:0 1px 4px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
}
/* Personal Records container */
[data-theme="light"] .dash-widget-zone .personal-records-card,
[data-theme="light"] [class*="personal-record"] {
  box-shadow:0 1px 3px rgba(0,0,0,.05);
}

/* ── Safari overflow fixes ────────────────────────────────────── */
body {
  overflow-x:hidden;
  -webkit-text-size-adjust:100%;
}
#view-main, .sub-view {
  max-width:100%; /* v2.20.0 r5: was 100vw */
  overflow-x:clip;
}
#app-grid-container {
  overflow:hidden;
}
.app-tile {
  min-width:0;
}
.view.active {
  min-height:100dvh;
}

/* ── Sticky app bar: smoother animation ───────────────────────── */
.dash-sticky {
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity 250ms ease-out;
}

/* ── KPI error state ──────────────────────────────────────────── */
.kpi-value.kpi-error {
  color:var(--ref-amber-500);
  font-size:var(--ref-font-md);
}
.kpi-card.kpi-error-card {
  border-left:3px solid var(--ref-amber-500);
}
.kpi-error-hint {
  font-size:11px;
  color:var(--ref-amber-600);
  margin-top:2px;
}

/* ── Personal records: empty state ────────────────────────────── */
.personal-records-empty {
  text-align:center;
  padding:var(--ref-space-6) var(--ref-space-4);
  color:var(--sys-text-sec);
  font-size:var(--ref-font-xs);
  line-height:1.5;
}
.personal-records-empty-icon {
  font-size:32px;
  margin-bottom:var(--ref-space-2);
  opacity:0.5;
}

/* ── Widget overflow & layout containment ─────────────────────── */
/* v2.20.1: Removed max-height:65vh and overflow-y:auto that caused
   "double scrolling" — widgets now expand to natural height and the
   page scrolls as one continuous document. No nested scroll traps.
   
   CRITICAL: Must use overflow:clip (not hidden). overflow:hidden
   creates a scroll container per CSS spec, which traps position:sticky
   inside the widget body. overflow:clip clips visual overflow without
   creating a scroll container, so plugin sticky headers (Surveys
   batch history, Sketch save bar) bind to the page-level scroll
   context (#sv-dash.sub-view) and stick correctly.
   
   Do NOT change to overflow:hidden or overflow:auto — plugins depend
   on this NOT being a scroll container. */
.dash-widget-body {
  overflow:clip;
  max-width:100%;
}
/* Prevent any child inside a widget from creating horizontal overflow */
.dash-widget-body * {
  max-width:100%;
  box-sizing:border-box;
}
/* Widget stat grids (Leads "2 BATCHES / 11 LEADS" row) — ensure
   the grid wraps instead of overflowing on narrow screens */
.dash-widget-body .tsl-stats,
.dash-widget-body [class*="stats-row"],
.dash-widget-body [class*="stat-grid"] {
  flex-wrap:wrap;
  overflow:hidden;
}
/* Tables inside widgets should scroll horizontally within the body */
.dash-widget-body table {
  display:block;
  overflow-x:auto;
  max-width:100%;
}

/* ── Layout shift prevention ──────────────────────────────────── */
/* The sticky app bar was shifting left/right on appear/disappear.
   Force containment so it doesn't affect document flow. */
.dash-sticky {
  contain:layout style;
}
/* The app dock should reserve its space to prevent CLS when it
   scrolls out of view and the sticky bar replaces it.
   v2.20.1: Removed contain:layout — it created an independent
   formatting context that could miscalculate grid column widths
   on high-DPI iOS devices with sub-pixel rounding. */
#app-dock {
  min-height:68px; /* prevent collapse before icons load */
}

/* ── v2.20.1: Dash-sticky desktop sidebar offset ────────────── */
/* On ≥820px the left sidebar is 88px wide. The dash-sticky is
   position:fixed so it must offset left to avoid covering the nav. */
@media (min-width: 820px) {
  .dash-sticky {
    left: 88px;
  }
}

/* ── v2.20.1: Mobile top spacing reduction ──────────────────── */
/* Tighten the gap between the search bar and mini-app icons on
   portrait phones. Every pixel counts for usable widget space. */
@media (max-width: 819.98px) {
  .dash-top {
    padding-bottom: var(--ref-space-1);  /* was ref-space-2 (8px→4px) */
  }
  .greeting-row {
    padding: var(--ref-space-1) 0 0;     /* was ref-space-2 0 ref-space-1 */
  }
  .app-dock {
    padding-top: var(--ref-space-1);     /* was ref-space-3 (12px→4px) */
    padding-bottom: var(--ref-space-2);  /* was ref-space-4 (16px→8px) */
  }
}

/* ── v2.20.1: Search bar compact-on-scroll ──────────────────── */
/* When user scrolls down, the dash-top shrinks dramatically to
   reclaim screen real estate. JS adds .dash-compact class at ~40%
   of the sticky threshold, and .dash-sticky-on when the icon strip
   appears. GPU-accelerated via translateZ(0) for consistent
   smoothness across iPhone, iPad, and desktop. */
.dash-top {
  transition: padding 150ms ease, box-shadow 100ms ease;
  transform: translateZ(0); /* GPU composite layer — consistent across devices */
  -webkit-transform: translateZ(0);
}
.dash-search-trigger {
  transition: padding 150ms ease, min-height 150ms ease, font-size 150ms ease;
}
.dash-top.dash-compact {
  padding-top: calc(env(safe-area-inset-top, 0px) + 2px);
  padding-bottom: 0;
}
/* v2.20.1: Solid box-shadow extends below dash-top, covering scrolling
   content in the gap between search bar and sticky icon strip. ONLY
   active when both compact AND sticky bar are visible (.dash-sticky-on
   added by JS when the icon strip appears). Without this gate, the
   shadow covers dock tiles that are still visible during the compact-
   only window (compact triggers before sticky). */
.dash-top.dash-compact.dash-sticky-on {
  box-shadow: 0 80px 0 0 var(--sys-bg);
}
[data-theme="dark"] .dash-top.dash-compact.dash-sticky-on {
  box-shadow: 0 80px 0 0 var(--ref-gray-950);
}
@media(prefers-color-scheme:dark){
  [data-theme="system"] .dash-top.dash-compact.dash-sticky-on {
    box-shadow: 0 80px 0 0 var(--ref-gray-950);
  }
}
[data-theme="sunlight"] .dash-top.dash-compact.dash-sticky-on {
  box-shadow: 0 80px 0 0 #FFFFFF;
}
.dash-top.dash-compact .dash-search-trigger {
  padding: 8px var(--ref-space-3);
  min-height: 36px;
  font-size: 14px;
  border-radius: var(--ref-radius-sm);
}
.dash-top.dash-compact .greeting-row {
  display: none;
}

/* ── Kiosk / Demo Mode (v2.21.0) ─────────────────────────────────────────
   A persistent, unobtrusive top banner shown only while the device is in
   demo mode (running as the shared General account). It signals the safe
   state and offers the PIN-gated exit. Pinned to the top respecting the
   safe-area inset; pushes nothing else since it overlays a thin strip. */
#ts-kiosk-banner{
  position:fixed;top:0;left:0;right:0;z-index:99998;
  display:flex;align-items:center;justify-content:space-between;gap:var(--ref-space-3);
  padding:calc(env(safe-area-inset-top,0px) + 6px) var(--ref-space-4) 6px;
  background:linear-gradient(135deg,#0f766e 0%,#0d9488 100%);
  color:#fff;font-size:var(--ref-font-xs);font-weight:var(--sys-font-wt-sb);
  box-shadow:0 2px 10px rgba(13,148,136,.35);
}
#ts-kiosk-banner .ts-kiosk-banner-label{display:flex;align-items:center;gap:6px;line-height:1}
#ts-kiosk-banner .ts-kiosk-banner-label i,
#ts-kiosk-banner .ts-kiosk-banner-label svg{width:15px;height:15px}
#ts-kiosk-banner .ts-kiosk-banner-exit{
  background:rgba(255,255,255,.18);color:#fff;border:1px solid rgba(255,255,255,.35);
  border-radius:var(--ref-radius-md);padding:3px 12px;font-size:var(--ref-font-xs);
  font-weight:var(--sys-font-wt-sb);cursor:pointer;white-space:nowrap;
}
#ts-kiosk-banner .ts-kiosk-banner-exit:active{background:rgba(255,255,255,.3)}
/* When the banner is present, nudge the app content down so the greeting
   is not hidden under the strip. */
body:has(#ts-kiosk-banner) #view-main{padding-top:calc(env(safe-area-inset-top,0px) + 44px)}
/* The settings "Exit Demo Mode" button gets the same teal accent so it reads
   as the deliberate way out rather than a generic outline button. */
.ts-kiosk-exit-btn{border-color:#0d9488 !important;color:#0d9488 !important}

/* ================================================================
 *  v2.21.3: LEADS ACTION TILE
 *  "You have N leads to contact" — surfaced at the top of the
 *  dashboard for a salesperson from the unified action-items feed.
 *  Visual language matches .dash-search-trigger / dock tiles: a
 *  raised surface card, rounded-lg, 48px+ tap target, subtle hover
 *  shadow, scale-on-press. The left accent rail is driven by the
 *  --leads-tile-accent custom property set inline from the server's
 *  item color, with urgency modifiers for emphasis.
 * ================================================================ */
.leads-tile{margin-top:var(--ref-space-2);margin-bottom:var(--ref-space-3)}
.leads-tile-btn{
  display:flex;align-items:center;gap:var(--ref-space-3);
  width:100%;padding:var(--ref-space-3) var(--ref-space-4);
  background:var(--sys-surface-raised,var(--sys-surface));
  border:1.5px solid var(--sys-border);
  border-left:4px solid var(--leads-tile-accent,#22C55E);
  border-radius:var(--ref-radius-lg);min-height:56px;
  color:var(--sys-text);text-align:left;cursor:pointer;
  touch-action:manipulation;
  transition:border-color var(--ref-dur-fast) var(--ref-ease),
    box-shadow var(--ref-dur-fast) var(--ref-ease),
    transform var(--ref-dur-fast) var(--ref-ease)}
.leads-tile-btn:hover{border-color:var(--leads-tile-accent,#22C55E);box-shadow:var(--sys-shadow-sm)}
.leads-tile-btn:active{transform:scale(.99)}
.leads-tile-btn:focus-visible{box-shadow:var(--sys-focus)}
.leads-tile-icon{
  flex-shrink:0;display:flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:var(--ref-radius-md);
  color:var(--leads-tile-accent,#22C55E);
  background:color-mix(in srgb,var(--leads-tile-accent,#22C55E) 14%,transparent)}
.leads-tile-icon svg{width:22px;height:22px}
.leads-tile-body{flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--ref-space-2)}
.leads-tile-count{
  font-size:var(--ref-font-xl);font-weight:var(--sys-font-wt-b,700);
  color:var(--leads-tile-accent,#22C55E);line-height:1;flex-shrink:0}
.leads-tile-label{
  font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-md,500);
  color:var(--sys-text-sec);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.leads-tile-cta{
  flex-shrink:0;font-size:var(--ref-font-sm);font-weight:var(--sys-font-wt-sb,600);
  color:var(--leads-tile-accent,#22C55E);white-space:nowrap}
/* Urgency modifiers: high gets a soft tinted field + heavier weight. */
.leads-tile--high .leads-tile-btn,.leads-tile-btn.leads-tile--high{
  background:color-mix(in srgb,var(--leads-tile-accent,#22C55E) 8%,var(--sys-surface-raised,var(--sys-surface)))}
@media (prefers-reduced-motion:reduce){
  .leads-tile-btn{transition:none}
  .leads-tile-btn:active{transform:none}}

/* v2.21.5: Inline orchestrator answer card (dashboard "ask" field) */
#ts-inline-answer{margin:var(--ref-space-2,8px) 0 var(--ref-space-1,4px)}
.ts-inline-card{background:var(--sys-surface,#fff);border:1px solid var(--sys-border,#E2E8F0);
  border-left:3px solid var(--sys-brand,#6366F1);border-radius:var(--ref-radius-md,10px);
  padding:var(--ref-space-3,12px) var(--ref-space-4,16px);box-shadow:var(--sys-shadow-sm,0 1px 2px rgba(0,0,0,.06));
  font-size:var(--ref-font-sm,15px);color:var(--sys-text,#0F172A)}
.ts-inline-loading{color:var(--sys-text-ter,#94A3B8);font-style:italic}
.ts-inline-name{font-weight:var(--sys-font-wt-sb,600);font-size:var(--ref-font-base,16px);margin-bottom:var(--ref-space-2,8px)}
.ts-inline-co{font-weight:400;color:var(--sys-text-sec,#64748B)}
.ts-inline-row{display:flex;align-items:center;gap:8px;min-height:36px;padding:4px 0;
  color:var(--sys-text,#0F172A);text-decoration:none;line-height:1.35;word-break:break-word}
a.ts-inline-row{color:var(--sys-brand,#6366F1)}
.ts-inline-ic{flex:0 0 auto;width:20px;text-align:center}
.ts-inline-msg{line-height:1.45}
.ts-inline-sub{margin-top:6px;color:var(--sys-text-sec,#64748B)}
.ts-inline-note{margin-top:6px;font-size:var(--ref-font-xs,13px);color:var(--sys-text-ter,#94A3B8)}
.ts-inline-chat{margin-top:var(--ref-space-3,12px);min-height:36px;padding:6px 14px;
  font-size:var(--ref-font-sm,14px);font-weight:600;color:var(--sys-brand,#6366F1);
  background:transparent;border:1px solid var(--sys-brand,#6366F1);border-radius:var(--ref-radius-sm,8px);
  cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
.ts-inline-chat:hover{color:#fff;background:var(--sys-brand,#6366F1)}

/* ============================================================
   v2.23.0: INLINE ASK FIELD + RESULTS DROPDOWN
   Replaces the pop-up command-palette overlay. The field sits inline in
   #dash-top; #cmd-results drops down beneath it (absolute, so it overlays the
   dashboard tiles instead of pushing them down). Reuses .cmd-item/.ci-* styles.
   ============================================================ */
.dash-ask { position:relative; }
.dash-ask-field {
  display:flex; align-items:center; gap:var(--ref-space-2);
  width:100%; box-sizing:border-box;
  padding:12px 16px; min-height:48px;
  background:var(--sys-bg-alt, var(--sys-surface)); color:var(--sys-text);
  border:1px solid var(--sys-border); border-radius:var(--ref-radius-md);
  transition:border-color 150ms, box-shadow 150ms;
}
.dash-ask-field:focus-within { border-color:var(--sys-brand); box-shadow:var(--sys-shadow-sm); }
.dash-ask-field svg { color:var(--sys-text-ter); flex-shrink:0; }
.dash-ask-field input {
  flex:1; min-width:0; border:none; outline:none; background:none;
  font-family:inherit; font-size:var(--ref-font-base); color:var(--sys-text);
  user-select:text;
}
.dash-ask-field input::placeholder { color:var(--sys-text-ter); }
.dash-ask-field input:focus-visible { box-shadow:none; } /* the wrap shows focus */

/* Results dropdown — hidden until JS adds .show */
.dash-ask .cmd-results {
  display:none;
  position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:200;
  max-height:min(60vh, 460px); overflow-y:auto;
  background:var(--sys-surface); border:1px solid var(--sys-border);
  border-radius:var(--ref-radius-md); box-shadow:var(--sys-shadow-xl);
  padding:var(--ref-space-1);
  -webkit-overflow-scrolling:touch;
}
.dash-ask .cmd-results.show { display:block; }
