/* ============================================================================
   sadaa.css — the Reception design system, authored.

   This is the file DESIGN.md specifies. It is hand-written CSS plus custom
   properties: no build step, no bundler, no framework, no external request.

   MIGRATION CONTRACT (DESIGN.md > Styling foundation, step 3-4)
   -------------------------------------------------------------------------
   Tailwind is NOT removed. It is stopped. `vendor/tailwind.css` still ships
   and every unmigrated surface keeps working exactly as it did. This file
   adds the token layer and the components for surfaces that HAVE migrated,
   one at a time, and `vendor/tailwind.css` is deleted only when the
   class-contract allowlist reaches zero.

   Load order matters and is fixed in every page head:
       vendor/fonts.css  ->  vendor/tailwind.css  ->  tokens.css  ->  sadaa.css

   CASCADE NOTE, and it is the reason this file is short rather than defensive.
   `vendor/tailwind.css` is Tailwind v4 and puts everything inside
   `@layer theme, base, components, utilities`. Unlayered CSS beats layered CSS
   outright, whatever the specificity. So every rule in this file already wins
   against every Tailwind utility, and none of them needs `!important` or an
   inflated selector. The corollary is the discipline this file follows:
   NOTHING here may be broader than a single `.sd-*` class or a `:root` custom
   property, because a bare element selector would silently reach into the
   twenty surfaces that have not migrated yet.

   SPECIFICITY NOTE. Every element reset is wrapped in `:where()` so it scores
   zero and can never outrank a component. The calibration pass lost an entire
   button palette to `.spec button` at (0,1,1) beating `.sd-btn--primary` at
   (0,1,0), with nothing in the markup or the spec revealing it. That must not
   happen again in shipped code.

   TOKENS.CSS. The Phase 0 token names (`--text`, `--success`, `--danger`,
   `--accent`, ...) are now ALIASES of the Reception tokens below, so there is
   one palette. `.sd-badge` and `.sd-focus` are still authored there, on
   Reception values; `.sd-card` is authored here only (CARD).
   ========================================================================= */

/* ==========================================================================
   TOKENS — the BRIDGE (Wave 6-Lite, 2026-11-02).

   The values now come from the console prototype, copied by script into
   `echo-tokens.css` (tools/console-copy/sync-tokens.cjs; loaded right before
   this file): the warm-grey ground, white cards, warm ink, Ember for the one
   main action, Manrope + Almarai + the Arabic digits, light AND dark.

   This block keeps every name the console already reads and points it at
   those tokens, so the 26 pages that have not been ported yet change colour,
   type and theme without being edited. It defines no colour that
   echo-tokens.css owns (--ground, --surface, --ink, --ink-2, --ok, --danger,
   --line …): a second definition here would silently win the cascade.

   Where the prototype has no token for an old role (a hairline a step
   stronger than --line, the amber warning, a solid red that carries white
   text), the value is written here for both themes, in the two blocks below.

   Dark mode is `:root[data-theme="dark"]`, which `theme-boot.js` sets before
   the first paint. Because almost every name below is a var() of a prototype
   token, it turns dark by itself.
   ========================================================================== */
:root {
  /* ---- ground & surfaces (the prototype's --ground/--surface are used as they are) */
  --surface-sunk:  var(--surface-2);
  --surface-warm:  var(--surface-2);
  --surface-hover: var(--surface-2);

  /* ---- ink: --ink and --ink-2 are the prototype's; the two quieter steps map
     onto its own (both clear AA on white, on the ground and on surface-2). ---- */
  --muted:  var(--ink-2);
  --faint:  var(--ink-3);

  /* ---- rules (non-text) -------------------------------------------------- */
  --rule:     var(--line);
  --rule-2:   #D6D1CA;
  --rule-3:   var(--line-strong);
  --wave-off: var(--rule-2);

  /* ---- the accent: Ember replaces the old teal "echo". As TEXT on light only
     the ink shade is legible (--ember-ink #A84A0C); the bright Ember is a fill
     (the main action) and a signal (the live dot), never small text. ------- */
  --echo:      var(--ember-ink);
  --echo-2:    #8F3E0A;
  --echo-soft: color-mix(in oklab, var(--ember) 15%, var(--surface));
  --echo-line: color-mix(in oklab, var(--ember) 45%, var(--surface));
  --echo-3:    var(--ember);

  /* ---- the primary action: an Ember fill with INK text (never white) ---- */
  --action:       var(--ember-fill);
  --action-hover: var(--ember-hover);
  --action-press: color-mix(in oklab, var(--ember-fill) 86%, var(--ink));
  --on-action:    var(--on-ember);

  /* UI-ROUND-2 (owner, 2026-11-14): a switch that is ON is the light brand orange in light mode
     and white in dark mode, everywhere. The OFF track and the knob are unchanged. */
  --switch-on:    var(--ember);
  /* Owner answer, 14 Nov: in light mode the ON knob has a thin deep-ember ring, 3.9:1 against
     the orange (#7A3200 on #FF8A3D), so the knob reads at 3:1 or more. Dark mode: no ring. */
  --switch-on-ring: #7A3200;

  /* The vendored Tailwind's last brand colour, rebound (see the note at the top
     of tokens.css's history): utilities that read it now paint Ember ink. */
  --color-brand:     var(--ember-ink);
  --color-brand-600: var(--echo-2);

  /* ---- semantic: colour only ever means something ------------------------ */
  --ok-soft: var(--ok-wash);
  --ok-line: color-mix(in oklab, var(--ok) 35%, var(--surface));
  --warn:      #8A5300;
  --warn-soft: #FBF1DE;
  --warn-line: color-mix(in oklab, var(--warn) 35%, var(--surface));
  --crit:      var(--danger-ink);
  --crit-soft: var(--danger-wash);
  --crit-line: color-mix(in oklab, var(--danger) 40%, var(--surface));
  /* A solid red that carries WHITE text (End call). --danger itself is only
     3.9:1 with white, so the solid is the ink shade on light. */
  --crit-solid: var(--danger-ink);
  --info:      var(--tint-slate-fg);
  --info-soft: var(--tint-slate-bg);
  --info-line: color-mix(in oklab, var(--tint-slate-fg) 30%, var(--surface));
  /* The holding shape behind another company's full-colour logo (Integrations): white in
     both themes, because the brands ask for their full-colour marks on white. */
  --brand-tile: #FFFFFF;

  /* ---- elevation: the prototype's card / float / press shadows ---------- */
  --sh-1:   var(--sh-card);
  --sh-2:   var(--sh-float);
  --sh-btn: var(--sh-press);

  /* ---- radius: softer, as the prototype (pills for buttons and chips are set
     on the components themselves). Controls 12, surfaces 20, sheets 28. ---- */
  --r-1: 12px; --r-2: 20px; --r-3: 28px; --r-full: 999px;
  /* The prototype's own card / inset / sheet corners, for ported screens. */
  --r-card: 24px; --r-inset: 18px; --r-sheet: 28px;

  /* ---- type: the prototype's stacks (echo-tokens.css --font-sans / --font-arabic). */
  --f-sans: var(--font-sans);
  --f-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
  --f-ar:   var(--font-arabic);

  /* The OLD size scale stays for the pages that have not been ported (their
     layouts were measured at these sizes). Ported screens use the prototype's
     roles from echo-tokens.css: --text-meta 13 / --text-small 15 / --text-body 17
     / --text-lead 20 / --text-title 24 / --text-head 30. */
  --t-micro: 12px; --t-meta: 13px;  --t-body: 14px; --t-ui: 15px;
  --t-small: 12.5px;
  --t-lead:  16px;   --t-h3:   15.5px; --t-h2:   19px; --t-h1: 24px;
  --t-figure: 30px;  --t-figure-lead: 36px;  --t-display: 40px;
  /* The one label style: sentence case, medium weight, muted. */
  --label-w: 560;

  /* ---- geometry ---------------------------------------------------------- */
  --h-ctl:  36px;   /* every control. WCAG 2.2 2.5.8 floor with margin. */
  --h-row:  48px;
  /* Review fixes B (owner, item 19): narrower than the prototype's 268px (echo-tokens.css). */
  --rail-w: 244px;
  --rail-w-compact: 64px;
  --topbar-h: 64px;
  --tabbar-h: 68px;

  /* ---- motion: the older names, read from motion.css's one set of tokens -- */
  --e: var(--motion-ease-standard);
  --d-1: var(--motion-fast); --d-2: var(--motion-base); --d-3: var(--motion-slow);

  /* ---- layers. ONE scale, so an overlay's place is a decision rather than a
     number picked to beat the last one. Anything a person must act on is above
     the chrome; a confirmation is above the drawer that opened it; a toast is
     above all, because it reports what just happened in whichever layer. */
  --z-sticky: 3;
  --z-menu: 60;
  --z-tabbar: 60;
  --z-nav-scrim: 65;
  --z-nav-sheet: 70;
  --z-drawer-scrim: 72;
  --z-drawer: 74;
  --z-modal: 80;
  --z-skip: 100;
  --z-toast: 1000;

  /* ---- the vendored Tailwind's palette, rebound. A few pages still draw with
     its utilities (`bg-white`, `text-ink-700`, `border-line` …). Its theme
     variables live in `@layer theme` and this file is unlayered, so these win,
     and because each is a var() of a prototype token they turn dark too: white
     becomes the card surface, the ink scale follows the ink. ------------- */
  --color-white:   var(--surface);
  --color-ink-900: var(--ink);
  --color-ink-800: var(--ink);
  --color-ink-700: var(--ink-2);
  --color-ink-600: var(--ink-2);
  --color-ink-500: var(--ink-3);
  --color-ink-400: var(--line-strong);
  --color-ink-300: var(--rule-2);
  --color-ink-200: var(--line);
  --color-ink-100: var(--surface-2);
  --color-ink-50:  var(--surface-2);
  --color-line:    var(--line);
  --color-surface: var(--surface-2);
  --color-ok:      var(--ok);
  --color-warn:    var(--warn);
  --color-danger:  var(--danger-ink);

  /* `.font-display` (a Tailwind utility) reads this; `--font-arabic` is the
     prototype's own (echo-tokens.css). */
  --font-display: var(--font-sans);
}

/* The values the prototype has no token for, in the dark. Everything else in
   the bridge is a var() and follows echo-tokens.css by itself. */
:root[data-theme="dark"] {
  --rule-2:     #45413D;
  --echo-2:     #FFB27D;
  --warn:       #E9B872;
  --warn-soft:  #3A2F1C;
  --crit-solid: #B8322D;
  --switch-on:  #FFFFFF;
  --switch-on-ring: transparent;
}

/* ==========================================================================
   RESET — zero specificity, scoped to the shell. See the SPECIFICITY NOTE.

   READ THIS BEFORE STYLING A BUTTON WITH TAILWIND INSIDE `.sd-app`.
   `:where()` scores zero, so this reset cannot outrank a component by
   SPECIFICITY — which is what the note above is about, and it holds. It says
   nothing about LAYERS, and that is the half that bites: this file is
   unlayered and every Tailwind utility lives in `@layer utilities`, so this
   reset beats `bg-ink-900`, `px-3.5`, `rounded-md`, `border` and `text-white`
   outright on any `<button>` inside the shell, however many utilities are
   stacked on it.

   The consequence is invisible in review and in a diff: the classes are
   present, spelled correctly, and the button renders as bare text on no
   background. It is how "Upload document", "New subscription", the create
   drawer's whole footer and a three-option language picker all rendered as
   unstyled runs of words. `<a>` is NOT affected the same way — its reset only
   takes `color` and `text-decoration` — which is why some link-buttons on the
   same page looked right and made the pattern hard to see.

   So: inside `.sd-app`, a button is styled with `.sd-btn` and its modifiers.
   Never with utilities.
   ========================================================================== */
.sd-app :where(button) {
  font: inherit; color: inherit; background: none; border: 0;
  padding: 0; margin: 0; cursor: pointer; text-align: inherit;
}
.sd-app :where(a) { color: inherit; text-decoration: none; }
.sd-app :where(ul, ol) { list-style: none; margin: 0; padding: 0; }
.sd-app :where(h1, h2, h3, h4, p) { margin: 0; }
.sd-app *, .sd-app *::before, .sd-app *::after { box-sizing: border-box; }

/* ==========================================================================
   ARABIC INSIDE AN LTR SHELL — DESIGN.md > Mixed Arabic and English
   Optical match by `font-size-adjust`, NEVER by a font-size multiplier: the
   Arabic class is more specific than most component rules, so an `em`
   multiplier replaces a declared size instead of adjusting it. That bug
   shipped once and shrank the transcript it was meant to enlarge.
   ========================================================================== */
.sd-ar, .sd-app .font-arabic {
  font-family: var(--f-ar);
  /* Manrope's aspect ratio (x-height 0.54 of the em, measured in Chromium 2026-11-02; Inter's
     was 0.545), so Arabic set beside it in an English page reads at the same size. */
  font-size-adjust: 0.54;
}
/* Every free-text block that can hold customer content. Without plaintext a
   sentence mixing Arabic and Latin puts its terminal punctuation on the wrong
   side. */
.sd-plain { unicode-bidi: plaintext; }

/* ==========================================================================
   LANGUAGE AND THEME — Wave 6-Lite (theme-boot.js, sadaa-i18n.js)
   A page in Arabic (a ported page, the sign-in pages) and the shell's own
   Arabic chrome around an English page take the prototype's Arabic stack.
   Arabic is never letter-spaced and never case-transformed: tracking breaks
   the connected script (the prototype's base rule).
   ========================================================================== */
.sd-app:lang(ar), .sd-shell__col:lang(ar), .sd-sheet-layer:lang(ar) { font-family: var(--font-arabic); }
/* An English page body inside Arabic chrome keeps the Latin stack (and its line metrics). */
.sd-shell__col:lang(en) { font-family: var(--f-sans); }
/* `:lang()` follows the inherited language, so this reaches the Arabic chrome and Arabic islands,
   never the English body of a page that has not been ported (its column says lang="en"). */
.sd-app :lang(ar) {
  letter-spacing: 0 !important; text-transform: none !important;
}
/* An English island inside an Arabic page keeps its own face (a phone number, an email). */
.sd-app:lang(ar) [lang=en], .sd-shell__col:lang(ar) [lang=en] { font-family: var(--f-sans); }
/* Direction-isolated values (SadaaI18n.Ltr / Bdi): phone numbers, URLs, keys and clocks never
   reorder; times, dates and money keep their own script's direction. */
.sd-ltr { unicode-bidi: isolate-override; font-variant-numeric: tabular-nums; }
.sd-bdi { unicode-bidi: isolate; font-variant-numeric: tabular-nums; }
/* The colour scheme of form controls and scrollbars follows the theme. */
:root[data-theme="dark"] { color-scheme: dark; }
.sd-app ::selection { background: var(--selection); }

/* ==========================================================================
   APP SHELL — one grid. The rail is a column; everything else is a row stack
   in the second column. The admin context layer is a ROW OF THIS GRID, which
   is the whole point: it can never be misaligned by a rail width it does not
   know about, and it has a correct position in all three shell states.
   ========================================================================== */
.sd-app {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  height: 100%;
  width: 100%;
  background: var(--ground);
  font-family: var(--f-sans);
  font-size: var(--t-ui);
  line-height: 1.5;
  color: var(--ink-2);
  -webkit-font-smoothing: antialiased;
  font-feature-settings: 'cv11','ss01';
  font-variant-numeric: tabular-nums;
}
.sd-shell__col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  /* UI-SCROLL: clip, not hidden -- `hidden` made the column a scroll container that a focus()
     or a scrollIntoView inside the page could move, sliding the top bar out of the window. */
  overflow: hidden;
  overflow: clip;
}
/* The page's own scrolling region.
   Pages hand the shell a `<main class="flex-1 h-full overflow-y-auto">` — the
   element that used to be a flex SIBLING of the rail and is now a CHILD of the
   shell column. Two things have to be corrected and nothing else:

     `h-full` is height:100%, which inside a column that also holds a top bar
     and a context row overflows by exactly their combined height;
     `flex-1` was sized against a row and must now size against a column.

   These selectors score (0,1,1) and beat both utilities at (0,1,0), so a page
   migrates by changing its wrapper and nothing else.

   Not set on the generic selectors: `overflow`. kb-workspace hands the shell a
   `main.overflow-hidden` because it manages two scroll regions of its own, and
   forcing a value there would break it silently. */
.sd-shell__col > main,
.sd-shell__col > .flex-1,
.sd-shell__col > .sd-shell__body {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
}
/* ONE scrolling model for every product page: the page's `main.sd-page` is
   the scroller. The column above is `overflow: hidden` and `main` is a
   shrinking flex item, so a page that forgot to declare its own overflow was
   not broken-looking — it was CLIPPED. Settings, Usage & plan, Calls, Agents
   and Agent detail all lost everything below the fold at 1366x768, including
   the password form. Before this rule each page opted in (`.sd-ov`, `.sd-vt`,
   `.sd-bookings`), and each new page repeated the defect. Those rules are now
   harmless duplicates. kb-workspace does not use `.sd-page`, so it keeps its
   own scroll regions. */
.sd-shell__col > main.sd-page { overflow-y: auto; }
/* The other half of the same fix, and the fourth time this stylesheet has met
   the trap (after the tab strip, the voice list and the KPI band; their rules
   below say so too): a panel with
   `overflow: hidden` has an automatic minimum height of ZERO, so inside a
   column that cannot fit it, it is CRUSHED and clips its own rows instead of
   giving the page something to scroll. Calls' ledger panel sat at 567px with
   752px of rows at 1366x768. A page section keeps its height; the page
   scrolls. It scores (0,1,0) and sits above every page rule in this file, so
   a section that is MEANT to flex — the voice console body — still says so
   with its own `flex` and wins. */
.sd-shell__col :where(.sd-shell__col > main.sd-page > *) { flex-shrink: 0; }
/* UI-SCROLL (owner 2026-11-11: "scrolling flings the page and leaves the side panel behind").
   The rule above makes `main` THE scroller, but nothing stopped the DOCUMENT from becoming a
   second one behind it. Any absolutely placed piece inside the page whose nearest positioned
   ancestor is above `main` -- every `.sd-sr` screen-reader label in a button, a tooltip, a
   badge -- is placed against the window, not the page: it does not scroll with the page, and
   where it lands below the fold it makes the document taller than the window. Then a wheel or
   a touch fling scrolls `main` to its end and carries on into the document, and the whole
   shell -- the rail (left in English, right in Arabic), the top bar, the page -- slides up
   and away. Home measured 1,115 px of document in a 900 px window at 1440. Booking settings
   met the same thing once and was fixed in its own table head (`.sd-table thead th` below);
   this is the fix for every page at once:
     1. the page's scroller anchors its own absolute descendants (`position: relative`), so
        they scroll with the page and add to ITS height, never the document's;
     2. the app clips anything that still gets out (`overflow: clip`, which, unlike `hidden`,
        does not make `.sd-app` a scroll container that script or focus could move);
     3. a page with the console shell never scrolls its document, and a fling that reaches
        the end of the page stops there (`overscroll-behavior`), on wheel and touch alike.
   Fixed layers (drawers, sheets, the tab bar, toasts) are not affected: they are placed
   against the window by design and `overflow` on an ancestor does not clip them. */
/* (Only the vertical axis: a sideways trackpad or edge swipe keeps the browser's Back.) */
:root:has(.sd-app), :root:has(.sd-app) > body { height: 100%; overflow: hidden; overscroll-behavior-y: none; }
.sd-app { position: relative; overflow: hidden; overflow: clip; }
.sd-shell__col > main { position: relative; overscroll-behavior-y: contain; }
/* The page's few bounded regions (a call's transcript, Try's conversation, the voice list) keep
   their own height so a live call can follow its newest line, but they no longer TRAP the wheel:
   their `overscroll-behavior: contain` is gone, so a wheel or a fling that reaches a region's end
   carries on with the page instead of stopping dead over it. Only layers above the page (sheets,
   dialogs) keep `contain`, so the page behind them stays where it is. */
/* A side panel that stays in view while its page scrolls. One rule for every page (Assistants'
   cards, the call link's preview, Knowledge's side column), and only where it has a column of
   its own. A panel taller than the page's window cannot stay in view whole: stuck to the top,
   its lower part sat below the window until the very end of the page, out of reach. The
   shell measures each one (app-shell.jsx, useStickFit) and lets a panel that does not fit
   scroll with the page like any other card (`data-sd-stick="off"`). */
@media (min-width: 1280px) {
  .sd-stick { position: sticky; inset-block-start: 24px; }
  .sd-stick[data-sd-stick="off"] { position: relative; inset-block-start: auto; }
}

.sd-skip {
  position: absolute; inset-inline-start: 12px; top: 12px; z-index: var(--z-skip);
  transform: translateY(-200%);
  background: var(--ink); color: var(--ground);
  padding: 9px 16px; border-radius: var(--r-full);
  font-size: var(--t-body); font-weight: 700;
}
.sd-skip:focus { transform: translateY(0); }

/* ==========================================================================
   RAIL — Wave 6-Lite: the console prototype's rail (src/shell/Shell.tsx).
   It stands on the ground; the only raised things in it are the business
   card and where you are. Pills, 48px rows, the prototype's type roles.
   Logical properties throughout: in Arabic the whole grid mirrors (the
   `.sd-app` carries the console's `dir`), so the rail sits on the right.
   ========================================================================== */
/* Review fixes B (owner, item 19): the rail never scrolls sideways, and its foot (you, language,
   theme) is always on the screen: on a short window only the destinations scroll. */
.sd-rail {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  overflow: hidden;
  padding: 24px 16px 20px;
  background: var(--ground);
  font-family: var(--f-sans);
}
.sd-rail .sd-nav { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; scrollbar-width: none; }
.sd-rail .sd-nav::-webkit-scrollbar { display: none; }
.sd-rail:lang(ar), .sd-topbar:lang(ar), .sd-tabbar:lang(ar), .sd-sheet-layer:lang(ar) { font-family: var(--font-arabic); }
.sd-brand {
  display: inline-flex; align-items: center; gap: 10px; align-self: flex-start; flex: none;
  min-height: 44px; padding: 0 8px; border-radius: 12px; color: var(--ink);
}
.sd-brand__tag {
  font-size: var(--text-meta); font-weight: 700; color: var(--ink-2);
  background: var(--surface-2); border-radius: var(--r-full); padding: 2px 10px;
}

/* The business card: whose business this is (a client), or the client a staff member is
   viewing, with the way back to every client. */
.sd-biz, .sd-rail__ws--client {
  margin-top: 20px; flex: none; min-width: 0;
  display: flex; align-items: center; gap: 12px;
  padding: 12px; border-radius: 22px;
  background: var(--surface); box-shadow: var(--sh-card);
}
:root[data-theme="dark"] .sd-biz { background: var(--surface-2); }
.sd-biz--staff { display: block; }
.sd-biz__row { display: flex; align-items: center; gap: 12px; min-width: 0; }
.sd-biz__t { display: grid; min-width: 0; flex: 1; }
.sd-biz__name, .sd-rail__wsnm {
  text-align: match-parent;
  display: block; font-size: var(--text-small); line-height: var(--lh-small); font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-biz__meta { display: flex; align-items: center; gap: 6px; font-size: var(--text-meta); color: var(--ink-2); }
.sd-biz__meta i { width: 7px; height: 7px; border-radius: var(--r-full); flex: none; }
.sd-rail__back {
  display: inline-flex; align-items: center; gap: 6px; min-height: 32px;
  margin-bottom: 8px; padding: 0 10px; border-radius: var(--r-full);
  font-size: var(--text-meta); font-weight: 600; color: var(--ink-2);
}
.sd-rail__back:hover { background: var(--surface-2); color: var(--ink); }
.sd-app[dir="rtl"] .sd-flip { transform: scaleX(-1); }
.sd-flip { display: inline-flex; }

/* Monograms on the prototype's low-chroma tints (they turn dark with the theme). */
.sd-mono, .sd-biz__mono {
  display: inline-grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: var(--r-full);
  font-size: .875rem; font-weight: 800; line-height: 1; letter-spacing: 0;
  font-family: var(--f-sans);
}
.sd-mono--slate { background: var(--tint-slate-bg); color: var(--tint-slate-fg); }
.sd-mono--sage  { background: var(--tint-sage-bg);  color: var(--tint-sage-fg); }
.sd-mono--olive { background: var(--tint-olive-bg); color: var(--tint-olive-fg); }
.sd-mono--lilac { background: var(--tint-lilac-bg); color: var(--tint-lilac-fg); }
.sd-mono--rose  { background: var(--tint-rose-bg);  color: var(--tint-rose-fg); }

/* The sections. */
.sd-nav { margin-top: 20px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; align-content: start; }
.sd-nav__group {
  margin: 0; padding: 14px 16px 4px;
  font-size: var(--text-meta); font-weight: 700; color: var(--ink-3);
}
.sd-nav__item {
  display: flex; align-items: center; gap: 12px;
  height: 48px; padding: 0 16px; border-radius: var(--r-full);
  font-size: 1rem; font-weight: 600; color: var(--ink-2);
  transition: transform var(--motion-fast) var(--motion-ease-spring), background-color var(--motion-fast) var(--motion-ease-standard),
    color var(--motion-base) var(--motion-ease-standard), box-shadow var(--motion-fast) var(--motion-ease-standard);
}
.sd-nav__item:lang(ar) { font-weight: 700; }
.sd-nav__item svg { flex: none; }
.sd-nav__item:hover { background: color-mix(in oklab, var(--surface) 70%, transparent); color: var(--ink); }
:root[data-theme="dark"] .sd-nav__item:hover { background: color-mix(in oklab, var(--surface-2) 60%, transparent); }
.sd-nav__item:active:not(.is-soon) { transform: scale(var(--motion-press-scale)); transition-timing-function: var(--motion-ease-out); }
.sd-nav__item.is-current { background: var(--surface); color: var(--ink); box-shadow: var(--sh-card); }
:root[data-theme="dark"] .sd-nav__item.is-current { background: var(--surface-2); }
.sd-nav__item.is-soon { cursor: not-allowed; color: var(--ink-3); }
.sd-nav__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-nav__count { flex: none; font-size: var(--text-meta); font-variant-numeric: tabular-nums; color: var(--ink-2); }

/* The foot: the "new calls paused" line, you, and the language + theme controls. */
/* minmax(0, 1fr): a grid column sizes to its widest content, so a long email used to push the
   language and theme buttons out of the rail. */
.sd-rail__foot { margin-top: auto; padding-top: 16px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; flex: none; }
.sd-live, .sd-live--paused {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: 18px; background: var(--surface-2);
  font-size: var(--text-meta); line-height: var(--lh-meta); font-weight: 600; color: var(--ink-2);
}
.sd-live:lang(ar) { font-weight: 700; }
:root[data-theme="dark"] .sd-live { background: var(--surface); }
.sd-live[href]:hover { color: var(--ink); }
.sd-live__ring { margin-top: 4px; width: 10px; height: 10px; flex: none; border-radius: var(--r-full); border: 1.5px solid var(--ink-3); }

.sd-account-wrap { position: relative; min-width: 0; }
.sd-account {
  display: flex; align-items: center; gap: 10px; width: 100%; min-width: 0;
  padding: 4px; border-radius: 18px; text-align: start;
}
.sd-account:hover { background: color-mix(in oklab, var(--surface) 70%, transparent); }
:root[data-theme="dark"] .sd-account:hover { background: color-mix(in oklab, var(--surface-2) 60%, transparent); }
.sd-account__av { width: 36px; height: 36px; font-size: .8125rem; }
.sd-account__t { flex: 1; min-width: 0; }
.sd-account__name {
  text-align: match-parent;
  display: block; font-size: var(--text-small); line-height: var(--lh-small); font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-account__role {
  display: block; font-size: var(--text-meta); color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sd-tools { display: flex; flex: none; align-items: center; justify-content: flex-end; gap: 2px; }
.sd-tool-wrap { position: relative; }
.sd-tool, .sd-tool--lang {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; padding: 0 12px; border-radius: var(--r-full);
  font-size: var(--text-small); font-weight: 800; color: var(--ink);
  transition: transform var(--motion-fast) var(--motion-ease-spring), background-color var(--motion-fast) var(--motion-ease-standard);
}
.sd-tool:hover { background: var(--surface-2); }
:root[data-theme="dark"] .sd-tool:hover { background: var(--surface-3); }
.sd-tool:active { transform: scale(var(--motion-press-scale)); transition-timing-function: var(--motion-ease-out); }

/* Menus (you, theme): a floating card, 44px rows. */
.sd-menu {
  position: absolute; inset-inline: 0; z-index: var(--z-menu);
  min-width: 220px; padding: 6px;
  background: var(--surface); border-radius: 18px; box-shadow: var(--sh-float);
}
:root[data-theme="dark"] .sd-menu { background: var(--surface-2); }
.sd-menu--up { bottom: calc(100% + 6px); }
.sd-menu--down { top: calc(100% + 6px); }
.sd-menu--tool { inset-inline: auto 0; width: 232px; }
/* The rail clips what leaves it, so its menus fit inside it. */
.sd-rail .sd-menu { min-width: 0; }
.sd-rail .sd-menu--tool { width: 200px; }
.sd-menu__head { padding: 8px 10px 10px; margin-bottom: 4px; border-bottom: 1px solid var(--line); }
.sd-menu__mail { font-size: var(--text-meta); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; text-align: start; }
.sd-menu__ctx { display: flex; flex-direction: column; gap: 3px; margin-top: 6px; min-width: 0; font-size: var(--text-meta); color: var(--ink-2); }
.sd-menu__ws { display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--ink-2); }
.sd-menu__ws svg { flex: none; color: var(--ink-3); }
.sd-menu__ws span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext; }
.sd-menu__sep { height: 1px; background: var(--line); margin: 4px 6px; }
.sd-menu__item {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
  padding: 0 12px; border-radius: 12px; text-align: start;
  font-size: var(--text-small); font-weight: 600; color: var(--ink);
}
.sd-menu__item:lang(ar) { font-weight: 700; }
.sd-menu__item:hover { background: var(--surface-2); }
:root[data-theme="dark"] .sd-menu__item:hover { background: var(--surface-3); }
.sd-menu__item svg { flex: none; color: var(--ink-2); }
.sd-menu__item.is-on { font-weight: 800; }
.sd-menu__check { margin-inline-start: auto; display: inline-flex; color: var(--ember-ink); }
.sd-menu__item--danger, .sd-menu__item--danger svg { color: var(--danger-ink); }
.sd-menu__item--danger:hover { background: var(--danger-wash); }

/* A value that has not arrived yet. Only ever wraps a NAME, never a whole
   region — see AdminContext. */
.sd-wait {
  display: inline-block; height: 11px; border-radius: 3px;
  background: var(--surface-3); vertical-align: -1px;
  animation: sd-shimmer 1.4s var(--e) infinite;
}
@keyframes sd-shimmer { 0%,100% { opacity: 1 } 50% { opacity: .45 } }

/* ==========================================================================
   ADMIN CONTEXT LAYER — DESIGN.md > The admin context layer

   Four audited defects, and what replaces each:
     1. rendered late      -> renders from the tenant id, which resolveTenantId
                              returns synchronously from the URL. Only the NAME
                              waits, and only the name shimmers.
     2. position: fixed;
        left: 232px        -> a row of the shell grid. Nothing computes an
                              offset; no width is hard-coded anywhere.
     3. call detail escaped -> a shell rule, not CSS.
     4. admin actions were
        indistinguishable  -> .sd-adm / .sd-admnote / .sd-admbox below.
   ========================================================================== */
.sd-ctx {
  display: flex; align-items: center; gap: 14px; flex: none;
  padding: 10px 28px; min-height: 52px;
  background: var(--surface-warm);
  border-bottom: 1px solid var(--rule);
  box-shadow: inset 3px 0 0 var(--echo);
}
.sd-ctx__ico {
  width: 30px; height: 30px; border-radius: var(--r-1); flex: none;
  display: grid; place-items: center;
  background: var(--echo-soft); color: var(--echo-2);
  border: 1px solid var(--echo-line);
}
.sd-ctx__t { min-width: 0; flex: 1; }
.sd-ctx__ttl {
  display: block; font-size: var(--t-body); font-weight: 620; color: var(--ink);
  unicode-bidi: plaintext;
}
.sd-ctx__sub { display: block; font-size: var(--t-meta); color: var(--muted); margin-top: 1px; }
.sd-ctx__back { flex: none; }

/* An action only Sadaa staff may take, inside a customer's workspace.
   A restrained administrative signal, NOT a semantic colour. A destructive
   admin action is still red because it is destructive; this composes with it
   rather than replacing it. */
.sd-adm { position: relative; }
.sd-adm::after {
  content: ''; position: absolute; top: -3px; right: -3px;
  width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--echo); border: 1.5px solid var(--surface);
  pointer-events: none;
}
.sd-admnote {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-micro); font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--echo-2);
}
.sd-admnote::before {
  content: ''; width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--echo); flex: none;
}
.sd-admbox {
  border: 1px solid var(--echo-line); border-radius: var(--r-2);
  background: var(--echo-soft); padding: 14px 16px;
}

/* ==========================================================================
   PHONE — a top bar (whose business, language, theme) and a floating bottom
   tab bar for one thumb (the four busiest sections + More). Below 1024px.
   ========================================================================== */
.sd-topbar {
  display: none; align-items: center; gap: 2px; flex: none;
  height: var(--topbar-h); padding: env(safe-area-inset-top, 0px) 12px 0;
  background: var(--ground);
  position: relative; z-index: var(--z-sticky);
}
.sd-topbar__home {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  min-height: 44px; padding-inline-start: 4px; border-radius: 14px; color: var(--ink);
}
.sd-topbar__mark {
  display: grid; place-items: center; flex: none;
  width: 36px; height: 36px; border-radius: 11px;
  background: var(--surface); box-shadow: var(--sh-card); color: var(--ink);
}
:root[data-theme="dark"] .sd-topbar__mark { background: var(--surface-2); }
.sd-topbar__ws {
  font-size: var(--text-body); line-height: var(--lh-body); font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-topbar__live { display: none; padding: 0 16px 8px; flex: none; }
.sd-iconbtn {
  width: var(--h-ctl); height: var(--h-ctl); border-radius: var(--r-full); flex: none;
  display: grid; place-items: center; color: var(--ink-2);
  border: 1px solid transparent;
}
.sd-iconbtn:hover:not([disabled]) { background: var(--surface-sunk); }
.sd-iconbtn:active:not([disabled]) { background: var(--rule); }
/* It had no disabled state, so a close button frozen during a save looked
   exactly as pressable as it did a second earlier. */
.sd-iconbtn[disabled] { color: var(--faint); cursor: not-allowed; background: none; }

.sd-tabbar {
  display: none; position: fixed; z-index: var(--z-tabbar);
  inset-inline: 12px; bottom: max(12px, env(safe-area-inset-bottom, 0px));
  margin: 0 auto; max-width: 560px; height: var(--tabbar-h);
  align-items: stretch; padding: 6px; border-radius: 26px;
  background: var(--surface); box-shadow: var(--sh-float);
}
:root[data-theme="dark"] .sd-tabbar { background: var(--surface-2); }
.sd-tabbar a, .sd-tabbar__more {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  border-radius: 20px; font-size: .75rem; font-weight: 600; color: var(--ink-2);
  transition: transform var(--motion-fast) var(--motion-ease-spring), color var(--motion-base) var(--motion-ease-standard);
}
.sd-tabbar a:active, .sd-tabbar__more:active { transform: scale(var(--motion-press-scale)); transition-timing-function: var(--motion-ease-out); }
.sd-tabbar a.is-current, .sd-tabbar__more.is-current { color: var(--ink); }
.sd-tabbar__ico {
  position: relative; display: grid; place-items: center;
  height: 28px; width: 48px; border-radius: var(--r-full);
  transition: background-color var(--motion-base) var(--motion-ease-standard);
}
.sd-tabbar .is-current .sd-tabbar__ico { background: var(--surface-3); }
.sd-tabbar__t { max-width: 100%; padding: 0 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-tabbar__dot {
  position: absolute; top: 2px; inset-inline-end: 8px;
  width: 8px; height: 8px; border-radius: var(--r-full); background: var(--ember);
}

/* "More": a bottom sheet over a scrim. */
.sd-sheet-layer { position: fixed; inset: 0; z-index: var(--z-nav-sheet); }
.sd-sheet__scrim { position: absolute; inset: 0; background: var(--scrim); }
.sd-sheet {
  position: absolute; inset-inline: 0; bottom: 0; max-height: 88dvh; overflow-y: auto;
  margin: 0 auto; max-width: 640px;
  padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  border-radius: 28px 28px 0 0; background: var(--surface); box-shadow: var(--sh-float);
  animation: sd-sheet-in var(--motion-slow) var(--motion-ease-out) both;
}
:root[data-theme="dark"] .sd-sheet { background: var(--surface-2); }
@keyframes sd-sheet-in { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }
.sd-sheet__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 4px 8px; }
.sd-sheet__title { margin: 0; font-size: var(--text-lead); line-height: var(--lh-lead); font-weight: 800; color: var(--ink); }
.sd-sheet__nav { display: grid; gap: 4px; }
.sd-sheet__item {
  display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 12px;
  border-radius: 18px; font-size: var(--text-body); font-weight: 600; color: var(--ink);
}
.sd-sheet__item:lang(ar) { font-weight: 700; }
.sd-sheet__item:hover, .sd-sheet__item.is-current { background: var(--surface-2); }
:root[data-theme="dark"] .sd-sheet__item:hover, :root[data-theme="dark"] .sd-sheet__item.is-current { background: var(--surface-3); }
.sd-sheet__item.is-soon { color: var(--ink-3); cursor: not-allowed; }
.sd-sheet .sd-biz { margin-top: 16px; box-shadow: none; background: var(--surface-2); }
:root[data-theme="dark"] .sd-sheet .sd-biz { background: var(--surface-3); }
.sd-sheet__foot { margin-top: 12px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }

/* The "Soon" pattern: a feature the console does not have yet shows where the design puts it,
   disabled, with this small badge — never a fake action. (SadaaUI.Soon / SoonButton.) */
.sd-soon, .sd-nav__soon {
  display: inline-flex; align-items: center; flex: none;
  min-height: 22px; padding: 1px 9px; border-radius: var(--r-full);
  border: 1px dashed var(--line-strong);
  font-size: .75rem; line-height: 1.3; font-weight: 700; color: var(--ink-2);
  white-space: nowrap; font-family: inherit;
}
.sd-soonbtn { cursor: not-allowed; opacity: 1; }
.sd-soonbtn[aria-disabled="true"] { color: var(--ink-3); }

/* ==========================================================================
   CONTROLS — only the ones the shell itself ships in this pass.
   ========================================================================== */
.sd-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: var(--h-ctl); padding: 0 16px; border-radius: var(--r-full);
  font-size: var(--t-body); font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
  transition: background var(--motion-fast) var(--motion-ease-standard), border-color var(--motion-fast) var(--motion-ease-standard),
    color var(--motion-fast) var(--motion-ease-standard), transform var(--motion-fast) var(--motion-ease-spring);
}
/* The prototype's pill presses in while held and springs back when let go (motion.css); Arabic
   labels a step bolder. */
.sd-btn:lang(ar) { font-weight: 700; }
.sd-btn:active:not([disabled]):not([aria-disabled="true"]) { transform: scale(var(--motion-press-scale)); transition-timing-function: var(--motion-ease-out); }
/* REDESIGN: the primary action is echo, not ink. One filled button per view,
   in the colour Sadaa owns — which makes "what do I do next" and "this is
   Sadaa" the same answer. `--action` is the one place to change it back. */
.sd-btn--primary   { background: var(--action); color: var(--on-action); box-shadow: var(--sh-btn); }
.sd-btn--primary:hover { background: var(--action-hover); }
.sd-btn--secondary { background: var(--surface); border-color: var(--rule-2); color: var(--ink); box-shadow: var(--sh-btn); }
.sd-btn--secondary:hover { background: var(--surface-hover); border-color: var(--rule-3); }
.sd-btn--ghost     { color: var(--ink-2); }
.sd-btn--ghost:hover { background: var(--surface-hover); color: var(--ink); }
.sd-btn--danger    { background: var(--surface); border-color: var(--crit-line); color: var(--crit); }
.sd-btn--danger:hover { background: var(--crit-soft); }
.sd-btn--sm { height: 30px; padding: 0 11px; font-size: var(--t-meta); }
/* ACTIVE — the pressed half of the state set DESIGN.md > Controls requires
   ("half a set is not a set"). A tone, never a movement: nothing jumps. */
.sd-btn--primary:active:not([disabled]):not([aria-disabled="true"])   { background: var(--action-press); }
.sd-btn--secondary:active:not([disabled]):not([aria-disabled="true"]) { background: var(--rule); }
.sd-btn--ghost:active:not([disabled]):not([aria-disabled="true"])     { background: var(--rule); }
.sd-btn--danger:active:not([disabled]):not([aria-disabled="true"])    { background: var(--crit-soft); border-color: var(--crit); }
.sd-btn[disabled], .sd-btn[aria-disabled="true"] {
  background: var(--surface-sunk); border-color: var(--rule);
  color: var(--faint); cursor: not-allowed; box-shadow: none;
}
/* BUSY is not DISABLED. A button that is working on what was just asked of it
   is disabled only so it cannot be pressed twice; painted in the disabled
   greys, "Saving…" read as "you may not save", and the one control that was
   answering the operator looked like the one that was refusing them. A busy
   button keeps its own tone, slightly quieted, with a progress cursor.

   Recognised two ways, so every existing call site is lifted without an edit:
   `aria-busy="true"` (the shared primitives set it), or a disabled button that
   is carrying the shared spinner, which is how ~20 pages already say "busy". */
.sd-btn[aria-busy="true"],
.sd-btn[disabled]:has(> .sd-spinner) { cursor: progress; }
.sd-btn--primary[aria-busy="true"],
.sd-btn--primary[disabled]:has(> .sd-spinner) {
  background: var(--action); border-color: transparent; color: var(--on-action);
  box-shadow: var(--sh-btn); opacity: .78;
}
.sd-btn--secondary[aria-busy="true"],
.sd-btn--secondary[disabled]:has(> .sd-spinner) {
  background: var(--surface); border-color: var(--rule-2); color: var(--ink-2);
}
.sd-btn--ghost[aria-busy="true"],
.sd-btn--ghost[disabled]:has(> .sd-spinner) {
  background: none; border-color: transparent; color: var(--ink-2);
}
.sd-btn--danger[aria-busy="true"],
.sd-btn--danger[disabled]:has(> .sd-spinner) {
  background: var(--surface); border-color: var(--crit-line); color: var(--crit); opacity: .85;
}
.sd-btn .sd-spinner { flex: none; }

/* REDESIGN: a status is a tinted tag with a dot — two signals, not three.
   The outline is gone: on a row carrying two or three statuses it was the
   border, not the colour, that made the ledger read as a wall of boxes. Still
   Wave 6-Lite: the console prototype draws chips as pills (owner-approved design;
   DESIGN.md's 6px rectangle is retired with the teal). */
.sd-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding: 0 10px 0 9px; border-radius: var(--r-full);
  font-size: var(--t-micro); font-weight: 600; line-height: 1;
  border: 1px solid transparent; white-space: nowrap;
}
.sd-chip::before {
  content: ''; width: 6px; height: 6px; border-radius: var(--r-full);
  background: currentColor; flex: none;
}
.sd-chip--ok      { color: var(--ok);    background: var(--ok-soft); }
.sd-chip--warn    { color: var(--warn);  background: var(--warn-soft); }
.sd-chip--crit    { color: var(--crit);  background: var(--crit-soft); }
.sd-chip--info    { color: var(--info);  background: var(--info-soft); }
.sd-chip--echo    { color: var(--echo-2);background: var(--echo-soft); }
.sd-chip--neutral { color: var(--muted); background: var(--surface-sunk); }
/* A chip that carries its own glyph (the follow-up warning, a spinner) does
   not also get the dot: one mark, not two. */
.sd-chip:has(> svg)::before, .sd-chip:has(> .sd-spinner)::before { display: none; }


/* ==========================================================================
   PAGE HEADER — DESIGN.md > Page header. One per surface.
   ========================================================================== */
/* REDESIGN: the page keeps a reading measure on wide screens. The padding
   grows instead of the content, so the scroller (this element) still spans
   the column and its scrollbar stays at the window's edge. */
.sd-page {
  padding: 28px 36px 44px;
  padding-inline: max(36px, calc((100% - 1320px) / 2));
  display: flex; flex-direction: column; gap: 20px; min-width: 0;
}
.sd-pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.sd-pagehead__t { min-width: 0; }
.sd-h1 {
  font-size: var(--t-h1); font-weight: 800; color: var(--ink);
  letter-spacing: -.03em; line-height: 1.2; unicode-bidi: plaintext;
}
/* The server's total beside a list's title: a quiet tag, not a second title. */
.sd-h1__count {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px;
  border-radius: 6px; background: var(--surface-sunk);
  font-size: var(--t-micro); font-weight: 600; color: var(--ink-2); letter-spacing: 0;
  font-variant-numeric: tabular-nums; margin-left: 10px; vertical-align: 4px;
}
.sd-sub { font-size: var(--t-meta); color: var(--muted); margin-top: 6px; max-width: 72ch; line-height: 1.55; }
.sd-pagehead__actions { display: flex; align-items: center; gap: 9px; flex: none; }

/* ==========================================================================
   PANEL — elevation 1, hairline. No cards inside cards.
   ========================================================================== */
.sd-panel {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-1); overflow: hidden;
  min-width: 0;
}

/* ==========================================================================
   RECORD TABLE — DESIGN.md > Tables and lists.

   The desktop list stays a real <table> with <tbody><tr><td>: six assertions
   in e2e/tests/product-journey.spec.js select on exactly that, and the
   contract is pinned in DESIGN.md > What this design must not break.
   ========================================================================== */
/* UI-SCROLL: a table that scrolls sideways anchors its own screen-reader text, so that text moves
   with the table instead of staying behind on the page. */
.sd-tablewrap { position: relative; overflow-x: auto; }
.sd-table { width: 100%; border-collapse: collapse; }
/* REDESIGN: the head is a quiet line of sentence-case labels on the same white
   as the rows, not a grey band of capitals. The outer cells take the panel's
   20px inset so the first column lines up with the titles above it. */
/* `position: relative` anchors screen-reader text (.sd-sr, which is absolute)
   to its own header cell. Unanchored, it was placed against a box outside the
   page's scroller: it did not scroll with the table, and on Booking settings
   its "Remove" headers stretched the document, so the laptop view had a second
   scroller behind the page. */
.sd-table thead th {
  position: relative;
  text-align: left; padding: 11px 14px; background: var(--surface);
  font-size: var(--t-micro); font-weight: var(--label-w); letter-spacing: 0;
  color: var(--muted);
  border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.sd-table tbody td {
  padding: 0 14px; height: 56px;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-body); color: var(--ink-2); vertical-align: middle;
}
.sd-table thead th:first-child, .sd-table tbody td:first-child { padding-left: 20px; }
.sd-table thead th:last-child, .sd-table tbody td:last-child { padding-right: 20px; }
.sd-table tbody tr:last-child td { border-bottom: 0; }
.sd-table tbody tr.is-clickable { cursor: pointer; }
.sd-table tbody tr.is-clickable:hover td { background: var(--surface-hover); }
/* The ROW is the control, so the row takes the focus ring. A 12px chevron
   cannot be the only keyboard target — WCAG 2.2 2.4.7 and 2.5.8. */
.sd-table tbody tr.is-clickable:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--echo);
}
.sd-table__r { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sd-table__num { font-variant-numeric: tabular-nums; }

/* The caller cell: two lines, neither of which may wrap. A wrapped Egyptian
   phone number reads as two numbers, which is worse than a truncated one. */
.sd-who { display: flex; align-items: center; gap: 11px; min-width: 0; }
.sd-who__t { min-width: 0; flex: 1; }
.sd-who__1, .sd-who__2 {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  unicode-bidi: plaintext; text-align: left;
}
.sd-who__1 { color: var(--ink); font-weight: 600; font-size: var(--t-body); line-height: 1.4; letter-spacing: -.003em; }
.sd-who__2 { font-size: var(--t-meta); color: var(--muted); font-weight: 400; margin-top: 1px; }
.sd-who__av {
  width: 32px; height: 32px; border-radius: var(--r-full); flex: none;
  display: grid; place-items: center; font-size: var(--t-meta); font-weight: 650;
  background: var(--surface-sunk); color: var(--muted);
  border: 0; line-height: 1;
}

/* ==========================================================================
   ABSENCE — DESIGN.md > Status and the vocabulary of absence.
   The em dash is banned as a value. These carry the words that replace it.
   REDESIGN: absence is quieter than presence. A ledger column that is mostly
   "None recorded" should recede so the rows that DID record something are
   the ones the eye finds.
   ========================================================================== */
.sd-absent, .sd-absent--na { font-size: inherit; font-weight: 400; white-space: nowrap; }
.sd-absent { color: var(--muted); }
.sd-absent--na { color: var(--faint); }
.sd-table .sd-absent { color: var(--faint); font-size: var(--t-meta); }

/* ==========================================================================
   AGENT IDENTITY — DESIGN.md > Assistant avatar.

   Three shapes and no fourth, because the shape carries the truth: an
   operator can tell what kind of thing is speaking before reading a word.
   No illustrated humans, no robots, no gradient orbs — fourteen of Sadaa's
   voices are actual named people, and a generic face asserts something false
   about who is talking.

     .sd-av--agent   soft squircle   software is speaking
     .sd-av--person  circle          a recorded human, or a real caller
     .sd-av--studio  rounded tile    a synthesised preset, which has no
                                     initial it could honestly carry

   Nothing is sharp: every corner is generous and every stroke round-capped,
   so the mark reads friendly without pretending to be a face.
   ========================================================================== */
.sd-av {
  --av-size: 38px;
  width: var(--av-size); height: var(--av-size); flex: none;
  display: grid; place-items: center; position: relative;
  font-family: var(--f-sans), "Noto Sans Arabic";
  font-size: var(--t-meta); font-weight: 650; line-height: 1;
  background: var(--surface-sunk); color: var(--ink-2);
  border: 1px solid var(--rule);
}
.sd-av--agent  { border-radius: 14px; }
.sd-av--person { border-radius: var(--r-full); }
.sd-av--studio { border-radius: 13px; }
.sd-av--sm { --av-size: 28px; font-size: var(--t-micro); border-radius: 10px; }
.sd-av--sm.sd-av--person { border-radius: var(--r-full); }
.sd-av--lg { --av-size: 54px; font-size: var(--t-h3); border-radius: 18px; }
.sd-av--lg.sd-av--person { border-radius: var(--r-full); }
.sd-av svg { display: block; }

/* A recorded PERSON is marked; a generated preset is not. This is the one
   place the product distinguishes a real speaker from a synthesised one
   visually, and it is permitted precisely because the distinction is true. */
.sd-av--rec::after {
  content: ""; position: absolute; right: -1px; bottom: -1px;
  width: 9px; height: 9px; border-radius: var(--r-full);
  background: var(--echo); border: 2px solid var(--surface);
}
.sd-av--lg.sd-av--rec::after { width: 12px; height: 12px; }

/* ==========================================================================
   AGENT COLLECTION — DESIGN.md > Agents.

   An agent is one of typically one to four objects. A six-column table for
   one row is the defect; restyling that table would preserve it. Two of
   those columns held long free text truncated into a cell, which is the
   clearest possible sign the table was the wrong container.
   ========================================================================== */
.sd-agents {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  min-width: 0;
}
.sd-agentcard {
  position: relative;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-1);
  display: flex; flex-direction: column; min-width: 0;
  transition: border-color var(--d-1) var(--e), box-shadow var(--d-2) var(--e), transform var(--motion-fast) var(--motion-ease-spring);
}
.sd-agentcard:hover { border-color: var(--rule-2); box-shadow: var(--sh-2); }
.sd-agentcard.is-idle { background: var(--surface-warm); }
.sd-agentcard__top { display: flex; align-items: flex-start; gap: 12px; padding: 15px 15px 0; min-width: 0; }
.sd-agentcard__id { min-width: 0; flex: 1; }
.sd-agentcard__nm {
  display: block; font-size: var(--t-ui); font-weight: 620; color: var(--ink);
  letter-spacing: -.01em; unicode-bidi: plaintext;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The whole card is the target, so the NAME carries the link and stretches
   its hit area over the card. A 12px chevron cannot be the only target —
   WCAG 2.2 2.5.8. Controls inside the card sit above the overlay. */
.sd-agentcard__nm:hover { text-decoration: underline; text-underline-offset: 2px; }
.sd-agentcard__nm::after { content: ""; position: absolute; inset: 0; border-radius: var(--r-2); }
.sd-agentcard__meta {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-top: 5px;
  font-size: var(--t-meta); color: var(--muted);
}
.sd-agentcard__meta i { width: 3px; height: 3px; border-radius: var(--r-full); background: var(--rule-2); flex: none; }
.sd-lang {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px;
  border-radius: 6px; background: var(--surface-sunk); border: 1px solid transparent;
  font-size: var(--t-micro); font-weight: 600; color: var(--ink-2); white-space: nowrap;
}
.sd-agentcard__body { padding: 13px 15px 0; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sd-agentcard__foot {
  margin-top: auto; padding: 13px 15px; display: flex; align-items: center;
  justify-content: space-between; gap: 10px; flex-wrap: wrap;
}
.sd-agentcard__foot, .sd-agentcard__actions, .sd-agentvoice, .sd-agentcard__st { position: relative; z-index: 1; }
.sd-agentcard__actions { display: flex; align-items: center; gap: 7px; }

/* Readiness, from fields the record actually carries. No call counts, no
   success rate, no readiness percentage: none of those exist on AgentRead,
   and a number nobody computed is worse than an absent one. */
.sd-ready { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.sd-ready__i { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-micro); font-weight: 550; }
.sd-ready__i svg { flex: none; }
.sd-ready__i.is-set { color: var(--ok); }
.sd-ready__i.is-unset { color: var(--faint); }

/* The voice, inline and playable, rather than a metadata line. */
.sd-agentvoice {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 8px 12px 8px 8px; border-radius: 10px;
  background: var(--surface-warm); border: 1px solid var(--rule);
}
.sd-agentvoice__t { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.sd-agentvoice__nm {
  font-size: var(--t-meta); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-agentvoice__or { font-size: var(--t-micro); color: var(--muted); }

/* ==========================================================================
   VOICE LIBRARY — DESIGN.md > Voice system.

   Not a form field: a library panel. Gender first, mirroring the backend's
   write-time rule so the two cannot disagree, then two labelled sections
   inside the chosen gender.
   ========================================================================== */
.sd-voicelib { display: flex; flex-direction: column; gap: 11px; min-width: 0; }
.sd-voicelib__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.sd-voicelib__n { font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Segmented control. The track is sunk and the selected segment is a raised
   surface: the elevation language the rest of Reception already uses. */
.sd-seg {
  display: inline-flex; padding: 3px; gap: 3px; flex: none;
  background: var(--surface-sunk); border: 1px solid var(--rule); border-radius: var(--r-full);
}
/* Wave 6-Lite: the prototype's segmented control, pills inside a pill. */
.sd-seg__b {
  height: 30px; padding: 0 14px; border-radius: var(--r-full);
  font-size: var(--t-meta); font-weight: 600; color: var(--muted);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  transition: background var(--d-1) var(--e), color var(--d-2) var(--e), transform var(--motion-fast) var(--motion-ease-spring);
}
.sd-seg__b:hover { color: var(--ink); }
/* The experimental runtime option is loud on purpose: warning colour whether
   or not it is selected, so it can never pass for a preference. */
.sd-seg__b--experimental { color: var(--warn); }
.sd-seg__b--experimental[aria-pressed="true"] { color: var(--warn); background: var(--warn-soft); box-shadow: inset 0 0 0 1px var(--warn-line); }
.sd-seg--runtime { flex-wrap: nowrap; }
.sd-vt__experimental { margin-top: 12px; }
.sd-vt__experimental strong { font-weight: 700; }
.sd-seg__b[aria-pressed="true"], .sd-seg__b[aria-selected="true"],
.sd-seg__b[aria-checked="true"] {
  background: var(--surface); color: var(--ink); box-shadow: var(--sh-btn);
}
.sd-seg__n { font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Bounded scroll: twenty-three cards must never push the form off a phone
   screen. This is what the source comment always intended and what the dead
   `max-h-72` never did. */
.sd-voicescroll {
  /* Same trap as `.sd-tabs`: a scrolling box inside a column flex parent has
     `min-height: auto` resolved to zero and will be crushed rather than
     scrolled. */
  flex: none;
  max-height: 420px; overflow-y: auto;
  border: 1px solid var(--rule); border-radius: var(--r-2);
  background: var(--surface); padding: 0 0 6px;
}
.sd-voice__sect {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-warm); border-bottom: 1px solid var(--rule);
  padding: 8px 12px;
}
.sd-voice__sectt {
  font-size: var(--t-micro); font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-2);
}
.sd-voice__secth { font-size: var(--t-micro); color: var(--muted); margin-top: 2px; }
.sd-voice__list { display: grid; gap: 6px; padding: 8px 8px 2px; grid-template-columns: 1fr; }

.sd-voice {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 8px 10px; border-radius: var(--r-1);
  border: 1px solid var(--rule); background: var(--surface);
  border-left: 2px solid transparent;
  cursor: pointer; transition: background var(--d-1) var(--e), border-color var(--d-1) var(--e);
}
.sd-voice:hover { background: var(--surface-warm); border-color: var(--rule-2); }
/* Selected is a fill, an edge AND a check glyph — never colour alone, and
   never the word "Selected". DESIGN.md > The voice card. */
.sd-voice[aria-checked="true"] {
  background: var(--echo-soft); border-color: var(--echo-line);
  border-left-color: var(--echo);
}
.sd-voice[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
.sd-voice__t { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.sd-voice__nm {
  font-size: var(--t-body); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-voice__or { font-size: var(--t-micro); color: var(--muted); }
.sd-voice__ck { flex: none; display: grid; place-items: center; width: 18px; height: 18px; color: var(--echo); }

/* The preview control. Three unambiguous states and never an empty circle:
   a real triangle at rest, a three-bar meter while playing, a ring while
   loading. All three used to render as nothing, which is exactly what the
   source comment said the control existed to prevent. */
.sd-voice__prev {
  flex: none; width: 32px; height: 32px; border-radius: var(--r-full);
  display: grid; place-items: center;
  border: 1px solid var(--rule-2); background: var(--surface); color: var(--ink-2);
  transition: background var(--d-1) var(--e), color var(--d-1) var(--e), border-color var(--d-1) var(--e);
}
.sd-voice__prev:hover:not([disabled]) { border-color: var(--ink-2); color: var(--ink); }
.sd-voice__prev.is-playing { background: var(--ink); border-color: var(--ink); color: var(--ground); }
.sd-voice__prev[disabled] { color: var(--faint); background: var(--surface-sunk); cursor: not-allowed; }
.sd-voice__prev--sm { width: 28px; height: 28px; }

/* Loading ring. Reduced motion turns the spin off globally; the ring's
   partial stroke still reads as "working" when it is not turning. */
.sd-spin {
  width: 13px; height: 13px; border-radius: var(--r-full);
  border: 2px solid var(--rule-2); border-top-color: var(--ink-2);
  animation: sd-rotate .7s linear infinite;
}
.sd-voice__prev.is-playing .sd-spin { border-color: color-mix(in oklab, var(--ground) 40%, transparent); border-top-color: var(--ground); }
@keyframes sd-rotate { to { transform: rotate(360deg); } }

/* The playing meter. Three bars, so playing is a SHAPE as well as a motion
   and still reads when animation is off. */
.sd-meter { display: flex; align-items: center; gap: 2px; height: 13px; }
.sd-meter i {
  width: 2.5px; border-radius: 2px; background: currentColor;
  animation: sd-meter 900ms var(--e) infinite;
}
.sd-meter i:nth-child(1) { height: 6px;  animation-delay: 0ms; }
.sd-meter i:nth-child(2) { height: 12px; animation-delay: 160ms; }
.sd-meter i:nth-child(3) { height: 8px;  animation-delay: 320ms; }
@keyframes sd-meter { 0%, 100% { transform: scaleY(.55); } 50% { transform: scaleY(1); } }

/* ==========================================================================
   WAVEFORM THUMBNAIL — the real amplitude envelope of the packaged preview.

   Twenty-two bars, drawn from `preview_envelope` on the catalog response.
   It is a MEASUREMENT: every preview says the same sentence, so two shapes
   differ only in delivery. Nothing here generates a shape — a voice with no
   envelope renders no thumbnail, because an invented one would be worth less
   than nothing. DESIGN.md > The voice card.
   ========================================================================== */
.sd-wave { display: flex; align-items: flex-end; gap: 1.5px; flex: none; height: 20px; }
.sd-wave i { width: 2px; border-radius: 1px; background: var(--wave-off); min-height: 2px; }
.sd-wave--thumb { height: 20px; }
.sd-voice[aria-checked="true"] .sd-wave i { background: var(--echo); }
.sd-agentvoice .sd-wave i { background: var(--rule-2); }

/* ==========================================================================
   SECTION TABS — Agent detail. Scrolls inside itself below 900px rather than
   pushing the page wide. DESIGN.md > Responsive rules.
   ========================================================================== */
.sd-tabs {
  display: flex; gap: 2px; min-width: 0;
  border-bottom: 1px solid var(--rule);
  overflow-x: auto; scrollbar-width: thin;
  /* `flex: none` is load-bearing, and it was found by rendering at 390px
     rather than by reasoning. The strip is a flex item of `.sd-page`, a column
     that runs out of vertical room as soon as a section is taller than the
     viewport. `overflow-x: auto` resolves this item's `min-height: auto` to
     ZERO, so the column happily shrank the whole tab strip to its 1px border
     and the six sections became unreachable — while every child still
     reported itself 44px tall and "visible". Any scrolling box inside a
     column flex parent has this shape; see `.sd-voicescroll`. */
  flex: none;
}
.sd-tabs__b {
  height: 38px; padding: 0 13px; flex: none;
  font-size: var(--t-body); font-weight: 550; color: var(--muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  transition: color var(--d-2) var(--e), border-color var(--d-1) var(--e), transform var(--motion-fast) var(--motion-ease-spring);
}
.sd-tabs__b:hover { color: var(--ink); }
.sd-tabs__b[aria-selected="true"] { color: var(--ink); font-weight: 620; border-bottom-color: var(--echo); }
.sd-tabs__b svg { color: var(--faint); }
.sd-tabs__b[aria-selected="true"] svg { color: var(--echo); }

/* ==========================================================================
   CARD — a titled region inside a page. Not a card inside a card: the panel
   IS the card, and this is its head and its body.
   ========================================================================== */
/* REDESIGN: the head is typography, not a band. A rule under every card title
   was the second most frequent line on every page after the table rows; the
   title and its spacing already say where the head ends. */
/* Wave 6-Lite: the prototype's card — white (or the dark surface), soft shadow, no hairline. */
.sd-card { background: var(--surface); border: 1px solid transparent; border-radius: var(--r-2); box-shadow: var(--sh-1); min-width: 0; }
.sd-card__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; padding: 18px 20px 12px; flex-wrap: wrap;
}
.sd-card__title { font-size: var(--t-h3); font-weight: 620; color: var(--ink); letter-spacing: -.012em; line-height: 1.35; }
.sd-card__note { font-size: var(--t-meta); color: var(--muted); margin-top: 3px; max-width: 64ch; line-height: 1.5; }
.sd-card__body { padding: 4px 20px 20px; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* A card with no head starts its body at the card's own edge. */
.sd-card > .sd-card__body:first-child { padding-top: 18px; }
.sd-card__ctl { display: flex; align-items: center; gap: 8px; flex: none; }

/* The Phase 0 `SadaaKit.Card` takes its children directly rather than a
   `__head` / `__body` pair, so its header is a row inside the card's own
   padding rather than a bordered band. Same typography as `.sd-card__head`,
   without the rule that would sit in the wrong place here. */
.sd-kithead {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; min-width: 0;
}


/* ==========================================================================
   READ-ONLY AS A READING EXPERIENCE — DESIGN.md > Agent detail.

   The tenant sees VALUES, at reading size, in the script they were written
   in — not twenty greyed-out inputs. The sentence "Sadaa configures your
   agent" is said once, under the page title, and never again per field.
   ========================================================================== */
.sd-facts { display: grid; gap: 18px 20px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); min-width: 0; }
.sd-facts--one { grid-template-columns: 1fr; }
.sd-ro { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
/* REDESIGN: one label style for the whole product — sentence case, medium
   weight, muted. Capitals and tracking were doing the work that contrast and
   weight now do, and on every page they made a form of what is a reading. */
.sd-ro__k {
  font-size: var(--t-micro); font-weight: var(--label-w); letter-spacing: 0;
  color: var(--muted);
}
.sd-ro__v {
  font-size: 14.5px; color: var(--ink); line-height: 1.5;
  unicode-bidi: plaintext; overflow-wrap: anywhere;
}
.sd-ro__v--mono { font-family: var(--f-mono); font-size: var(--t-meta); }
/* A long prompt is prose. It gets a reading measure and grows with its text:
   the page is the one scroller (2026-10-05) -- a 300px box with its own thin
   scrollbar inside a card read as a second, unfinished page. */
.sd-ro--block .sd-ro__v {
  background: var(--surface-warm); border: 1px solid var(--rule);
  border-radius: var(--r-1); padding: 12px 14px;
  max-width: 74ch;
  white-space: pre-wrap; font-size: var(--t-body); color: var(--ink-2);
}
.sd-ro__v.is-absent { color: var(--faint); }

/* ==========================================================================
   EDITABLE FIELD — view-as only. Same information architecture as the
   read-only view; the values gain an editable state.
   ========================================================================== */
.sd-textarea {
  width: 100%; min-width: 0; max-width: 74ch;
  border: 1px solid var(--rule-2); border-radius: var(--r-1);
  background: var(--surface); color: var(--ink);
  padding: 10px 12px; font-size: var(--t-body); line-height: 1.6;
  font-family: inherit; resize: vertical; unicode-bidi: plaintext;
}
.sd-textarea::placeholder { color: var(--faint); }
.sd-textarea:focus-visible, .sd-input:focus-visible { border-color: var(--echo); }
.sd-input--w { width: 100%; max-width: 340px; }
/* A field holding a credential, an id or a key. The generated password rendered
   in the UI sans, where `l`, `I` and `1` are near-identical and an operator is
   about to copy it by eye or read it down a phone. `.sd-ro__v--mono` could not
   be reused: that styles a read-only VALUE, and this is an input. */
.sd-input--mono { font-family: var(--f-mono); font-size: var(--t-meta); }

/* The browser's own parts inside a field. A number field's spin arrows are
   dropped: they drew a second, grey control inside ours and crowded the narrow
   hours cells, and the arrow keys still step the number. A date or time
   field's picker icon is a button, so it shows a hand and comes up to full
   strength on hover. Its colour follows the theme through `color-scheme`. */
.sd-input[type="number"], .sd-as-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.sd-input[type="number"]::-webkit-inner-spin-button, .sd-input[type="number"]::-webkit-outer-spin-button,
.sd-as-input[type="number"]::-webkit-inner-spin-button, .sd-as-input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none; appearance: none; margin: 0;
}
.sd-input::-webkit-calendar-picker-indicator, .sd-as-input::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.7; }
.sd-input:hover::-webkit-calendar-picker-indicator, .sd-as-input:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* ==========================================================================
   NOTE — one sentence that explains a state or a restriction.
   ========================================================================== */
.sd-note {
  display: flex; align-items: flex-start; gap: 10px; min-width: 0;
  padding: 11px 14px; border-radius: 10px;
  border: 1px solid var(--rule); background: var(--surface-warm);
  font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55;
}
.sd-note svg { flex: none; margin-top: 1px; color: var(--faint); }
.sd-note--warn { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn); }
.sd-note--warn svg { color: var(--warn); }
.sd-note--echo { border-color: var(--echo-line); background: var(--echo-soft); color: var(--echo-2); }
.sd-note--crit { border-color: var(--crit-line); background: var(--crit-soft); color: var(--crit); }
.sd-note--crit svg { color: var(--crit); }
.sd-note--echo svg { color: var(--echo-2); }
.sd-note__t { min-width: 0; }

/* ==========================================================================
   UNSAVED CHANGES — a ROW OF THE SHELL COLUMN, never a fixed bar.

   What this replaces was `position: fixed; left: 232px` — a hard-coded rail
   offset with no correct value in any of the three shell states, and whose
   class was itself dead, so the bar shrink-wrapped against the right edge.
   As a flex row after the scrolling <main> it needs no offset at all: it is
   correct at 1440, 1024 and 390 for the same reason the admin context layer
   is, and it cannot cover content because it is not over content.
   ========================================================================== */
.sd-savebar {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  padding: 12px 36px; background: var(--surface);
  border-top: 1px solid var(--rule-2); box-shadow: 0 -8px 24px -14px color-mix(in oklab, var(--ink) 26%, transparent);
}
.sd-savebar__t { display: inline-flex; align-items: center; gap: 8px; font-size: var(--t-meta); color: var(--ink-2); min-width: 0; }
.sd-savebar__t i { width: 6px; height: 6px; border-radius: var(--r-full); background: var(--echo); flex: none; }
.sd-savebar__a { display: flex; align-items: center; gap: 8px; flex: none; }


/* Announced, not shown. Used for the live region that says which voice is
   auditioning: the meter shows it, and a screen reader needs words. */
/* UI-SCROLL: pinned to the top start corner of whatever it is placed against. Left at its
   place in the flow, one deep in a long list or a wide table stretched the scroller holding it
   (it was the piece that made the document scroll behind the page); at the corner it never
   adds to anyone's scroll size. */
.sd-sr {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   DIAGNOSTICS — internal identifiers, folded away. Never in the Overview.
   ========================================================================== */
.sd-diag { border-top: 1px solid var(--rule); padding-top: 12px; }
.sd-diag > summary {
  font-size: var(--t-meta); font-weight: 550; color: var(--muted);
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 7px;
  /* WCAG 2.2 2.5.8: the disclosure is a target and 20px did not clear 24. */
  min-height: 24px;
}
.sd-diag > summary::-webkit-details-marker { display: none; }
.sd-diag > summary:hover { color: var(--ink); }
.sd-diag__rows { margin-top: 10px; display: flex; flex-direction: column; gap: 7px; }
.sd-diag__row { display: flex; align-items: baseline; gap: 10px; font-size: var(--t-micro); min-width: 0; flex-wrap: wrap; }
.sd-diag__k { color: var(--muted); min-width: 128px; flex: none; }
.sd-diag__v { font-family: var(--f-mono); color: var(--ink-2); overflow-wrap: anywhere; min-width: 0; }

/* ==========================================================================
   NARROW RECORD LIST — replaces the table below 900px.
   ========================================================================== */
.sd-records { display: none; }
.sd-records li {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 16px; border-bottom: 1px solid var(--rule);
  min-height: 68px;
}
.sd-records li:last-child { border-bottom: 0; }
.sd-records li.is-clickable { cursor: pointer; }
.sd-records li.is-clickable:hover { background: var(--surface-hover); }
.sd-records li.is-clickable:focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px var(--echo);
}
.sd-records__t { flex: 1; min-width: 0; }
/* Wraps rather than crushing the caller: with a status chip AND a follow-up
   chip on one line, a 390px row was truncating the phone number to `+201…`.
   The chips take a second line before the identity gives up any characters. */
.sd-records__l1 { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.sd-records__who {
  font-size: var(--t-ui); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  unicode-bidi: plaintext; min-width: 11ch; flex: 1 1 auto;
}
/* REDESIGN: the second line wraps rather than truncating. "Duration
   unavailable" cut to "Duration unavaila…" said less than two lines do. */
.sd-records__l2 {
  display: block; font-size: var(--t-meta); color: var(--muted); margin-top: 4px;
  line-height: 1.45; overflow-wrap: anywhere;
}
.sd-records__go { flex: none; color: var(--faint); }

/* ==========================================================================
   FILTER BAR — one pattern. Admin carries more axes than tenant.
   ========================================================================== */
.sd-filters { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; min-width: 0; }
.sd-field { display: inline-flex; flex-direction: column; gap: 5px; min-width: 0; }
.sd-field__label {
  font-size: var(--t-micro); font-weight: var(--label-w); letter-spacing: 0;
  color: var(--muted);
}
.sd-sel, .sd-input {
  height: var(--h-ctl); padding: 0 11px;
  background: var(--surface); border: 1px solid var(--rule-2);
  border-radius: var(--r-1); font-size: var(--t-body); color: var(--ink);
  min-width: 0; font-family: inherit;
}
.sd-input::placeholder { color: var(--faint); }
.sd-sel:focus-visible, .sd-input:focus-visible {
  outline: none; border-color: var(--echo);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus) 22%, transparent);
}
/* THE REST OF THE FIELD STATE SET — DESIGN.md > Controls: "error where it can
   be invalid". Before this a field had rest and focus and nothing else: an
   invalid value was announced by a sentence somewhere below it while the
   field itself looked fine, and a disabled field was indistinguishable from
   an empty one. The error is carried by `aria-invalid`, which is also what a
   screen reader reads, so the colour and the announcement cannot disagree. */
.sd-input:hover:not(:disabled):not(:focus-visible):not([aria-invalid="true"]),
.sd-sel:hover:not(:disabled):not(:focus-visible):not([aria-invalid="true"]),
.sd-textarea:hover:not(:disabled):not(:focus-visible):not([aria-invalid="true"]) {
  border-color: var(--rule-3);
}
.sd-input[aria-invalid="true"], .sd-sel[aria-invalid="true"], .sd-textarea[aria-invalid="true"] {
  border-color: var(--crit);
}
.sd-input:disabled, .sd-sel:disabled, .sd-textarea:disabled {
  background: var(--surface-sunk); border-color: var(--rule);
  color: var(--muted); cursor: not-allowed; opacity: 1;
}
.sd-input:read-only:not(:disabled):not(select), .sd-textarea:read-only:not(:disabled) {
  background: var(--surface-warm);
}
/* The help line under a field, and the error line that replaces it. Paired
   with `aria-describedby` by the caller. */
.sd-field__help { font-size: var(--t-meta); color: var(--muted); line-height: 1.5; }
/* The search box's width is a custom property the caller may set, not an
   inline width: an inline width beat the phone rule that makes it full-width,
   and search was the one narrow field in a column of full-width ones. */
.sd-search { position: relative; display: inline-flex; align-items: center; min-width: 0; width: var(--search-w, auto); }
.sd-search__ico { position: absolute; left: 11px; color: var(--faint); pointer-events: none; display: flex; }
.sd-search .sd-input { padding-left: 33px; width: 100%; }
.sd-fcount { font-size: var(--t-meta); color: var(--muted); }
/* The one field in a filter row that should absorb the leftover width -- a
   search box, always. Authored rather than written as `flex-1 min-w-[200px]`,
   which is an arbitrary-value utility the frozen Tailwind build does not carry
   and which therefore rendered nothing at all. */
.sd-filters__grow { flex: 1 1 220px; min-width: 200px; }
.sd-btnrow { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
/* Chips inside a table cell stay on one line — see .sd-absent above for why. */
.sd-chips { display: flex; align-items: center; gap: 7px; flex-wrap: nowrap; }

/* ==========================================================================
   PAGINATION — backend-authoritative. The count in a header is the server's
   own total or it is not shown.
   ========================================================================== */
.sd-pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding: 12px 20px; border-top: 1px solid var(--rule);
  background: var(--surface-warm); flex-wrap: wrap;
}
.sd-pager__t { font-size: var(--t-meta); color: var(--muted); }
.sd-pager__t b { color: var(--ink); font-weight: 620; font-variant-numeric: tabular-nums; }
.sd-pages { display: inline-flex; align-items: center; gap: 6px; }
.sd-pages__n { font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   LEDGER — a record list as ONE surface. REDESIGN 2026-09.
   Toolbar, a one-line note on what the columns mean, the rows, the pager: a
   single white panel on the canvas, so the filters read as the list's own
   controls rather than a second strip of boxes above it.
   ========================================================================== */
.sd-ledger { display: flex; flex-direction: column; }
.sd-ledger__bar {
  padding: 14px 20px 15px; border-bottom: 1px solid var(--rule); background: var(--surface);
}
/* On a wide screen the folded group is not a box at all: its fields flow in
   the toolbar's own row. The toggle exists only on a phone. */
.sd-ledger__more { display: contents; }
.sd-ledger__toggle { display: none; }
@media (max-width: 639px) {
  .sd-ledger__bar { padding: 12px 16px; }
  .sd-ledger__bar > .sd-field:first-child { flex: 1 1 0; width: auto; }
  .sd-ledger__bar > .sd-ledger__toggle { display: inline-flex; align-self: flex-end; height: 44px; flex: none; }
  .sd-ledger__more { display: none; }
  .sd-ledger__more.is-open { display: flex; flex-direction: column; gap: 10px; width: 100%; }
  /* Every field in the phone toolbar clears the 44px touch floor, and the
     search box matches the Filters button beside it. */
  .sd-ledger__bar .sd-input, .sd-ledger__bar .sd-sel { height: 44px; }
  .sd-ledger__note { padding: 9px 16px; }
}
.sd-ledger__note {
  display: flex; align-items: flex-start; gap: 8px; margin: 0;
  padding: 9px 20px; border-bottom: 1px solid var(--rule); background: var(--surface-warm);
  font-size: var(--t-small); color: var(--muted); line-height: 1.5;
}
.sd-ledger__note svg { flex: none; margin-top: 2px; color: var(--faint); }
/* "Updated 2:20 PM" beside Refresh. A refresh that failed says so in the
   warning tone, because the list on screen is now older than it looks. */
.sd-updated { font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; }
.sd-updated.is-stale { color: var(--warn); font-weight: 560; }
/* A recording that exists: said in the ink scale, not in a tag. */
.sd-rec { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-meta); color: var(--ink-2); white-space: nowrap; }
.sd-rec svg { color: var(--faint); flex: none; }
/* Call Detail's recording: Play/Pause, seek, time. */
.sd-recplayer { display: flex; align-items: center; gap: 12px; min-width: 0; }
.sd-recplayer audio { display: none; }
.sd-recplayer__seek { flex: 1; min-width: 0; accent-color: var(--echo); cursor: pointer; }
.sd-recplayer__seek:disabled { cursor: default; opacity: .5; }
.sd-recplayer__time {
  flex: none; font-size: var(--t-meta); color: var(--ink-2); font-variant-numeric: tabular-nums;
}
/* When a call started: one line where the column has room, date over time
   where it does not. */
.sd-when { display: inline-flex; gap: 6px; white-space: nowrap; font-size: var(--t-meta); color: var(--ink-2); font-variant-numeric: tabular-nums; }
.sd-when__d { color: var(--ink-2); }
.sd-when__t { color: var(--muted); }
@media (max-width: 1279px) {
  .sd-when { flex-direction: column; align-items: flex-end; gap: 1px; }
}
.sd-table .sd-absent--na { font-size: var(--t-meta); }

/* ==========================================================================
   REGION STATES — loading, empty, failure. DESIGN.md > Empty, error, loading.
   ========================================================================== */
.sd-state { padding: 52px 24px; text-align: center; }
/* The state's mark: a soft tile carrying the glyph. It sits in echo-soft by
   default — an empty region is Sadaa waiting, not an error — and in the
   critical tone only when something failed. */
.sd-state__ico {
  width: 44px; height: 44px; border-radius: 12px; margin: 0 auto 14px;
  display: grid; place-items: center;
  background: var(--surface-sunk); color: var(--muted);
  border: 1px solid transparent;
}
.sd-state__ico--crit { background: var(--crit-soft); color: var(--crit); border-color: transparent; }
.sd-state__ttl { font-size: var(--t-h3); font-weight: 620; color: var(--ink); }
.sd-state__txt { font-size: var(--t-body); color: var(--muted); margin: 6px auto 0; max-width: 52ch; line-height: 1.6; }
.sd-state__code {
  font-family: var(--f-mono); font-size: var(--t-micro); color: var(--muted);
  background: var(--surface-sunk); border: 1px solid var(--rule);
  border-radius: var(--r-1); padding: 2px 7px; display: inline-block; margin-top: 10px;
}
.sd-state__act { margin-top: 16px; display: flex; justify-content: center; gap: 9px; }

/* Loading is a skeleton shaped like the content, never a spinner in the
   middle of a region. */
.sd-skel__row {
  height: 56px; border-bottom: 1px solid var(--rule);
  display: flex; align-items: center; gap: 14px; padding: 0 20px;
}
.sd-skel__row:last-child { border-bottom: 0; }
.sd-skel__bar {
  height: 10px; border-radius: 5px; background: var(--surface-sunk);
  animation: sd-shimmer 1.4s var(--e) infinite;
}

/* The shared state components (ui-states.jsx) render the classes above, so a
   page that authors `.sd-state` itself and a page that calls
   `SadaaUI.EmptyState` look like one product. What they add: */
.sd-state__ico--echo { background: var(--echo-soft); color: var(--echo-2); border-color: transparent; }

/* `SadaaUI.SkeletonRows` — a table-shaped placeholder at the real row height.
   Standing alone it carries its own hairline; inside a panel, card, drawer or
   dialog it drops it, because a bordered box inside a bordered box is the card
   in a card DESIGN.md forbids. */
.sd-skel {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); overflow: hidden; min-width: 0;
}
.sd-panel .sd-skel, .sd-card .sd-skel, .sd-drawer .sd-skel, .sd-modal .sd-skel {
  border: 0; border-radius: 0; background: transparent;
}
.sd-skel__head {
  height: 38px; display: flex; align-items: center; gap: 16px; padding: 0 16px;
  background: var(--surface-sunk); border-bottom: 1px solid var(--rule);
}
.sd-skel__head .sd-skel__bar { background: var(--rule); }
.sd-skel__lead { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }

/* `SadaaUI.PageLoader` — the auth bootstrap. On the ground colour, so the
   hand-over to the shell does not flash white-then-warm. Inside a region it
   is transparent and only as tall as it needs to be; DESIGN.md prefers a
   skeleton there, and the pages that still use this are listed in the
   product-UX review for their owners. */
.sd-pageload {
  width: 100%; height: 100%; min-height: 220px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  background: var(--ground); font-family: var(--f-sans);
}
.sd-card .sd-pageload, .sd-panel .sd-pageload, .sd-page .sd-pageload { background: transparent; height: auto; }
.sd-pageload__t { display: flex; align-items: center; gap: 8px; font-size: var(--t-meta); color: var(--muted); }

/* `SadaaUI.ErrorBoundary` — what replaces a blank white page when a render
   throws. */
.sd-crash {
  min-height: 100%; display: flex; align-items: center; justify-content: center;
  padding: 24px 16px; background: var(--ground); font-family: var(--f-sans);
}
.sd-crash .sd-state {
  width: 100%; max-width: 520px; background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--r-2); box-shadow: var(--sh-1);
}

/* `SadaaUI.StaleNotice` — data kept, refresh failed. One line, centred on its
   own retry. */
.sd-stale { align-items: center; }
.sd-stale .sd-note__t { flex: 1; }
.sd-stale__retry { margin: -5px -6px -5px 0; color: var(--warn); }
.sd-stale__retry:hover:not([disabled]) { background: color-mix(in oklab, var(--warn) 10%, transparent); color: var(--warn); }

/* NO SIDEWAYS JUMP WHEN DATA ARRIVES. A page's `main` is its own scroller
   (APP SHELL above). While it holds a skeleton or an empty state it does not
   scroll; the moment a real list lands it does, a classic scrollbar takes
   15px (measured, Chrome on Windows, 1440 wide) and every header, filter and
   column shifts left under the operator's eye. Reserving the gutter makes
   loading -> loaded a change of content only. Overlay scrollbars (macOS,
   phones) reserve nothing, so this costs them nothing. */
.sd-shell__col > main.sd-page { scrollbar-gutter: stable; }

/* ==========================================================================
   TOASTS — `window.toast` (ui-states.jsx). DESIGN.md does not specify one;
   this is the product's existing toast brought onto the system: a surface at
   elevation 2 with a hairline, the semantic colour spent on the icon only,
   the ink scale for the words. The host lives on <body>, OUTSIDE `.sd-app`,
   so it states its own type, focus ring and reduced-motion behaviour rather
   than inheriting them.
   ========================================================================== */
.sd-toasts {
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
  z-index: var(--z-toast); width: max-content; max-width: calc(100vw - 32px);
  display: flex; flex-direction: column; align-items: center;
  pointer-events: none; font-family: var(--f-sans);
}
.sd-toasts__lane { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.sd-toasts__lane + .sd-toasts__lane:not(:empty) { margin-top: 8px; }
.sd-toast {
  pointer-events: auto; box-sizing: border-box;
  display: flex; align-items: flex-start; gap: 10px;
  width: min(420px, calc(100vw - 32px));
  padding: 10px 8px 10px 12px; border-radius: var(--r-2);
  background: var(--surface); border: 1px solid var(--rule-2); box-shadow: var(--sh-2);
  color: var(--ink); font-size: var(--t-body); font-weight: 500; line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  opacity: 0; transform: translateY(var(--motion-rise));
  transition: opacity var(--motion-slow) var(--motion-ease-in), transform var(--motion-slow) var(--motion-ease-in);
}
.sd-toast.is-in { opacity: 1; transform: none; transition-timing-function: var(--motion-ease-out); }
.sd-toast--error { border-color: var(--crit-line); }
.sd-toast--warn  { border-color: var(--warn-line); }
.sd-toast__ico {
  flex: none; width: 24px; height: 24px; border-radius: var(--r-full);
  display: grid; place-items: center; margin-top: -1px;
}
.sd-toast--success .sd-toast__ico { color: var(--ok);   background: var(--ok-soft); }
.sd-toast--error   .sd-toast__ico { color: var(--crit); background: var(--crit-soft); }
.sd-toast--warn    .sd-toast__ico { color: var(--warn); background: var(--warn-soft); }
.sd-toast--info    .sd-toast__ico { color: var(--info); background: var(--info-soft); }
.sd-toast__t { flex: 1; min-width: 0; padding-top: 2px; overflow-wrap: anywhere; unicode-bidi: plaintext; }
.sd-toast__x {
  flex: none; width: 26px; height: 26px; margin: -1px 0;
  display: grid; place-items: center; padding: 0;
  border: 0; border-radius: var(--r-1); background: none; color: var(--muted); cursor: pointer;
}
.sd-toast__x:hover { background: var(--surface-sunk); color: var(--ink); }
.sd-toast__x:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo); }
@media (max-width: 639px) {
  /* Above the bottom tab bar, not on it. */
  .sd-toasts { bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 12px); }
}
@media (prefers-reduced-motion: reduce) {
  .sd-toast { transition: none; transform: none; }
}
/* FE-FIX-1: "A new version is ready" (api-client.jsx, after a deploy under an open tab). Top
   centre, out of the toasts' way; it stays until Reload or Later. */
.sd-update {
  position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: var(--z-toast); box-sizing: border-box;
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  width: max-content; max-width: calc(100vw - 32px);
  padding: 8px 8px 8px 14px; border-radius: var(--r-2);
  background: var(--surface); border: 1px solid var(--rule-2); box-shadow: var(--sh-2);
  color: var(--ink); font-family: var(--f-sans); font-size: var(--t-body); font-weight: 500; line-height: 1.45;
}
.sd-update[dir="rtl"] { padding: 8px 14px 8px 8px; }
.sd-update__t { min-width: 0; }
.sd-update__go, .sd-update__later {
  min-height: 36px; padding: 0 12px; border-radius: var(--r-1); cursor: pointer;
  font: inherit; font-weight: 600; border: 1px solid var(--rule-2);
}
.sd-update__go { background: var(--ink); color: var(--surface); border-color: var(--ink); }
.sd-update__later { background: none; color: var(--ink); }
.sd-update__go:focus-visible, .sd-update__later:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo); }

/* ==========================================================================
   NARROW — the table gives way to the record list.
   ========================================================================== */
@media (max-width: 899px) {
  .sd-page { padding: 18px 16px 28px; gap: 16px; }
  /* Hidden, NOT removed: six E2E assertions select on `tbody tr`, and the
     desktop-table contract is pinned in DESIGN.md. */
  .sd-tablewrap { display: none; }
  .sd-records { display: block; }
  .sd-pagehead { align-items: flex-start; }
  .sd-pagehead__actions { flex-wrap: wrap; max-width: 100%; }
  .sd-filters > * { flex: 1 1 auto; }
  .sd-field, .sd-search { width: 100%; }
  .sd-sel, .sd-input { width: 100%; }
}
@media (max-width: 639px) {
  .sd-pager { flex-direction: column; align-items: stretch; gap: 10px; }
  .sd-pages { justify-content: center; }
}


/* Pass 3 surfaces at narrow widths. The agent grid is already auto-fit, so it
   collapses on its own; what does NOT collapse on its own is a card head with
   two controls, a save bar with page-level padding, and a voice list whose
   bounded height is most of a phone screen. */
@media (max-width: 899px) {
  .sd-agents { grid-template-columns: 1fr; gap: 11px; }
  .sd-card__head { flex-direction: column; align-items: stretch; padding: 16px 16px 10px; }
  /* In a column, a child's px flex-basis is a HEIGHT: a title block given
     `flex: 1 1 320px` for the row layout stood 320px tall on a phone, with
     its buttons stranded under a blank gap. Stacked, each part is its own
     height. */
  .sd-card > .sd-card__head > * { flex: none; }
  .sd-card__ctl { width: 100%; }
  .sd-card__body { padding: 4px 16px 16px; }
  .sd-card > .sd-card__body:first-child { padding-top: 16px; }
  .sd-savebar { padding: 11px 16px; }
  .sd-voicescroll { max-height: 340px; }
  .sd-facts { grid-template-columns: 1fr; gap: 13px; }
  .sd-diag__k { min-width: 0; }
  /* Every card control clears the 44px touch floor. WCAG 2.2 2.5.8 asks for
     24; 44 is the pointer-target size this product ships on a phone. */
  .sd-agentcard__actions .sd-btn, .sd-savebar__a .sd-btn { min-height: 44px; }
  .sd-voice { padding: 10px; min-height: 56px; }
  .sd-voice__prev { width: 40px; height: 40px; }
  .sd-tabs__b { min-height: 44px; }
}
@media (max-width: 639px) {
  .sd-savebar { flex-direction: column; align-items: stretch; }
  .sd-savebar__a { justify-content: stretch; }
  .sd-savebar__a .sd-btn { flex: 1; }
  .sd-agentcard__foot { flex-direction: column; align-items: stretch; }
  .sd-agentcard__actions .sd-btn { flex: 1; }
}

/* ==========================================================================
   VOICE TEST — DESIGN.md > Implementation pass 4.

   A testing console, not a dashboard. Four things in rank order: who is
   being tested, what the session is doing, the conversation, and what the
   agent shared. The conversation is the largest surface on the page at every
   width, because it is the only one that carries the answer.

   WHAT IS NOT HERE, AND WHY
   -------------------------
   There is no live audio meter. Nothing in the browser is given a real
   amplitude for this session: the page's `level` number was set from the
   status word — 0.8 for speaking, 0.3 for listening — and drawn as a bar
   chart that recomputed itself from `Date.now()` on every render. That is a
   picture of a variable, not of a voice. The static envelope on the voice
   tile is a real measurement of the packaged preview clip and stays; it is
   never animated, because animating it would assert that it is the caller.
   Speaking is reported by the media server and is therefore said in words.
   ========================================================================== */
.sd-vt { overflow-y: auto; }

/* An inline link or link-button inside running prose. Underlined, because a
   colour difference alone is not a link — WCAG 1.4.1. */
.sd-link {
  color: var(--echo-2); font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px; border-radius: 2px;
}
.sd-link:hover { color: var(--echo); }

/* The attached agent audio elements. Present in the DOM because a detached
   track does not play; never seen and never announced. */
.sd-vt__audio { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* ---- session header: the agent as the subject of the page -------------- */
/* REDESIGN 2026-09: the head is the session's stage. Who is being tested on
   the left; on the right, the state (orb, clock, word) and the controls that
   answer it. While a session is open the panel's edge takes the echo tone. */
.sd-vt__head {
  flex: none; display: flex; flex-direction: column; align-items: stretch;
  gap: 0; padding: 0;
  transition: border-color var(--d-2) var(--e), box-shadow var(--d-2) var(--e);
}
.sd-vt__head.is-live { border-color: var(--echo-line); box-shadow: 0 0 0 3px var(--echo-soft), var(--sh-1); }
/* Tier one: who. The voice sits at the right of the name, not under it. */
.sd-vt__id { display: flex; align-items: center; gap: 16px; min-width: 0; padding: 20px 22px; }
.sd-vt__idt {
  min-width: 0; flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 24px; row-gap: 7px; align-items: center;
}
.sd-vt__idt > .sd-vt__nm, .sd-vt__idt > .sd-vt__meta { grid-column: 1; }
.sd-vt__idt > .sd-vt__voice { grid-column: 2; grid-row: 1 / span 2; }
.sd-vt__nm {
  font-size: var(--t-h2); font-weight: 650; color: var(--ink);
  letter-spacing: -.018em; line-height: 1.2; unicode-bidi: plaintext;
}
.sd-vt__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-vt__lang { font-size: var(--t-meta); color: var(--muted); }
.sd-vt__call-id { font-family: var(--f-mono); font-size: var(--t-meta); color: var(--muted); overflow-wrap: anywhere; min-width: 0; }

/* The voice, as an object rather than a metadata line: name, origin, the real
   envelope of its own preview clip, and a play button — auditioning is not
   editing, so it is offered to both roles.

   A LAYOUT HOOK ONLY. `.sd-agentvoice` already carries the fill, the hairline
   and the padding; giving this one a border too made a card inside a card. */
.sd-vt__voice { display: flex; min-width: 0; max-width: 420px; }
.sd-vt__voice .sd-agentvoice { flex: 1; min-width: 0; }

/* ---- the session block ------------------------------------------------- */
/* Tier two: the session. State on the left — orb, clock, the status word —
   and the controls that answer it on the right. */
.sd-vt__session {
  display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
  padding: 16px 22px; border-top: 1px solid var(--rule); background: var(--surface-warm);
  border-radius: 0 0 var(--r-2) var(--r-2);
}
.sd-vt__live { display: flex; align-items: center; gap: 16px; flex: none; }
.sd-vt__session > .sd-vt__ctl { flex: 1 1 auto; }
.sd-vt__ctl > .sd-chip { margin-right: auto; }
.sd-vt__clock { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.sd-vt__clockk { font-size: var(--t-micro); font-weight: var(--label-w); color: var(--muted); }
.sd-vt__timer {
  font-size: var(--t-figure); font-weight: 620; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.02em;
}
.sd-vt__ctl { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }

/* ---- the session orb (voice-test-shared.jsx > SessionOrb) ---------------
   State, not level: a tone per phase and, while waiting or while the agent
   speaks, rings that breathe by opacity. */
.sd-orb {
  position: relative; width: 56px; height: 56px; border-radius: var(--r-full); flex: none;
  display: grid; place-items: center;
  background: var(--surface-sunk); color: var(--faint);
  transition: background var(--d-2) var(--e), color var(--d-2) var(--e);
}
.sd-orb svg { position: relative; z-index: 1; }
.sd-orb__r1, .sd-orb__r2 {
  position: absolute; border-radius: var(--r-full); border: 2px solid currentColor;
  opacity: 0; pointer-events: none;
}
.sd-orb__r1 { inset: -6px; }
.sd-orb__r2 { inset: -13px; border-width: 1.5px; }
.sd-orb.is-wait  { background: var(--info-soft); color: var(--info); }
.sd-orb.is-wait .sd-orb__r1 { animation: sd-breathe 1.8s var(--e) infinite; }
.sd-orb.is-on    { background: var(--echo-soft); color: var(--echo); }
.sd-orb.is-on .sd-orb__r1 { opacity: .22; }
.sd-orb.is-think { background: var(--echo-soft); color: var(--echo); }
.sd-orb.is-think .sd-orb__r1 { animation: sd-breathe 1.2s var(--e) infinite; }
.sd-orb.is-speak { background: var(--action); color: var(--on-action); }
.sd-orb.is-speak .sd-orb__r1 { color: var(--echo-3); animation: sd-breathe 1.6s var(--e) infinite; }
.sd-orb.is-speak .sd-orb__r2 { color: var(--echo-3); animation: sd-breathe 1.6s var(--e) .5s infinite; }
.sd-orb.is-warn  { background: var(--warn-soft); color: var(--warn); }
.sd-orb.is-warn .sd-orb__r1 { animation: sd-breathe 1.4s var(--e) infinite; }
.sd-orb.is-crit  { background: var(--crit-soft); color: var(--crit); }

/* End Call is the only solid red in the product, and it is the only control
   on this page that is taller than the standard 36px. Everything beside it is
   outlined, so the destructive action is distinguishable by weight and by
   fill, not only by hue. */
.sd-vt__end {
  height: 44px; padding: 0 20px; font-size: var(--t-ui); font-weight: 620;
  background: var(--crit-solid); color: #fff; border-color: var(--crit-solid);
  box-shadow: var(--sh-btn);
}
.sd-vt__end:hover { background: color-mix(in oklab, var(--crit-solid) 86%, #000); border-color: color-mix(in oklab, var(--crit-solid) 86%, #000); }
.sd-vt__start { height: 44px; padding: 0 20px; font-size: var(--t-ui); font-weight: 620; }

/* ---- body: conversation, then the actions rail ------------------------- */
.sd-vt__body {
  flex: 1 1 auto; min-height: 0; min-width: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; align-items: stretch;
  /* Below this the transcript stops being readable, so the PAGE gives way
     instead of the conversation. Measured, not guessed: at 1024x800 with an
     error note and the composer up, the log had been squeezed to 185px. */
  min-height: 430px;
}
.sd-vt__conv { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.sd-vt__convh {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--rule);
  background: var(--surface);
}
.sd-vt__convt {
  font-size: var(--t-h3); font-weight: 620; letter-spacing: -.01em; color: var(--ink);
}
.sd-vt__convn { font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }

/* The transcript. A growing flex child that scrolls needs `min-height: 0`
   explicitly: `auto` resolves to the content height and the box grows instead
   of scrolling, which is the same trap `.sd-tabs` fell into in pass 3. */
.sd-vt__log {
  flex: 1 1 auto; min-height: 220px; overflow-y: auto;
  padding: 18px 18px 20px; display: flex; flex-direction: column; gap: 18px;
}
.sd-vt__wrap { position: relative; display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }

/* Jump to latest, so auto-scroll never has to steal a reader's place. */
.sd-vt__jump {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 12px; z-index: 3;
  height: 32px; padding: 0 14px; border-radius: var(--r-full);
  background: var(--ink); color: var(--ground); font-size: var(--t-meta); font-weight: 550;
  box-shadow: var(--sh-2); display: inline-flex; align-items: center; gap: 7px;
}

/* ---- one speaker run, not one avatar per turn -------------------------- */
.sd-run { display: flex; gap: 12px; min-width: 0; }
.sd-run__av { flex: none; }
/* Turns hug their own text. Without this the column stretches every bubble to
   full width and a three-word answer reads as a paragraph. */
.sd-run__t { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
.sd-run__who {
  font-size: var(--t-micro); font-weight: 600; letter-spacing: 0;
  color: var(--muted); unicode-bidi: plaintext;
}
.sd-run--caller { flex-direction: row-reverse; }
.sd-run--caller .sd-run__t { align-items: flex-end; }
.sd-run--caller .sd-run__who { text-align: right; }

/* REDESIGN: Sadaa speaks in echo. The agent's turns sit on the echo tint and
   the other party's on the neutral inset, so a transcript can be skimmed by
   voice before it is read by word. The words above each run are what actually
   say who spoke — the surface is a second channel, never the only one. A turn
   whose speaker was not recorded has neither. */
.sd-turn {
  max-width: 64ch; padding: 10px 14px; border-radius: 14px;
  font-size: var(--t-lead); line-height: 1.6; color: var(--ink);
  unicode-bidi: plaintext; overflow-wrap: anywhere;
}
.sd-run--agent .sd-turn {
  background: var(--echo-soft); border: 1px solid transparent;
  border-top-left-radius: 5px;
}
.sd-run--caller .sd-turn {
  background: var(--surface-sunk); border: 1px solid transparent;
  border-top-right-radius: 5px;
}
.sd-run--agent .sd-run__who { color: var(--echo-2); }

/* ---- composer ---------------------------------------------------------- */
.sd-vt__composer { flex: none; border-top: 1px solid var(--rule); padding: 12px 16px; background: var(--surface); }
.sd-comp { display: flex; align-items: flex-end; gap: 10px; min-width: 0; }
.sd-comp__f { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.sd-comp__b { flex: none; height: 44px; padding: 0 20px; }
.sd-comp__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sd-comp__hint { font-size: var(--t-micro); color: var(--faint); }
.sd-comp__n { font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }
.sd-comp__n.is-over { color: var(--crit); font-weight: 620; }

/* ---- live actions rail ------------------------------------------------- */
.sd-vt__rail { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.sd-acts {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 14px; display: flex; flex-direction: column; gap: 10px;
}
.sd-act {
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--surface); overflow: hidden; min-width: 0;
  /* `overflow: hidden` resolves a flex item's minimum height to zero, and the
     rail is a column that runs out of room: the cards were being crushed and
     cut mid-line instead of the rail scrolling. The same trap as `.sd-tabs`. */
  flex: none;
}
.sd-act__head { display: flex; align-items: flex-start; gap: 9px; padding: 11px 12px; min-width: 0; }
.sd-act__mark { flex: none; width: 18px; height: 18px; display: grid; place-items: center; margin-top: 1px; }
.sd-act__mark--ok { color: var(--ok); }
/* A lookup that completed and came back "no" — a time not available, a schedule
   not configured. Not a failure: the lookup itself worked, and marking it with
   the failure colour would tell an operator the tool broke when it answered. */
.sd-act__mark--warn { color: var(--warn); }
.sd-act__mark--crit { color: var(--crit); }
.sd-act__t { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.sd-act__ttl {
  font-size: var(--t-meta); font-weight: 620; color: var(--ink);
  line-height: 1.4; unicode-bidi: plaintext; overflow-wrap: anywhere;
}
/* The outcome word, never the colour alone. */
.sd-act__st { font-size: var(--t-micro); font-weight: 620; }
.sd-act__st--ok { color: var(--ok); }
.sd-act__st--warn { color: var(--warn); }
.sd-act__st--crit { color: var(--crit); }
.sd-act__meta { font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }
.sd-act__body { padding: 10px 12px 12px; border-top: 1px solid var(--rule); background: var(--surface-warm); }
.sd-act--crit { border-color: var(--crit-line); background: var(--crit-soft); }

/* The shared result itself: a bounded, read-only rendering of the fields the
   worker sent. No payload dump, and nothing that could be mistaken for a
   control — these cards cannot order, reserve, book, cancel or pay. */
.sd-res { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.sd-res__row { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; min-width: 0; }
.sd-res__nm { font-size: var(--t-meta); font-weight: 600; color: var(--ink); unicode-bidi: plaintext; }
.sd-res__i { min-width: 0; display: flex; flex-direction: column; }
.sd-res__line { display: block; }
.sd-comp__x { max-width: 100%; }
/* "You" is a word, not an initial: it needs the smaller size to sit in the
   same 32px circle the rest of the product uses for a caller. */
.sd-run__av .sd-who__av { font-size: var(--t-micro); font-weight: 620; }
.sd-res__d { font-size: var(--t-micro); color: var(--muted); line-height: 1.5; margin-top: 2px; unicode-bidi: plaintext; }
.sd-res__p { font-size: var(--t-meta); font-weight: 650; color: var(--ink); font-variant-numeric: tabular-nums; flex: none; }
.sd-res__tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.sd-res__tag {
  font-size: var(--t-micro); color: var(--muted);
  border: 1px solid var(--rule); border-radius: var(--r-full); padding: 1px 7px;
}
.sd-res__tag--off { color: var(--crit); border-color: var(--crit-line); background: var(--crit-soft); font-weight: 600; }
.sd-res__k {
  font-size: var(--t-micro); font-weight: var(--label-w); letter-spacing: 0;
  color: var(--muted);
}
.sd-res__v { font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55; unicode-bidi: plaintext; }
.sd-res__note { font-size: var(--t-micro); color: var(--faint); line-height: 1.5; }
.sd-res__sep { border-top: 1px solid var(--rule); padding-top: 8px; }

/* ---- the agent picker, when there is more than one agent ---------------
   Uses the shell's own `.sd-field` / `.sd-field__label` / `.sd-sel`. Pass 4
   authored a second `.sd-field` and a `.sd-select` here without noticing the
   originals; being LATER in the sheet, the duplicate overrode the filter
   bar's `inline-flex` on every ledger page. Nothing was dead, so the
   class-contract guard could not see it — a collision is not a gap. */
.sd-vt .sd-sel { min-width: 200px; max-width: 280px; }
.sd-vt .sd-sel[disabled] { background: var(--surface-sunk); color: var(--faint); cursor: not-allowed; }

/* ---- tablet ------------------------------------------------------------ */
@media (max-width: 1179px) {
  .sd-vt__body { grid-template-columns: minmax(0, 1fr) 268px; gap: 14px; }
  .sd-vt__timer { font-size: var(--t-figure); }
}

/* ---- narrow ------------------------------------------------------------ */
/* Below 900px the console stops being a two-column stage and becomes one
   reading column that scrolls: identity, session, conversation, composer,
   then the actions. The transcript keeps its own bounded scroll so the
   composer and End Call never leave the screen. */
@media (max-width: 899px) {
  .sd-vt { overflow-y: auto; }
  .sd-vt__body { display: flex; flex-direction: column; gap: 14px; flex: none; min-height: 0; }
  .sd-vt__conv { flex: none; }
  .sd-vt__wrap { flex: none; }
  .sd-vt__log { flex: none; max-height: 52vh; min-height: 200px; padding: 14px; gap: 15px; }
  .sd-vt__rail { flex: none; }
  /* The rail's cards flow with the page on a phone: a scroller inside the
     page's own scroller cut a card in half at 340px. */
  .sd-acts { flex: none; max-height: none; overflow: visible; }
  .sd-vt__id { padding: 16px; align-items: flex-start; }
  .sd-vt__idt { display: flex; flex-direction: column; align-items: flex-start; }
  .sd-vt__session { padding: 14px 16px 16px; gap: 14px; }
  .sd-vt__timer { font-size: var(--t-h1); }
  .sd-orb { width: 48px; height: 48px; }
  .sd-vt__ctl { width: 100%; }
  .sd-vt__ctl > .sd-btn { flex: 1 1 auto; }
  .sd-vt__ctl > .sd-vt__end, .sd-vt__ctl > .sd-vt__start { flex: 1 1 100%; }
  .sd-vt .sd-pagehead__actions { width: 100%; flex-wrap: wrap; }
  .sd-vt .sd-pagehead__actions > .sd-field { flex: 1 1 100%; }
  .sd-vt .sd-seg { width: 100%; }
  .sd-vt .sd-seg__b { flex: 1; justify-content: center; }
  .sd-turn { max-width: 100%; font-size: var(--t-body); }
  /* Every control the fingers actually use clears 44px at phone size. The
     segmented control is 30px in the voice library, where it sits in a form;
     here it is a primary page control and is scoped up rather than changed
     for every surface that uses it. */
  .sd-vt__ctl .sd-btn { height: 44px; padding: 0 16px; }
  .sd-comp__b, .sd-vt__end, .sd-vt__start { height: 44px; }
  .sd-vt .sd-seg { padding: 4px; }
  .sd-vt .sd-seg__b { height: 44px; padding: 0 16px; }
  .sd-vt .sd-voice__prev { width: 44px; height: 44px; }
  .sd-vt .sd-sel { height: 44px; max-width: 100%; }
  .sd-vt__voice { max-width: 100%; }
  /* The envelope is 62px of fixed width competing with the voice's NAME in a
     280px column, and the name was losing — "Female 5" rendered as "Fe…" with
     "Studio · Default" broken over three lines. On a phone the name and the
     play button are the identity; the shape gives way. */
  .sd-vt__voice .sd-wave { display: none; }
}
/* On a phone the session tier is a two-column grid: the state (orb and clock)
   beside its word on the first row, then the controls, End or Start across
   the full width where the thumb is. `.sd-vt__ctl` steps out of the box model
   (it carries no role) so its chip and buttons can take those cells; every
   query that looks inside it still finds them. */
@media (max-width: 639px) {
  .sd-vt__session { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: center; }
  .sd-vt__session > .sd-vt__ctl { display: contents; }
  .sd-vt__ctl > .sd-chip { justify-self: end; margin-right: 0; }
  .sd-vt__ctl > .sd-btn { width: 100%; }
  .sd-vt__ctl > .sd-vt__end, .sd-vt__ctl > .sd-vt__start { grid-column: 1 / -1; }
}

/* ==========================================================================
   OVERVIEW — DESIGN.md > Charts, and Implementation pass 5.

   The page answers one question: what is happening in this workspace, and is
   it ready. Everything on it is served by `GET /tenants/{id}/overview` and
   `GET /tenants/{id}/usage` today. There is no trend arrow, no sparkline, no
   previous-period figure and no range control, because the overview route
   returns cumulative counters and cannot compute any of them.
   ========================================================================== */
.sd-ov { overflow-y: auto; }

/* ---- the pulse --------------------------------------------------------- */
/* REDESIGN 2026-09. The answer before the figures: who is answering, when the
   last call was, and what — if anything — needs the reader. Built from the same
   payload as everything below it (workspace-overview.jsx > pulseOf). The live
   dot breathes by OPACITY, never scale (DESIGN.md > Motion), and holds still
   under reduced motion. */
.sd-pulse {
  flex: none; min-width: 0; overflow: hidden;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-1);
}
.sd-pulse__top { display: flex; align-items: center; gap: 16px; padding: 20px 22px; flex-wrap: wrap; }
.sd-pulse__dot {
  position: relative; width: 12px; height: 12px; border-radius: var(--r-full); flex: none;
  background: var(--faint); box-shadow: 0 0 0 5px var(--surface-sunk);
  margin: 0 4px 0 2px;
}
.sd-pulse__dot.is-live { background: var(--echo-3); box-shadow: 0 0 0 5px var(--echo-soft); }
.sd-pulse__dot.is-live::after {
  content: ''; position: absolute; inset: -6px; border-radius: var(--r-full);
  border: 2px solid var(--echo-3); opacity: 0;
  animation: sd-breathe 2.8s var(--e) infinite;
}
.sd-pulse__dot.is-warn { background: var(--warn); box-shadow: 0 0 0 5px var(--warn-soft); }
@keyframes sd-breathe { 0% { opacity: .5; } 70%, 100% { opacity: 0; } }
.sd-pulse__t { flex: 1 1 320px; min-width: 0; }
.sd-pulse__ttl {
  font-size: var(--t-h2); font-weight: 640; color: var(--ink);
  letter-spacing: -.018em; line-height: 1.3; unicode-bidi: plaintext;
}
.sd-pulse__facts { font-size: var(--t-meta); color: var(--muted); margin-top: 3px; }
.sd-pulse__act { display: flex; align-items: center; gap: 9px; flex: none; }
.sd-pulse__att {
  border-top: 1px solid var(--rule); background: var(--surface-warm);
  padding: 12px 22px;
}
.sd-pulse__ok {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--t-meta); color: var(--ink-2);
}
.sd-pulse__ok svg { color: var(--ok); flex: none; }
.sd-pulse__list { display: flex; flex-direction: column; gap: 10px; }
.sd-pulse__i {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0;
  font-size: var(--t-meta); color: var(--ink-2); line-height: 1.5;
}
.sd-pulse__it { flex: 1 1 280px; min-width: 0; unicode-bidi: plaintext; }
.sd-pulse__it b { font-weight: 620; color: var(--ink); }
.sd-pulse__go {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  font-weight: 600; color: var(--echo-2); border-radius: 4px;
}
.sd-pulse__go:hover { color: var(--echo); text-decoration: underline; text-underline-offset: 2px; }

/* ---- the KPI band ------------------------------------------------------ */
/* ONE raised surface on the warm ground, not four detached floating cards.
   The lead figure outranks the rest so the row can be scanned rather than
   read, and the only colour in the band is a semantic foot where a real
   state exists. */
.sd-kpis {
  /* THE THIRD TIME THIS TRAP HAS BEEN SPRUNG in this stylesheet — after
     `.sd-tabs` and `.sd-voicescroll`. Any `overflow` other than `visible`
     makes a flex item's automatic minimum size ZERO, so this band sat inside
     the column `.sd-page` at 2px tall with 183px children while the body grid
     took the space. `flex: none` is the fix; the rounded corners need the
     overflow, so the overflow stays. Measured in the browser, not reasoned. */
  flex: none;
  display: grid; grid-template-columns: 1.3fr repeat(4, minmax(0, 1fr));
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-1); overflow: hidden;
}
.sd-kpi { padding: 18px 20px 18px; border-left: 1px solid var(--rule); min-width: 0; }
.sd-kpi:first-child { border-left: 0; }
.sd-kpi--lead { background: var(--surface); }
.sd-kpi__top { display: flex; align-items: center; gap: 9px; min-width: 0; }
.sd-kpi__ico {
  width: 24px; height: 24px; border-radius: 7px; flex: none;
  display: grid; place-items: center;
  background: var(--surface-sunk); color: var(--muted);
}
.sd-kpi--lead .sd-kpi__ico { background: var(--echo-soft); color: var(--echo); }
.sd-kpi__label {
  font-size: var(--t-small); font-weight: var(--label-w); letter-spacing: 0;
  color: var(--muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sd-kpi__value {
  margin-top: 12px; font-size: var(--t-figure); font-weight: 640;
  letter-spacing: -.03em; color: var(--ink); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.sd-kpi--lead .sd-kpi__value { font-size: var(--t-figure-lead); }
.sd-kpi__value small { font-size: var(--t-h3); font-weight: 500; color: var(--faint); letter-spacing: -.01em; }
/* The absence vocabulary at figure scale. A workspace with no calls yet has
   not failed at anything, so its zero is quiet rather than alarming. */
.sd-kpi__value.is-none { color: var(--faint); font-weight: 560; }
/* A measure whose value is a SENTENCE, not a number — "No subscription".
   At 42px it wrapped onto two lines and became the loudest thing on the page,
   which is the opposite of what an absence should be. */
.sd-kpi__value.is-word,
.sd-kpi--lead .sd-kpi__value.is-word {
  font-size: var(--t-lead); font-weight: 600; line-height: 1.3;
  letter-spacing: -.01em; color: var(--muted);
}
.sd-kpi__foot {
  margin-top: 9px; font-size: var(--t-meta); color: var(--muted);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; line-height: 1.45;
}
.sd-kpi__foot b { font-weight: 620; color: var(--ink-2); }
/* The only colour in the band, and it always means something. */
.sd-kpi__foot--ok,   .sd-kpi__foot--ok b   { color: var(--ok); }
.sd-kpi__foot--crit, .sd-kpi__foot--crit b { color: var(--crit); }
.sd-kpi__foot--warn, .sd-kpi__foot--warn b { color: var(--warn); }
/* A KPI's own meter. Real bounded data only — minutes against a plan the
   backend actually sold. Nothing else on this page gets one. */
.sd-kpi__meter { margin-top: 12px; height: 6px; border-radius: 3px; background: var(--surface-sunk); overflow: hidden; }
.sd-kpi__meter i { display: block; height: 100%; background: var(--echo); border-radius: 3px; }
.sd-kpi__meter i.is-warn { background: var(--warn); }
.sd-kpi__meter i.is-crit { background: var(--crit); }

/* =========================================================================
   USAGE & PLAN
   Two components, both of which exist because the honest version of this page
   has to show a *bound* as well as a figure.
   ========================================================================= */

/* An allowance and how much of it is spent. Used for the additional-minute
   credit, where the number that matters is what is LEFT, not what is used --
   a caller is refused the moment it reaches zero. The bar is decoration; every
   figure it encodes is also written out beside it, because a bar cannot be
   read by a screen reader and must not be the only place a limit is stated. */
.sd-allow { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.sd-allow__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; font-size: var(--t-meta); color: var(--muted);
}
.sd-allow__n { font-family: var(--f-mono); color: var(--ink-2); }
.sd-allow__bar {
  height: 6px; border-radius: 3px; background: var(--surface-sunk); overflow: hidden;
}
.sd-allow__fill { display: block; height: 100%; border-radius: 3px; background: var(--ink); }
.sd-allow__fill.is-warn { background: var(--warn); }
.sd-allow__fill.is-crit { background: var(--crit); }

/* The catalogue. Cards rather than a table: on a narrow screen a four-column
   pricing table is unreadable, and there are only ever a handful of plans. */
.sd-plans {
  display: grid; gap: 11px; min-width: 0;
  grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
}
.sd-plan {
  border: 1px solid var(--rule); border-radius: var(--r-1);
  padding: 13px 15px; display: flex; flex-direction: column; gap: 7px; min-width: 0;
}
/* The tenant's own plan, marked with a border and a chip rather than a colour
   fill: this is a statement of fact, not a recommendation, and nothing on this
   page is trying to sell an upgrade. */
.sd-plan.is-current { border-color: var(--echo-line); background: var(--echo-soft); }
.sd-plan__top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-plan__nm { font-size: var(--t-ui); font-weight: 620; color: var(--ink); min-width: 0; }
.sd-plan__price {
  font-family: var(--f-mono); font-size: var(--t-meta); color: var(--ink-2);
  margin-left: auto; white-space: nowrap;
}
.sd-plan__terms { font-size: var(--t-small); color: var(--muted); line-height: 1.55; }
.sd-plan__terms b { font-weight: 560; color: var(--ink-2); }

/* ---- page body --------------------------------------------------------- */
.sd-ov__body {
  display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 18px; align-items: start; min-width: 0;
}
.sd-ov__col { display: flex; flex-direction: column; gap: 18px; min-width: 0; }

/* ---- readiness --------------------------------------------------------- */
/* Explicit states, never a single invented "87% ready" score. Each row says
   what it is and what it is waiting for, in words. */
.sd-rdy { display: flex; flex-direction: column; }
.sd-rdy__r {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; padding: 12px 0; border-top: 1px solid var(--rule); min-width: 0;
}
.sd-rdy__r:first-child { border-top: 0; padding-top: 0; }
.sd-rdy__t { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sd-rdy__nm { font-size: var(--t-body); font-weight: 600; color: var(--ink); }
.sd-rdy__d { font-size: var(--t-meta); color: var(--muted); line-height: 1.45; unicode-bidi: plaintext; }
.sd-rdy__s { flex: none; }

/* Per-agent knowledge coverage — the one honest distribution today's API can
   back, because `knowledge.per_agent` already carries the four counts. */
.sd-cov { display: flex; flex-direction: column; min-width: 0; }
.sd-cov__i { padding: 12px 0; border-top: 1px solid var(--rule); min-width: 0; }
.sd-cov__i:first-child { border-top: 0; padding-top: 0; }
.sd-cov__row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; min-width: 0; }
.sd-cov__nm {
  font-size: var(--t-body); font-weight: 600; color: var(--ink); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-cov__n { font-size: var(--t-meta); color: var(--muted); flex: none; font-variant-numeric: tabular-nums; }
.sd-cov__bar { margin-top: 8px; height: 8px; border-radius: 3px; background: var(--surface-sunk); display: flex; overflow: hidden; }
.sd-cov__bar span { display: block; height: 100%; }
.sd-cov__bar .is-indexed { background: var(--ok); }
.sd-cov__bar .is-processing { background: var(--warn); }
.sd-cov__bar .is-failed { background: var(--crit); }

/* ---- setup checklist --------------------------------------------------- */
/* Completed and incomplete differ by GLYPH and by WORD before they differ by
   colour. A checklist that reads only as green and grey is a checklist a
   third of readers cannot use. */
.sd-chk { display: flex; flex-direction: column; }
.sd-chk__i {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 11px 0; border-top: 1px solid var(--rule); min-width: 0;
}
.sd-chk__i:first-child { border-top: 0; padding-top: 0; }
.sd-chk__m {
  width: 20px; height: 20px; border-radius: var(--r-full); flex: none;
  display: grid; place-items: center; margin-top: 1px;
  background: var(--surface-sunk); color: var(--faint);
}
.sd-chk__m--ok { background: var(--ok-soft); color: var(--ok); }
.sd-chk__m--warn { background: var(--warn-soft); color: var(--warn); }
.sd-chk__t { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.sd-chk__nm { font-size: var(--t-body); font-weight: 550; color: var(--ink); }
.sd-chk__d { font-size: var(--t-meta); color: var(--muted); line-height: 1.45; unicode-bidi: plaintext; overflow-wrap: anywhere; }
.sd-chk__i.is-done .sd-chk__nm { color: var(--ink-2); font-weight: 500; }
.sd-chk__n {
  font-size: var(--t-micro); font-weight: 620; color: var(--muted);
  font-variant-numeric: tabular-nums; flex: none;
}

/* ---- recent activity preview ------------------------------------------- */
/* A PREVIEW, not a second Calls ledger. Five rows, four columns, and a route
   to the real thing. */
.sd-prev { min-width: 0; }
.sd-prev__foot {
  padding: 11px 16px; border-top: 1px solid var(--rule);
  display: flex; align-items: center; justify-content: flex-end;
}

/* ---- usage ------------------------------------------------------------- */
.sd-use { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sd-use__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.sd-use__v { font-size: var(--t-figure); font-weight: 620; color: var(--ink); letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.sd-use__v small { font-size: var(--t-h3); font-weight: 500; color: var(--faint); }
.sd-use__p { font-size: var(--t-meta); color: var(--muted); }

/* ---- the honest gap ---------------------------------------------------- */
/* Names what a panel WOULD show and why it does not. It is not a teaser: it
   exists so nobody re-adds the fabricated version. */
.sd-await {
  border: 1px dashed var(--rule-2); border-radius: var(--r-2);
  background: repeating-linear-gradient(-45deg, transparent 0 8px, color-mix(in oklab, var(--ink) 2%, transparent) 8px 16px);
  padding: 15px 16px; display: flex; align-items: flex-start; gap: 13px; flex-wrap: wrap;
}
.sd-await__ico {
  width: 32px; height: 32px; border-radius: var(--r-1); flex: none;
  display: grid; place-items: center; background: var(--surface);
  border: 1px solid var(--rule); color: var(--muted);
}
.sd-await__t { flex: 1; min-width: 220px; }
/* Both are spans inside a span, so they need to be told they are blocks or
   the sentence runs into the heading. */
.sd-await__ttl { display: block; font-size: var(--t-body); font-weight: 620; color: var(--ink-2); }
.sd-await__d { display: block; }
.sd-await__d { font-size: var(--t-meta); color: var(--muted); margin-top: 5px; line-height: 1.5; }

/* ---- tablet ------------------------------------------------------------ */
@media (max-width: 1179px) {
  /* The band wraps to two rows rather than shrinking five columns into
     unreadable slivers. REDESIGN: the lead holds both rows on the left and
     the other four sit two by two beside it, so no cell is left empty. */
  .sd-kpis { grid-template-columns: 1.2fr repeat(2, minmax(0, 1fr)); }
  .sd-kpi--lead { grid-row: span 2; }
  .sd-kpi:nth-child(n+4) { border-top: 1px solid var(--rule); }
  .sd-ov__body { grid-template-columns: minmax(0, 1fr); }
}

/* ---- narrow ------------------------------------------------------------ */
@media (max-width: 899px) {
  /* REDESIGN: the lead measure takes the whole first row and the other four
     sit two by two beneath it. Five full-width rows made the band longer
     than a phone screen, and the page's answer (the pulse) scrolled away
     behind it. */
  .sd-kpis { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .sd-kpi { padding: 14px 15px 15px; border-left: 0; border-top: 1px solid var(--rule); }
  .sd-kpi:first-child { border-top: 0; }
  .sd-kpi--lead { grid-column: 1 / -1; grid-row: auto; }
  .sd-kpi:nth-child(odd):not(:first-child) { border-left: 1px solid var(--rule); }
  .sd-kpi__value { font-size: 24px; }
  .sd-kpi--lead .sd-kpi__value { font-size: var(--t-figure); }
  .sd-kpi__foot { font-size: var(--t-small); }
  .sd-ov__body { gap: 16px; }
  .sd-use__v { font-size: var(--t-h1); }
  .sd-pulse__top { padding: 16px; gap: 12px 14px; align-items: flex-start; }
  .sd-pulse__dot { margin-top: 6px; }
  /* The sentence stays beside its dot; only the action takes a row of its own. */
  .sd-pulse__t { flex: 1 1 0; }
  .sd-pulse__att { padding: 12px 16px; }
  .sd-pulse__act { width: 100%; }
  .sd-pulse__act .sd-btn { flex: 1; min-height: 44px; }
  .sd-pulse__ttl { font-size: 17px; }
  .sd-kpi__label { white-space: normal; }
}
/* Below 360px two figures side by side no longer fit a "1,840 / 3,000" on one
   line, so the band returns to one measure per row. */
@media (max-width: 359px) {
  .sd-kpis { grid-template-columns: minmax(0, 1fr); }
  .sd-kpi, .sd-kpi:nth-child(odd):not(:first-child) { border-left: 0; }
}

/* ==========================================================================
   CALL DETAIL — REDESIGN 2026-09.
   One call, read in the order a person asks about it: who and how it ended
   (the head), what happened (the summary), the evidence (the conversation,
   the recording), and — beside it — what the call left behind (booking,
   follow-up) and the facts of record.
   ========================================================================== */
.sd-crumbs {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: var(--t-meta); color: var(--muted); margin-bottom: -6px;
}
.sd-crumbs svg { color: var(--faint); flex: none; }
.sd-cd__head { align-items: center; }
.sd-cd__who { display: flex; align-items: center; gap: 14px; min-width: 0; }
.sd-cd__av {
  width: 44px; height: 44px; border-radius: var(--r-full); flex: none;
  display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--rule); color: var(--muted);
  box-shadow: var(--sh-1);
}
.sd-cd__chips { flex-wrap: wrap; justify-content: flex-end; }
.sd-cd__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px;
  gap: 20px; align-items: start; min-width: 0;
}
.sd-cd__main, .sd-cd__side { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.sd-cd__summary .sd-card__body { gap: 14px; }
.sd-cd__conv .sd-card__body { gap: 16px; padding-top: 10px; }
/* The summary is prose, at reading size. */
.sd-cd__lead {
  font-size: var(--t-lead); line-height: 1.65; color: var(--ink);
  max-width: 70ch; unicode-bidi: plaintext; margin: 0;
}
.sd-cd__lead.is-absent { color: var(--faint); font-size: var(--t-body); }
.sd-cd__verdict {
  display: flex; gap: 28px; flex-wrap: wrap;
  padding-top: 14px; border-top: 1px solid var(--rule);
}
.sd-cd__avail { font-size: var(--t-micro); font-weight: 560; color: var(--muted); padding-top: 3px; }
/* A mark in the conversation that is the system's, not a speaker's: the
   call's start and its observed end. Centred, quiet, and never a bubble, so
   it cannot be mistaken for something either party said. */
.sd-sys {
  display: flex; align-items: center; gap: 12px;
  font-size: var(--t-micro); font-weight: 560; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.sd-sys::before, .sd-sys::after { content: ''; flex: 1; height: 1px; background: var(--rule); }
.sd-sys span { flex: none; }
/* A booking that stands is the one outcome a business acts on: its card
   carries the echo edge. */
.sd-cd__book.is-booked { border-color: var(--echo-line); box-shadow: inset 3px 0 0 var(--echo), var(--sh-1); }
.sd-cd__ref {
  font-size: var(--t-meta); font-weight: 600; color: var(--ink);
  padding: 2px 8px; border-radius: 6px; background: var(--surface-sunk);
}
.sd-cd__attn { border-color: var(--warn-line); }
/* Facts of record: label and value on one line in a narrow column. */
.sd-dl { display: flex; flex-direction: column; }
.sd-dl .sd-ro {
  display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 12px;
  padding: 9px 0; border-top: 1px solid var(--rule);
}
.sd-dl .sd-ro:first-child { border-top: 0; padding-top: 0; }
.sd-dl .sd-ro__k { padding-top: 2px; }
.sd-dl .sd-ro__v { font-size: var(--t-body); }
.sd-dl__note { display: block; margin-top: 4px; font-size: var(--t-micro); color: var(--muted); line-height: 1.5; }
.sd-cd .sd-diag { margin-top: 2px; }
/* What the page's polling changes sits ABOVE the conversation: the summary
   arrives, the recording note becomes a player, "Recording details" appears.
   The browser's scroll anchoring holds the first thing on screen still, which
   is often the tail of one of those cards, so the conversation being read
   below was pushed down by their growth (29px for the recording and 57px for
   the summary, measured at 1440x900). The head and every card above the
   conversation are taken out of anchoring, so the browser holds the
   conversation still instead. No script, no saved positions -- CSS Scroll
   Anchoring. */
.sd-cd > :not(.sd-cd__grid),
.sd-cd__main > :not(.sd-cd__conv) { overflow-anchor: none; }

@media (max-width: 1179px) {
  .sd-cd__grid { grid-template-columns: minmax(0, 1fr); }
  /* One column. The head's chips already carry the booking outcome at a
     glance; the side cards follow the evidence, side by side while the
     width allows. */
  .sd-cd__side { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); align-items: start; }
}
@media (max-width: 899px) {
  .sd-cd__grid, .sd-cd__main { gap: 16px; }
  .sd-cd__side { display: flex; gap: 16px; align-items: stretch; }
  .sd-cd__chips { justify-content: flex-start; }
  .sd-cd__av { width: 40px; height: 40px; }
  .sd-dl .sd-ro { grid-template-columns: 1fr; gap: 3px; }
}

/* ==========================================================================
   BOOKINGS CALENDAR — DESIGN.md > Calendar code reuse, and Implementation
   pass 7. Month · Week · List.

   Ported from `design/specimens/sadaa-coverage.css`, which is the approved
   surface. The drawer OVERLAYS the grid rather than taking a column: measured
   on the specimen, a 392px column beside a 240px rail leaves the month grid
   723px wide — 103px per day — and every chip truncated to two letters. A
   booking detail is transient; the grid is the surface.
   ========================================================================== */
/* Now also covered by the shell rule on `.sd-shell__col > main.sd-page`; kept
   because it is harmless and this is where the defect was first found.
   Omitting it did not look broken at desktop: `main` is a `flex: 1 1 auto`
   item in a column with `overflow: hidden`, so it SHRINKS below its content
   and the overflow is simply clipped. At 390 that put the last agenda row
   under the phone tab bar, unreachable. Found by trying to tap it. */
.sd-bookings { min-width: 0; overflow-y: auto; }

.sd-calbar {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; min-width: 0;
}
.sd-calnav { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.sd-calnav__t {
  font-size: var(--t-h2); font-weight: 640; color: var(--ink);
  letter-spacing: -.015em; min-width: 186px;
}
.sd-stepper { display: inline-flex; align-items: center; gap: 4px; }
.sd-stepper button {
  height: 32px; min-width: 32px; padding: 0 10px; border-radius: var(--r-1);
  border: 1px solid var(--rule-2); background: var(--surface);
  display: inline-grid; place-items: center;
  font-size: var(--t-meta); font-weight: 550; color: var(--ink-2);
  cursor: pointer;
}
.sd-stepper button:hover { background: var(--surface-sunk); }

/* The count line, and the one sentence that keeps a filtered fragment from
   reading as the whole period. */
.sd-calcount { font-size: var(--t-meta); color: var(--muted); margin: -6px 0 0; }
.sd-calcount__part { color: var(--warn); }

/* The search field takes the slack in the filter row rather than every
   control sharing it — a 90px search box is not a search box. */
.sd-field--grow { flex: 1 1 180px; min-width: 150px; }
.sd-field--grow .sd-input { width: 100%; }

.sd-calwrap { position: relative; min-width: 0; }
.sd-calbody { min-width: 0; }
.sd-calload { min-width: 0; }

/* ---- Month ---- 42 cells, six rows, seven columns. The one piece of the
   supplied EventManager's arithmetic that is correct, kept verbatim. */
.sd-month {
  border: 1px solid var(--rule); border-radius: var(--r-2);
  background: var(--surface); box-shadow: var(--sh-1); overflow: hidden;
}
.sd-month__dow, .sd-month__grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.sd-month__dow > div {
  padding: 9px 10px; text-align: left; background: var(--surface-sunk);
  font-size: var(--t-micro); font-weight: 620; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
  border-bottom: 1px solid var(--rule); border-left: 1px solid var(--rule);
}
.sd-month__dow > div:first-child { border-left: 0; }
.sd-cell {
  min-height: 106px; padding: 7px 8px 9px;
  border-left: 1px solid var(--rule); border-top: 1px solid var(--rule);
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
}
.sd-cell:nth-child(7n+1) { border-left: 0; }
.sd-cell--out { background: var(--surface-warm); }
.sd-cell--out .sd-cell__n { color: var(--faint); }
.sd-cell__n {
  font-size: var(--t-meta); font-weight: 600; color: var(--ink-2);
  width: 24px; height: 24px; border-radius: var(--r-full);
  display: grid; place-items: center; flex: none;
  background: none; border: 0; cursor: pointer; font-family: inherit;
}
.sd-cell__n:hover { background: var(--surface-sunk); }
/* Today is a FILLED disc, not a tint: shape as well as colour, so 1.4.1 holds
   without asking anyone to distinguish two greens. */
.sd-cell--today .sd-cell__n { background: var(--action); color: var(--on-action); }
.sd-cell--today .sd-cell__n:hover { background: var(--echo-2); }
/* The selected day carries a ring — again a shape, not a second tint. */
.sd-cell.is-selected { background: var(--echo-soft); }
.sd-cell.is-selected .sd-cell__n { box-shadow: 0 0 0 2px var(--echo-line); }
.sd-cell__evs { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-cell__more {
  font-size: var(--t-micro); color: var(--muted); padding: 2px;
  background: none; border: 0; text-align: left; cursor: pointer;
  min-height: 24px; font-family: inherit;
}
.sd-cell__more:hover { color: var(--ink); }

/* ---- one booking, in a month cell ----
   NOT a colour swatch: the fill is the semantic status and nothing else, so
   a calendar and the list beneath it use one vocabulary. A non-ok tone also
   carries a MARK, because a fill on its own is colour-only information.
   Minimum 26px tall for WCAG 2.2 2.5.8. */
.sd-ev {
  display: flex; align-items: center; gap: 6px; width: 100%;
  min-height: 26px; padding: 3px 7px; border-radius: 5px;
  font-size: var(--t-micro); font-weight: 600; text-align: left;
  border: 1px solid transparent; overflow: hidden; cursor: pointer;
  font-family: inherit;
}
.sd-ev__m   { flex: none; font-weight: 700; }
.sd-ev__t   { flex: none; font-variant-numeric: tabular-nums; opacity: .85; }
.sd-ev__who {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; unicode-bidi: plaintext;
}
.sd-ev--ok      { background: var(--ok-soft);   border-color: var(--ok-line);   color: var(--ok); }
.sd-ev--warn    { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.sd-ev--crit    { background: var(--crit-soft); border-color: var(--crit-line); color: var(--crit); }
.sd-ev--info    { background: var(--info-soft); border-color: var(--info-line); color: var(--info); }
.sd-ev--neutral { background: var(--surface-sunk); border-color: var(--rule); color: var(--muted); }
/* An event Sadaa did not create.
   NOT a sixth status: an external Google event has no workflow status to be in,
   which is exactly why it needs a treatment of its own rather than borrowing
   `neutral` from a cancelled booking. It is quieter than every booking tone —
   somebody else's commitment, present and not competing for attention — and it
   carries a dotted edge and a ring mark so the difference survives greyscale,
   print, and a reader who cannot separate two soft fills. */
.sd-ev--ext {
  background: var(--surface); border-style: dotted;
  border-color: var(--ink-2); color: var(--ink-2);
}
.sd-ev--ext .sd-ev__t { opacity: .7; }
/* A booking the provider has not settled. Deliberately not solid: it is in
   flight, and the calendar says so rather than drawing it as done. */
.sd-ev--flight  { border-style: dashed; }
.sd-ev.is-open, .sd-wev.is-open { box-shadow: 0 0 0 2px var(--ink-2); }

/* ---- Week ---- business hours only, and a proportional column, not the
   supplied code's 24 fixed hour buckets. */
.sd-week {
  border: 1px solid var(--rule); border-radius: var(--r-2);
  background: var(--surface); box-shadow: var(--sh-1); overflow: hidden;
}
.sd-week__head { display: grid; grid-template-columns: 62px repeat(7, 1fr); }
.sd-week__head > div {
  padding: 9px 10px; background: var(--surface-sunk);
  border-bottom: 1px solid var(--rule); border-left: 1px solid var(--rule);
  font-size: var(--t-micro); font-weight: 620; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.sd-week__head > div:first-child { border-left: 0; }
.sd-week__head b {
  display: block; font-size: var(--t-body); font-weight: 640;
  color: var(--ink); letter-spacing: 0; text-transform: none; margin-top: 2px;
}
.sd-week__head .is-today b { color: var(--echo-2); }
.sd-week__body {
  display: grid; grid-template-columns: 62px repeat(7, 1fr); position: relative;
}
.sd-week__hours { border-right: 1px solid var(--rule); }
.sd-week__hours div {
  padding: 4px 9px 0 0; text-align: right;
  font-size: var(--t-micro); color: var(--faint);
  font-variant-numeric: tabular-nums; border-top: 1px solid var(--rule);
}
.sd-week__hours div:first-child { border-top: 0; }
.sd-week__col { position: relative; border-left: 1px solid var(--rule); min-width: 0; }
.sd-week__col i { display: block; border-top: 1px solid var(--rule); }
.sd-week__col i:first-child { border-top: 0; }
/* A booking positioned by its real start and duration. `left`/`width` come
   from the overlap layout, so two bookings at one time are two columns. */
.sd-wev {
  /* A fanned block: `left` is set per column and `right` runs to the day
     edge, so a cluster layers instead of dividing into unreadable slivers.
     The border makes the layering visible as layering. */
  position: absolute; border-radius: 5px;
  padding: 4px 6px; overflow: hidden; text-align: left;
  font-size: var(--t-micro); font-weight: 600; border: 1px solid transparent;
  min-height: 24px; cursor: pointer; font-family: inherit;
}
.sd-wev b {
  display: block; font-weight: 640; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; unicode-bidi: plaintext;
}
.sd-wev__m { font-weight: 700; margin-right: 3px; }
.sd-wev span {
  display: block; font-weight: 500; opacity: .82;
  font-variant-numeric: tabular-nums; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.sd-nowline {
  position: absolute; left: 62px; right: 0; height: 0;
  border-top: 2px solid var(--crit-solid); pointer-events: none;
}
.sd-nowline::before {
  content: ''; position: absolute; left: -4px; top: -5px;
  width: 8px; height: 8px; border-radius: var(--r-full); background: var(--crit-solid);
}

/* ---- List ---- */
.sd-bkwhen { display: block; font-size: var(--t-body); color: var(--ink); }
.sd-bkwhen__t {
  display: block; font-size: var(--t-micro); color: var(--muted);
  font-variant-numeric: tabular-nums; margin-top: 2px;
}
.sd-bkwho {
  display: block; font-weight: 600; color: var(--ink);
  unicode-bidi: plaintext; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; max-width: 22ch;
}

/* ---- the selected day's real list ----
   Desktop does not need it; the grid says it. It appears when a day is
   selected, and below 900px it is the agenda. */
.sd-dayagenda { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sd-dayagenda__none { font-size: var(--t-meta); color: var(--muted); }
/* The record list is normally the below-900px replacement for a table. Here
   it is the agenda at EVERY width, so this one instance opts in. */
.sd-records--always { display: block; }

/* ---- booking status words that are not chips ---- */
.sd-bk__st { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-bk__ver {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--t-micro); font-weight: 600; color: var(--ok);
}
.sd-bk__unver { font-size: var(--t-micro); font-weight: 550; color: var(--muted); }
.sd-bk__err {
  border: 1px solid var(--crit-line); background: var(--crit-soft);
  border-radius: var(--r-1); padding: 10px 12px;
}
.sd-bk__errcode {
  font-family: var(--f-mono); font-size: var(--t-micro);
  color: var(--crit); margin-bottom: 3px;
}
.sd-bk__errtxt { font-size: var(--t-meta); color: var(--ink-2); }

/* ---- what kind of thing this row is ----
   The words that stop an external event reading as a booking with missing
   data. Small, secondary, and always present on an external row — never a
   colour swatch, and never the only signal either. */
.sd-evkind {
  display: block; font-size: var(--t-micro); font-weight: 550;
  color: var(--muted); margin-top: 2px; letter-spacing: .01em;
}
/* The same fact on a booking that has drifted from its calendar. Warning
   weight, because unlike the line above this one needs somebody to act. */
.sd-evdiv {
  display: block; font-size: var(--t-micro); font-weight: 600;
  color: var(--warn); margin-top: 2px;
}

/* ---- where this calendar came from ----
   Present only when the answer is not "current". A badge that said "Live" on a
   live calendar would be noise, and noise is what makes a real warning easy to
   miss. */
.sd-calsync {
  font-size: var(--t-meta); color: var(--muted);
  margin: -2px 0 8px; display: flex; align-items: center; gap: 6px;
}
.sd-calsync::before {
  content: ''; width: 6px; height: 6px; border-radius: var(--r-full);
  background: var(--muted); flex: none;
}
.sd-calsync--warn { color: var(--warn); font-weight: 550; }
.sd-calsync--warn::before { background: var(--warn); }

/* ---- Google and Sadaa disagree about this booking ----
   Above the facts in the drawer, because a booking whose event Google deleted
   still reads "Verified" three lines below and somebody has to see this first.
   No action button: no reconciliation action exists, and one that only looked
   like it did would be worse than none. */
.sd-diverge {
  border: 1px solid var(--warn-line); background: var(--warn-soft);
  border-radius: var(--r-1); padding: 10px 12px; margin-bottom: 12px;
}
.sd-diverge__t {
  font-size: var(--t-meta); font-weight: 640; color: var(--warn);
}
.sd-diverge__b { font-size: var(--t-meta); color: var(--ink-2); margin-top: 2px; }
.sd-diverge__r {
  display: flex; flex-direction: column; gap: 2px; margin-top: 6px;
  font-size: var(--t-micro); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   BOOKING DRAWER — two placements, one panel.
   `--over` is the calendar's: it overlays the grid, no scrim, because the
   point of a calendar is that you keep reading it. `--fixed` is the admin
   ledger's existing right-side sheet, kept so this pass does not relayout a
   page it was told not to redesign.
   ========================================================================== */
/* NOT `.sd-scrim`. That name is the app SHELL's, for the mobile rail: it is
   `display: none` by default and turns on with `.is-open` below 900px. A
   second definition here could not override the display and rendered a 0x0
   backdrop — the same collision `.sd-field` caused in pass 5, and the third
   time this stylesheet has been bitten by reusing a shell name. The guard
   cannot see it: both names are defined, so nothing is dead. */
.sd-drawer__scrim {
  position: fixed; inset: 0; z-index: var(--z-drawer-scrim);
  background: var(--scrim); backdrop-filter: blur(1px);
}
.sd-drawer {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-1);
  display: flex; flex-direction: column; overflow: hidden; min-width: 0;
}
.sd-drawer--over {
  position: absolute; top: 0; right: 0; width: 392px;
  max-height: 100%; box-shadow: var(--sh-2); z-index: 5;
}
.sd-drawer--fixed {
  position: fixed; inset-block: 0; inset-inline-end: 0; width: 420px;
  border-radius: 0; border-block: 0; border-inline-end: 0;
  box-shadow: var(--sh-2); z-index: var(--z-drawer);
}
.sd-drawer__head {
  padding: 15px 18px; border-bottom: 1px solid var(--rule);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  flex: none;
}
.sd-drawer__ttl {
  font-size: var(--t-h2); font-weight: 640; color: var(--ink);
  letter-spacing: -.015em; margin-top: 9px; unicode-bidi: plaintext;
}
.sd-drawer__sub { font-size: var(--t-meta); color: var(--muted); margin-top: 2px; }
.sd-drawer__body {
  padding: 16px 18px; display: flex; flex-direction: column; gap: 16px;
  overflow-y: auto; min-height: 0;
}
.sd-drawer__foot {
  margin-top: auto; padding: 14px 18px; border-top: 1px solid var(--rule);
  background: var(--surface-warm); display: flex; gap: 9px; flex-wrap: wrap;
  align-items: center; flex: none;
}

/* ---- the centred dialog -------------------------------------------------
   A drawer is for a record you are reading. A dialog is for a decision you are
   making right now, and the two want different shapes: full height for the
   first, only as much as the question needs for the second. Deliberately its
   own name rather than a modifier of `.sd-drawer`, because the drawer is fixed
   to a screen edge and this is centred — reusing that name is exactly the trap
   `.sd-drawer__scrim` documents one block above.

   Its z-index sits ABOVE the drawer's, since a confirmation the drawer opened
   has to be able to cover the drawer that opened it. */
/* FLEX, not `grid; place-items: center`, and the difference is load-bearing.
   In a grid the implicit row sizes to its content, so the panel's own
   `max-height: 100%` resolved against a row already 1264px tall and bounded
   nothing: a modal taller than the viewport put its footer — the Create button
   — off the bottom of the screen with no way to reach it. It never showed
   while the only modals here were a sentence and two buttons. A flex line
   resolves that percentage against the container's real height, which is the
   viewport less this padding, so the panel is bounded and `.sd-modal__body`
   scrolls inside it as it was always meant to. */
.sd-modal__scrim {
  position: fixed; inset: 0; z-index: var(--z-modal); padding: 20px;
  background: var(--scrim); backdrop-filter: blur(1px);
  display: flex; align-items: center; justify-content: center;
}
.sd-modal {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-2); box-shadow: var(--sh-2);
  width: 100%; max-width: 440px; max-height: 100%; outline: none;
  display: flex; flex-direction: column; min-width: 0;
}
.sd-modal__head {
  padding: 15px 18px; border-bottom: 1px solid var(--rule); flex: none;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
.sd-modal__ttl {
  font-size: var(--t-h3); font-weight: 640; color: var(--ink); letter-spacing: -.01em;
}
.sd-modal__body {
  padding: 16px 18px; display: flex; flex-direction: column; gap: 14px;
  overflow-y: auto; min-height: 0;
}
.sd-modal__txt {
  font-size: var(--t-body); color: var(--ink-2); line-height: 1.6; margin: 0;
}
.sd-modal__foot {
  padding: 13px 18px; border-top: 1px solid var(--rule); background: var(--surface-warm);
  display: flex; align-items: center; justify-content: flex-end; gap: 9px;
  flex-wrap: wrap; flex: none;
}

/* A form is not a confirmation. `.sd-modal`'s 440px is right for a sentence and
   two buttons and wrong for a dialog carrying a 23-voice library: at 440 the
   voice cards lose their waveform and the segmented language control wraps.
   A modifier rather than a `width` prop — DESIGN.md > Controls: a dimension
   belongs to a named state, not to whatever each caller passes. */
.sd-modal--wide { max-width: 560px; }

/* DESIGN.md > Radius: sheets and modals are `--r-3`. Every other value in the
   dialog already matched the spec; this one had drifted to the card radius. */
.sd-modal { border-radius: var(--r-3); }
.sd-modal__head { border-radius: var(--r-3) var(--r-3) 0 0; }
.sd-modal__foot { border-radius: 0 0 var(--r-3) var(--r-3); }

/* ENTER — DESIGN.md > Motion (motion.css): a dialog fades in as it scales up from
   just under its size; a drawer comes in from the side it lives on. It is seen to
   ARRIVE from the action that opened it rather than cutting in, it never delays a
   key press, and reduced motion removes it. Its way out is console-motion.css's. */
.sd-modal__scrim, .sd-drawer__scrim { animation: motion-fade-in var(--motion-slow) var(--motion-ease-out); }
.sd-modal { animation: motion-scale-in var(--motion-slow) var(--motion-ease-out); }
.sd-drawer--fixed { animation: motion-slide-in-from-inline-end var(--motion-slow) var(--motion-ease-out); }

/* The facts a confirmation is about, and the typed confirmation — see
   ConfirmDialog in client-modals.jsx. */
.sd-confirm__facts {
  margin: 0; display: grid; gap: 6px; padding: 11px 13px;
  background: var(--surface-warm); border: 1px solid var(--rule); border-radius: var(--r-1);
}
.sd-confirm__fact { display: flex; gap: 12px; min-width: 0; font-size: var(--t-body); line-height: 1.45; }
.sd-confirm__fact dt { flex: none; width: 34%; color: var(--muted); }
.sd-confirm__fact dd { margin: 0; min-width: 0; color: var(--ink); font-weight: 550; overflow-wrap: anywhere; unicode-bidi: plaintext; }
.sd-confirm__typed { display: flex; width: 100%; }
.sd-confirm__typed .sd-input { width: 100%; }
.sd-confirm__typedlbl { font-size: var(--t-meta); color: var(--ink-2); }
.sd-confirm__typedlbl b { font-weight: 650; color: var(--ink); font-family: var(--f-mono); font-size: var(--t-meta); }

/* The field-level error. `.sd-field` already stacks label / control / this, so
   the message sits under the control it is about rather than at the top of a
   form the operator then has to search. */
.sd-field__err {
  font-size: var(--t-meta); color: var(--crit); line-height: 1.5;
}

/* LANGUAGE CHOICE — the segmented control plus the one sentence that says what
   the selected mode does on a call. The name of a mode cannot carry "AR/EN
   opens in Arabic and offers English at the end of the greeting", and that is
   the fact an operator is choosing between. */
.sd-langpick { display: flex; flex-direction: column; align-items: flex-start; gap: 7px; min-width: 0; }
.sd-langpick__note {
  font-size: var(--t-meta); color: var(--muted); line-height: 1.5; margin: 0; max-width: 58ch;
}

/* ---- a titled group inside a drawer or a card ---------------------------
   A run of seven identical label/field pairs hides the fact that they are
   about THREE different things — a workspace, the business its agents answer
   for, and a person's login. The provisioning form said so with `border-t`
   before it was migrated and lost it in the port; this is that rule, named,
   so the same grouping is available to the record drawer beside it instead of
   an inline `style={{fontSize}}` on a card title.

   Grouped with a rule and space rather than nested cards: a card inside a
   drawer is a second elevation for no hierarchy that is not already there. */
.sd-sect { display: flex; flex-direction: column; gap: 11px; min-width: 0; }
.sd-sect + .sd-sect { border-top: 1px solid var(--rule); padding-top: 17px; }
.sd-sect__h {
  font-size: var(--t-ui); font-weight: 620; color: var(--ink); letter-spacing: -.005em;
}
.sd-sect__d {
  font-size: var(--t-meta); color: var(--muted); line-height: 1.5;
  margin-top: -7px; max-width: 58ch;
}

/* "Optional" is not an absent VALUE, so it must not borrow the absence scale.
   `.sd-absent--na` means "this does not apply to this record"; a field that may
   be left empty is a property of the FORM. Same tone, different word, and the
   two stay separable if either ever changes. */
.sd-field__opt {
  font-size: var(--t-micro); font-weight: 500; color: var(--faint); letter-spacing: 0; text-transform: none;
}

/* ---- tablet: the drawer narrows but still OVERLAYS, so the month grid keeps
   the whole width at 1024 rather than being squeezed into seven ~103px
   columns beside a permanent panel. --------------------------------------- */
@media (max-width: 1179px) {
  .sd-drawer--over { width: 344px; }
  .sd-week__head, .sd-week__body { grid-template-columns: 54px repeat(7, 1fr); }
  .sd-nowline { left: 54px; }
}

/* ---- narrow: the mobile calendar ---------------------------------------
   The month keeps SEVEN COLUMNS and loses its text. At 390px a day cell is
   47px wide, so a chip holds two letters and an ellipsis — which is not
   information. The bookings become semantic dots, the day NUMBER becomes the
   tap target (the dots are indicators, not 7px controls), and the selected
   day's real list is rendered underneath. Week is removed from the switcher
   by the page, not hidden here: a control that cannot work is not offered. */
@media (max-width: 899px) {
  .sd-cell { min-height: 62px; padding: 4px; align-items: center; gap: 4px; }
  .sd-cell__n { width: 26px; height: 26px; font-size: var(--t-micro); }
  .sd-cell__evs { flex-direction: row; flex-wrap: wrap; gap: 3px; justify-content: center; }
  .sd-ev {
    width: 7px; height: 7px; min-height: 0; padding: 0;
    border-radius: var(--r-full); overflow: hidden; pointer-events: none;
  }
  .sd-ev__m, .sd-ev__t, .sd-ev__who { display: none; }
  .sd-ev--ok      { background: var(--ok); }
  .sd-ev--warn    { background: var(--warn); }
  .sd-ev--crit    { background: var(--crit); }
  .sd-ev--info    { background: var(--info); }
  .sd-ev--neutral { background: var(--muted); }
  .sd-ev--flight  { background: var(--surface); border-color: var(--info); }
  .sd-cell__more { display: none; }
  .sd-month__dow > div {
    padding: 7px 2px; text-align: center; font-size: 9px; letter-spacing: .03em;
  }
  .sd-calnav__t { min-width: 0; font-size: var(--t-h3); }
  .sd-calbar { align-items: stretch; }
  /* Four stacked full-width controls put the calendar 550px down the page —
     below the fold on a phone, for a surface whose whole point is the grid.
     Two per row, search across the bottom. Measured, not guessed. */
  .sd-bookings .sd-filters { gap: 8px 10px; }
  .sd-bookings .sd-filters .sd-field { flex: 1 1 calc(50% - 5px); min-width: 0; }
  .sd-bookings .sd-filters .sd-field .sd-sel { width: 100%; }
  .sd-bookings .sd-filters .sd-seg { flex: 1 1 100%; }
  .sd-bookings .sd-filters .sd-field--grow { flex: 1 1 100%; }
  /* The drawer stops overlaying and becomes a full-width sheet in reading
     order below the calendar. It never becomes a squeezed column. */
  .sd-drawer--over {
    position: static; width: 100%; max-height: none;
    box-shadow: var(--sh-1); margin-top: 14px;
  }
  .sd-drawer--fixed { width: 100%; border-left: 0; }
}

@media (max-width: 639px) {
  .sd-drawer__foot { flex-direction: column; align-items: stretch; }
  .sd-drawer__foot .sd-btn { width: 100%; justify-content: center; }
}

/* ==========================================================================
   FOCUS — every interactive element, without exception. DESIGN.md > Focus.
   ========================================================================== */
/* `summary` is focusable and was NOT in this list, so every <details>
   disclosure in the product — Diagnostics on Agent detail as well as on Voice
   test — took keyboard focus with nothing to show for it. WCAG 2.2 2.4.7.
   Found by tabbing the page, not by reading it. */
.sd-app :where(a, button, summary, [tabindex], input, select, textarea):focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo);
  border-radius: var(--r-1);
}
/* A text field is already a bordered box: its focus is the border turning
   echo (7.6:1 against white) inside a soft echo halo, not a second outline
   around the first. Redesign 2026-09. */
.sd-app .sd-input:focus-visible, .sd-app .sd-sel:focus-visible, .sd-app .sd-textarea:focus-visible {
  border-color: var(--echo);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus) 24%, transparent);
}

/* ==========================================================================
   TWO SHELL STATES, driven by viewport width (the prototype's `lg`):
   desk >= 1024px: the rail; below: the top bar + the floating tab bar.
   ========================================================================== */
@media (max-width: 1023px) {
  .sd-app { grid-template-columns: minmax(0, 1fr); }
  .sd-rail { display: none; }
  .sd-topbar { display: flex; }
  .sd-topbar__live { display: block; }
  .sd-tabbar { display: flex; }
  /* The COLUMN clears the floating tab bar, not just `main`: padding only the page's
     scroller left every other row of the column -- the unsaved-changes bar on Agent Detail
     above all -- underneath the tab bar, where it could be seen and not pressed. */
  .sd-shell__col {
    padding-bottom: calc(var(--tabbar-h) + 24px + env(safe-area-inset-bottom, 0px));
  }
  .sd-ctx { padding: 9px 16px; }
}
@media (max-width: 639px) {
  /* The context layer keeps its identity and sheds its explanation; the back control keeps
     its accessible name and sheds its label. */
  .sd-ctx { padding: 9px 14px; gap: 10px; }
  .sd-ctx__sub { display: none; }
  .sd-ctx__back span { display: none; }
  .sd-ctx__back { padding: 0 10px; }
}

/* Screenshots (the parity gate, ?shot=1): the floating tab bar sits at the end of the page,
   motion is frozen, no caret blinks. */
:root[data-shot="1"] .sd-tabbar { position: absolute; }
:root[data-shot="1"] .sd-app *, :root[data-shot="1"] .sd-app *::before, :root[data-shot="1"] .sd-app *::after {
  animation: none !important; transition: none !important; caret-color: transparent !important;
}

/* ==========================================================================
   MOTION — DESIGN.md > Motion.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .sd-app *, .sd-app *::before, .sd-app *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  .sd-wait { animation: none; opacity: .7; }
  /* Skeletons hold still at a mid tone rather than blinking once and
     stopping. Named outright so they also stop where they render outside
     the shell (a drawer portal, the auth bootstrap). */
  .sd-skel__bar { animation: none !important; opacity: .8; }
  .sd-modal, .sd-modal__scrim, .sd-drawer__scrim, .sd-drawer--fixed { animation: none !important; }
}


/* ==========================================================================
   BOOKING ACTIONS — the Calendar's write half. Part 16.

   The slot grid is the only new shape here, and it is a grid rather than a
   <select> for a reason: a person picking an appointment time is comparing
   options, and a dropdown hides all but one of them behind a click. It is also
   the whole of the booking form's freedom -- there is no time input anywhere on
   these panels, because a time Sadaa did not offer is not a time it will book.

   Everything else reuses what the Calendar already ships: `.sd-drawer--fixed`
   is the sheet (full width below 900px, where it becomes the mobile sheet),
   `.sd-field` the labelled control, `.sd-note` the sentence that explains a
   state. One sheet in this product, not two that drift.
   ========================================================================== */
.sd-form { display: flex; flex-direction: column; gap: 15px; min-width: 0; }
/* `.sd-field` is inline-flex for the filter bar, where controls sit in a row.
   In a form it is a full-width stack. */
.sd-form .sd-field { display: flex; }

.sd-slots {
  display: grid; gap: 7px; min-width: 0;
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  max-height: 216px; overflow-y: auto; padding: 1px;
}
.sd-slot {
  height: 34px; padding: 0 6px; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--rule-2); border-radius: var(--r-1);
  background: var(--surface); color: var(--ink);
  font-size: var(--t-body); font-weight: 550; font-variant-numeric: tabular-nums;
  transition: border-color var(--d-1) var(--e), background var(--d-1) var(--e),
              color var(--d-1) var(--e);
}
.sd-slot:hover { border-color: var(--echo); background: var(--echo-soft); }
.sd-slot:focus-visible {
  outline: none; border-color: var(--echo); box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus) 20%, transparent);
}
/* The chosen time reads as chosen without relying on fill alone: it inverts,
   which survives a monochrome print and a low-contrast screen. */
.sd-slot[aria-pressed="true"] {
  background: var(--ink); border-color: var(--ink); color: var(--surface);
}

/* Loading, empty and failed are three different sentences in one place. An
   outage and a fully-booked Tuesday must never look the same. */
.sd-slots__state {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 12px 13px; border-radius: var(--r-1);
  border: 1px dashed var(--rule); background: var(--surface-warm);
  font-size: var(--t-meta); color: var(--muted); line-height: 1.5;
}
.sd-slots__state--bad {
  border-style: solid; border-color: var(--warn-line);
  background: var(--warn-soft); color: var(--warn);
}

.sd-table__act { text-align: right; white-space: nowrap; }

/* ── Imported-information review ──────────────────────────────────────────────
   The action row and the published state. A published entry is a statement, not
   a pending decision, so it reads as one: the state sits at the left of the row
   where an operator scans first, and the controls stay at the right at their
   ordinary weight instead of a large primary button that cannot be pressed.
   DESIGN.md > Business information. */

.sd-revacts {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 8px; flex-wrap: wrap;
  padding: 10px 16px; border-top: 1px solid var(--rule);
}

.sd-revacts__live { margin-right: auto; display: inline-flex; align-items: center; gap: 8px; }

.sd-revacts__livenote { font-size: var(--t-micro); color: var(--muted); }

/* One import is one review session, and it scrolls with the page.

   This used to be a bounded box -- `max-height: min(62vh, 720px)` with its own
   overflow -- so that the bulk controls above it stayed reachable. That reason
   was real and the cure was worse: ninety-seven entries in a 720px window is a
   reading strip about six rows tall inside a page that is not scrolling, and an
   operator loses their place in it constantly. Worse, the browser's own find,
   the scrollbar position and "am I at the end of the list?" all stop meaning
   anything.

   The controls stay reachable by sticking to the top of the page instead, which
   is what they needed in the first place. One scroll region per page. */
.sd-revlist { display: flex; flex-direction: column; gap: 12px; }

/* The bulk bar, kept in reach without trapping the list behind it. `--surface`
   rather than transparent because rows scroll underneath it, and a shadow only
   once it has actually left the top so it does not draw a line across a page
   that has not moved. */
.sd-revbar {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface); border-bottom: 1px solid var(--rule);
  padding: 10px 0; margin-bottom: 12px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; min-width: 0;
}

/* One published record, rendered as the thing it is rather than as a headline
   and a shrug. A fact has a label and a value; a FAQ has a question and an
   answer; an offering has a name, a category, a price and a description. The
   review surface showed all three as one bold line plus one grey line, so
   "Label" and "Question" looked identical and an operator had to open every
   item to find out which fields they were even looking at.

   `dt`/`dd` because that is what this is, and because it gives the field name a
   real semantic relationship to its value for anything reading the page aloud. */
.sd-recfields {
  display: grid; grid-template-columns: minmax(96px, max-content) minmax(0, 1fr);
  gap: 4px 16px; margin-top: 8px; min-width: 0;
}
.sd-recfields__k {
  font-size: var(--t-micro); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); padding-top: 2px;
}
.sd-recfields__v {
  font-size: var(--t-body); color: var(--ink); min-width: 0;
  overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-recfields__v--strong { font-weight: 600; }
.sd-recfields__v--muted { color: var(--muted); }

/* At 390 a two-column definition list puts a 96px label beside a 180px value
   and hyphenates everything. Stacked, each field gets the full width. */
@media (max-width: 639px) {
  .sd-recfields { grid-template-columns: minmax(0, 1fr); gap: 2px 0; }
  .sd-recfields__k { padding-top: 6px; }
}

/* Ten to sixteen visible lines, which is the difference between a box that
   looks like a search field and one that looks like somewhere to paste a menu.
   `min-height` rather than `rows` alone so the floor survives the flex column
   it sits in; `resize: vertical` because the operator knows how much they have
   to paste better than this file does. */
.sd-importbox {
  min-height: 300px; resize: vertical;
  /* `.sd-textarea` caps at 74ch, which is a *reading* measure and exactly right
     for editing one answer. A paste target is not prose being read: the
     operator is checking that the structure of what they pasted survived, and
     at 1440 a 74ch column re-wraps a menu into something that looks nothing
     like the menu they copied. */
  max-width: none;
}
@media (max-width: 899px) {
  /* Still a paste target, not a caption. 240px is the floor the phone layout
     has to keep. */
  .sd-importbox { min-height: 240px; }
}
@media (max-width: 639px) {
  /* On a phone the desktop's fourteen rows push the action off the fold, so
     the box sits at its floor and stays resizable for anyone who wants more. */
  .sd-importbox { height: 240px; }
  /* The primary action goes full width. At 390 a 193px button floated right of
     a wrapped sentence reads as secondary to it, and this is the only thing on
     the card the operator is meant to press. */
  .sd-cardfoot > .sd-btn,
  .sd-cardfoot > button { width: 100%; justify-content: center; }
  .sd-cardfoot__note, .sd-cardfoot > div:first-child { width: 100%; }
}

.sd-drop {
  border: 2px dashed var(--rule); border-radius: var(--r-2);
  padding: 32px 16px; text-align: center; cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}

.sd-drop:hover { background: var(--surface-sunk); }

.sd-drop.is-over { border-color: var(--echo); background: var(--echo-soft); }

/* ── Imported-information review: rows, evidence and refusals ─────────────────
   Every badge on this screen is a server field, so the styling's whole job is
   to keep them distinguishable: the difference table is the one place an
   operator sees what would change if they published, the quote is the sentence
   the extraction came from, and a refusal replaces a control rather than
   disabling one. DESIGN.md > Business information. */

.sd-diffbox {
  margin-top: 12px; border: 1px solid var(--warn-line);
  border-radius: var(--r-2); overflow: hidden;
}

.sd-diffbox__row {
  display: grid; grid-template-columns: minmax(90px, .7fr) 1fr 1fr;
  font-size: var(--t-body); border-bottom: 1px solid var(--rule);
}

.sd-diffbox__row:last-child { border-bottom: 0; }

/* The excerpt the reading came from. Quoted rather than boxed: it is evidence,
   not another finding competing with the row's own state. */
.sd-quote {
  margin-top: 4px; padding-left: 12px; border-left: 2px solid var(--rule);
  font-size: var(--t-small); color: var(--muted); line-height: 1.6;
}

.sd-revhead {
  margin-top: 8px; font-size: 14.5px; font-weight: 560;
  color: var(--ink); line-height: 1.35;
}

.sd-hint { margin-top: 4px; font-size: var(--t-micro); color: var(--muted); line-height: 1.4; }
/* A link inside a hint is still a link: coloured AND underlined (WCAG 1.4.1).
   The shell's reset had left it as plain grey words. */
.sd-hint a { color: var(--echo-2); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

.sd-hint--block { margin-top: 8px; font-size: var(--t-small); }

.sd-hint--crit { margin-top: 6px; font-size: var(--t-small); color: var(--crit); }

.sd-alert {
  margin-top: 12px; display: flex; align-items: flex-start; gap: 8px;
  padding: 8px 12px; border-radius: var(--r-1);
  border: 1px solid transparent;
}

.sd-alert--crit { background: var(--crit-soft); border-color: var(--crit-line); }

.sd-alert--page { margin: 0 0 20px; padding: 12px 16px; border-radius: var(--r-2); }

.sd-alert__t { font-size: var(--t-small); color: var(--crit); line-height: 1.4; }

/* A blocked entry gets a sentence where a control would be, so nothing renders
   dead and unexplained. */
.sd-blocked {
  margin-top: 12px; display: flex; align-items: flex-start; gap: 6px;
  font-size: var(--t-small); color: var(--muted); line-height: 1.4;
}

.sd-back {
  margin-bottom: 16px; height: 32px; padding: 0 8px; margin-left: -8px;
  border-radius: var(--r-1); display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-small); color: var(--muted); background: none; border: 0;
  cursor: pointer;
}

.sd-back:hover { color: var(--ink); background: var(--surface-sunk); }

.sd-dangerbtn {
  flex: none; height: 32px; padding: 0 10px; border-radius: var(--r-1);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-size: var(--t-small); color: var(--muted);
  background: none; border: 0; cursor: pointer;
}

.sd-dangerbtn--icon { width: 32px; padding: 0; }

.sd-dangerbtn:hover:not(:disabled) { color: var(--crit); background: var(--crit-soft); }

.sd-dangerbtn:disabled { opacity: .4; cursor: default; }

.sd-chk__box { margin-top: 2px; }

.sd-revregion { padding: 28px 32px; }

/* The reading measure for the import / review column. The page carried
   `max-w-[900px]` for this and the purged `vendor/tailwind.css` does not define
   it, so the column had been running the full width of the page -- a paste box
   and a diff table at 1136px, which is not a measure anyone chose. Authored
   here so the intent is real, and removed from the dead-class allowlist. */

/* A card's closing row: what this does on the left, the action on the right.
   It WRAPS, which is the whole reason it is a named class rather than three
   utilities -- at 390px an unwrapped row squeezed "Nothing here reaches your
   callers until you publish it." into a four-line column about 100px wide
   beside the button. Wrapped, the sentence keeps its own line and the action
   sits under it. */
.sd-cardfoot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: 14px; min-width: 0;
}
.sd-cardfoot__note { font-size: var(--t-meta); color: var(--muted); min-width: 0; }

/* The per-row action menu. Anchored right because it belongs to the row it acts
   on, and the closed state removes it from the pointer and the tab order rather
   than only fading it -- an invisible menu that still takes focus is a set of
   controls an operator can reach and cannot see. */
/* `left` and `bottom` are reset because `.sd-menu` (the account menu) sets
   both: with `top` here and `bottom` there, the box was sized to the space
   between them -- two pixels -- and `overflow: hidden` clipped Reindex and
   Delete out of reach. With `left` there as well, `right: 0` was ignored and
   the menu ran off the right edge of a phone. */
.sd-menu--row {
  position: absolute; right: 0; top: calc(100% + 4px); width: 170px;
  left: auto; bottom: auto;
  /* It opens inside a right-aligned table cell; its items read left to right. */
  text-align: left;
  transform-origin: top right;
  opacity: 0; transform: scale(.96); pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
}

.sd-menu--row.is-open { opacity: 1; transform: scale(1); pointer-events: auto; }


/* ==========================================================================
   SETTINGS — the labelled row, the booking-mode statement, and the Google
   connection list. DESIGN.md > Settings.

   The mode statement is the piece this surface was missing. Settings could
   previously see a *connection* and nothing else, so it printed "Connected"
   for a workspace whose bookings were still Sadaa's own, and printed nothing
   at all for the legacy shared service account. Which provider owns the
   schedule is one sentence, and it goes first.
   ========================================================================== */
.sd-set { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; flex-wrap: wrap; min-width: 0; }
.sd-set + .sd-set { border-top: 1px solid var(--rule); padding-top: 15px; }
.sd-set__t { flex: 1 1 240px; min-width: 0; }
.sd-set__ttl { font-size: var(--t-body); font-weight: 620; color: var(--ink); }
.sd-set__d { font-size: var(--t-meta); color: var(--muted); margin-top: 4px; line-height: 1.55; max-width: 62ch; }
.sd-set__a { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; flex: none; }

/* Public calling (public-call-link.jsx): the agent's call link, a copy button
   beside it, and the quieter actions under it. The link is LTR whatever the
   surrounding text, and never truncated mid-word on a phone: it wraps. */
.sd-pcl { min-width: 0; }
.sd-pcl__row { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; align-items: stretch; }
.sd-pcl__url {
  flex: 1 1 260px; min-width: 0; font-family: var(--f-mono); font-size: var(--t-meta);
  color: var(--ink); background: var(--surface-warm);
}
.sd-pcl__copy { flex: none; }
.sd-pcl__more { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 10px; margin-left: -8px; }
.sd-pcl__qr { margin: 0 auto 4px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.sd-pcl__qr svg { width: min(260px, 70vw); height: auto; border: 1px solid var(--rule); border-radius: var(--r-1); }
.sd-pcl__qrcap { font-family: var(--f-mono); font-size: 11px; color: var(--muted); overflow-wrap: anywhere; text-align: center; }
/* Why the link can't take calls: the English sentence, then the Arabic one. */
.sd-pcl__en, .sd-pcl__ar { display: block; }
.sd-pcl__ar { margin-top: 2px; }
@media (max-width: 640px) {
  .sd-pcl__copy { flex: 1 1 100%; justify-content: center; }
}

.sd-mode {
  border: 1px solid var(--rule); border-radius: var(--r-2);
  background: var(--surface-warm); padding: 14px 16px; min-width: 0;
}
.sd-mode__top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sd-mode__ttl { font-size: var(--t-ui); font-weight: 620; color: var(--ink); line-height: 1.4; min-width: 0; }
.sd-mode__d { font-size: var(--t-meta); color: var(--ink-2); line-height: 1.6; margin-top: 8px; max-width: 74ch; }
/* Tone is the semantic palette and nothing else: crit is a refusal in
   progress, warn is a transition that stops writes, info is a transition that
   does not. A healthy mode is quiet. */
.sd-mode--ok   { border-color: var(--ok-line);   background: var(--ok-soft); }
.sd-mode--warn { border-color: var(--warn-line); background: var(--warn-soft); }
.sd-mode--crit { border-color: var(--crit-line); background: var(--crit-soft); }
.sd-mode--info { border-color: var(--info-line); background: var(--info-soft); }

.sd-conn {
  border: 1px solid var(--rule); border-radius: var(--r-2); padding: 14px 16px;
  display: flex; flex-direction: column; gap: 13px; min-width: 0;
}
.sd-conn__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.sd-conn__id { display: flex; align-items: flex-start; gap: 11px; min-width: 0; flex: 1 1 260px; }
.sd-conn__ico {
  width: 34px; height: 34px; border-radius: var(--r-1); flex: none;
  display: grid; place-items: center;
  background: var(--surface-sunk); border: 1px solid var(--rule); color: var(--muted);
}
.sd-conn__nm { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.sd-conn__mail { font-size: var(--t-meta); color: var(--ink-2); unicode-bidi: plaintext; overflow-wrap: anywhere; margin-top: 3px; }
.sd-conn__d { font-size: var(--t-meta); color: var(--muted); margin-top: 5px; line-height: 1.55; max-width: 62ch; }
.sd-conn__acts { display: flex; align-items: center; gap: 8px; flex: none; flex-wrap: wrap; }

/* The calendars one grant can see. A pressed-state list rather than radios:
   selecting one is a write to the server, not a form value. */
.sd-callist { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.sd-cal {
  display: flex; align-items: flex-start; gap: 11px; width: 100%; min-width: 0;
  text-align: left; padding: 11px 13px;
  border: 1px solid var(--rule); border-radius: var(--r-1); background: var(--surface);
  transition: border-color var(--d-1) var(--e), background var(--d-1) var(--e);
}
.sd-cal:hover:not([disabled]) { border-color: var(--echo-line); background: var(--echo-soft); }
.sd-cal[aria-pressed="true"] { border-color: var(--echo); background: var(--echo-soft); }
.sd-cal[disabled] { background: var(--surface-sunk); cursor: not-allowed; }
.sd-cal__m {
  width: 18px; height: 18px; border-radius: var(--r-full); flex: none; margin-top: 1px;
  border: 1px solid var(--rule-2); background: var(--surface);
  display: grid; place-items: center; color: transparent;
}
.sd-cal[aria-pressed="true"] .sd-cal__m { border-color: var(--action); background: var(--action); color: var(--on-action); }
.sd-cal__t { min-width: 0; flex: 1; }
.sd-cal__nm {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: var(--t-body); font-weight: 600; color: var(--ink); unicode-bidi: plaintext;
}
.sd-cal__meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: var(--t-micro); color: var(--muted); margin-top: 4px; }
.sd-cal__no { color: var(--warn); font-weight: 620; }
/* Only exact metadata twins fall this far, and then the provider id is
   secondary text -- never the calendar's label. */
.sd-cal__id { display: block; font-family: var(--f-mono); font-size: var(--t-micro); color: var(--faint); margin-top: 4px; overflow-wrap: anywhere; }

/* === AGENTS (owner: agents worker) ===
   Agent-page-only rules. Shared primitives are not restyled here. */
.sd-ready__i.is-state { color: var(--muted); font-weight: 500; }
.sd-ready__i.is-state[data-booking="on"] { color: var(--ok); }
.sd-agentcaps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.sd-agentcaps__i { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.sd-agentcaps__i .sd-chip { flex: none; min-width: 92px; justify-content: center; }
.sd-agentcaps__t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sd-agentcaps__n { font-weight: 550; }
.sd-agentcaps__d { color: var(--muted); font-size: var(--t-micro); overflow-wrap: anywhere; }
.sd-agentcaps__why { margin: 6px 0 0; padding-left: 18px; color: var(--muted); }
.sd-agentcaps__why li + li { margin-top: 3px; }
@media (max-width: 480px) { .sd-agentcaps__i { flex-direction: column; gap: 4px; } }

/* ==========================================================================
   AGENT DETAIL — REDESIGN 2026-09.
   The agent as a thing you configure, not a record you inspect: its identity
   leads, and the first tab answers what it is and what it will do on a call,
   side by side.
   ========================================================================== */
.sd-agenthead { align-items: center; }
.sd-agenthead__id { display: flex; align-items: center; gap: 16px; min-width: 0; }
.sd-agenthead__meta { margin-top: 7px; }
.sd-agentov {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 20px; align-items: start; min-width: 0;
}
.sd-agentov__col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.sd-agentcaps__i + .sd-agentcaps__i { border-top: 1px solid var(--rule); padding-top: 10px; }
.sd-agentcaps__n { color: var(--ink); font-size: var(--t-body); }
@media (max-width: 1179px) {
  .sd-agentov { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 899px) {
  .sd-agentov, .sd-agentov__col { gap: 16px; }
  .sd-agenthead__id { align-items: flex-start; gap: 12px; }
}

/* ==========================================================================
   VOICE PRO — 2026-10-03 (voice-pro.jsx).
   An agent's Echo voice, speaking style and languages, and the Voice Test's
   action list. Provider-neutral by construction: nothing here names a runtime.
   ========================================================================== */
.sd-vp-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.sd-vp-row > .sd-btn:last-child { margin-left: auto; }
.sd-vp-hero { display: flex; align-items: center; gap: 16px; min-width: 0; }
.sd-vp-hero__t { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-vp-vname { display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
.sd-vp-vname b { font-weight: 640; color: var(--ink); font-size: var(--t-h3); }
.sd-vp-vname--sm b { font-size: var(--t-body); font-weight: 600; }
.sd-vp-vname__ar { color: var(--muted); font-weight: 500; }
.sd-vp-vname__d { flex-basis: 100%; color: var(--muted); font-size: var(--t-meta); }
.sd-vp-vname--sm .sd-vp-vname__d { display: none; }
.sd-vp-play {
  width: 36px; height: 36px; flex: none; border-radius: var(--r-full);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--echo-soft); color: var(--echo); border: 1px solid var(--echo-line);
}
.sd-vp-play[aria-pressed="true"] { background: var(--echo); color: var(--surface); }
.sd-vp-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px;
}
.sd-vp-vcard {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px; min-width: 0;
  border: 1px solid var(--rule); border-radius: var(--r-2); background: var(--surface);
  cursor: pointer; transition: border-color var(--d-1) var(--e), box-shadow var(--d-1) var(--e);
}
.sd-vp-vcard:hover { border-color: var(--echo-line); }
.sd-vp-vcard:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo); }
.sd-vp-vcard.is-selected { border-color: var(--echo); box-shadow: inset 0 0 0 1px var(--echo); }
.sd-vp-vcard > svg { margin-left: auto; color: var(--echo); flex: none; }
.sd-vp-styles { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.sd-vp-style {
  display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; text-align: left;
  border: 1px solid var(--rule); border-radius: var(--r-2); background: var(--surface);
}
.sd-vp-style b { display: block; font-weight: 600; color: var(--ink); }
.sd-vp-style small { display: block; color: var(--muted); font-size: var(--t-meta); margin-top: 2px; }
.sd-vp-style__dot {
  width: 16px; height: 16px; margin-top: 2px; flex: none; border-radius: var(--r-full);
  border: 1.5px solid var(--rule-2); background: var(--surface);
}
.sd-vp-style.is-selected { border-color: var(--echo); box-shadow: inset 0 0 0 1px var(--echo); }
.sd-vp-style.is-selected .sd-vp-style__dot { border: 5px solid var(--echo); }
/* Wave 5: the languages callers may use -- a searchable checklist under the two
   choices, and the voice in other languages below it. K18: logical properties, colours
   from the tokens. */
.sd-vp-langs-wrap { margin-block-start: 14px; }
.sd-vp-langs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px 14px;
  max-block-size: 320px; overflow-y: auto; margin: 0; padding-block: 4px; padding-inline: 2px; list-style: none;
}
.sd-vp-lang label { display: flex; align-items: center; gap: 8px; min-block-size: 32px; cursor: pointer; }
.sd-vp-lang__native { color: var(--muted); font-size: var(--t-meta); }
.sd-vp-sub { margin-block: 18px 2px; margin-inline: 0; font-size: var(--t-body); font-weight: 600; color: var(--ink); }
.sd-vp-langs__group + .sd-vp-langs__group { margin-block-start: 6px; }
.sd-vp-voice-other { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-block-start: 18px; padding-block-start: 14px; border-block-start: 1px solid var(--rule); }
/* Voice Studio's search and filter row: a catalog of thirty voices needs both. */
.sd-vp-vfilter { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.sd-vp-vsearch { flex: 1 1 220px; max-width: 320px; }
.sd-vp-abilities { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; }
.sd-vp-abilities__i { display: flex; align-items: center; gap: 10px; }
.sd-vp-minutes { font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; }
/* The Voice Test's action list, beside the conversation. */
.sd-vp-acts { display: flex; flex-direction: column; min-width: 0; }
.sd-vp-act {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--rule);
}
.sd-vp-act:first-child { border-top: 0; }
.sd-vp-act__ic { width: 8px; height: 8px; margin-top: 6px; border-radius: var(--r-full); background: var(--ok); }
.sd-vp-act.is-simulated .sd-vp-act__ic { background: var(--info); }
.sd-vp-act.is-failed .sd-vp-act__ic { background: var(--crit); }
.sd-vp-act__t { font-weight: 600; color: var(--ink); }
.sd-vp-act__d { color: var(--muted); font-size: var(--t-meta); }
@media (max-width: 599px) {
  .sd-vp-hero { align-items: flex-start; }
  .sd-vp-grid, .sd-vp-styles { grid-template-columns: minmax(0, 1fr); }
  .sd-vp-vsearch { max-width: none; }
}

/* ==========================================================================
   CLIENT CONSOLE POLISH — 2026-10-05.
   Owner brief: a console that reads as a deliberate product. Hierarchy over
   boxes, one page scroller, no decorative glyphs, larger important labels,
   fewer tiny captions. Every rule below is scoped to its own `.sd-*` class.
   ========================================================================== */

/* ---- agent page ------------------------------------------------------- */
.sd-agentpanel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* Five text tabs. Below the width they need they still swipe sideways, but
   without a scrollbar track drawn under the strip. */
.sd-tabs--quiet { scrollbar-width: none; }
.sd-tabs--quiet::-webkit-scrollbar { display: none; }
.sd-tabs--quiet .sd-tabs__b { padding: 0 16px; font-size: var(--t-ui); }
.sd-agentcaps__codes { font-family: var(--f-mono); color: var(--faint); }

/* ---- an editor: instructions, a greeting ------------------------------ */
.sd-editor .sd-card__body { gap: 20px; }
.sd-editor__field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.sd-editor__label {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--t-body); font-weight: 620; color: var(--ink);
}
.sd-editor__help { font-size: var(--t-meta); color: var(--muted); line-height: 1.55; max-width: 72ch; }
.sd-editor__name { width: 100%; max-width: 420px; height: 40px; font-size: var(--t-ui); }
.sd-editor__area {
  max-width: none; min-height: 300px; padding: 14px 16px;
  font-size: var(--t-ui); line-height: 1.7; color: var(--ink);
  overflow-y: hidden;
}
.sd-editor__area--short { min-height: 118px; overflow-y: auto; }
.sd-editor__area:focus-visible { outline: none; border-color: var(--echo); box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus) 22%, transparent); }
.sd-editor__read {
  white-space: pre-wrap; unicode-bidi: plaintext; overflow-wrap: anywhere;
  font-size: var(--t-ui); line-height: 1.7; color: var(--ink-2);
  background: var(--surface-warm); border: 1px solid var(--rule);
  border-radius: var(--r-1); padding: 14px 16px;
}
.sd-editor__read--name {
  background: none; border: 0; padding: 0;
  font-size: var(--t-lead); font-weight: 600; color: var(--ink);
}
.sd-editor__read.is-absent { color: var(--faint); }
.sd-editor__foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.sd-editor__hint { font-size: var(--t-meta); color: var(--muted); }
.sd-editor__count {
  margin-left: auto; font-size: var(--t-micro); color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.sd-editor__state { display: inline-flex; align-items: center; min-height: 22px; font-size: var(--t-meta); color: var(--muted); }
.sd-editor__dirty {
  display: inline-flex; align-items: center; height: 20px; padding: 0 7px;
  border-radius: var(--r-1); background: var(--warn-soft); color: var(--warn);
  font-size: var(--t-micro); font-weight: 600;
}
.sd-editor__saved { display: inline-flex; align-items: center; gap: 5px; color: var(--ok); font-weight: 600; }

/* ---- the business's own instructions (agent-instructions.jsx, Wave 6 B2) ---- */
.sd-instr__hint { font-size: var(--t-meta); color: var(--muted); }
.sd-instr__hint > summary { cursor: pointer; font-weight: 600; color: var(--echo-2); }
.sd-instr__hint > ul { margin-top: 6px; }
.sd-instr__locked {
  margin: 0; padding-inline-start: 18px; display: grid; gap: 4px;
  font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55;
}
.sd-instr__lockbox {
  display: grid; gap: 6px; padding: 12px 14px; border-radius: var(--r-1);
  border: 1px solid var(--rule); background: var(--surface-sunk);
}
.sd-instr__lock-t { margin: 0; font-size: var(--t-meta); font-weight: 620; color: var(--ink); }
.sd-instr__actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.sd-instr__versions { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.sd-instr__version {
  display: grid; gap: 8px; padding: 10px 12px;
  border: 1px solid var(--rule); border-radius: var(--r-1);
}
.sd-instr__vhead {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0;
  font-size: var(--t-meta); color: var(--muted);
}
.sd-instr__vname { color: var(--ink); font-weight: 620; }
.sd-instr__vact { margin-inline-start: auto; display: inline-flex; gap: 6px; flex-wrap: wrap; }
.sd-instr__vtext {
  white-space: pre-wrap; unicode-bidi: plaintext; overflow-wrap: anywhere;
  max-height: 240px; overflow-y: auto; padding: 10px 12px; border-radius: var(--r-1);
  background: var(--surface-warm); font-size: var(--t-meta); line-height: 1.6; color: var(--ink-2);
}
.sd-instr__ask {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: var(--t-meta); color: var(--ink-2);
}

/* ---- what the agent asks before booking (agent-booking-questions.jsx, Wave 6 Task B) ---- */
.sd-bq__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.sd-bq__row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55;
}
.sd-bq__q {
  display: grid; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--rule); border-radius: var(--r-1);
}
.sd-bq__grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.sd-bq__opts { display: grid; gap: 8px; }
.sd-bq__foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }

/* ---- a switch: the console's one look (--switch-on when on, 52x32, as `.sd-as-switch`), thumb sliding to the inline end ---- */
.sd-switch {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 40px; padding: 4px 6px; border-radius: var(--r-1);
}
.sd-switch__track {
  position: relative; flex: none; width: 52px; height: 32px; border-radius: var(--r-full);
  background: var(--surface-3); box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: background-color var(--motion-base) var(--motion-ease-standard), box-shadow var(--motion-base) var(--motion-ease-standard);
}
.sd-switch__thumb {
  position: absolute; inset-block-start: 4px; inset-inline-start: 4px; width: 24px; height: 24px; border-radius: var(--r-full);
  background: var(--surface); box-shadow: var(--sh-press);
  transition: transform var(--motion-base) var(--motion-ease-spring), background-color var(--motion-base) var(--motion-ease-standard);
}
.sd-switch[aria-checked="true"] .sd-switch__track { background: var(--switch-on); box-shadow: none; }
.sd-switch[aria-checked="true"] .sd-switch__thumb { box-shadow: 0 0 0 1.5px var(--switch-on-ring), var(--sh-press); transform: translateX(calc(var(--motion-inline-dir) * 20px)); background: var(--ground); }
.sd-switch__t { font-size: var(--t-body); font-weight: 620; color: var(--ink); min-width: 3ch; text-align: left; }
.sd-switch:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo); }
.sd-switch[disabled] { cursor: not-allowed; }
.sd-switch[disabled] .sd-switch__track { opacity: .5; }
.sd-switch[disabled] .sd-switch__t { color: var(--faint); }
.sd-switch[aria-busy="true"] { cursor: progress; }

/* ---- booking on calls: the agent card and the settings rows ------------ */
.sd-bkcard .sd-card__body { gap: 14px; }
.sd-bkcard__row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; min-width: 0; }
.sd-bkcard__t { flex: 1 1 260px; min-width: 0; }
.sd-bkcard__t .sd-card__title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: var(--t-lead); }
.sd-bkcard__msg { margin-top: 6px; font-size: var(--t-body); color: var(--ink-2); line-height: 1.55; max-width: 62ch; }
.sd-bkcard__a { flex: none; }
.sd-bkcard__next { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sd-bkcard__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  border-top: 1px solid var(--rule); padding-top: 14px; font-size: var(--t-meta); color: var(--muted);
}
.sd-bkrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 0; min-width: 0; }
.sd-bkrow + .sd-bkrow { border-top: 1px solid var(--rule); }
.sd-bkrow__t { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 200px; }
.sd-bkrow__name { font-size: var(--t-ui); font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.sd-bkrow__msg { font-size: var(--t-meta); color: var(--muted); line-height: 1.5; }
.sd-bkrow__a { display: flex; align-items: center; gap: 10px; flex: none; flex-wrap: wrap; }

/* ---- voice studio ----------------------------------------------------- */
.sd-vp-current {
  display: flex; align-items: center; gap: 14px; min-width: 0;
  padding: 12px 14px; border: 1px solid var(--rule); border-radius: var(--r-2);
  background: var(--surface-warm);
}
.sd-vp-locale { margin-left: auto; }
.sd-backup { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sd-backup__t { font-size: var(--t-body); color: var(--ink); line-height: 1.55; }
.sd-backup__why { flex: 1 1 240px; }
@media (max-width: 599px) {
  .sd-vp-locale { margin-left: 0; }
  .sd-tabs--quiet .sd-tabs__b { padding: 0 12px; }
  .sd-bkcard__a { width: 100%; }
}

/* ---- how one agent speaks: Global or Efficient (2026-10-20) ----
   voice-tier-switch.jsx. Two EQUAL cards (owner): the same size, structure and
   weight, side by side on a desktop and stacked on a phone. Nothing marks one
   above the other; "In use" marks whichever the agent uses, the same way for
   either. The section reads right to left in Arabic, so spacing that has a side
   is written with logical properties. */
.sd-tier__grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: stretch;
}
.sd-tier__card {
  display: flex; flex-direction: column; gap: 14px; min-width: 0; padding: 16px;
  border: 1px solid var(--rule); border-radius: var(--r-2); background: var(--surface);
  transition: border-color var(--d-1) var(--e), box-shadow var(--d-1) var(--e);
}
.sd-tier__card.is-selected { border-color: var(--echo); box-shadow: inset 0 0 0 1px var(--echo); }
.sd-tier .sd-card__note { max-width: none; }
.sd-tier__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; min-width: 0; }
.sd-tier__t { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-tier__title { font-size: var(--t-h3); font-weight: 640; color: var(--ink); line-height: 1.35; }
.sd-tier__text { font-size: var(--t-meta); color: var(--muted); line-height: 1.5; }
.sd-tier__slot { flex: none; display: flex; align-items: center; min-height: 32px; }
.sd-tier__inuse {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
  border-radius: var(--r-1); background: var(--echo-soft); color: var(--echo-2);
  font-size: var(--t-meta); font-weight: 600; white-space: nowrap;
}
.sd-tier__slot .sd-btn { white-space: nowrap; }
.sd-tier__sample { display: flex; align-items: center; gap: 10px; min-width: 0; font-size: var(--t-meta); color: var(--ink-2); }
.sd-tier__off {
  display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 10px 12px;
  border-radius: 10px; background: var(--surface-sunk); color: var(--ink-2);
  font-size: var(--t-meta); line-height: 1.55;
}
.sd-tier__off svg { flex: none; margin-top: 2px; color: var(--faint); }
.sd-tier__card.is-off .sd-tier__voices { opacity: .72; }
.sd-tier__voices {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px;
  padding-top: 14px; border-top: 1px solid var(--rule); min-width: 0;
}
.sd-tier__voice {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding-block: 6px; padding-inline: 6px 10px;
  border: 1px solid var(--rule); border-radius: var(--r-2); background: var(--surface);
}
.sd-tier__voice .sd-vp-play { width: 32px; height: 32px; }
.sd-tier__voice.is-pickable { cursor: pointer; }
.sd-tier__voice.is-pickable:hover { border-color: var(--echo-line); }
.sd-tier__voice:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--echo); }
.sd-tier__voice.is-selected { border-color: var(--echo); box-shadow: inset 0 0 0 1px var(--echo); }
.sd-tier__voice > svg { margin-inline-start: auto; color: var(--echo); flex: none; }
.sd-tier__vname { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.sd-tier__vname b { font-size: var(--t-body); font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.sd-tier__vname small { font-size: var(--t-micro); color: var(--muted); }
.sd-tier__confirm {
  display: flex; flex-direction: column; gap: 12px; min-width: 0; padding: 16px;
  border: 1px solid var(--echo-line); border-radius: var(--r-2); background: var(--surface-warm);
}
.sd-tier__changes { display: grid; gap: 8px; margin: 0; }
.sd-tier__changes > div { display: grid; grid-template-columns: minmax(120px, 200px) minmax(0, 1fr); gap: 12px; }
.sd-tier__changes dt { font-size: var(--t-meta); color: var(--muted); }
.sd-tier__changes dd { margin: 0; font-size: var(--t-body); font-weight: 560; color: var(--ink); overflow-wrap: anywhere; }
.sd-tier__still { font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55; }
.sd-tier__actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.sd-tier__done-t { display: block; font-weight: 600; }
.sd-tier__done { margin: 4px 0; padding-inline-start: 18px; }
@media (min-width: 760px) {
  /* Side by side, both texts take two lines, so the cards' parts line up. */
  .sd-tier__text { min-height: 3em; }
}
@media (max-width: 759px) {
  .sd-tier__grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 599px) {
  .sd-tier__card, .sd-tier__confirm { padding: 14px; }
  .sd-tier__voices { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sd-tier__voice { gap: 8px; padding-inline: 5px 8px; }
  .sd-tier__changes > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .sd-tier__actions .sd-btn { flex: 1; min-height: 44px; }
}
@media (max-width: 430px) {
  /* Five sections nearly fit a phone; what does not, scrolls inside the strip. */
  .sd-tabs--quiet .sd-tabs__b { padding: 0 9px; font-size: var(--t-body); }
}

/* ---- call detail ------------------------------------------------------ */
/* The conversation scrolls in its own region once it is long: a 40-minute
   call is read like a chat, not as a page thousands of pixels tall. Its head
   (title, turn count) stays put above it. */
.sd-cd__log {
  display: flex; flex-direction: column; gap: 10px; min-width: 0;
  max-height: min(68vh, 640px); overflow-y: auto;
  padding: 4px 6px 4px 2px; margin: 0 -6px 0 -2px;
  scrollbar-width: thin;
}
.sd-cd__log:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--echo-line); border-radius: var(--r-1); }
.sd-cd__unavail { display: flex; flex-direction: column; gap: 10px; }
.sd-cd__unavail-t { font-size: var(--t-body); color: var(--ink-2); }
@media (max-width: 899px) {
  .sd-cd__log { max-height: 62vh; }
}

/* ---- voice test: the conversation is a chat surface -------------------- */
/* The stage has a height of its own on a laptop and up, so a long session
   scrolls inside the conversation (and the actions beside it) rather than
   making the page grow without end. The newest turn follows as it arrives
   unless the reader has scrolled up; "Jump to latest" brings it back. */
@media (min-width: 900px) {
  .sd-vt__body { height: clamp(460px, calc(100vh - 260px), 760px); flex: none; }
  .sd-vt__log { min-height: 0; }
}

/* ---- home: one lead figure and three that qualify it ------------------- */
.sd-stats {
  flex: none; display: grid; grid-template-columns: minmax(240px, 1fr) minmax(0, 2.3fr);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-2);
  box-shadow: var(--sh-1); min-width: 0; overflow: hidden;
}
.sd-stat { display: flex; flex-direction: column; gap: 6px; padding: 22px 24px; min-width: 0; }
.sd-stat--lead { padding: 26px 28px; justify-content: center; border-right: 1px solid var(--rule); background: var(--surface); }
.sd-stats__rest { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); min-width: 0; }
.sd-stats__rest > .sd-stat + .sd-stat { border-left: 1px solid var(--rule); }
.sd-stat__label { font-size: var(--t-body); font-weight: 600; color: var(--ink-2); }
.sd-stat--lead .sd-stat__label { font-size: var(--t-lead); color: var(--ink); }
.sd-stat__value {
  font-size: 30px; font-weight: 650; color: var(--ink); line-height: 1.1;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.sd-stat--lead .sd-stat__value { font-size: 52px; letter-spacing: -.03em; }
.sd-stat__value small { font-size: var(--t-ui); font-weight: 500; color: var(--faint); letter-spacing: 0; }
.sd-stat__value.is-none { color: var(--faint); }
.sd-stat__value.is-word, .sd-stat--lead .sd-stat__value.is-word { font-size: 22px; letter-spacing: -.01em; }
.sd-stat .sd-kpi__foot { margin-top: 0; font-size: var(--t-meta); }
.sd-stat .sd-kpi__meter { margin-top: 4px; }
.sd-rdy__go { align-self: flex-start; margin-top: 3px; font-size: var(--t-meta); font-weight: 600; color: var(--echo); }
.sd-rdy__go:hover { color: var(--echo-2); text-decoration: underline; }
@media (max-width: 1179px) {
  .sd-stats { grid-template-columns: minmax(0, 1fr); }
  .sd-stat--lead { border-right: 0; border-bottom: 1px solid var(--rule); }
}
@media (max-width: 639px) {
  .sd-stats__rest { grid-template-columns: minmax(0, 1fr); }
  .sd-stats__rest > .sd-stat + .sd-stat { border-left: 0; border-top: 1px solid var(--rule); }
  .sd-stat { padding: 16px 18px; }
  .sd-stat--lead { padding: 20px 18px; }
  .sd-stat--lead .sd-stat__value { font-size: 44px; }
}

/* ---- usage & plan: minutes remaining leads ----------------------------- */
.sd-usage {
  flex: none; display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-2);
  box-shadow: var(--sh-1); min-width: 0; overflow: hidden;
}
.sd-usage__main { display: flex; flex-direction: column; gap: 8px; padding: 28px 30px; min-width: 0; }
.sd-usage__label { font-size: var(--t-lead); font-weight: 620; color: var(--ink); }
.sd-usage__value {
  font-size: 56px; font-weight: 650; line-height: 1.05; letter-spacing: -.03em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.sd-usage__value.is-none { color: var(--faint); }
.sd-usage__meter { margin-top: 6px; max-width: 520px; height: 8px; border-radius: 4px; }
.sd-usage__sub { font-size: var(--t-body); color: var(--ink-2); }
.sd-usage__facts {
  margin: 0; display: flex; flex-direction: column; justify-content: center; gap: 18px;
  padding: 24px 28px; border-left: 1px solid var(--rule); background: var(--surface-warm); min-width: 0;
}
.sd-usage__fact { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.sd-usage__fk { font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.sd-usage__fd { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
.sd-usage__fv { font-size: var(--t-lead); font-weight: 620; color: var(--ink); }
.sd-usage__fv--warn { color: var(--warn); }
.sd-usage__fn { font-size: var(--t-meta); color: var(--muted); }
@media (max-width: 899px) {
  .sd-usage { grid-template-columns: minmax(0, 1fr); }
  .sd-usage__facts { border-left: 0; border-top: 1px solid var(--rule); }
}
@media (max-width: 639px) {
  .sd-usage__main { padding: 22px 18px; }
  .sd-usage__facts { padding: 18px; gap: 14px; }
  .sd-usage__value { font-size: 46px; }
}

/* ---- booking settings -------------------------------------------------- */
.sd-bkpage { gap: 18px; }
.sd-bksetup .sd-card__body { gap: 14px; }
.sd-bksetup__facts {
  margin: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px 24px; min-width: 0;
}
.sd-bksetup__fact { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sd-bksetup__fact dt { font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.sd-bksetup__fact dd { margin: 0; font-size: var(--t-lead); font-weight: 600; color: var(--ink); display: flex; align-items: center; min-height: 26px; }
.sd-bksetup__msg { font-size: var(--t-body); color: var(--ink-2); line-height: 1.55; max-width: 70ch; }
.sd-sform {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px 18px; min-width: 0;
}
.sd-sform__f { display: flex; width: 100%; }
.sd-sform__f .sd-input, .sd-sform__f .sd-sel { width: 100%; }
.sd-sform__v { font-size: var(--t-ui); font-weight: 600; color: var(--ink); min-height: var(--h-ctl); display: flex; align-items: center; }
.sd-sched .sd-card__body { gap: 22px; }
.sd-sched__part { display: flex; flex-direction: column; gap: 10px; min-width: 0; border-top: 1px solid var(--rule); padding-top: 18px; }
.sd-sched__h { font-size: var(--t-ui); font-weight: 620; color: var(--ink); }
.sd-week { display: flex; flex-direction: column; min-width: 0; }
.sd-week__day {
  display: grid; grid-template-columns: 120px minmax(0, 1fr) auto; align-items: center;
  gap: 12px; padding: 8px 0; min-width: 0;
}
.sd-week__day + .sd-week__day { border-top: 1px solid var(--rule); }
.sd-week__name { font-size: var(--t-body); font-weight: 600; color: var(--ink); }
.sd-week__slots { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sd-week__slot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.sd-week__slot.is-off .sd-week__t { opacity: .55; }
.sd-week__t { width: 138px; }
.sd-week__to { font-size: var(--t-meta); color: var(--muted); }
.sd-week__note { flex-basis: 100%; }
.sd-week__closed { font-size: var(--t-body); color: var(--faint); }
.sd-week__add { justify-self: end; }
.sd-closures { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sd-closures__row { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; min-width: 0; }
.sd-closures__why { flex: 1 1 180px; }
.sd-closures__why .sd-input { width: 100%; }
.sd-adv { border-top: 1px solid var(--rule); padding-top: 14px; }
.sd-adv > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
  min-height: 28px; font-size: var(--t-body); font-weight: 600; color: var(--ink-2);
}
.sd-adv > summary::-webkit-details-marker { display: none; }
.sd-adv > summary svg { transition: transform var(--d-1) var(--e); }
.sd-adv[open] > summary svg { transform: rotate(90deg); }
.sd-adv__body { margin-top: 14px; }
.sd-adv__on { align-self: start; }
.sd-sched > .sd-cardfoot {
  margin: 0; padding: 14px 20px; border-top: 1px solid var(--rule);
  background: var(--surface); border-radius: 0 0 var(--r-2) var(--r-2);
}
.sd-adv__chk { display: inline-flex; align-items: center; gap: 8px; min-height: var(--h-ctl); font-size: var(--t-body); color: var(--ink); }
.sd-bklist { gap: 0; }
.sd-gcal .sd-card__body { gap: 14px; }
.sd-gcal__d { font-size: var(--t-body); color: var(--ink-2); line-height: 1.55; }
.sd-gcal__row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; min-width: 0; }
.sd-gcal__k { width: 120px; flex: none; font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.sd-gcal__v { font-size: var(--t-ui); font-weight: 600; color: var(--ink); overflow-wrap: anywhere; min-width: 0; }
.sd-gcal__sel { min-width: 0; flex: 1 1 240px; max-width: 420px; }
.sd-gcal__warn {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; border-radius: var(--r-1); background: var(--warn-soft); color: var(--warn);
  font-size: var(--t-body);
}
@media (max-width: 899px) {
  .sd-bksetup__facts { grid-template-columns: minmax(0, 1fr); }
  .sd-week__day { grid-template-columns: minmax(0, 1fr) auto; }
  .sd-week__slots { grid-column: 1 / -1; grid-row: 2; }
}
@media (max-width: 599px) {
  .sd-week__t { width: 104px; }
  .sd-gcal__k { width: 100%; }
}

/* ---- settings: four short sections ------------------------------------- */
.sd-setpage { max-width: none; }
.sd-setx .sd-card__head { align-items: center; }
.sd-setx__facts {
  margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px 24px; min-width: 0;
}
.sd-setx__fact { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-setx__fact dt { font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.sd-setx__fact dd { margin: 0; font-size: var(--t-ui); font-weight: 560; color: var(--ink); overflow-wrap: anywhere; unicode-bidi: plaintext; }
.sd-setx__mono { font-family: var(--f-mono); font-size: var(--t-body); }
.sd-setx__form { display: flex; flex-direction: column; gap: 16px; max-width: 520px; }
.sd-setx__form .sd-sel, .sd-setx__form .sd-input { width: 100%; max-width: 420px; }
.sd-setx__ro { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; font-size: var(--t-ui); color: var(--ink); }
.sd-setx__muted { font-size: var(--t-body); color: var(--muted); }

/* ===== KNOWLEDGE — 2026-10-05 ===== */
/* Knowledge > Quick facts (business-review-page.jsx) and Knowledge > Documents
   (kb-workspace-page.jsx), under the one header both render
   (knowledge-header.jsx). Owner brief: hierarchy over boxes, one page
   scroller, outcomes said in words. Nothing here scrolls inside the page; the
   only scroll boxes are the drawers and dialogs, which sit over it. */

/* ---- the page and its header ------------------------------------------- */
.sd-knpage { gap: 18px; }
.sd-knhead { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
/* Two text tabs, underlined in echo when current. The first lines up with the
   page title above it; the underline sits exactly under each word. */
.sd-kntabs {
  display: flex; align-items: flex-end; gap: 24px; min-width: 0;
  border-bottom: 1px solid var(--rule);
}
.sd-kntabs__a {
  display: inline-flex; align-items: center; height: 40px; padding: 0 2px;
  margin-bottom: -1px; border-bottom: 2px solid transparent;
  font-size: var(--t-ui); font-weight: 560; color: var(--muted); white-space: nowrap;
  transition: color var(--d-1) var(--e), border-color var(--d-1) var(--e);
}
.sd-kntabs__a:hover { color: var(--ink); }
.sd-kntabs__a[aria-current="page"] { color: var(--ink); font-weight: 650; border-bottom-color: var(--echo); }

/* Which business, only when there is more than one. */
.sd-knbiz { flex-direction: row; align-items: center; gap: 10px; flex-wrap: wrap; }
.sd-knbiz .sd-field__label { font-size: var(--t-meta); font-weight: 600; color: var(--ink-2); }
.sd-knbiz .sd-sel { min-width: 220px; max-width: 100%; }

/* The real number of things in a list, beside its title. */
.sd-kncount {
  display: inline-flex; align-items: center; height: 20px; padding: 0 7px; margin-left: 8px;
  border-radius: 6px; background: var(--surface-sunk); vertical-align: 2px;
  font-size: var(--t-micro); font-weight: 600; color: var(--ink-2); letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

/* ---- one list pattern: documents, live quick facts, past imports ------- */
.sd-kndocs__t, .sd-knlive__t, .sd-knadd__t { min-width: 0; flex: 1 1 320px; }
.sd-knused { margin-top: 6px; font-size: var(--t-meta); color: var(--ink-2); }
.sd-knlist { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; min-width: 0; }
.sd-knitem {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 20px;
  padding: 14px 0; min-width: 0;
}
.sd-knitem + .sd-knitem { border-top: 1px solid var(--rule); }
.sd-knitem__main { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sd-knitem__title {
  font-size: var(--t-ui); font-weight: 600; color: var(--ink); line-height: 1.4;
  overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-knitem__detail {
  font-size: var(--t-body); color: var(--ink-2); line-height: 1.5;
  overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-knitem__meta { font-size: var(--t-meta); color: var(--muted); }
.sd-knitem__state {
  flex: 0 1 240px; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
}
.sd-knitem__note { font-size: var(--t-meta); color: var(--muted); line-height: 1.45; }
.sd-knitem__act { flex: none; margin-left: auto; display: flex; align-items: center; gap: 4px; }
.sd-knempty { font-size: var(--t-body); color: var(--muted); line-height: 1.6; padding: 6px 0; max-width: 62ch; }

/* A document's other actions, behind one control. `.sd-menu--row` places it. */
.sd-knmenu { position: relative; flex: none; }
.sd-knmenu .sd-menu { width: 180px; padding: 4px 0; }
.sd-knmenu__b[aria-expanded="true"] { background: var(--surface-sunk); }

/* ---- adding and changing a document ----------------------------------- */
.sd-kndrop { display: block; width: 100%; color: var(--ink-2); }
.sd-kndrop[disabled] { cursor: progress; opacity: .7; }
.sd-kndrop__t { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.sd-kndrop__name {
  font-size: var(--t-body); font-weight: 600; color: var(--ink);
  overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-kndrop__h { font-size: var(--t-meta); color: var(--muted); }
/* A form field the width of its dialog or drawer. */
.sd-knfield { display: flex; width: 100%; }
.sd-knfield .sd-input, .sd-knfield .sd-sel, .sd-knfield .sd-textarea { width: 100%; max-width: none; }
.sd-knpaste { min-height: 220px; }
.sd-knreq { color: var(--crit); }

/* ---- the reading sheet: View, Test knowledge, Edit ---------------------- */
.sd-kndrawer { width: min(560px, 100%); }
.sd-kndrawer__t { min-width: 0; }
.sd-kndrawer .sd-drawer__ttl { margin-top: 0; overflow-wrap: anywhere; }
.sd-kndrawer .sd-drawer__body { flex: 1 1 auto; }
.sd-knedit__foot { justify-content: flex-end; }
.sd-knview__state { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.sd-kndocpages { display: flex; flex-direction: column; gap: 22px; }
.sd-kndocpage { display: flex; flex-direction: column; gap: 8px; }
.sd-kndocpage__n { font-size: var(--t-meta); font-weight: 620; color: var(--muted); }
.sd-kntext {
  white-space: pre-wrap; overflow-wrap: anywhere; unicode-bidi: plaintext;
  font-size: var(--t-body); line-height: 1.7; color: var(--ink);
}

/* Test knowledge: a question, then what the agent would find and where from. */
.sd-knask { display: flex; flex-direction: column; gap: 14px; }
.sd-knask__agent {
  display: flex; align-items: center; min-height: var(--h-ctl);
  font-size: var(--t-ui); font-weight: 600; color: var(--ink); unicode-bidi: plaintext;
}
.sd-knresult { border-top: 1px solid var(--rule); padding-top: 16px; min-width: 0; }
.sd-knresult__body { display: flex; flex-direction: column; gap: 12px; }
.sd-knresult__q {
  font-size: var(--t-meta); font-weight: 600; color: var(--muted);
  unicode-bidi: plaintext; overflow-wrap: anywhere;
}
.sd-knanswers { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 14px; }
.sd-knanswer {
  display: flex; flex-direction: column; gap: 5px;
  padding: 2px 0 2px 14px; border-left: 3px solid var(--echo-line);
}
.sd-knanswer__t {
  font-size: var(--t-body); color: var(--ink); line-height: 1.6;
  white-space: pre-wrap; overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-knanswer__src {
  font-size: var(--t-meta); font-weight: 600; color: var(--echo-2);
  overflow-wrap: anywhere; unicode-bidi: plaintext;
}
.sd-knoutcome { font-size: var(--t-body); color: var(--ink-2); line-height: 1.6; max-width: 60ch; }

/* ---- Quick facts --------------------------------------------------------- */
.sd-knsearch { width: 240px; max-width: 100%; }
.sd-knadd .sd-cardfoot { margin-top: 0; }
/* Small fixes 1: the reader-failure note carries its own "Try again". */
.sd-knfail { align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.sd-knfail .sd-note__t { flex: 1 1 240px; }
.sd-knfail .sd-btn { margin-inline-start: auto; }
/* Imports waiting for the owner: one quiet soft-white band above the paste box. */
.sd-knwait {
  margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; min-width: 0;
  background: var(--surface-warm); border: 1px solid var(--rule); border-radius: var(--r-1);
}
.sd-knwait__row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px 16px;
  flex-wrap: wrap; padding: 10px 14px; min-width: 0;
}
.sd-knwait__row + .sd-knwait__row { border-top: 1px solid var(--rule); }
.sd-knwait__t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sd-knwait__what { font-size: var(--t-body); font-weight: 620; color: var(--ink); }
.sd-knwait__when { font-size: var(--t-meta); color: var(--muted); }

/* Past imports: history, folded to one line until it is asked for. */
.sd-knfold { min-width: 0; }
.sd-knfold > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
  min-height: 36px; font-size: var(--t-body); font-weight: 600; color: var(--ink-2);
}
.sd-knfold > summary::-webkit-details-marker { display: none; }
.sd-knfold > summary:hover { color: var(--ink); }
.sd-knfold > summary svg { color: var(--faint); transition: transform var(--d-1) var(--e); }
.sd-knfold[open] > summary svg { transform: rotate(90deg); }
.sd-knfold > summary .sd-kncount { margin-left: 0; }
.sd-knfold__body {
  margin-top: 10px; padding: 4px 20px 16px; min-width: 0;
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-2); box-shadow: var(--sh-1);
}
.sd-knfold__foot { display: flex; justify-content: flex-end; border-top: 1px solid var(--rule); padding-top: 14px; }

/* The review of one import. */
.sd-knreview { display: flex; flex-direction: column; min-width: 0; }
.sd-knreview .sd-back { align-self: flex-start; }
.sd-knreview__filters { margin-bottom: 16px; }
.sd-knreview .sd-revbar {
  padding: 12px 16px; border: 1px solid var(--rule); border-radius: var(--r-2); box-shadow: var(--sh-1);
}
.sd-knreview__head { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
.sd-knreview__alert { font-size: var(--t-body); font-weight: 620; color: var(--crit); }
.sd-knreview__why { margin-top: 2px; font-size: var(--t-meta); color: var(--ink-2); line-height: 1.55; }
.sd-knrev {
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-2);
  box-shadow: var(--sh-1); min-width: 0;
}
.sd-knrev.is-live { border-color: var(--ok-line); }
.sd-knrev__main { display: flex; align-items: flex-start; gap: 12px; padding: 16px; min-width: 0; }
.sd-knrev__body { flex: 1 1 auto; min-width: 0; }
.sd-knrev__chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-knrev__type { font-size: var(--t-meta); font-weight: 620; color: var(--ink-2); margin-right: 2px; }
.sd-knrev__evidence { margin-top: 12px; }
.sd-knrev__label { font-size: var(--t-micro); font-weight: 600; color: var(--muted); }
.sd-knseg { flex-wrap: wrap; align-self: flex-start; max-width: 100%; }
/* What publishing would change: live now beside what it would become. */
.sd-diffbox__head { background: var(--surface-warm); font-size: var(--t-micro); font-weight: 600; color: var(--muted); }
.sd-diffbox__c { padding: 8px 12px; min-width: 0; overflow-wrap: anywhere; }
.sd-diffbox__c--k { color: var(--muted); }
.sd-diffbox__c--was {
  color: var(--muted); text-decoration: line-through; text-decoration-color: var(--rule-3);
  unicode-bidi: plaintext;
}
.sd-diffbox__c--now { color: var(--ink); font-weight: 560; unicode-bidi: plaintext; }

/* ---- phones -------------------------------------------------------------- */
@media (max-width: 639px) {
  .sd-kntabs { gap: 20px; }
  .sd-knbiz .sd-sel { min-width: 0; flex: 1 1 200px; }
  .sd-knsearch { width: 100%; }
  .sd-knitem__state { flex: 1 1 auto; }
  /* The row's actions are touch targets. */
  .sd-knitem__act .sd-btn--sm { height: 36px; }
  .sd-knmenu__b { width: 40px; height: 40px; }
  .sd-knrev__main { padding: 14px; }
  .sd-knfold__body { padding: 4px 16px 14px; }
  .sd-knreview .sd-revbar { padding: 10px 12px; }
}

/* ==== CONSOLE-A (launch UI fixes) ========================================== */
/* UI audit F18: a user-supplied name (business, agent, caller) inside the
   console's English layout. The name itself sits in a <bdi>, which keeps an
   Arabic name in its own order; the block around it keeps the PAGE's start
   edge instead of following the name to the right. `plaintext` on the shared
   name classes is what turned an Arabic first letter into a right-aligned line. */
.sd-bidi-name { unicode-bidi: normal; text-align: left; }
.sd-bidi-name:dir(rtl) { text-align: right; }
/* Bookings states the business's time zone once (UI audit F11). */
.sd-calzone { font-size: var(--t-meta); color: var(--muted); margin: 0; }
/* STAFF (UI audit F19): New businesses' narrow list is Arabic too; its chevron
   points the way the row reads. */
.sd-records__go:dir(rtl) { transform: scaleX(-1); }

/* ==== Boot skeleton (launch speed, UI audit P24) ============================ */
/* The built pages (app-build/build-app.js) put this in #root: the mark and a
   spinner while the scripts load, replaced by React's first render. Fixed and
   out of flow, so the page that replaces it shifts nothing; the spinner only
   appears after 400 ms, so a fast load shows the mark and nothing else. */
.sd-boot {
  position: fixed; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; pointer-events: none;
}
.sd-boot__spin {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--ink) 14%, transparent); border-top-color: var(--ember-ink);
  opacity: 0;
  animation: sd-boot-in 0.2s ease 0.4s forwards, sd-boot-turn 0.8s linear infinite;
}
.sd-boot__label {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@keyframes sd-boot-in { to { opacity: 1; } }
@keyframes sd-boot-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sd-boot__spin { animation: sd-boot-in 0.2s ease 0.4s forwards; }
}
/* FE-FIX-1: the page never drew (theme-boot.js, after 15 s or a script that failed to load):
   the spinner goes, one line and a Reload button take its place. */
.sd-boot[data-boot="failed"] { pointer-events: auto; }
.sd-boot[data-boot="failed"] .sd-boot__spin,
.sd-boot[data-boot="failed"] .sd-boot__label { display: none; }
.sd-boot__fail[hidden] { display: none; }
.sd-boot__fail {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 0 16px; max-width: 32rem; text-align: center;
  color: var(--ink); font-size: 15px; line-height: 1.5;
}
.sd-boot__failtext { margin: 0; }
.sd-boot__reload {
  min-height: 44px; padding: 0 18px; border-radius: 10px; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--ink); background: transparent;
  border: 1px solid color-mix(in oklab, var(--ink) 28%, transparent);
}
.sd-boot__reload:focus-visible { outline: 2px solid var(--ember-ink); outline-offset: 2px; }
/* Today's hours (Home, Wave-1 add-on; today-hours-card.jsx): phone-first, 44 px targets. */
.sd-today .sd-btn, .sd-today .sd-input, .sd-today .sd-sel, .sd-today .sd-seg__b { min-height: 44px; }
.sd-today__facts { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.sd-today__day { font-weight: 600; color: var(--ink); }
.sd-today__line { margin-top: 0; }
.sd-today__times { display: flex; flex-wrap: wrap; gap: 12px; }
.sd-today__times .sd-field { flex: 1 1 130px; }
.sd-today__actions .sd-btn { white-space: normal; height: auto; padding-block: 8px; }
@media (max-width: 480px) { .sd-today__actions .sd-btn { flex: 1 1 100%; } }

/* ==========================================================================
   INTEGRATIONS — the business's own tools, webhooks, API keys and lookups:
   the forms, lists and delivery log of a tool's page. The hub's
   cards, the logo tiles and a tool page's header and sections are the page's own
   sheet, console-integrations.css. .sd-int__vh is here because the shell, the
   Reception page and the sign-in pages use it too.
   ========================================================================== */
.sd-int__vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.sd-int__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.sd-int__facts { display: flex; flex-direction: column; gap: 8px; margin: 0 0 8px; }
.sd-int__facts > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sd-int__facts dt { font-size: var(--t-micro); font-weight: 600; color: var(--muted); }
.sd-int__facts dd { margin: 0; font-size: var(--t-body); color: var(--ink); overflow-wrap: anywhere; }
.sd-int__result { margin-top: 10px; }
.sd-int__item { padding: 16px 18px; border: 1px solid var(--rule); border-radius: var(--r-2); background: var(--surface); display: flex; flex-direction: column; gap: 8px; }
.sd-int__itemhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.sd-int__url, .sd-int__code { font-family: var(--f-mono); font-size: var(--t-meta); color: var(--ink); overflow-wrap: anywhere; }
.sd-int__keyname { font-size: var(--t-ui); font-weight: 650; color: var(--ink); }
.sd-int__danger { color: var(--crit); }
.sd-int__empty { padding: 8px 0; }
.sd-int__emptyt { margin: 0 0 4px; font-size: var(--t-ui); font-weight: 650; color: var(--ink); }
.sd-int__secret { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; padding: 12px 14px; border-radius: var(--r-2); background: var(--surface-sunk); }
.sd-int__secret .sd-int__code { flex: 1; min-width: 0; user-select: all; }
.sd-int__form { display: flex; flex-direction: column; gap: 16px; }
.sd-int__form .sd-field { display: flex; }
.sd-int__checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 6px 16px; }
.sd-int__check { display: inline-flex; align-items: flex-start; gap: 9px; font-size: var(--t-body); color: var(--ink); cursor: pointer; }
.sd-int__check input { margin-top: 3px; flex: none; }
.sd-int__check--block > span { display: flex; flex-direction: column; gap: 2px; }
.sd-int__pair { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start; }
.sd-int__rows { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.sd-int__rowedit { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.sd-int__rowedit .sd-input { flex: 1 1 150px; }
/* A lookup input or answer row, with its own error; an answer list (enum) indented. */
.sd-int__rowblock { display: flex; flex-direction: column; gap: 6px; }
.sd-int__values { display: flex; flex-direction: column; gap: 6px; padding-inline-start: 12px; border-inline-start: 2px solid var(--rule); }
.sd-int__secretset { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-int__dots { font-family: var(--f-mono); color: var(--ink-2); }
.sd-int__try { border-top: 1px solid var(--rule); padding-top: 14px; display: flex; flex-direction: column; gap: 10px; }
.sd-int__tryttl { margin: 0; font-size: var(--t-ui); font-weight: 650; color: var(--ink); }
.sd-int__tryrow { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.sd-int__tryout { padding: 12px 14px; border-radius: var(--r-2); border: 1px solid var(--rule); display: flex; flex-direction: column; gap: 8px; }
.sd-int__tryout--ok { border-color: var(--ok); background: var(--ok-soft); }
.sd-int__tryout--info { background: var(--info-soft); }
.sd-int__tryout--crit { border-color: var(--crit-line); background: var(--crit-soft); }
.sd-int__tryhead { margin: 0; font-size: var(--t-body); font-weight: 620; color: var(--ink); }
.sd-int__presets { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--rule); }
.sd-int__preset { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sd-int__log td { vertical-align: top; }
.sd-int__notet { display: flex; flex-wrap: wrap; column-gap: .35em; }
.sd-int__testchip { margin-inline-start: 6px; }
.sd-int__why { display: block; margin-top: 4px; font-size: var(--t-micro); color: var(--muted); }
/* The delivery log below 900 px (the console's record list), inside the card's padding. */
.sd-int__records li { padding-inline: 0; min-height: 0; }
.sd-int__records li:first-child { padding-block-start: 0; }
/* The rail's "Needs attention" dot: beside the label, and on the icon when the
   rail is icons only. */
.sd-nav__dot { flex: none; width: 8px; height: 8px; border-radius: 4px; background: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); }
.sd-nav__item:has(> .sd-nav__dot) { position: relative; }
/* Settings > Workspace admins (Wave 5, lane I-F): one row per login. */
.sd-setx__people { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.sd-setx__person { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 10px 0; border-top: 1px solid var(--rule); }
.sd-setx__person:first-child { border-top: 0; padding-top: 0; }
.sd-setx__who { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 200px; }
.sd-setx__name { font-size: var(--t-ui); font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
