/* mobile.css — the Alice mobile shell.
   ====================================================================
   Every NEW component the 7-screen mobile shell needs: bottom rail,
   persistent header + call strip, transcript, composer, sheets, cards,
   plan list, approvals stack, and the full motion inventory.

   Rules this file keeps:
   - No raw hex / rgba in component rules. Every colour is a token. New
     tokens are declared ONCE at the top, in :root, each with a body.dark
     value. Everything else reads from styles.css.
   - No inline styles anywhere (strict CSP) and no inline handlers.
   - Every new class is prefixed `.m-` and every new token `--m-`, so
     nothing here can collide with the 15 existing views' class names.
   - NOT mobile-only, since 2026-08-04 ("one UI at every width"). This
     bullet used to say the component layer sat inside
     `@media (max-width:900px)` with `.m-shell` hidden above it. It does
     not: the wrapper is `@media all`, the shell runs at EVERY width, and
     it is the classic sidebar that is hidden (`body.m-on .shell`). The
     layout responds instead of the structure — rail across the bottom
     under 900px, down the left above it (see the `min-width:901px` block
     near the end). The stale wording is called out rather than just
     deleted because it is what leads people to conclude this file is
     phone-only, including about which Alice transcript UI a DESKTOP
     visitor sees. They see this one.
   - Keyframes and the reduced-motion neutraliser live at top level, so
     they are defined regardless of viewport.

   Reading order: TOKENS · MOTION · SHELL · HEADER · CALL STRIP · RAIL ·
   SIDE NAV · COMMAND PALETTE ·
   TABS · TRANSCRIPT · IDLE STAGE · COMPOSER · PLAN · PRIMITIVES ·
   BUTTONS · HOME · INBOX · CUSTOMERS · ADS · FILES · SETTINGS ·
   SHEETS · POPOVERS · MORE · CALENDAR · REPORTING DESKS.
   ==================================================================== */


/* ====================================================================
   1. TOKENS
   Only colours with no existing equivalent in styles.css are declared
   here. Anything that already has a token (--accent-bright,
   --accent-deep, --accent-text, --accent-text-soft, --accent-soft,
   --holo, --holo-text, --holo-tint, --success, --success-soft,
   --danger, --danger-soft, --on-accent, --bg-surface, --text-primary,
   --text-secondary, --text-tertiary, --font-serif, --font-sans) is
   read, not redefined.
   ==================================================================== */
:root{
  /* Warm paper canvas. A deliberate departure from the cool
     --bg-canvas:#F2F2F7 the desktop app uses; the handoff flags it as
     "mobile shell only, confirm with the designer before the whole
     product moves". Kept as its own token precisely so that decision is
     one line, not a sweep. */
  --m-paper:#FBFAF7;

  /* Warm hairlines. --sep in styles.css is cool (rgba(60,60,67,.16))
     and reads grey-blue on paper; these are the ink-warm ladder the
     prototype uses. Three steps only — the handoff's .07 and .08 are
     indistinguishable on screen and collapse into --m-line. */
  --m-line:rgba(28,22,16,.08);        /* separators + card borders */
  --m-line-2:rgba(28,22,16,.13);      /* input borders, quiet buttons */
  --m-line-dash:rgba(28,22,16,.16);   /* the dropzone dash */

  /* Ink steps between --text-secondary (.62) and nothing. */
  --m-ink-quiet:rgba(35,32,29,.62);   /* row subtitles — .62 clears AA (4.6:1) on paper (A11Y-M2) */
  --m-ink-faint:rgba(35,32,29,.34);   /* YOU/ALICE labels, chevrons */

  /* Neutral greys — iOS system fill. Used for tracks, seg-control beds,
     inactive toggles and the customer-side chat bubble. */
  --m-fill:rgba(120,120,128,.12);
  --m-track:rgba(120,120,128,.16);
  --m-fill-strong:rgba(120,120,128,.24);

  /* Alice-blue structure. --holo-soft already exists at .12; the shell
     needs a border weight and a stronger one for focus/hover. */
  --m-holo-line:rgba(46,134,220,.20);
  --m-holo-line-strong:rgba(46,134,220,.32);
  /* The call strip's bed: --holo-tint at half strength over paper. */
  --m-holo-wash:rgba(231,241,253,.55);
  /* Orb + ripple + glow. Composite so the component rules stay clean. */
  --m-orb-breath:radial-gradient(circle at 34% 30%,rgba(46,134,220,.30),rgba(46,134,220,.10) 58%,rgba(46,134,220,0) 74%);
  --m-orb-ink:radial-gradient(circle at 40% 36%,rgba(46,134,220,.34),rgba(46,134,220,.08) 70%);
  --m-orb-core:rgba(46,134,220,.16);
  --m-ripple-line:rgba(46,134,220,.46);
  --m-glow-ring:rgba(46,134,220,.30);

  /* Orange structure — the border weight that pairs with --accent-soft. */
  --m-accent-line:rgba(246,106,34,.30);

  /* Danger. --danger (#D7382E) is the fill/border step; #C3352B is the
     text step the prototype uses on white for Hang up / Veto / Stop.
     Same split as the orange and blue ramps in styles.css. */
  --m-danger-text:#C3352B;
  --m-danger-line:rgba(215,56,46,.32);

  /* Chrome washes — translucent white over paper for the composer and
     the rail, so content scrolls under them. */
  --m-chrome-wash:rgba(255,255,255,.74);
  /* The 36%-wide white sweep across the live call strip. */
  --m-sheen:rgba(255,255,255,.55);
  /* Scrim behind sheets and popovers. Warm, not neutral black. */
  --m-scrim:rgba(24,18,10,.28);

  /* Elevation. Shadows are colour and belong in tokens like the rest. */
  --m-shadow-sheet:0 -18px 50px -20px rgba(24,18,10,.34);
  --m-shadow-pop:0 26px 60px -18px rgba(24,18,10,.45);
  --m-shadow-talk:0 10px 24px -12px rgba(246,106,34,.72);
  --m-shadow-knob:0 1px 3px rgba(0,0,0,.25);

  /* The ad-draft image placeholder. No asset — a CSS hatch. */
  --m-hatch:repeating-linear-gradient(135deg,#F1EEE8 0 9px,#E8E4DC 9px 18px);

  /* Apple-Settings grouped lists (the More tab). Loggia's warm palette
     playing the grouped-list grey roles; the page paper stays --m-paper. */
  --m-set-card:#FFFFFF;               /* row/card background */
  --m-set-sep:rgba(28,22,16,.22);     /* in-card hairline separators */
  --m-set-2nd:rgba(35,32,29,.62);     /* subtitles, eyebrows, footers — .62 clears AA on the white card (A11Y-M1) */
  --m-set-3rd:rgba(35,32,29,.28);     /* chevrons */
  --m-select:rgba(120,113,105,.16);   /* wide-sidebar selection pill */
  --m-press:rgba(120,113,105,.12);    /* pressed row (:active) */
  --m-hover:rgba(120,113,105,.07);    /* hover, only under the hover guard */

  /* More-row icon tiles. Apple keeps tile colours identical in dark;
     ours carry lightened dark variants for the warm dark paper. */
  --m-tile-files:#2E86DC;
  --m-tile-settings:#8E8A85;
  --m-tile-flow:#F66A22;
  --m-tile-leadfinder:#3E9C5C;
  --m-tile-agent:#5B5BD6;
  --m-tile-studio:#8A56C9;
  --m-tile-agency:#2E9E93;
  --m-tile-owner:#A97E42;
  /* The three capability doors (P0-1/2/3): hues distinct from every tile
     above so a door never reads as the thin screen it stands beside. */
  --m-tile-workspace:#56708A;
  --m-tile-adsdesk:#CE4257;
  --m-tile-crmdesk:#7E9C3E;
  /* Calendar shipped as a More row on 2026-08-28 with NO token and no
     .m-tile-- rule, so renderMore wrote `m-tile m-tile--calendar` and the
     tile drew with no background at all — a white glyph on nothing. The
     three below close that hole and the two desks that landed with it. Free
     hue bands, deliberately: rose (nothing else is magenta), cyan (apart
     from files' brighter blue and agency's greener teal) and a deep
     emerald (much darker than leadfinder's, and the one colour money
     should be). */
  --m-tile-ads:#6B4BC4;
  --m-tile-calendar:#BE3B7C;
  --m-tile-analytics:#2C8FA8;
  --m-tile-finance:#146B4E;
  /* Sites & Jobs (wave 2). A warm terracotta — the one earth tone in the set,
     and deliberately not another blue: files, analytics and workspace already
     hold that band, and a property tile has to be findable in a list of
     seventeen 29px squares at a glance. */
  --m-tile-sites:#B4573A;
  --m-tile-pricebook:#7A5B2E;
  --m-tile-social:#A84D29;
}

body.dark{
  --m-paper:#131214;                  /* warm-shifted near-black */

  --m-line:rgba(235,235,245,.10);
  --m-line-2:rgba(235,235,245,.16);
  --m-line-dash:rgba(235,235,245,.20);

  --m-ink-quiet:rgba(235,235,245,.55);   /* .55 clears AA (5.3:1) on dark paper (A11Y-M2) */
  --m-ink-faint:rgba(235,235,245,.32);

  --m-fill:rgba(235,235,245,.10);
  --m-track:rgba(235,235,245,.14);
  --m-fill-strong:rgba(235,235,245,.22);

  --m-holo-line:rgba(59,168,255,.26);
  --m-holo-line-strong:rgba(59,168,255,.40);
  --m-holo-wash:rgba(59,168,255,.10);
  --m-orb-breath:radial-gradient(circle at 34% 30%,rgba(59,168,255,.34),rgba(59,168,255,.12) 58%,rgba(59,168,255,0) 74%);
  --m-orb-ink:radial-gradient(circle at 40% 36%,rgba(59,168,255,.36),rgba(59,168,255,.10) 70%);
  --m-orb-core:rgba(59,168,255,.20);
  --m-ripple-line:rgba(59,168,255,.50);
  --m-glow-ring:rgba(59,168,255,.34);

  --m-accent-line:rgba(246,106,34,.42);

  /* On #191A1D the dark red fails; the danger text step lightens the
     same way --accent-text does. */
  --m-danger-text:#FF6F63;
  --m-danger-line:rgba(255,111,99,.36);

  --m-chrome-wash:rgba(25,26,29,.76);
  --m-sheen:rgba(255,255,255,.10);
  --m-scrim:rgba(0,0,0,.52);

  --m-shadow-sheet:0 -18px 50px -20px rgba(0,0,0,.62);
  --m-shadow-pop:0 26px 60px -18px rgba(0,0,0,.68);
  --m-shadow-talk:0 10px 24px -12px rgba(246,106,34,.55);
  --m-shadow-knob:0 1px 3px rgba(0,0,0,.5);

  --m-hatch:repeating-linear-gradient(135deg,#232327 0 9px,#1B1B1F 9px 18px);

  /* Grouped-list roles, dark. The card lifts well above the paper so the
     grouped-inset contrast survives (#262428 on #131214 keeps Apple's
     card/page delta). */
  --m-set-card:#262428;
  --m-set-sep:rgba(235,232,228,.26);
  --m-set-2nd:rgba(240,237,233,.55);
  --m-set-3rd:rgba(240,237,233,.30);
  --m-select:rgba(235,232,228,.14);
  --m-press:rgba(235,232,228,.10);
  --m-hover:rgba(235,232,228,.05);

  --m-tile-files:#4699E8;
  --m-tile-settings:#98938D;
  --m-tile-flow:#F97B39;
  --m-tile-leadfinder:#4FB06E;
  --m-tile-agent:#6E6EE0;
  --m-tile-studio:#9B6AD6;
  --m-tile-agency:#3EB0A4;
  --m-tile-owner:#B8905A;
  --m-tile-workspace:#6E88A2;
  --m-tile-adsdesk:#DC5C71;
  --m-tile-crmdesk:#92AF52;
  --m-tile-ads:#8F72DE;
  --m-tile-calendar:#D45B96;
  --m-tile-analytics:#45A8C2;
  --m-tile-finance:#2E8C68;
  --m-tile-sites:#CB6D4E;
  --m-tile-pricebook:#9A7740;
  --m-tile-social:#B86641;
}


/* ====================================================================
   2. MOTION — the full keyframes inventory named in the handoff.
   Declared at top level so they exist whatever the viewport. All of
   them are neutralised in the reduced-motion block at the end of this
   section.
   ==================================================================== */

/* breathing orb */
@keyframes al-brth{0%,100%{transform:scale(1);opacity:.92}50%{transform:scale(1.055);opacity:1}}
/* expanding ripple rings, staggered 0/1/2s by the three .m-ripple children */
@keyframes al-rip{0%{transform:scale(.72);opacity:.55}100%{transform:scale(1.5);opacity:0}}
/* slow drifting blurred blob */
@keyframes al-ink{
  0%,100%{border-radius:48% 52% 55% 45%/50% 46% 54% 50%;transform:rotate(0deg) scale(1)}
  50%{border-radius:56% 44% 43% 57%/44% 56% 44% 56%;transform:rotate(180deg) scale(1.04)}
}
/* sheets, cards, toasts arriving */
@keyframes al-rise{from{transform:translateY(7px);opacity:0}to{transform:none;opacity:1}}
/* the content pane on screen change — header, strip and rail stay put */
@keyframes al-swap{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}
/* count badges changing value */
@keyframes al-pop{0%{transform:scale(.6);opacity:0}60%{transform:scale(1.14)}100%{transform:scale(1);opacity:1}}
/* the idle mic asking for attention */
@keyframes al-glow{0%,100%{box-shadow:0 0 0 0 var(--m-glow-ring)}50%{box-shadow:0 0 0 7px transparent}}
/* white sweep across the live call strip */
@keyframes al-sheen{0%{transform:translateX(-60%)}100%{transform:translateX(220%)}}
/* recording dot */
@keyframes al-rec{0%,100%{opacity:1}50%{opacity:.25}}
/* connecting spinner */
@keyframes al-spin{to{transform:rotate(360deg)}}
/* supporting: popovers dropping from the header, scrims, typing caret,
   full-height sheets. Named off the same al- family. */
@keyframes al-drop{from{transform:translateY(-10px) scale(.985);opacity:0}to{transform:none;opacity:1}}
@keyframes al-fade{from{opacity:0}to{opacity:1}}
@keyframes al-sheet{from{transform:translateY(100%)}to{transform:none}}
@keyframes al-caret{50%{opacity:0}}

/* More split view: iPhone push/pop. The outgoing pane's steady state is
   display:none; holding display:block INSIDE the frames keeps it rendered
   mid-flight (discrete display animation), and the default fill mode lets
   the steady state re-apply at animationend. Never add forwards/both here:
   a forwards fill would pin the outgoing pane's display:block forever. */
@keyframes m-split-in{from{transform:translateX(100%)}to{transform:translateX(0)}}
@keyframes m-split-under{from{display:block;transform:translateX(0)}to{display:block;transform:translateX(-30%)}}
@keyframes m-split-back{from{transform:translateX(-30%)}to{transform:translateX(0)}}
@keyframes m-split-out{from{display:block;transform:translateX(0)}to{display:block;transform:translateX(100%)}}
/* reduced motion: a true two-pane crossfade, not a jump cut */
@keyframes m-split-show{from{opacity:0}to{opacity:1}}
@keyframes m-split-hide{from{display:block;opacity:1}to{display:block;opacity:0}}

/* Reduced motion. Everything above becomes a no-op and transitions
   collapse. `!important` is load-bearing and deliberate here: the rules
   below set `animation` in single-class selectors of equal or higher
   specificity than this list, and a preference this strong should not
   lose a cascade race. Every use of !important in this file lives in
   this block: the blanket neutraliser, then the More-split crossfade
   exception right after it. */
@media (prefers-reduced-motion:reduce){
  .m-shell,.m-shell *,.m-shell *::before,.m-shell *::after,
  .m-scrim,.m-sheet,.m-sheet *,.m-pop,.m-pop *,
  .m-cmd,.m-cmd *,.m-cmd-scrim,.m-cmd-chord,
  .ms-scrim,.ms-sheet,.ms-sheet *{
    animation:none !important;
    transition-duration:1ms !important;
    transition-delay:0s !important;
    scroll-behavior:auto !important;
  }
  /* Exception: the More push/pop becomes a 200ms two-pane crossfade —
     parity with iOS "Prefer Cross-Fade Transitions". Same !important
     weight, higher specificity, so it beats the blanket rule above. */
  .m-split.m-sub-open .m-split-detail{animation:m-split-show 200ms ease-out !important;}
  .m-split.m-sub-open .m-split-list{animation:m-split-hide 200ms ease-out !important;}
  .m-split.m-split-pop .m-split-list{animation:m-split-show 200ms ease-out !important;}
  .m-split.m-split-pop .m-split-detail{animation:m-split-hide 200ms ease-out !important;}
}


/* ONE UI AT EVERY WIDTH, as of 2026-08-04.
   This file used to open with `.m-shell{display:none}` and wrap everything in
   (max-width:900px), so desktop kept the fifteen-item sidebar and the phone got
   Alice. Two products, one codebase, and every screen built twice.

   Now the shell runs everywhere and the LAYOUT responds instead of the
   structure: rail across the bottom under 900px, rail down the left above it.
   `@media all` rather than unwrapping 1,800 lines — the condition is the only
   thing that had to change, and leaving the block intact keeps this diff
   readable as "where does it apply", not "what did you rewrite".

   The classic sidebar is hidden, NOT removed: six heavy tools still live in
   those <section> nodes and the shell reparents them (see mobile-shell.js).
   Deleting .shell would delete Agent Studio, Lead Finder, the Owner console,
   the agency console, the agent console and the Ad Program canvas. */
body.m-on .shell{display:none;}


@media all{

/* ====================================================================
   3. SHELL — the fixed frame. Header and call strip pin to the top,
   the rail pins to the bottom, only .m-view scrolls.
   ==================================================================== */
.m-shell{
  display:flex;flex-direction:column;
  position:fixed;inset:0;z-index:10;
  height:100vh;height:100dvh;
  background:var(--m-paper);
  color:var(--text-primary);
  font-family:var(--font-sans);
  letter-spacing:-.01em;
  overflow:hidden;
}
/* the swappable content pane */
.m-view{
  flex:1;min-height:0;
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
  padding:11px 14px 22px;
  animation:al-swap .22s ease-out;
}
.m-view--flush{padding:0;}
/* Alice's transcript sizes itself against the composer, so it opts out
   of .m-view's padding and manages its own. */
.m-view--conv{padding:11px 14px 22px;display:flex;flex-direction:column;gap:15px;}


/* ====================================================================
   4. HEADER — 53px, hairline underneath, serif title left, actions right
   ==================================================================== */
.m-header{
  flex:none;display:flex;align-items:center;gap:8px;
  min-height:53px;
  padding:9px 14px 9px 15px;
  border-bottom:1px solid var(--m-line);
  background:var(--m-chrome-wash);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
}
.m-header-title{
  flex:none;white-space:nowrap;
  font-family:var(--font-serif);font-size:21px;font-weight:600;
  letter-spacing:-.02em;line-height:1.15;
}
.m-header-sub{
  min-width:0;font-size:11.5px;color:var(--m-ink-faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-header-spacer{flex:1;}
.m-header-actions{flex:none;display:flex;align-items:center;gap:7px;}
/* mobile-shell.js mounts the mic into a wrapper div rather than into the
   actions row directly, so call-strip.js has a stable node to replace.
   `display:contents` keeps that wrapper out of the layout entirely: the
   mic stays a direct flex item of .m-header-actions and its gap still
   applies. A plain <div> has no implicit role, so this costs nothing to
   the accessibility tree. */
.m-mic-host{display:contents;}

/* Mic — the one mic on screen, idle state only. 34px glyph but a 40px
   tap target via the transparent outline. */
.m-mic{
  flex:none;width:34px;height:34px;padding:0;
  border-radius:50%;
  border:1px solid var(--m-holo-line-strong);
  background:var(--holo-tint);color:var(--holo-text);
  display:inline-flex;align-items:center;justify-content:center;
  outline:3px solid transparent;outline-offset:0;
  animation:al-glow 3.4s ease-in-out infinite;
  transition:background .18s,color .18s;
}
.m-mic svg{width:17px;height:17px;}
.m-mic:hover,.m-mic:focus-visible{background:var(--holo);color:var(--on-accent);outline:none;}
.m-mic:active{transform:scale(.94);}

/* Briefings pill — dot + count, shown when unread briefs exist. */
.m-pill-brief{
  flex:none;white-space:nowrap;
  display:inline-flex;align-items:center;gap:6px;
  min-height:30px;padding:6px 11px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-holo-line-strong);
  background:var(--holo-tint);color:var(--holo-text);
  font-size:12px;font-weight:600;
  animation:al-pop .3s cubic-bezier(.2,.9,.3,1);
}
.m-pill-brief:hover{border-color:var(--holo-text);}
.m-dot,
.ms-brief-dot,
.ms-draft-dot,
.ms-live-dot{width:6px;height:6px;border-radius:50%;background:var(--holo);flex:none;}

/* "Needs you" — approvals waiting. */
.m-pill-needs{
  flex:none;white-space:nowrap;
  display:inline-flex;align-items:center;gap:7px;
  min-height:30px;padding:6px 11px 6px 9px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-accent-line);
  background:var(--accent-soft);color:var(--accent-text-soft);
  font-size:12px;font-weight:600;
}
.m-pill-needs:hover{border-color:var(--accent-bright);}
.m-pill-needs svg{width:14px;height:14px;flex:none;}
/* The badge is keyed on the number in JS so it re-mounts and re-pops. */
.m-needs-badge{
  min-width:18px;height:18px;padding:0 5px;
  border-radius:9px;
  background:var(--accent-deep);color:var(--on-accent);
  font-size:11px;font-weight:700;
  display:inline-flex;align-items:center;justify-content:center;
  animation:al-pop .32s cubic-bezier(.2,.9,.3,1);
}


/* ====================================================================
   5. CALL STRIP — replaces the mic once a call is open, directly under
   the header, outside every [data-view] so navigation never kills it.
   ==================================================================== */
/* The strip's mount point, a sibling of .m-view inside .m-shell. Out of
   the layout for the same reason .m-mic-host is: .m-call is written as a
   flex CHILD of the shell column (flex:none, so it never gives when the
   view scrolls), and a wrapper that stretched would take that away.
   Empty between calls, and an empty display:contents box draws nothing.

   ORDER IS LOAD-BEARING. call-strip.js:241 toggles .m-call ONTO this same
   element once a line opens, so the host carries both names. The two
   rules have equal specificity and .m-call is declared second, which is
   the only reason display:flex beats display:contents when a call is up.
   Do not move .m-call-host below .m-call. */
.m-call-host{display:contents;}
.m-call{
  position:relative;overflow:hidden;
  flex:none;display:flex;align-items:center;gap:9px;
  background:var(--m-holo-wash);
}
/* connecting */
.m-call--connecting{
  padding:9px 15px;
  border-bottom:1px solid var(--m-line);
}
.m-call-spin{
  flex:none;width:12px;height:12px;border-radius:50%;
  border:2px solid var(--m-holo-line);
  border-top-color:var(--holo-text);
  animation:al-spin .8s linear infinite;
}
.m-call-status{font-size:12.5px;font-weight:600;color:var(--holo-text);}

/* live */
.m-call--live{
  padding:8px 11px 8px 14px;
  border-bottom:1px solid var(--m-holo-line);
}
.m-call-sheen{
  position:absolute;top:0;bottom:0;left:0;width:36%;
  background:linear-gradient(90deg,transparent,var(--m-sheen),transparent);
  animation:al-sheen 4.6s ease-in-out infinite;
  pointer-events:none;
}
.m-call-icon{
  position:relative;flex:none;
  width:26px;height:26px;border-radius:50%;
  background:var(--holo);color:var(--on-accent);
  display:flex;align-items:center;justify-content:center;
}
.m-call-icon svg{width:14px;height:14px;}
.m-call-timer{
  position:relative;flex:none;
  font-size:13px;font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--holo-text);
}
/* "on the line" — shown when you are already on the Alice screen */
.m-call-note{
  position:relative;flex:1;min-width:0;
  font-size:12px;color:var(--m-ink-quiet);
}
/* her last line — shown everywhere else; tapping returns to the
   transcript mid-sentence. Truncated to 46 chars in JS, ellipsis here
   as the belt-and-braces. */
.m-call-line{
  position:relative;flex:1;min-width:0;
  text-align:left;border:none;background:transparent;padding:0;
  font-family:var(--font-serif);font-size:13px;line-height:1.35;
  color:var(--holo-text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-call-line:hover{text-decoration:underline;}
.m-hangup{
  position:relative;flex:none;
  min-height:30px;padding:6px 12px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-danger-line);
  background:var(--bg-surface);color:var(--m-danger-text);
  font-size:12.5px;font-weight:600;
}
.m-hangup:hover{background:var(--danger-soft);}

/* The two names call-strip.js's own header hands over as "new and still
   needing rules here" (call-strip.js:66-68, :80-81).

   The refusal. The server's wording, verbatim, rendered in the strip slot
   — which is where the line would have been, so the reason stands exactly
   where the thing it refused would be. It carries role="alert", so it is
   announced; it must therefore also LOOK like a stop, not like a caption.
   --m-danger-text on --danger-soft is 4.57:1 light, 5.74:1 dark. */
.m-call-notice{
  flex:none;margin:0;
  padding:9px 15px;
  border-bottom:1px solid var(--m-danger-line);
  background:var(--danger-soft);color:var(--m-danger-text);
  font-size:12.5px;line-height:1.45;font-weight:600;
}
/* A phrase INSIDE that refusal which is really a control. call-strip.js
   turns the words the server already wrote ("Turn her on", "Settings")
   into buttons in place, so the sentence still reads as a sentence. It is
   underlined rather than recoloured on purpose: a second colour inside a
   role="alert" bar reads as a second severity, and this is the way out of
   the one severity already there. Inline, so it never breaks the line. */
.m-call-notice-link{
  appearance:none;-webkit-appearance:none;
  display:inline;margin:0;padding:0;border:none;background:none;
  font:inherit;color:inherit;cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:2px;
}
.m-call-notice-link:hover{text-decoration-thickness:3px;}
.m-call-notice-link:focus-visible{
  outline:2px solid currentColor;outline-offset:3px;border-radius:3px;
}

/* The audio sink. It is an <audio> with no `controls`, parented to <body>
   for the life of the page, and it must render nothing while continuing
   to play — so it is collapsed rather than `display:none`, which some
   engines have historically treated as a reason to stop feeding a media
   element. Zero-sized and out of flow costs no layout and no paint. */
.m-call-audio{
  position:absolute;
  width:0;height:0;
  opacity:0;pointer-events:none;
}


/* ====================================================================
   6. BOTTOM RAIL — 7 equal items, icon + label, ≥44px tap target
   ==================================================================== */
.m-rail{
  flex:none;
  display:grid;grid-template-columns:repeat(7,1fr);
  gap:2px;
  padding:5px 5px calc(5px + env(safe-area-inset-bottom,0px));
  border-top:1px solid var(--m-line);
  background:var(--m-chrome-wash);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
}
.m-rail-item{
  position:relative;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;
  min-height:44px;padding:5px 1px;
  border:none;border-radius:12px;
  background:transparent;
  color:var(--text-tertiary);
  transition:background .18s,color .18s;
}
.m-rail-item svg{width:21px;height:21px;flex:none;}
.m-rail-label{
  font-size:9.5px;font-weight:600;letter-spacing:0;
  line-height:1.1;white-space:nowrap;
}
.m-rail-item:active{transform:scale(.94);}
.m-rail-item.on{
  background:var(--accent-soft);
  color:var(--accent-text-soft);
}
/* An unread marker riding a rail icon. */
.m-rail-dot{
  position:absolute;top:6px;left:calc(50% + 5px);
  width:7px;height:7px;border-radius:50%;
  background:var(--accent-deep);
}



/* ====================================================================
   6b. SIDE NAV — the wide-screen left column (2026-08-28).

   The rail above is the PHONE idiom and still is, unchanged, under
   901px: icons across the bottom edge. Above 901px the rail is hidden
   and THIS takes the same grid area — 260px, a workspace switcher on
   top, named groups, disclosure children, badge/shortcut furniture on
   the right of a row, and the two standing actions pinned under a
   hairline at the bottom.

   Same six destinations, same order, same names: Alice, Home, Inbox,
   Business, More, Settings. Rows that lead nowhere yet are flagged in
   the MODEL (mobile-shell.js SIDE_NAV, `dead:true`) and never in CSS —
   they are meant to look exactly like the live ones while the shape is
   being judged, and each one lands on the same honest placeholder.

   Tokens only, as everywhere in this file. The selection pill reads
   --m-select, the same "wide sidebar" grey the More split already uses,
   so the two left columns agree with each other in both palettes.
   ==================================================================== */
.m-side{
  display:none;                     /* the 901px block turns this on */
  flex-direction:column;min-height:0;
  padding:12px 11px 11px;
  border-right:1px solid var(--m-line);
  background:var(--m-chrome-wash);
}

/* -- workspace switcher ------------------------------------------- */
.m-side-ws{
  flex:none;display:flex;align-items:center;gap:11px;
  width:100%;padding:8px 9px;margin-bottom:13px;
  border:none;border-radius:10px;background:transparent;
  color:var(--text-primary);
  font-family:inherit;text-align:left;
  transition:background .16s;
}
.m-side-ws:hover{background:var(--m-hover);}
.m-side-ws:active{background:var(--m-press);}
.m-side-mark{
  flex:none;width:31px;height:31px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;
  background:var(--accent-deep);color:var(--on-accent);
  font-size:13px;font-weight:700;
}
.m-side-wstext{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.m-side-wsname{
  font-size:13px;font-weight:600;line-height:1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-side-wsplan{font-size:11px;line-height:1;color:var(--m-ink-quiet);}
.m-side-ws .m-side-chev{
  flex:none;width:15px;height:15px;color:var(--m-ink-faint);
  transition:transform .18s ease,color .16s;
}
.m-side-ws:hover .m-side-chev{color:var(--text-secondary);}
.m-side-ws[aria-expanded="true"] .m-side-chev{transform:rotate(180deg);}

/* The switcher's menu. Anchored to .m-side (position:relative at 901px)
   rather than to the button, so it spans the column's full width. */
.m-side-pop{
  position:absolute;top:52px;left:11px;right:11px;z-index:20;
  display:flex;flex-direction:column;gap:2px;
  padding:5px;
  border:1px solid var(--m-line-2);border-radius:12px;
  background:var(--m-paper);
  box-shadow:var(--m-shadow-pop);
  animation:al-swap .16s ease-out;
}
.m-side-pop-row{
  width:100%;padding:8px 10px;
  border:none;border-radius:8px;background:transparent;
  color:var(--text-primary);
  font-family:inherit;font-size:13px;text-align:left;
  transition:background .16s;
}
.m-side-pop-row:hover{background:var(--m-hover);}
.m-side-pop-row.on{background:var(--accent-soft);color:var(--accent-text-soft);font-weight:600;}
.m-side-pop-row:disabled{color:var(--m-ink-faint);}
.m-side-pop-sep{height:1px;margin:4px 6px;background:var(--m-line);}
/* Click-off catcher. A transparent button so a pointer exit exists
   alongside Escape, and so it never paints over what it dismisses. */
.m-side-scrim{
  position:fixed;inset:0;z-index:19;
  padding:0;border:none;background:transparent;
}

/* -- groups -------------------------------------------------------- */
.m-side-scroll{
  flex:1;min-height:0;overflow-y:auto;
  display:flex;flex-direction:column;gap:15px;
  scrollbar-width:none;-ms-overflow-style:none;
}
.m-side-scroll::-webkit-scrollbar{display:none;}
.m-side-group{display:flex;flex-direction:column;gap:2px;}
.m-side-head{
  margin:0 0 5px;padding:0 9px;
  font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--m-ink-faint);
}

/* -- a row --------------------------------------------------------- */
.m-side-item{
  position:relative;
  display:flex;align-items:center;gap:10px;
  width:100%;min-height:32px;padding:6px 9px;
  border:none;border-radius:7px;background:transparent;
  color:var(--m-ink-quiet);
  font-family:inherit;font-size:13px;
  text-align:left;text-decoration:none;
  transition:background .16s,color .16s;
}
/* display:flex above out-specifies the UA's [hidden]{display:none}, and the
   role-gated desk rows are hidden by attribute (refreshSide). */
.m-side-item[hidden]{display:none;}
.m-side-item:hover{background:var(--m-hover);color:var(--text-primary);}
.m-side-item:active{background:var(--m-press);}
.m-side-item.on{background:var(--m-select);color:var(--text-primary);font-weight:600;}
.m-side-item svg{flex:none;width:16px;height:16px;color:var(--m-ink-faint);transition:color .16s;}
.m-side-item:hover svg,
.m-side-item.on svg{color:var(--text-primary);}
.m-side-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* Right-edge furniture: shortcut hint (hover only), count, unread dot,
   disclosure chevron. */
.m-side-kbd{
  flex:none;display:none;align-items:center;justify-content:center;
  height:19px;padding:0 5px;border-radius:5px;
  border:1px solid var(--m-line-2);background:var(--m-fill);
  font-family:inherit;font-size:10px;font-weight:600;
  color:var(--m-ink-faint);
}
.m-side-item:hover .m-side-kbd{display:inline-flex;}
.m-side-count{
  flex:none;min-width:19px;height:19px;padding:0 6px;border-radius:10px;
  background:var(--accent-deep);color:var(--on-accent);
  font-size:10.5px;font-weight:700;line-height:19px;text-align:center;
}
.m-side-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--accent-deep);}
.m-side-item .m-side-chev,
.m-side-twist .m-side-chev{
  flex:none;width:13px;height:13px;color:var(--m-ink-faint);
  transition:transform .2s ease,color .16s;
}
.m-side-item[aria-expanded="true"] .m-side-chev,
.m-side-twist[aria-expanded="true"] .m-side-chev{transform:rotate(90deg);}

/* -- branch AND destination: two targets, one row -------------------- */
/* Business and More go somewhere AND own a folder. One button cannot do both
   without the folder being impossible to close (pressing it re-opened what the
   press was trying to shut), so the row is two sibling buttons: .m-side-item
   navigates, .m-side-twist discloses. Siblings in a plain div, never nested —
   a button inside a button is invalid and the inner one stops receiving
   clicks in some engines. */
.m-side-row{display:flex;align-items:center;gap:2px;}
.m-side-row[hidden]{display:none;}
/* The item is width:100% on its own; inside the row it shares the space. */
.m-side-row .m-side-item{width:auto;flex:1;min-width:0;}
.m-side-twist{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:26px;min-height:26px;padding:0;
  border:none;border-radius:6px;background:transparent;
  color:var(--m-ink-quiet);cursor:pointer;
  transition:background .16s,color .16s;
}
.m-side-twist:hover{background:var(--m-hover);color:var(--text-primary);}
.m-side-twist:hover .m-side-chev{color:var(--text-primary);}
.m-side-twist:active{background:var(--m-press);}

/* -- disclosure children ------------------------------------------- */
/* 0fr to 1fr, so the drawer animates to its CONTENT height with no
   measured pixel value anywhere. visibility rides along on a delay equal
   to the slide, which takes a closed drawer out of the tab order without
   cutting the animation short. (The reduced-motion block zeroes the
   delay, so there it simply snaps.) */
.m-side-kids{
  display:grid;grid-template-rows:0fr;
  opacity:0;visibility:hidden;
  transition:grid-template-rows .24s ease,opacity .2s ease,visibility 0s linear .24s;
}
.m-side-kids.open{
  grid-template-rows:1fr;opacity:1;visibility:visible;
  transition:grid-template-rows .24s ease,opacity .2s ease,visibility 0s;
}
.m-side-kidsin{
  position:relative;overflow:hidden;min-height:0;
  display:flex;flex-direction:column;gap:2px;
  padding-top:2px;
}
/* The rule the children hang off, centred under the parent's icon. */
.m-side-kidsin::before{
  content:"";position:absolute;top:2px;bottom:2px;left:17px;
  border-left:1px solid var(--m-line);
}
.m-side-item--kid{padding-left:29px;}
.m-side-item--kid svg{width:14px;height:14px;}

/* -- the two standing actions -------------------------------------- */
.m-side-foot{
  flex:none;display:flex;flex-direction:column;gap:2px;
  margin-top:11px;padding-top:11px;
  border-top:1px solid var(--m-line);
}

/* -- the placeholder every unwired row lands on -------------------- */
.m-dead{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:12px;min-height:44vh;text-align:center;
}
.m-dead-ico{
  width:46px;height:46px;border-radius:14px;
  display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--m-line-dash);color:var(--m-ink-faint);
}
.m-dead-ico svg{width:22px;height:22px;}
.m-dead-title{margin:0;font-family:var(--font-serif);font-size:19px;font-weight:600;}
.m-dead-sub{margin:0;max-width:340px;font-size:12.5px;line-height:1.6;color:var(--m-ink-quiet);}


/* ====================================================================
   6c. COMMAND PALETTE — the keyboard's half of the column (2026-08-28).

   ⌘K/Ctrl-K opens it, `g` + a letter skips it entirely, `?` lists both.
   The Search row in §6b has worn a ⌘K cap since the column was built;
   this is the thing the cap was promising.

   IT MOUNTS ON <body>, NOT INSIDE .m-shell, and that is deliberate:
   screens.js raises its sheets on <body> at z-index 20/21 and marks
   every OTHER body child `inert` for as long as one is up. A palette
   living inside the shell would be painted over by a sheet and switched
   off by it at the same time. Out here at z-index 60/61 it clears the
   sheets, clears the toast (22), and clears the classic modals the
   hosted desks move to <body> (50) — and mobile-shell.js runs its own
   inert sweep, skipping any node that already carries the attribute so
   closing the palette can never release a sheet's trap.

   The cost of living outside .m-shell is that nothing is inherited, so
   font, colour and letter-spacing are set here rather than borrowed;
   the reduced-motion neutraliser at the top of this file names .m-cmd
   explicitly for the same reason, and the :focus-visible rule in §22
   was extended rather than duplicated.

   Tokens only, as everywhere in this file — both palettes follow.
   ==================================================================== */
.m-cmd-scrim{
  position:fixed;inset:0;z-index:60;
  background:var(--m-scrim);
  animation:al-fade .14s ease-out;
}
/* Centred with left/right/margin rather than a translate, because
   al-drop animates `transform` — a translateX(-50%) here would be
   overwritten mid-flight and the dialog would fly in off-centre. */
.m-cmd{
  position:fixed;z-index:61;left:0;right:0;margin:0 auto;
  top:calc(9vh + env(safe-area-inset-top,0px));
  width:min(560px,calc(100vw - 22px));
  max-height:min(74vh,calc(100dvh - 16vh));
  display:flex;flex-direction:column;
  border:1px solid var(--m-line-2);border-radius:16px;
  background:var(--bg-surface);
  box-shadow:var(--m-shadow-pop);
  color:var(--text-primary);
  font-family:var(--font-sans);letter-spacing:-.01em;
  overflow:hidden;
  animation:al-drop .16s ease-out;
}

/* -- the query field ----------------------------------------------- */
.m-cmd-field{
  flex:none;display:flex;align-items:center;gap:9px;
  padding:12px 14px;
  border-bottom:1px solid var(--m-line);
}
.m-cmd-input{
  flex:1;min-width:0;
  border:none;background:transparent;
  color:var(--text-primary);
  font-family:inherit;letter-spacing:-.01em;line-height:1.3;
  /* 16px and not a decimal under it: iOS zooms the whole page when a
     focused field is smaller than 16px, and this shell runs on phones. */
  font-size:16px;
}
/* …and the same three declarations again, one class heavier. MEASURED,
   because the rule above alone does not win: styles.css dresses every
   classic text field with

     input[type="text"],…,textarea,select{width:100%;padding:11px 13px;
       border:1px solid var(--border);border-radius:var(--radius);
       font-size:15px;background:var(--bg-surface)}

   and an attribute selector plus a type selector is (0,1,1), which
   out-specifies a single class. Driven in a browser, the palette field
   was wearing that whole skin: a 1px border, a 10px radius, 11/13px
   padding, a surface fill and 15px type — and 15px is exactly the size
   that makes iOS zoom the page the moment the field takes focus, which
   is the one thing §6c is supposed to prevent. Two classes beat (0,1,1)
   with no !important, and only the declarations that actually lost are
   repeated here. */
.m-cmd .m-cmd-input{
  padding:0;border:none;border-radius:0;
  background:transparent;font-size:16px;
}
/* The field is the only focus target in the dialog and it always has
   focus, so a ring around it would be permanent furniture. The ACTIVE
   ROW carries the visible selection instead (.m-cmd-opt.on), which is
   the thing the eye is actually tracking, and aria-activedescendant
   says the same to a screen reader.

   Two classes again for the same reason: styles.css's `input:focus`
   paints an iOS-blue 3.5px ring and repaints the border, and outline
   alone would leave both of them on. */
.m-cmd .m-cmd-input:focus{outline:none;border-color:transparent;box-shadow:none;}
.m-cmd-input::placeholder{color:var(--m-ink-faint);}

/* -- the results --------------------------------------------------- */
.m-cmd-list{
  flex:1;min-height:0;
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
  margin:0;padding:6px;list-style:none;
}
.m-cmd-opt{
  display:flex;align-items:center;gap:10px;
  min-height:44px;padding:7px 9px;
  border-radius:9px;
  color:var(--m-ink-quiet);
  font-size:14px;
  cursor:pointer;
}
.m-cmd-opt:hover{background:var(--m-hover);color:var(--text-primary);}
.m-cmd-opt.on{background:var(--m-select);color:var(--text-primary);font-weight:600;}
.m-cmd-ico{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:20px;height:20px;color:var(--m-ink-faint);
}
.m-cmd-ico svg{width:17px;height:17px;}
.m-cmd-opt.on .m-cmd-ico{color:var(--text-primary);}
.m-cmd-name{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.m-cmd-where{
  flex:none;white-space:nowrap;
  font-size:11px;font-weight:500;color:var(--m-ink-faint);
}
/* Same cap as the column's .m-side-kbd, one size up: it is read here
   rather than glanced at, and it is never hidden behind a hover. */
.m-cmd-kbd{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  min-width:22px;height:20px;padding:0 6px;border-radius:5px;
  border:1px solid var(--m-line-2);background:var(--m-fill);
  font-family:inherit;font-size:10.5px;font-weight:600;
  color:var(--m-ink-quiet);white-space:nowrap;
}
.m-cmd-empty{
  margin:0;padding:26px 16px;text-align:center;
  font-size:13px;color:var(--m-ink-quiet);
}
.m-cmd-empty[hidden]{display:none;}

/* -- the footer both modes share ----------------------------------- */
.m-cmd-foot{
  flex:none;display:flex;align-items:center;gap:10px;
  padding:9px 12px calc(9px + env(safe-area-inset-bottom,0px));
  border-top:1px solid var(--m-line);
}
.m-cmd-hint{flex:1;min-width:0;font-size:11px;line-height:1.4;color:var(--m-ink-faint);}
.m-cmd-more{
  flex:none;min-height:32px;padding:0 11px;
  border:1px solid var(--m-line-2);border-radius:8px;
  background:transparent;color:var(--m-ink-quiet);
  font-family:inherit;font-size:11.5px;font-weight:600;
  cursor:pointer;
}
.m-cmd-more:hover{background:var(--m-hover);color:var(--text-primary);}

/* -- `?`: the same dialog, showing the map instead of the list ----- */
.m-cmd-head{
  flex:none;display:flex;align-items:center;gap:10px;
  padding:14px 14px 10px;
}
.m-cmd-title{
  flex:1;min-width:0;margin:0;
  font-family:var(--font-serif);font-size:19px;font-weight:600;
  letter-spacing:-.02em;line-height:1.2;
}
.m-cmd-close{
  flex:none;width:32px;height:32px;padding:0;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--m-line-2);border-radius:50%;
  background:transparent;color:var(--m-ink-quiet);
  font-family:inherit;font-size:14px;line-height:1;
  cursor:pointer;
}
.m-cmd-close:hover{background:var(--m-hover);color:var(--text-primary);}
.m-cmd-keys{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:0 14px 10px;
}
.m-cmd-key{
  display:flex;align-items:center;gap:12px;
  min-height:38px;padding:5px 0;
  border-top:1px solid var(--m-line);
}
.m-cmd-key:first-child{border-top:none;}
.m-cmd-keyname{flex:1;min-width:0;font-size:13.5px;color:var(--m-ink-quiet);}

/* -- the chord's only visible state -------------------------------- */
/* `g` on its own has to say something, or it is indistinguishable from
   a key that did nothing. Top-right under the header, which is the one
   corner that is free at BOTH widths — the rail owns the bottom edge
   below 901px and the column owns the left above it. */
.m-cmd-chord{
  position:fixed;z-index:59;
  top:calc(62px + env(safe-area-inset-top,0px));right:14px;
  display:flex;align-items:center;justify-content:center;
  min-width:30px;height:30px;padding:0 9px;
  border:1px solid var(--m-line-2);border-radius:8px;
  background:var(--bg-surface);
  box-shadow:var(--m-shadow-pop);
  color:var(--text-primary);
  font-family:var(--font-sans);font-size:12px;font-weight:700;
  letter-spacing:.06em;
  animation:al-drop .12s ease-out;
}


/* ====================================================================
   7. TABS — two idioms. .m-seg is the .seg-ios bed at mobile radius
   (Alice, Inbox, Customers, ad window). .m-pilltabs scroll (Ads).
   ==================================================================== */
.m-seg,
.ms-seg,
.sa-tabs,
.ms-mode{
  display:inline-flex;background:var(--m-fill);
  border-radius:11px;padding:2px;gap:2px;margin-bottom:12px;
  max-width:100%;
}
.m-seg button,.ms-seg button,.sa-tabs button,
.ms-mode-btn{
  border:none;background:transparent;
  padding:7px 14px;min-height:34px;border-radius:9px;
  font-size:13px;font-weight:600;color:var(--text-primary);
  white-space:nowrap;
}
.m-seg button.on,.ms-seg button.on,.sa-tabs button.on,
.ms-mode-btn.on{background:var(--bg-surface);box-shadow:var(--shadow-card);}

/* Alice's Conversation / What she's doing / Her plan */
.m-tabs{flex:none;display:flex;gap:3px;padding:9px 15px 3px;}
.m-tab{
  padding:6px 12px;min-height:32px;
  border:none;border-radius:8px;
  background:transparent;color:var(--m-ink-quiet);
  font-size:12.5px;font-weight:600;
}
.m-tab.on{background:var(--accent-soft);color:var(--accent-text-soft);}

/* Ads — four pills that scroll rather than compress */
.m-pilltabs,
.ms-pill-tabs{
  display:flex;gap:5px;padding-bottom:11px;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.m-pilltabs::-webkit-scrollbar,.ms-pill-tabs::-webkit-scrollbar{display:none;}
.m-pilltab,
.ms-pill-tab{
  flex:none;white-space:nowrap;
  padding:7px 13px;min-height:34px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:12.5px;font-weight:600;
}
.m-pilltab.on,
.ms-pill-tab.on,
.ms-chip.on{
  background:var(--accent-soft);
  border-color:var(--m-accent-line);
  color:var(--accent-text-soft);
}

/* ── the gooey tab strip ──────────────────────────────────────────────────
   Ported from the framer-motion demo. Two layers, exactly as the original
   builds it:

     1. a FILTERED layer holding the moving blob and the bar it melts into.
        The SVG filter blurs, then a colour matrix pushes alpha far past 1
        and pulls it back (…0 0 0 19 -9), which turns two blurred shapes that
        overlap into one shape with a liquid neck. Nothing readable is ever
        inside this layer — the blur would eat it;
     2. the LABELS, unfiltered, on top. The original calls this the
        "interactive text overlay, no filter" and it is the whole trick.

   The blob is one element that moves, because that is what framer-motion's
   layoutId compiles down to: a shared element transitioning between slots. */
.ms-goo-tabs{position:relative;display:block;padding-bottom:11px;}
.ms-goo-layer{position:absolute;inset:0 0 11px 0;filter:url(#loggia-goo);
  pointer-events:none;}
/* The bar the blob melts into. Sits along the bottom of the strip so the
   active tab reads as cut from the same liquid. */
.ms-goo-bar{position:absolute;left:0;right:0;bottom:0;height:9px;
  border-radius:5px;background:var(--accent-soft);}
.ms-goo-blob{position:absolute;top:0;height:34px;border-radius:var(--radius-pill);
  background:var(--accent-soft);
  transition:transform .42s cubic-bezier(.22,1,.36,1), width .42s cubic-bezier(.22,1,.36,1);
  will-change:transform,width;}
.ms-goo-row{position:relative;display:flex;gap:5px;overflow-x:auto;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;}
.ms-goo-row::-webkit-scrollbar{display:none;}
.ms-goo-tab{flex:none;white-space:nowrap;padding:7px 13px;min-height:34px;
  border:none;background:none;border-radius:var(--radius-pill);
  font-size:12.5px;font-weight:600;color:var(--text-secondary);
  transition:color .2s;}
.ms-goo-tab[aria-selected="true"]{color:var(--accent-text-soft);}

/* Without backdrop-filter support the blur/matrix trick still works — it is
   an SVG filter, not a backdrop one — but a browser that cannot do SVG
   filters at all would show two hard rectangles. That degrades to a plain
   pill strip, which is exactly what the non-gooey variant looks like, so
   nothing needs saying here.

   Reduced motion keeps the blob (it is the selection indicator, not
   decoration) and drops the travel. */
@media (prefers-reduced-motion: reduce){
  .ms-goo-blob{transition:none;}
}


/* ====================================================================
   8. TRANSCRIPT — you-bubble, alice serif row, tool act, panel opened
   ==================================================================== */
.m-row,
.sa-row{display:flex;flex-direction:column;gap:4px;animation:al-rise .22s ease-out;}
.m-who,
.sa-row-label,
.ms-bubble-label{
  font-size:10px;font-weight:700;letter-spacing:.07em;
  color:var(--m-ink-faint);
}

/* Her name left, the model that wrote the reply right, on one line above the
   sentence. `width:100%` because .sa-row-alice is a column flex box that would
   otherwise shrink this row to its content and leave the chip mid-line. */
.sa-row-head{display:flex;align-items:baseline;gap:10px;width:100%;}
.sa-row-head .sa-row-label{flex:none;}
/* Same family as .sa-act-tool — mono, --m-fill, 6px — one step quieter, because
   a tool chip records something she DID and this only records what answered.
   max-width + ellipsis so a long local model id cannot push the row wide; the
   full id is on the element's title. */
.sa-model{
  margin-left:auto;flex:none;max-width:46%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:ui-monospace,Menlo,monospace;
  font-size:10.5px;font-weight:600;
  padding:2px 7px;border-radius:6px;
  background:var(--m-fill);color:var(--m-ink-quiet);
}

/* You — right, blue fill, white, 18/18/6/18 */
.m-row--you,
.sa-row-you{align-self:flex-end;align-items:flex-end;max-width:82%;}
.m-bubble-you,
.sa-bubble{
  padding:9px 14px;
  border-radius:18px 18px 6px 18px;
  background:var(--holo-text);color:var(--on-accent);
  font-size:14.5px;line-height:1.45;
}

/* Alice — left, serif on paper, no bubble */
.m-row--alice,
.sa-row-alice{align-self:flex-start;max-width:92%;}
.m-said,
.sa-said,
.sa-plan-goal{
  font-family:var(--font-serif);font-size:15.5px;line-height:1.52;
  color:var(--text-primary);
}
/* typewriter caret while speak() reveals word by word */
.m-caret{
  display:inline-block;width:2px;height:.95em;
  margin-left:3px;vertical-align:-2px;
  background:var(--m-ink-quiet);
  animation:al-caret 1.05s steps(2,start) infinite;
}

/* Tool act — the tool she reached for and what it did */
.m-act,
.sa-row-act{
  display:flex;align-items:baseline;gap:9px;
  padding:10px 0;border-top:1px solid var(--m-line);
}
.m-act-tool,
.sa-act-tool{
  flex:none;white-space:nowrap;
  font-family:ui-monospace,Menlo,monospace;
  font-size:11px;font-weight:600;
  padding:2px 7px;border-radius:6px;
  background:var(--m-fill);color:var(--text-primary);
}
.m-act-text,
.sa-act-text{flex:1;font-size:12.5px;line-height:1.5;color:var(--text-secondary);}
.m-act-state,
.sa-act-state{flex:none;white-space:nowrap;font-size:11px;font-weight:600;}
.m-act-state--read,.sa-state-read{color:var(--m-ink-quiet);}
.m-act-state--done,.sa-state-done{color:var(--success);}
.m-act-state--waiting,.sa-state-wait{color:var(--accent-text);}

/* Panel opened — "Alice put a settings sheet on your screen" */
.m-opened,
.sa-row-panel{
  align-self:stretch;
  display:flex;align-items:center;gap:9px;
  padding:9px 12px;border-radius:12px;
  background:var(--holo-tint);
  border:1px solid var(--m-holo-line);
  animation:al-rise .22s ease-out;
}
.m-opened svg,.sa-row-panel svg{width:15px;height:15px;flex:none;color:var(--holo-text);}
.m-opened-text,
.sa-panel-text{font-size:12px;font-weight:600;color:var(--holo-text);}


/* ====================================================================
   9. IDLE STAGE — 150px breathing orb + Talk to Alice
   ==================================================================== */
.m-idle,
.sa-idle{
  flex:1;display:flex;flex-direction:column;
  align-items:center;justify-content:center;
  gap:20px;padding:12px 6px 22px;text-align:center;
}
.m-stage,
.sa-orb-stage{
  position:relative;width:150px;height:150px;
  display:flex;align-items:center;justify-content:center;
}
/* Breath */
.m-orb--breath,
.sa-orb{
  width:132px;height:132px;border-radius:50%;
  background:var(--m-orb-breath);
  animation:al-brth 4.4s ease-in-out infinite;
}
/* Ripple — three rings, staggered 0/1/2s, around a solid core */
.m-orb--ripple{
  position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
}
.m-ripple{
  position:absolute;width:132px;height:132px;border-radius:50%;
  border:1.5px solid var(--m-ripple-line);
  animation:al-rip 3s ease-out infinite;
}
.m-ripple:nth-child(2){animation-delay:1s;}
.m-ripple:nth-child(3){animation-delay:2s;}
.m-ripple-core{
  width:58px;height:58px;border-radius:50%;
  background:var(--m-orb-core);
}
/* Ink */
.m-orb--ink{
  width:124px;height:124px;
  background:var(--m-orb-ink);
  filter:blur(1px);
  animation:al-ink 11s ease-in-out infinite;
}

.m-greet,
.sa-idle-title{
  margin:0 0 7px;
  font-family:var(--font-serif);font-size:27px;font-weight:600;
  letter-spacing:-.02em;line-height:1.16;
}
.m-greet-sub,
.sa-idle-sub{
  margin:0;font-size:13.5px;line-height:1.6;
  color:var(--text-secondary);max-width:250px;
}
.m-talk,
.sa-talk{
  padding:13px 26px;min-height:46px;
  border:none;border-radius:var(--radius-pill);
  background:var(--accent-deep);color:var(--on-accent);
  font-size:15px;font-weight:600;
  box-shadow:var(--m-shadow-talk);
}
.m-talk:hover,.sa-talk:hover{background:var(--accent-text);}
.m-talk:active,.sa-talk:active{transform:scale(.97);}

/* P1-12 — the "first real call" readiness checklist, under the Talk button
   on the idle stage. Owner-only, and only while a precondition is unmet. */
.sa-vr-card{
  margin:22px auto 0;max-width:340px;width:100%;
  padding:14px 15px;border-radius:14px;
  background:var(--bg-surface);border:1px solid var(--m-line);
  text-align:left;
}
/* The top tab: the strip the card is carried by. Pulled out to the card's
   own edges so it reads as a title BAR — the thing a window is dragged by —
   rather than as a heading that happens to respond to a press.
   touch-action:none is load-bearing: without it a touch drag scrolls the
   transcript underneath instead of moving the card. */
.sa-vr-head{
  display:flex;align-items:center;gap:9px;
  margin:-14px -15px 9px;padding:10px 15px;
  border-bottom:1px solid var(--m-line);
  border-radius:13px 13px 0 0;
  background:var(--m-fill);
  cursor:grab;touch-action:none;
  -webkit-user-select:none;user-select:none;
}
.sa-vr-head:active{cursor:grabbing;}
.sa-vr-card.sa-vr-dragging .sa-vr-head{cursor:grabbing;}
/* The grip is the KEYBOARD half of the handle (arrow keys move the card,
   pressing it puts the card back). The pointer half is the whole bar above,
   which is why this stays a quiet 30px square rather than a 44px control
   competing with the title beside it. */
.sa-vr-grip{
  appearance:none;-webkit-appearance:none;
  flex:none;width:30px;height:30px;margin:-5px 0 -5px -4px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  border:none;border-radius:8px;background:none;
  color:var(--text-tertiary);cursor:grab;
}
.sa-vr-grip:hover{background:var(--m-fill-strong);color:var(--text-secondary);}
.sa-vr-grip:focus-visible{outline:2px solid var(--holo);outline-offset:1px;}
.sa-vr-card.sa-vr-dragging .sa-vr-grip{cursor:grabbing;}
.sa-vr-title{margin:0;font-family:var(--font-serif);font-size:15px;font-weight:600;color:var(--text-primary);}
.sa-vr-sub{margin:0 0 10px;font-size:12.5px;line-height:1.5;color:var(--text-secondary);}
/* Carried off its corner (screen-alice.js placeCard). position:fixed, so
   "anywhere" means anywhere on the SCREEN and not anywhere inside a stage
   that is a different size at every breakpoint. The doubled class is what
   beats the >=900px rule further down on specificity rather than on source
   order; inside .m-shell (z-index:10) this still paints under a sheet. */
.sa-vr-card.sa-vr-card--free{
  position:fixed;right:auto;bottom:auto;
  margin:0;width:320px;max-width:calc(100vw - 16px);
  z-index:12;box-shadow:var(--m-shadow-pop);
}
.sa-vr-card.sa-vr-dragging{opacity:.97;}
.sa-vr-list{display:flex;flex-direction:column;gap:9px;}
.sa-vr-item{display:flex;align-items:flex-start;gap:9px;}
.sa-vr-mark{
  flex:none;width:18px;height:18px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:12px;line-height:1;
  background:var(--m-fill);color:var(--text-tertiary);
}
.sa-vr-item.ok .sa-vr-mark{background:var(--holo-tint);color:var(--holo-text);}
.sa-vr-body{flex:1;min-width:0;}
.sa-vr-label{font-size:13px;font-weight:600;color:var(--text-primary);}
.sa-vr-detail{margin:2px 0 0;font-size:12px;line-height:1.45;color:var(--text-secondary);}
.sa-vr-fix{
  flex:none;align-self:center;
  padding:5px 14px;min-height:44px;border:none;border-radius:var(--radius-pill);  /* 44px tap target (A11Y-M3) */
  background:var(--accent-deep);color:var(--on-accent);
  font-size:12px;font-weight:600;
}
.sa-vr-fix:hover{background:var(--accent-text);}


/* Opened AT a control: screen-alice.js's flashSwitch marks the row the
   caller was pointing at (call-strip's "Turn her on" lands on her master
   switch, not merely on the sheet that holds it). Two slow pulses, then
   nothing — long enough to find, short enough not to nag. */
.sa-flash{border-radius:12px;animation:sa-flash 2.2s ease-out 1;}
@keyframes sa-flash{
  0%{box-shadow:0 0 0 0 var(--m-glow-ring);}
  14%{box-shadow:0 0 0 5px var(--m-glow-ring);}
  50%{box-shadow:0 0 0 0 var(--m-glow-ring);}
  68%{box-shadow:0 0 0 5px var(--m-glow-ring);}
  100%{box-shadow:0 0 0 0 rgba(0,0,0,0);}
}
/* The sheet is parented to <body>, outside the screen element that carries
   .sa-reduced — so the media query is the whole fallback here. It keeps the
   ring rather than dropping it: the point was to say WHICH row. */
@media(prefers-reduced-motion:reduce){
  .sa-flash{animation:none;box-shadow:0 0 0 3px var(--m-glow-ring);}
}

/* ====================================================================
   10. COMPOSER — suggestion chips + input + 40px circular send
   ==================================================================== */
.m-composer,
.sa-composer{
  flex:none;
  padding:8px 13px 12px;
  border-top:1px solid var(--m-line);
  background:var(--m-chrome-wash);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
}
.m-chips,
.sa-chips,
.ms-chips{
  display:flex;gap:6px;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  padding:2px 2px 9px;scrollbar-width:none;
}
.m-chips::-webkit-scrollbar,.sa-chips::-webkit-scrollbar,.ms-chips::-webkit-scrollbar{display:none;}
.m-chip,
.sa-chip,
.ms-chip,
.sa-jump{
  flex:none;white-space:nowrap;
  padding:7px 12px;min-height:32px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:12px;font-weight:500;
  transition:border-color .14s,color .14s;
}
.m-chip:hover,.m-chip:focus-visible,
.sa-chip:hover,.sa-chip:focus-visible,
.ms-chip:hover,.ms-chip:focus-visible,
.sa-jump:hover,.sa-jump:focus-visible{
  border-color:var(--accent-bright);color:var(--accent-text);outline:none;
}
.m-composer-form,
.sa-composer-row,
.ms-composer{display:flex;align-items:center;gap:9px;}
.m-input,
.sa-input,
.ms-composer-input{
  flex:1;min-width:0;
  padding:11px 15px;
  border-radius:22px;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-primary);
  font-size:14.5px;
  transition:border-color .15s,box-shadow .15s;
}
.m-input::placeholder,.sa-input::placeholder,.ms-composer-input::placeholder{color:var(--m-ink-faint);}
.m-input:focus,
.sa-input:focus,
.ms-composer-input:focus{
  outline:none;border-color:var(--holo);
  box-shadow:0 0 0 3px var(--holo-soft);
}
.m-send,
.sa-send,
.ms-composer-send{
  flex:none;width:40px;height:40px;padding:0;
  border:none;border-radius:50%;
  background:var(--accent-deep);color:var(--on-accent);
  display:flex;align-items:center;justify-content:center;
}
.m-send svg,.sa-send svg,.ms-composer-send svg{width:17px;height:17px;}
.m-send:hover,.sa-send:hover,.ms-composer-send:hover{background:var(--accent-text);}
.m-send:active,.sa-send:active,.ms-composer-send:active{transform:scale(.94);}
.m-send:disabled,.sa-send:disabled,.ms-composer-send:disabled{opacity:.4;transform:none;}

/* The gear toggle (screen-alice.js). Chip-shaped and LATCHED: .on holds the
   holo tint so which gear the next send carries is readable at a glance, and
   .sa-riffing on the composer lets the input carry the same line — the mode
   is owner-visible state, never something inferred. */
.sa-riff{
  flex:none;white-space:nowrap;
  padding:7px 12px;min-height:32px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:12px;font-weight:600;
  transition:border-color .14s,color .14s,background .14s;
}
.sa-riff:hover,.sa-riff:focus-visible{border-color:var(--holo);color:var(--text-primary);outline:none;}
.sa-riff.on{border-color:var(--holo);background:var(--holo-soft);color:var(--text-primary);}
.sa-composer.sa-riffing .sa-input{border-color:var(--holo);}


/* ====================================================================
   11. PLAN — steps with state, Veto per write step, global Stop
   ==================================================================== */
.m-step,
.sa-step{
  display:flex;gap:11px;
  padding:11px 0 11px 12px;margin-bottom:8px;
  border-left:2px solid var(--m-line-2);
}
.m-step-main,
.sa-step-body{flex:1;min-width:0;}
.m-step-label,
.sa-step-label{display:block;font-size:14px;line-height:1.4;color:var(--text-primary);}
.m-step-meta,
.sa-step-meta,
.sa-plan-meta{display:block;font-size:11.5px;color:var(--m-ink-faint);margin-top:2px;}
/* Bucket — what KIND of step it is (screen-alice.js puts the bucket and
   the state class on the same <li>). Quiet for a read, blue for something
   reversible, orange for a write. */
.sa-step-read{border-left-color:var(--m-line-2);}
.sa-step-safe{border-left-color:var(--m-holo-line-strong);}
.sa-step-write{border-left-color:var(--m-accent-line);}
/* State lives on the left edge: grey reads, orange writes-in-waiting,
   green done, faded queued. It is declared AFTER the buckets so it wins
   the shared property whatever order the two names sit in the attribute. */
.m-step--done,
.sa-step-done{border-left-color:var(--success);}
.m-step--done .m-step-label,
.sa-step-done .sa-step-label{color:var(--m-ink-quiet);}
.m-step--waiting,
.sa-step-waiting{border-left-color:var(--accent-bright);}
.m-step--queued,
.sa-step-queued,
.sa-step-vetoed{border-left-color:var(--m-line-2);opacity:.66;}
.sa-step-running{border-left-color:var(--holo);}
.sa-step-failed{border-left-color:var(--danger);}
.m-veto,
.sa-step-veto{
  flex:none;align-self:center;
  padding:6px 11px;min-height:30px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-danger-line);
  background:var(--bg-surface);color:var(--m-danger-text);
  font-size:11.5px;font-weight:600;
}
.m-veto:hover,.sa-step-veto:hover{background:var(--danger-soft);}
.m-stop,
.sa-stop{
  width:100%;margin-top:6px;
  padding:12px;min-height:44px;
  border-radius:13px;
  border:1px solid var(--m-danger-line);
  background:var(--bg-surface);color:var(--m-danger-text);
  font-size:13.5px;font-weight:600;
}
.m-stop:hover,.sa-stop:hover{background:var(--danger-soft);}


/* ====================================================================
   12. PRIMITIVES — the shapes every screen reuses: card, grouped list,
   row, eyebrow, note, quote, bar, badge, state pill, icon chip.
   ==================================================================== */
.m-card,
.ms-card,
.ms-camp{
  background:var(--bg-surface);
  border:1px solid var(--m-line);
  border-radius:18px;
  padding:14px 15px;
  margin-bottom:12px;
}
.m-card--holo,
.ms-card-blue{border-color:var(--m-holo-line);}
.m-card--tint{background:var(--holo-tint);border-color:var(--m-holo-line);border-radius:16px;padding:13px 15px;}

/* Grouped list — rows hairlined against each other, corners clipped.
   (The .m-* halves moved to §22b — the More tab restyles them; the
   .ms-* rules below are byte-identical to before.) */
.ms-group{
  background:var(--bg-surface);
  border:1px solid var(--m-line);
  border-radius:18px;
  overflow:hidden;
  margin-bottom:12px;
}
.ms-row,
.ms-thread-row,
.ms-person,
.ms-file-row{
  width:100%;text-align:left;
  display:flex;align-items:center;gap:11px;
  padding:12px 14px;min-height:48px;
  border:none;border-top:1px solid var(--m-line);
  background:transparent;color:var(--text-primary);
}
.ms-row:first-child,
.ms-thread-row:first-child,
.ms-person:first-child,
.ms-file-row:first-child{border-top:none;}
.ms-row:hover,
.ms-thread-row:hover,
.ms-person:hover,
.ms-file-row:hover{background:var(--m-fill);}
.m-item-main,
.ms-row-main,
.ms-time-main,
.ms-person-main,
.ms-file-main{flex:1;min-width:0;}
.ms-row-title,
.ms-thread-name,
.ms-file-name{display:block;font-size:14.5px;line-height:1.35;}
.m-item-title--strong,
.ms-thread-name,
.ms-file-name{font-weight:500;}
.ms-row-sub,
.ms-thread-last,
.ms-file-sub{
  display:block;font-size:12px;color:var(--m-ink-quiet);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-item-value,
.ms-row-meta{flex:none;font-size:12.5px;color:var(--text-tertiary);}
.m-item-amount{flex:none;font-family:var(--font-serif);font-size:15px;font-weight:600;}
.ms-chev{flex:none;color:var(--m-ink-faint);font-size:15px;line-height:1;}

/* Section eyebrow above a group. */
.m-eyebrow,
.ms-eyebrow,
.ms-group-title{
  font-size:11px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;
  color:var(--text-tertiary);
  margin:0 4px 7px;
}
/* The small explanatory paragraph under a list. */
.m-note,
.sa-note,
.sa-plan-note,
.ms-gap,
.sa-empty,
.ms-empty,
.ms-loading,
.ms-error{
  margin:12px 4px 0;
  font-size:12px;line-height:1.6;
  color:var(--m-ink-quiet);
}
.m-note--tight,
.sa-plan-note{margin:4px 2px 10px;}
/* Alice speaking inside a non-transcript surface: serif, blue, left rule. */
.m-quote{
  margin:0 0 15px;
  padding-left:11px;
  border-left:2px solid var(--m-holo-line-strong);
  font-family:var(--font-serif);font-size:14.5px;line-height:1.55;
  color:var(--holo-text);
}

/* Progress bar. Orange = your money, blue = her reach. */
.m-bar{
  height:6px;border-radius:4px;
  background:var(--m-track);
  overflow:hidden;
}
.m-bar--tall{height:7px;}
.m-bar-fill{
  display:block;height:100%;border-radius:4px;
  background:var(--accent-bright);
  transition:width .45s cubic-bezier(.2,.9,.3,1);
}
.m-bar-fill--holo{background:var(--holo);transition-duration:.6s;}
.m-bar-fill--success{background:var(--success);}
.m-bar-fill--danger{background:var(--danger);}

/* Unread count. */
.m-badge{
  flex:none;min-width:20px;height:20px;padding:0 6px;
  border-radius:10px;
  background:var(--holo-text);color:var(--on-accent);
  font-size:11px;font-weight:700;
  display:inline-flex;align-items:center;justify-content:center;
}

/* Small status pill on a row. */
.m-state,
.ms-state,
.ms-rec-chip{
  flex:none;
  padding:3px 9px;border-radius:var(--radius-pill);
  font-size:11px;font-weight:600;white-space:nowrap;
  background:var(--m-fill);color:var(--text-secondary);
}
.m-state--needs,
.ms-state.needs{background:var(--accent-soft);color:var(--accent-text-soft);}
.m-state--ai,
.ms-state.ai,
.ms-state.qualified{background:var(--holo-tint);color:var(--holo-text);}
.m-state--closed,
.ms-state.closed,
.ms-state.neutral{background:var(--m-fill);color:var(--text-secondary);}
.m-state--good,
.ms-state.customer{background:var(--success-soft);color:var(--success);}
.m-state--bad{background:var(--danger-soft);color:var(--m-danger-text);}
.m-state--rec,
.ms-rec-chip{
  display:inline-flex;align-items:center;gap:5px;
  background:var(--danger-soft);color:var(--m-danger-text);font-weight:700;
}
.m-rec-dot{
  width:6px;height:6px;border-radius:50%;
  background:var(--m-danger-text);
  animation:al-rec 1.4s ease-in-out infinite;
}

/* 30px tinted icon chip on Today / schedule rows. */
.m-icon-chip,
.ms-icon-chip,
.ms-file-icon{
  flex:none;width:30px;height:30px;border-radius:9px;
  display:flex;align-items:center;justify-content:center;
  background:var(--m-fill);color:var(--text-secondary);
}
.m-icon-chip svg,.ms-icon-chip svg,.ms-file-icon svg{width:15px;height:15px;}
.m-icon-chip--call,
.ms-icon-chip.warm{background:var(--accent-soft);color:var(--accent-text-soft);}
.m-icon-chip--done,
.ms-icon-chip.good{background:var(--success-soft);color:var(--success);}
.m-icon-chip--mail,
.ms-icon-chip.cool{background:var(--holo-tint);color:var(--holo-text);}

/* Time column on Today / Schedule. */
.m-when,
.ms-time-col{
  flex:none;width:58px;
  font-size:12.5px;font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--holo-text);
}


/* ====================================================================
   13. BUTTONS — mobile weights. The desktop .btn family stays as it is;
   these are the full-width / sheet-footer / card-action shapes.
   ==================================================================== */
.m-btn,
.ms-brief-action,
.ms-back{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:11px 16px;min-height:44px;
  border:1px solid transparent;border-radius:12px;
  font-size:14px;font-weight:600;letter-spacing:-.01em;
  background:var(--m-fill);color:var(--text-primary);
  transition:background .15s,border-color .15s,color .15s,transform .08s;
}
.m-btn:active{transform:scale(.98);}
.m-btn:disabled{opacity:.4;transform:none;}
.m-btn--block{width:100%;}
.m-btn--grow{flex:1;}
.m-btn--sm,
.ms-brief-action,
.ms-back{padding:9px 14px;min-height:40px;font-size:13px;border-radius:11px;}
/* your move */
.m-btn--primary{background:var(--accent-deep);color:var(--on-accent);}
.m-btn--primary:hover{background:var(--accent-text);}
/* her move */
.m-btn--holo{
  background:var(--holo-tint);color:var(--holo-text);
  border-color:var(--m-holo-line-strong);
}
.m-btn--holo:hover{border-color:var(--holo-text);}
/* neutral outline */
.m-btn--quiet,
.ms-brief-action,
.ms-back{
  background:var(--bg-surface);color:var(--text-secondary);
  border-color:var(--m-line-2);
}
.m-btn--quiet:hover,.ms-brief-action:hover,.ms-back:hover{color:var(--text-primary);border-color:var(--accent-bright);}
/* stop / veto / hang up */
.m-btn--danger{
  background:var(--bg-surface);color:var(--m-danger-text);
  border-color:var(--m-danger-line);
}
.m-btn--danger:hover{background:var(--danger-soft);}
/* tinted orange, for "Approve all" style secondary affordances */
.m-btn--soft{
  background:var(--accent-soft);color:var(--accent-text-soft);
  border-color:var(--m-accent-line);
}
.m-btn--soft:hover{border-color:var(--accent-bright);}


/* ====================================================================
   14. HOME — brief card, stat cards, today rows, schedule rows
   ==================================================================== */
.m-brief,
.ms-brief{
  background:var(--bg-surface);
  border:1px solid var(--m-holo-line);
  border-radius:18px;
  padding:14px 15px;
  margin-bottom:14px;
}
.m-brief-head,
.ms-card-head,
.ms-camp-head{display:flex;align-items:center;gap:7px;margin-bottom:7px;}
.m-brief-eyebrow,
.ms-brief-eyebrow{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;
  color:var(--holo-text);
}
.m-brief-body,
.ms-brief-body{
  margin:0 0 11px;
  font-family:var(--font-serif);font-size:15.5px;line-height:1.52;
  color:var(--text-primary);
}

/* First-run setup card (P0-4) — pinned atop Home. Warm accent border so it
   reads as "do this", distinct from the holo brief above it. Its step rows
   reuse .ms-row; only the frame and header need their own rules. */
.ms-setup{
  background:var(--bg-surface);
  border:1px solid var(--m-accent-line);
  border-radius:18px;
  padding:12px 14px 14px;
  margin-bottom:14px;
}
.ms-setup-head{display:flex;align-items:center;justify-content:space-between;gap:8px;}
.ms-setup-eyebrow{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;
  color:var(--accent-text-soft);
}
.ms-setup-dismiss{
  flex:none;
  background:none;border:none;padding:11px 8px;min-height:44px;   /* 44px tap target — was ~20px, under the 24px AA floor (A11Y-M3) */
  font-size:12px;font-weight:500;color:var(--m-ink-quiet);cursor:pointer;
}
.ms-setup-dismiss:hover{color:var(--text-primary);}
.ms-setup-title{
  margin:6px 0 4px;
  font-family:var(--font-serif);font-size:15.5px;line-height:1.5;
  color:var(--text-primary);
}
.ms-setup .ms-row{padding-left:0;padding-right:0;}
.ms-setup .ms-actions{margin-top:10px;}

/* Stat cards — a grid of two, or a row of three. */
.m-stats,
.ms-stats{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:12px;}
.m-stats--three{grid-template-columns:repeat(3,1fr);}
.m-stat,
.ms-stat{
  background:var(--bg-surface);
  border:1px solid var(--m-line);
  border-radius:16px;
  padding:12px 13px;
  min-width:0;
}
.m-stat-k,
.ms-stat-label{font-size:12px;font-weight:500;color:var(--text-secondary);}
/* ≥24px, so --accent-bright is legal here and only here. */
.m-stat-v,
.ms-stat-value{
  font-family:var(--font-serif);font-size:24px;font-weight:600;
  letter-spacing:-.02em;line-height:1.15;margin-top:2px;
  color:var(--text-primary);
}
.m-stat-v--holo{color:var(--holo-text);}
.m-stat-v--success{color:var(--success);}
.m-stat-v--accent{color:var(--accent-text);}
.m-stat-sub,
.ms-stat-sub{font-size:11px;color:var(--text-tertiary);}

/* Schedule rows separate on a hairline rather than sitting in a group. */
.m-sched-row,
.ms-time-row{
  width:100%;text-align:left;
  display:flex;align-items:center;gap:12px;
  padding:12px 14px;min-height:48px;
  border:none;border-top:1px solid var(--m-line);
  background:transparent;color:var(--text-primary);
}
.m-sched-row:first-child,.ms-time-row:first-child{border-top:none;}
.m-sched-row:hover,.ms-time-row:hover{background:var(--m-fill);}


/* ====================================================================
   15. INBOX — thread rows, thread transcript, why-she-stopped note
   ==================================================================== */
.m-msg,
.ms-bubble{max-width:84%;display:flex;flex-direction:column;gap:3px;}
.m-msg-who{
  font-size:10px;font-weight:700;letter-spacing:.07em;
  color:var(--m-ink-faint);
}
/* the customer */
.m-msg--them{align-self:flex-start;}
.m-msg--them .m-msg-bubble{
  padding:9px 13px;border-radius:16px 16px 16px 5px;
  background:var(--m-fill);color:var(--text-primary);
  font-size:14px;line-height:1.45;
}
/* Alice */
.m-msg--hers{align-self:flex-end;text-align:right;}
.m-msg--hers .m-msg-who{color:var(--holo-text);}
.m-msg--hers .m-msg-bubble{
  padding:9px 13px;border-radius:16px 16px 5px 16px;
  background:var(--holo-tint);
  border:1px solid var(--m-holo-line);
  color:var(--text-primary);
  font-size:14px;line-height:1.45;text-align:left;
}
/* you, having taken over */
.m-msg--mine{align-self:flex-end;text-align:right;}
.m-msg--mine .m-msg-bubble{
  padding:9px 13px;border-radius:16px 16px 5px 16px;
  background:var(--accent-deep);color:var(--on-accent);
  font-size:14px;line-height:1.45;text-align:left;
}
/* Why she stopped — italic serif, blue, left rule. */
.m-why,
.ms-why,
.ms-alice-note{
  margin:2px 0 0;
  padding-left:11px;
  border-left:2px solid var(--m-holo-line-strong);
  font-family:var(--font-serif);font-style:italic;
  font-size:14px;line-height:1.5;
  color:var(--holo-text);
}
/* "You're replying — she's stepped back and is watching." */
.m-takeover{
  flex:none;margin:0 0 8px;
  padding:9px 12px;border-radius:12px;
  background:var(--accent-soft);
  border:1px solid var(--m-accent-line);
  font-size:12px;font-weight:600;
  color:var(--accent-text-soft);
}
/* A reply she drafted in a channel, waiting on Approve / Edit. */
.m-proposal{
  align-self:stretch;
  border:1px solid var(--m-accent-line);
  border-radius:14px;overflow:hidden;
  animation:al-rise .22s ease-out;
}
.m-proposal-head{
  padding:9px 13px;
  background:var(--accent-soft);
  font-size:11px;font-weight:700;letter-spacing:.06em;
  color:var(--accent-text-soft);
}
.m-proposal-body{padding:11px 13px;font-size:14px;line-height:1.45;}
.m-proposal-acts{display:flex;gap:7px;padding:0 13px 12px;}


/* ====================================================================
   16. CUSTOMERS — pipeline stages, people rows
   ==================================================================== */
.m-stage-head,
.ms-section-head{display:flex;align-items:baseline;gap:7px;margin:0 4px 7px;}
.m-stage-name,
.ms-stage-name,
.ms-section-title{
  font-size:11px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--text-tertiary);
}
.m-stage-meta,
.ms-stage-nums{font-size:11px;color:var(--m-ink-faint);}
/* 36px initials circle — the mobile weight of .avatar-ios. */
.m-avatar{
  flex:none;width:36px;height:36px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:600;
  background:var(--holo-text);color:var(--on-accent);
}
.m-avatar--accent{background:var(--accent-deep);}


/* ====================================================================
   17. ADS — headline metric, daily cap row, campaign cards, her draft,
   rules with a three-way mode, the fence, visibility bars
   ==================================================================== */
.m-metric-head{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:10px;margin-bottom:13px;
}
.m-metric-k{font-size:12px;color:var(--text-secondary);}
/* 34px display numeral — --accent-bright would be legal at this size,
   but the number is neutral information, so it stays ink. */
.m-metric-big{
  font-family:var(--font-serif);font-size:34px;font-weight:600;
  letter-spacing:-.02em;line-height:1.1;
}
.m-metric-big small{font-size:17px;color:var(--text-tertiary);font-weight:600;}
.m-metric-side,
.ms-row-end{display:flex;align-items:center;gap:7px;}
.m-metric-roas{font-family:var(--font-serif);font-size:20px;font-weight:600;}
.m-metric-foot,
.ms-camp-metrics{display:flex;gap:14px;flex-wrap:wrap;}
.m-metric-cell-k,
.ms-metric-k{font-size:11.5px;color:var(--m-ink-quiet);}
.m-metric-cell-v,
.ms-metric-v{font-size:15px;font-weight:600;}

/* Daily cap — label, progress, chevron into the sheet. */
.m-cap,
.ms-cap-row{
  width:100%;text-align:left;
  display:flex;align-items:center;gap:11px;
  padding:12px 14px;min-height:48px;
  background:var(--bg-surface);
  border:1px solid var(--m-line);
  border-radius:16px;
  margin-bottom:14px;
  color:var(--text-primary);
}
.m-cap:hover,.ms-cap-row:hover{border-color:var(--accent-bright);}
.m-cap .m-bar{flex:none;width:74px;}

/* Her draft — nothing created until you approve. */
.m-draft,
.ms-draft{
  background:var(--bg-surface);
  border:1px solid var(--m-holo-line);
  border-radius:18px;overflow:hidden;
  margin-bottom:12px;
  animation:al-rise .24s ease-out;
}
.m-draft-head{display:flex;align-items:center;gap:7px;padding:11px 14px 9px;}
.m-draft-eyebrow,
.ms-draft-eyebrow{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;
  color:var(--holo-text);
}
.m-draft-photo,
.ms-draft-image{
  height:132px;
  background:var(--m-hatch);
  display:flex;align-items:center;justify-content:center;
  font-family:ui-monospace,Menlo,monospace;
  font-size:11px;letter-spacing:.08em;
  color:var(--m-ink-faint);
}
.m-draft-body{padding:12px 14px;}
.m-draft-headline,
.ms-draft-headline{
  font-family:var(--font-serif);font-size:17px;font-weight:600;
  line-height:1.3;margin-bottom:4px;
}
.m-draft-copy,
.ms-draft-body{font-size:13px;line-height:1.5;color:var(--text-secondary);margin-bottom:10px;}
.m-draft-meta{font-size:12px;color:var(--m-ink-quiet);}
.m-draft-acts{
  display:flex;gap:7px;
  padding:11px 14px 13px;
  border-top:1px solid var(--m-line);
}

/* Standing rules — act / ask / off. */
.m-rule,
.ms-rule{
  display:flex;align-items:center;gap:11px;
  padding:12px 14px;min-height:52px;
  border-top:1px solid var(--m-line);
}
.m-rule:first-child,.ms-rule:first-child{border-top:none;}
.m-rule-main{flex:1;min-width:0;}
.m-rule-label{display:block;font-size:14px;font-weight:500;}
.m-rule-sub{display:block;font-size:12px;color:var(--m-ink-quiet);}
.m-mode{
  flex:none;
  padding:7px 12px;min-height:32px;
  border-radius:var(--radius-pill);
  font-size:11.5px;font-weight:700;letter-spacing:.03em;
  text-transform:lowercase;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  transition:background .18s,color .18s,border-color .18s;
}
/* act — she does it herself */
.m-mode--act{
  background:var(--success-soft);border-color:var(--success);color:var(--success);
}
/* ask — she drafts and waits */
.m-mode--ask{
  background:var(--accent-soft);border-color:var(--m-accent-line);color:var(--accent-text-soft);
}
/* off */
.m-mode--off{background:var(--m-fill);border-color:transparent;color:var(--text-tertiary);}

/* The fence around all of it. */
.m-fence,
.ms-fence{
  background:var(--holo-tint);
  border:1px solid var(--m-holo-line);
  border-radius:16px;
  padding:13px 15px;
}
.m-fence-eyebrow,
.ms-fence-title{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;
  color:var(--holo-text);margin-bottom:5px;
}
.m-fence-body,
.ms-fence-line{
  margin:0 0 10px;
  font-family:var(--font-serif);font-size:14.5px;line-height:1.5;
  color:var(--text-primary);
}
.m-fence-acts,
.ms-actions{display:flex;gap:7px;}

/* P1-8 — the "armed but cannot fire" line under a green rule whose delivery
   path is dead. Warm, because it is a warning the owner should act on, with a
   compact fix button that carries them to the exact door. */
.ms-armed{
  display:flex;align-items:center;gap:10px;
  margin:-2px 0 8px 40px;
  padding:9px 11px;
  border-radius:12px;
  background:var(--accent-soft);
  border:1px solid var(--m-accent-line);
}
.ms-armed-text{
  flex:1;min-width:0;
  font-size:12.5px;line-height:1.45;
  color:var(--accent-text-soft);
}
.ms-armed-fix{
  flex:none;
  padding:6px 13px;min-height:44px;   /* 44px tap target (A11Y-M3) */
  border-radius:var(--radius-pill);
  border:1px solid var(--m-accent-line);
  background:var(--bg-surface);color:var(--accent-text);
  font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;
}
.ms-armed-fix:hover{border-color:var(--accent-bright);}

/* AI-search visibility bars. */
.m-vis-row,
.ms-vis-row{margin-bottom:9px;}
.m-vis-head{
  display:flex;justify-content:space-between;gap:8px;
  font-size:12px;color:var(--text-secondary);margin-bottom:4px;
}


/* ====================================================================
   18. FILES — search field, file rows, dropzone
   ==================================================================== */
.m-search{
  width:100%;
  padding:11px 15px;
  border-radius:22px;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-primary);
  font-size:14px;
  margin-bottom:12px;
  transition:border-color .15s,box-shadow .15s;
}
.m-search::placeholder{color:var(--m-ink-faint);}
.m-search:focus{
  outline:none;border-color:var(--holo);
  box-shadow:0 0 0 3px var(--holo-soft);
}
.m-dropzone,
.ms-dropzone{
  margin-top:12px;
  border:1.5px dashed var(--m-line-dash);
  border-radius:16px;
  padding:16px;text-align:center;
  font-size:12.5px;line-height:1.5;
  color:var(--m-ink-quiet);
  transition:border-color .14s,background .14s,color .14s;
}
.m-dropzone.over,.m-dropzone:focus-visible,
.ms-dropzone.drag,.ms-dropzone:focus-visible{
  border-color:var(--accent-bright);
  background:var(--accent-soft);
  color:var(--accent-text-soft);
  outline:none;
}


/* ====================================================================
   19. SETTINGS — grouped rows, the minutes meter
   ==================================================================== */
.m-meter-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:10px;margin-bottom:7px;
}
.m-meter-label{font-size:13.5px;font-weight:600;}
.m-meter-value{font-size:12.5px;color:var(--m-ink-quiet);}


/* ====================================================================
   20. SHEETS — settings 61.8vh, detail 50vh, thread 72vh, over a scrim.
   Both rise with al-rise from translateY(7px); the full-height thread
   sheet slides with al-sheet.
   ==================================================================== */
.m-scrim,
.ms-scrim{
  position:fixed;inset:0;z-index:20;
  background:var(--m-scrim);
  animation:al-fade .18s ease-out;
}
.m-sheet,
.ms-sheet{
  position:fixed;left:0;right:0;bottom:0;z-index:21;
  display:flex;flex-direction:column;
  background:var(--bg-surface);
  border-radius:26px 26px 0 0;
  box-shadow:var(--m-shadow-sheet);
  animation:al-rise .22s ease-out;
}
/* the golden-section settings sheet */
.m-sheet--settings,.ms-sheet.tall{height:61.8vh;height:61.8dvh;}
/* half-height detail sheet */
.m-sheet--detail,.ms-sheet.half{height:50vh;height:50dvh;}
/* a conversation needs more room than a fact does */
.m-sheet--thread{height:72vh;height:72dvh;animation:al-sheet .28s cubic-bezier(.2,.9,.3,1);}

.m-sheet-head,
.ms-sheet-head{
  flex:none;display:flex;align-items:flex-start;gap:10px;
  padding:15px 17px 11px;
}
.m-sheet-headmain{flex:1;min-width:0;}
.m-sheet-kind{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;
  color:var(--text-tertiary);margin-bottom:4px;
}
.m-sheet-title,
.ms-sheet-title{
  font-family:var(--font-serif);font-size:21px;font-weight:600;
  letter-spacing:-.02em;line-height:1.2;
}
.m-sheet-close,
.ms-sheet-close{
  flex:none;width:32px;height:32px;padding:0;
  border-radius:50%;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--m-ink-quiet);
  font-size:14px;line-height:1;
  display:flex;align-items:center;justify-content:center;
}
.m-sheet-close:hover,.ms-sheet-close:hover{color:var(--text-primary);}

/* Provenance — same component, different line. Who put this on screen. */
.m-prov,
.ms-sheet-prov{
  display:inline-flex;align-items:center;gap:6px;
  padding:3px 9px;border-radius:var(--radius-pill);
  font-size:10px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;
  margin-bottom:6px;
}
.m-prov--you,.ms-sheet-prov{background:var(--m-fill);color:var(--text-secondary);}
.m-prov--alice,.ms-sheet-prov.alice{background:var(--holo-tint);color:var(--holo-text);}

.m-sheet-body,
.ms-sheet-body{
  flex:1;min-height:0;
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
  padding:0 17px 12px;
}
.m-sheet-foot,
.ms-sheet-foot{
  flex:none;display:flex;align-items:center;gap:10px;
  padding:12px 17px calc(18px + env(safe-area-inset-bottom,0px));
  border-top:1px solid var(--m-line);
}
.m-sheet-foot-note{
  flex:1;font-size:11.5px;line-height:1.4;
  color:var(--m-ink-faint);
}

/* key / value rows inside a detail sheet */
.m-kv,
.ms-kv{
  display:flex;align-items:baseline;gap:12px;
  padding:10px 0;border-top:1px solid var(--m-line);
}
.m-kv:first-child,.ms-kv:first-child{border-top:none;}
.m-kv-k,
.ms-kv-k{flex:none;width:104px;font-size:12.5px;color:var(--m-ink-quiet);}
.m-kv-v,
.ms-kv-v{flex:1;font-size:14px;}

/* a bordered inner box inside a sheet (cap, envelope, booking link) */
.m-fieldset{
  border:1px solid var(--m-line-2);
  border-radius:16px;
  padding:15px;
  margin-bottom:12px;
}
.m-fieldset--flush{padding:0;overflow:hidden;}
.m-field-label,
.ms-label{
  display:block;font-size:12.5px;color:var(--text-secondary);
  margin-bottom:7px;
}
.m-field-input,
.ms-input{
  width:100%;
  padding:11px 13px;
  border-radius:11px;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-primary);
  font-size:14px;
}
.m-field-input:focus,
.ms-input:focus{
  outline:none;border-color:var(--holo);
  box-shadow:0 0 0 3px var(--holo-soft);
}
/* The opening-message editor in the Automatic-messages sheet (P0-4). Same
   input skin, but a few lines tall and vertically resizable only. */
.ms-textarea{
  min-height:74px;
  line-height:1.5;
  resize:vertical;
  font-family:inherit;
}
.m-field-row{display:flex;align-items:center;gap:9px;}
.m-field-sep{font-size:13px;color:var(--text-tertiary);}
.m-range{width:100%;accent-color:var(--accent-bright);}
.m-range-ends{
  display:flex;justify-content:space-between;
  font-size:11px;color:var(--text-tertiary);margin-top:2px;
}
.m-money{
  font-family:var(--font-serif);font-size:27px;font-weight:600;
  letter-spacing:-.02em;
}
.m-money--cap{color:var(--accent-text);}
.m-money--big{font-size:34px;}

/* preset choices (envelope amounts) */
.m-choices{display:flex;gap:7px;}
.m-choice{
  flex:1;padding:10px 0;min-height:40px;
  border-radius:11px;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:13px;font-weight:600;
}
.m-choice.on{
  background:var(--accent-soft);
  border-color:var(--m-accent-line);
  color:var(--accent-text-soft);
}

/* iOS switch — permissions and automatic messages. */
.m-toggle-row{
  display:flex;align-items:center;gap:12px;
  padding:13px 15px;min-height:52px;
  border-top:1px solid var(--m-line);
}
.m-toggle-row:first-child{border-top:none;}
.m-toggle-main{flex:1;min-width:0;}
.m-toggle-label{display:block;font-size:14px;font-weight:500;}
.m-toggle-sub{display:block;font-size:12px;color:var(--m-ink-quiet);}
.m-switch,
.ms-switch-track{
  flex:none;width:46px;height:28px;padding:3px;
  border:none;border-radius:var(--radius-pill);
  background:var(--m-fill-strong);
  display:flex;justify-content:flex-start;
  transition:background .18s;
}
.m-switch.on{background:var(--accent-bright);justify-content:flex-end;}
.m-switch-knob,
.ms-switch{
  width:22px;height:22px;border-radius:50%;
  background:var(--on-accent);
  box-shadow:var(--m-shadow-knob);
  transition:transform .18s;
}
/* a checkbox row that reads like a switch row (the cap's stop switch) */
.m-check-row{
  display:flex;align-items:center;gap:11px;
  border:1px solid var(--m-line-2);border-radius:14px;
  padding:13px 15px;min-height:52px;
}
.m-check{width:18px;height:18px;flex:none;accent-color:var(--accent-bright);}


/* ====================================================================
   21. POPOVERS — the approvals stack and the briefings list, dropping
   from under the header rather than rising from the bottom.
   ==================================================================== */
.m-pop{
  position:fixed;top:56px;left:14px;right:14px;z-index:21;
  background:var(--bg-surface);
  border:1px solid var(--m-line);
  border-radius:22px;
  box-shadow:var(--m-shadow-pop);
  overflow:hidden;
  animation:al-drop .24s cubic-bezier(.2,.9,.3,1);
}
.m-pop-head{display:flex;align-items:center;gap:9px;padding:15px 16px 12px;}
.m-pop-headmain{flex:1;min-width:0;}
.m-pop-title{
  font-family:var(--font-serif);font-size:19px;font-weight:600;
  letter-spacing:-.02em;
}
.m-pop-sub{font-size:12px;color:var(--m-ink-quiet);margin-top:1px;}
.m-pop-close{
  flex:none;width:30px;height:30px;padding:0;
  border-radius:50%;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--m-ink-quiet);
  font-size:14px;line-height:1;
  display:flex;align-items:center;justify-content:center;
}
.m-pop-close:hover{color:var(--text-primary);}
.m-pop-list{
  max-height:min(52vh,400px);
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
}
.m-pop-foot{
  display:flex;align-items:center;gap:10px;
  padding:12px 16px;
  border-top:1px solid var(--m-line);
  background:var(--m-paper);
}
.m-pop-foot-note{flex:1;font-size:11.5px;line-height:1.45;color:var(--m-ink-faint);}

/* one draft awaiting your OK */
.m-approval{
  padding:13px 16px;
  border-top:1px solid var(--m-line);
  animation:al-rise .22s ease-out;
}
.m-approval:first-child{border-top:none;}
.m-approval-head{display:flex;align-items:center;gap:7px;margin-bottom:5px;}
.m-approval-kind{
  font-size:10px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--holo-text);
}
.m-approval-meta{font-size:11px;color:var(--m-ink-faint);}
.m-approval-text{font-size:14.5px;line-height:1.45;margin-bottom:10px;}
.m-approval-acts{display:flex;gap:7px;}

/* one brief she wrote while you were away */
.m-briefrow{
  padding:13px 16px;
  border-top:1px solid var(--m-line);
}
.m-briefrow:first-child{border-top:none;}
.m-briefrow.unread{background:var(--holo-tint);}
.m-briefrow-head{display:flex;align-items:center;gap:7px;margin-bottom:4px;}
.m-briefrow-title{font-size:14px;font-weight:600;}
.m-briefrow-when{font-size:11px;color:var(--m-ink-faint);margin-left:auto;}
.m-briefrow-body{
  margin:0 0 9px;
  font-family:var(--font-serif);font-size:14.5px;line-height:1.5;
}


/* ====================================================================
   22. UTILITIES
   ==================================================================== */
.m-spacer{flex:1;}
.m-hidden,.ms-hidden-input{display:none;}
/* The existing .toast is reused as-is (bottom centre, rise, auto-dismiss)
   but its bottom:22px lands under the 7-item rail. This is an additive
   opt-in modifier — put it on the toast element only in the mobile shell,
   so no existing toast anywhere else changes. */
.toast.m-above-rail{
  bottom:calc(74px + env(safe-area-inset-bottom,0px));
  z-index:22;
}
.m-scroll-x{
  display:flex;gap:6px;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.m-scroll-x::-webkit-scrollbar{display:none;}
/* Keyboard focus is visible everywhere in the shell. */
.m-shell :focus-visible,
.m-sheet :focus-visible,
.ms-sheet :focus-visible,
.m-cmd :focus-visible,
.m-pop :focus-visible{
  outline:2px solid var(--holo);
  outline-offset:2px;
}


/* ====================================================================
   23. THE OTHER TWO PREFIXES — `.sa-*` (screen-alice.js) and `.ms-*`
   (screens.js).

   Five agents built this shell in parallel and coined three prefixes
   independently. Reconciling them is a naming problem, not a design
   problem: WHEREVER AN `.m-*` RULE ALREADY EXPRESSES THE SHAPE, THE
   sa-/ms- SELECTOR WAS ADDED TO THAT RULE, in sections 3–22 above —
   grep `.sa-` or `.ms-` up there and you will find ~150 of them. That
   is deliberate. Two rules that say the same thing in two places is
   how a design system drifts.

   What is left here is only:
     (a) shapes with genuinely no `.m-*` equivalent — the SVG progress
         bar, the checkbox switch, the sheet grab handle, the search
         field with the icon inside it, the bubble that is its own
         wrapper;
     (b) one-line reconciliations where a grouped rule meets a
         different ELEMENT than the one it was written for (an <h2>
         needing its margin reset, a checkbox needing appearance:none,
         a flex column meeting a flex row).

   Tokens only, `body.dark` covered by the tokens themselves, and every
   rule inside the breakpoint like the rest of the file.
   ==================================================================== */

/* --------------------------------------------------------------------
   23.1 Alice screen — the frame screen-alice.js mounts
   -------------------------------------------------------------------- */
.sa-screen{
  display:flex;flex-direction:column;
  flex:1;min-height:0;height:100%;
}
/* .m-seg is inline-flex and would stretch as a column flex item. */
.sa-tabs{flex:none;align-self:flex-start;}
/* The tab row (B3): tabs on the left, the History door on the right. The
   row owns the bottom margin so the seg's own does not double it. */
.sa-tabrow{
  flex:none;
  position:relative;                /* the off-screen live region's anchor */
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin-bottom:12px;
}
/* Four tabs + the History pill can outgrow 393px: the seg scrolls inside
   the row rather than shoving History off the edge.

   `flex:0 1 auto` is what makes that sentence TRUE, and it was missing.
   .sa-tabs above sets flex:none — right for the column it also sits in,
   wrong here: with flex-shrink:0 the seg kept its full 292px, the row
   overflowed by 49px at 393px, and the audio control's right edge landed
   at x=430 in a 381px row. min-width:0 and overflow-x:auto were already
   here waiting for it; only the shrink was missing. Measured before and
   after — the row's scrollWidth goes 418 → 369 at a 393px viewport. */
.sa-tabrow .sa-tabs{
  margin-bottom:0;min-width:0;flex:0 1 auto;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.sa-tabrow .sa-tabs::-webkit-scrollbar{display:none;}
.sa-history{
  flex:none;white-space:nowrap;
  padding:7px 12px;min-height:32px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:12px;font-weight:600;
  transition:border-color .14s,color .14s;
}
.sa-history:hover,.sa-history:focus-visible{
  border-color:var(--accent-bright);color:var(--accent-text);outline:none;
}

/* "N running" (owner ask) — the live task counter, in the tab row between the
   tabs and the History door. Quiet status TEXT, not another pill: it is the
   one thing in this row you cannot tap, and dressing it like the two buttons
   beside it would say otherwise. Alice-blue (--holo-text) because the work
   being counted is hers.

   THE BOX IS ALWAYS IN THE LAYOUT and only `visibility` flips. `display:none`
   would re-measure the seg's scroller the instant she starts working — the tab
   under the reader's thumb would move at exactly the wrong moment — so the
   space is reserved instead: a fixed min-width wide enough for the widest
   number the counter can reach (RUN_MAX is 12), with tabular figures so
   1 → 2 → 12 never resizes it either. */
.sa-running{
  flex:none;
  display:inline-flex;align-items:center;gap:6px;
  min-width:78px;   /* measured: "12 running" renders at 73px, plus headroom */
  min-height:17px;  /* the empty box is 11px (the ring); hold the taller one */
  justify-content:flex-end;
  font-size:11.5px;font-weight:600;line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--holo-text);
  white-space:nowrap;
  visibility:hidden;
}
.sa-running.on{visibility:visible;}
/* The glyph, and the whole point of the feature. The recipe is .m-call-spin's,
   one size down — the shell already spins exactly this ring while the call
   connects, and a second, different spinner for "she is working" would be two
   vocabularies for one idea. Transform only, so it composites on the GPU and
   lays nothing out. al-spin is this file's existing keyframe (§2), which is
   why the prefers-reduced-motion neutraliser at the top already covers it;
   .sa-reduced repeats it below for the flag screen-alice.js sets live. */
.sa-run-dot{
  flex:none;width:11px;height:11px;border-radius:50%;
  border:1.6px solid var(--m-holo-line);
  border-top-color:var(--holo-text);
  animation:al-spin .8s linear infinite;
}
/* The label never shrinks. The box already reserves room for the longest
   number the counter can reach, so wrapping or ellipsing it could only ever
   be a bug telling the owner half a truth. */
.sa-run-text{flex:none;}
/* The polite announcement. Off-screen by clip — NEVER display:none and never
   inside the pill's visibility toggle, because both take a live region out of
   the accessibility tree, and a region that is not in the tree announces
   nothing when it returns. Absolutely positioned, so it costs no layout. */
.sa-run-live{
  position:absolute;width:1px;height:1px;
  margin:-1px;padding:0;border:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);
  white-space:nowrap;
}
/* setTab() toggles `hidden`; a display on the class would beat the UA
   rule for it, so the pane says so itself. */
.sa-pane{display:flex;flex-direction:column;flex:1;min-height:0;}
.sa-pane[hidden]{display:none;}
.sa-pane-conv{position:relative;}
.sa-pane-plan,
.sa-pane-drafts,
.sa-pane-done{
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
}
/* The one scroller on the screen. The composer below it is flex:none,
   so the transcript is what gives when the keyboard opens. */
.sa-transcript{
  flex:1;min-height:0;
  display:flex;flex-direction:column;gap:15px;
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
  padding:4px 0 14px;
}
/* "Newest" — in flow between the transcript and the composer rather
   than floating over it, so it can never sit on top of a line. */
.sa-jump{align-self:center;margin:-4px 0 8px;box-shadow:var(--shadow-card);}
.sa-jump[hidden]{display:none;}

/* .sa-row groups onto .m-row, which is a flex COLUMN. The act and panel
   rows group onto .m-act / .m-opened, which are flex ROWS and do not
   restate the direction, so it is restated here. */
.sa-row-act{flex-direction:row;align-items:baseline;}
.sa-row-panel{flex-direction:row;align-items:center;}
/* .m-act-state has no error step; this is the fourth. */
.sa-state-err{color:var(--m-danger-text);}

/* Per-message actions — the row of quiet icon buttons under each of HER
   replies (screen-alice.js's msgActions), never under the owner's own.

   A PORT, not an install. The design is shadcn's "ai-actions" component;
   there is no React, no Tailwind, no lucide and no build step in this repo, so
   what crossed over is the shape — a left-aligned row of small, icon-only,
   ghost buttons, muted until hovered — and the Tailwind utilities became these
   rules. screen-alice.js's header carries the full accounting.

   ONE BUTTON, not the upstream five. Copy is the only one of Retry / Like /
   Dislike / Copy / Share backed by something real here; the other four were
   dropped rather than drawn as controls that do nothing. The reasons are in
   the port note in screen-alice.js, beside the code, where they belong.

   36px, and NOT the shell's 44px tap target. Deliberate, and called out rather
   than quietly deviated from: this bar repeats under every reply, and a column
   of 44px squares down the transcript out-weighs the words they belong to. 36px
   is the upstream size and sits in the same family as .sa-chip, .sa-history and
   .m-mic, which are 32-34px for the same reason.

   The focus ring is NOT cancelled here. .sa-chip and its neighbours set
   outline:none because they substitute a border-colour change; a ghost button
   has no border to substitute, so it keeps the shell's `.m-shell :focus-visible`
   ring and that ring is the whole keyboard affordance. */
.sa-msgacts{
  display:flex;align-items:center;gap:2px;
  /* -10px pulls the first glyph's own padding out of the way so the icon's
     left edge lines up with the first letter of the reply above it. */
  margin:-2px 0 0 -10px;
}
.sa-msgact{
  flex:none;
  width:36px;height:36px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid transparent;border-radius:var(--radius);
  background:transparent;color:var(--m-ink-quiet);
  transition:background .14s,color .14s;
}
.sa-msgact svg{width:16px;height:16px;}
.sa-msgact:hover,.sa-msgact:focus-visible{
  background:var(--m-fill);color:var(--accent-text);
}
.sa-msgact:active{transform:scale(.94);}
/* The bar is offered only when there is a FINISHED sentence under it to act on:
   never over an empty row (`:empty` is live, so it uncovers itself the moment
   the first word lands), never while the typewriter is still revealing a typed
   reply, and never while a spoken utterance is still streaming in. Copying half
   a sentence is worse than waiting a beat for the whole one — and saying it in
   three selectors means no arrival path in screen-alice.js can forget to.

   `visibility`, NOT `display`, and for the same reason the "N running" counter
   above reserves its box: the transcript is pinned to its own bottom by
   follow(), and follow() runs on the typewriter tick BEFORE sa-typing comes
   off. Collapsing the bar would grow the row by 36px one frame after the last
   scroll, leaving the newest line sitting 36px above the fold. Reserved space
   costs nothing and cannot do that. visibility:hidden also takes the button
   out of hit-testing and out of the tab order, so a hidden bar is genuinely
   unreachable rather than merely invisible. */
.sa-said:empty + .sa-msgacts,
.sa-row-alice.sa-typing .sa-msgacts,
.sa-row-alice.sa-live .sa-msgacts{visibility:hidden;}

/* The typewriter caret. `.m-caret` is a real element in the .m-*
   transcript; screen-alice.js appends no node for it, so here it is a
   pseudo-element on the line being revealed. */
.sa-row-alice.sa-typing .sa-said::after{
  content:'';
  display:inline-block;width:2px;height:.95em;
  margin-left:3px;vertical-align:-2px;
  background:var(--m-ink-quiet);
  animation:al-caret 1.05s steps(2,start) infinite;
}
/* screen-alice.js sets .sa-reduced on the screen element live, and says
   in its own comments that it expects the stylesheet to honour it. It
   says the same thing the prefers-reduced-motion block at the top of
   this file already says — twice, deliberately, per that module's note. */
.sa-reduced .sa-orb{animation:none;}
.sa-reduced .sa-row-alice.sa-typing .sa-said::after{animation:none;opacity:0;}
.sa-reduced .sa-choice{animation:none;}
/* The running spinner stops turning; the ring and the count stay, because the
   FACT that work is in flight is information, not decoration. */
.sa-reduced .sa-run-dot{animation:none;}

.sa-idle-copy{max-width:280px;}
.sa-sending{opacity:.7;}

/* Plan pane */
.sa-plan-body{padding:2px 0 18px;}
.sa-steps{list-style:none;margin:12px 0 0;padding:0;}

/* Drafts pane — rows reuse .sa-step, the detail reuses .ms-kv; only the
   two quiet controls are new. Read/back are NOT vetoes, so they take the
   neutral line with the accent on touch, never the danger colours. */
.sa-drafts-body{padding:2px 0 18px;}
.sa-draft-open{
  flex:none;align-self:center;
  padding:6px 11px;min-height:30px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:11.5px;font-weight:600;
}
.sa-draft-back{
  align-self:flex-start;
  padding:6px 11px;min-height:30px;margin:2px 0 10px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:12px;font-weight:600;
}
.sa-draft-open:hover,.sa-draft-open:focus-visible,
.sa-draft-back:hover,.sa-draft-back:focus-visible{
  border-color:var(--accent-bright);color:var(--accent-text);outline:none;
}
.sa-draft-title{margin-top:2px;}

/* Done pane (B4) — rows reuse .sa-step; Undo reuses .sa-step-veto. */
.sa-done-body{padding:2px 0 18px;}

/* Approve on a queued proposal (B1). Green where Veto is red: the pair
   reads as the decision it is. Same pill geometry as .sa-step-veto so the
   two sit side by side without a wobble. */
.sa-step-approve{
  flex:none;align-self:center;
  padding:6px 11px;min-height:30px;margin-right:6px;
  border-radius:var(--radius-pill);
  border:1px solid var(--success);
  background:var(--bg-surface);color:var(--success);
  font-size:11.5px;font-weight:600;
}
.sa-step-approve:hover,.sa-step-approve:focus-visible{
  background:var(--success-soft);outline:none;
}

/* The post-call card (B2) and the past-conversation banner (B3). Both are
   transcript rows with a tinted bed — Alice-blue, the provenance colour —
   holding a one-line account and a row of quiet pill actions. */
.sa-call-card,
.sa-hist-banner{
  background:var(--holo-tint);
  border:1px solid var(--m-holo-line);
  border-radius:16px;
  padding:12px 14px;
}
.sa-call-sum{font-size:13.5px;line-height:1.5;color:var(--text-primary);}
.sa-call-acts{display:flex;flex-wrap:wrap;gap:8px;margin-top:9px;}
.sa-call-acts .sa-draft-back{margin:0;align-self:center;}
.sa-call-btn{
  flex:none;white-space:nowrap;
  padding:7px 12px;min-height:32px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-holo-line-strong);
  background:var(--bg-surface);color:var(--holo-text);
  font-size:12px;font-weight:600;
}
.sa-call-btn:hover,.sa-call-btn:focus-visible{
  border-color:var(--holo);color:var(--text-primary);outline:none;
}

/* Quick-reply choices (screen-alice.js): HER offered one-tap replies, in a NEW
   row ABOVE the suggestion chips — which do not move. Styled on the holo
   sa-call-btn vocabulary (Alice-provenance colour) so they read as her offers,
   not as the neutral suggestion chips below. A wrap, not a scroller: a two-to-
   four button set is short, and wrapping keeps every option reachable without a
   sideways scroll. Per-pill ellipsis so a long option never blows the row out. */
.sa-choices{display:flex;flex-wrap:wrap;gap:6px;padding:2px 2px 8px;}
/* The display above is an AUTHOR declaration, so it beats the UA's
   [hidden]{display:none} no matter the specificity — which meant row.hidden
   hid nothing, and an emptied row left a permanent 10px dead strip above the
   chips plus an empty labelled group sitting in the accessibility tree. Every
   other hideable surface in this file already says so for itself (.sa-pane,
   .sa-jump, .sa-appr); this one was missed. */
.sa-choices[hidden]{display:none;}
.sa-choice{
  flex:0 1 auto;min-width:44px;max-width:100%;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  padding:7px 14px;min-height:44px;   /* 44px iOS tap target (A11Y-M3); native button centres the label */
  border-radius:var(--radius-pill);
  border:1px solid var(--m-holo-line-strong);
  background:var(--bg-surface);color:var(--holo-text);
  font-size:12px;font-weight:600;
  /* A CSS animation, so the prefers-reduced-motion neutralizer (.m-shell *) and
     the live .sa-reduced fallback below both kill it. */
  animation:al-pop .18s ease both;
}
/* No outline:none in these two rules. The shell paints a 2px --holo ring on
   :focus-visible (.m-shell :focus-visible); cancelling it at equal specificity
   made focus visually identical to hover, so a keyboard user could not tell
   which pill they were on. */
/* ONE primary action. The server marks the affirmative of a yes/no confirm
   (lib/choices.js buildBlock, style:'primary'); without a rule for it the two
   answers drew as identical pills and "one primary action" was decided and then
   thrown away. A filled pill in the same holo vocabulary — no new colour. */
.sa-choice-primary{
  background:var(--holo);border-color:var(--holo);color:var(--on-accent);
}
.sa-choice-primary:hover,.sa-choice-primary:focus-visible{
  border-color:var(--holo);color:var(--on-accent);filter:brightness(1.06);
}
.sa-choice:hover,.sa-choice:focus-visible{
  border-color:var(--holo);color:var(--text-primary);
}

/* Six to eight options come as a dropdown instead of a row — and so does any
   set whose labels will not pack into two rows on a 375px phone (screen-alice.js
   renderChoices): "which draft?" against seven campaigns is a wall of pills on
   a phone, and a wrapped wall pushes the composer off screen. Same pill skin as
   .sa-choice so the two read as one control in two shapes, and the same 44px
   target. A native <select> on purpose — iOS and Android give it their own
   picker, which beats any list this screen could draw. No entry animation: the
   dropdown is one element, so there is nothing to stagger. */
.sa-choice-select{
  flex:1 1 auto;min-width:0;max-width:100%;
  padding:7px 14px;min-height:44px;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-holo-line-strong);
  background:var(--bg-surface);color:var(--holo-text);
  font-size:12px;font-weight:600;
}
.sa-choice-select:hover,.sa-choice-select:focus-visible{
  border-color:var(--holo);color:var(--text-primary);
}

/* The composer gate (B5): the reason reads where typing would happen, and
   the locked row dims rather than vanishes — what exists is still true. */
.sa-gate{margin:6px 4px 0;}
.sa-composer.sa-locked .sa-input,
.sa-composer.sa-locked .sa-chip,
.sa-composer.sa-locked .sa-choice,
.sa-composer.sa-locked .sa-choice-select,
.sa-composer.sa-locked .sa-riff{opacity:.55;}

/* The top-right audio/settings control (screen-alice.js). It sits in the tab
   row beside History and takes the same quiet-pill skin — but square and a
   full 44px, because it is a glyph-only target and a 32px pill with an icon in
   it is the tap nobody lands (A11Y-M3). Both palettes come from the tokens. */
.sa-audio{
  flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;padding:0;
  border-radius:var(--radius-pill);
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);color:var(--text-secondary);
  transition:border-color .14s,color .14s;
}
.sa-audio:hover,.sa-audio:focus-visible{
  border-color:var(--accent-bright);color:var(--accent-text);outline:none;
}
.sa-audio svg{display:block;}

/* The volume row inside that sheet. .m-range already carries the accent
   colour and the full width; only the row geometry and the readout are new,
   and the slider takes a 44px target of its own. */
.sa-vol-row{display:flex;align-items:center;gap:12px;}
.sa-vol-slider{flex:1;min-width:0;min-height:44px;}
.sa-vol-val{
  flex:none;min-width:44px;text-align:right;
  font-size:13px;font-variant-numeric:tabular-nums;
  color:var(--text-secondary);
}
/* The voice picker is a native <select> wearing the .ms-input skin (the OS
   wheel is the right control for ten values on a phone); only the tap target
   is short of the house minimum, so it is topped up here. */
.sa-voice{min-height:44px;}

/* --------------------------------------------------------------------
   23.1b The inline approve dock (screen-alice.js)

   Proposals waiting on you, decided from the conversation itself — the
   owner's "I shouldn't have to expand into a menu to approve every 5
   seconds". It sits between the transcript and the composer, so it is
   the last thing above the thumb and it never covers a line.

   Alice-blue bed, like the post-call card and the history banner: this
   is HER asking, and the shell's provenance colour says so without a
   label. Approve takes the accent fill (the affirmative, and the only
   filled button on the screen); Veto takes the danger outline it has on
   the Plan tab, so the pair reads the same in both places.
   -------------------------------------------------------------------- */
.sa-appr{
  flex:none;
  display:flex;flex-direction:column;gap:9px;
  margin:0 0 10px;padding:11px 13px;
  border:1px solid var(--m-holo-line);
  border-radius:16px;
  background:var(--m-holo-wash);
  /* A proposal's sentence is model output and has no length this file can
     count on. Two of them are bounded by the pane, never by the text: the
     dock scrolls inside itself rather than pushing the composer off a 393px
     screen with the keyboard open. */
  max-height:38vh;
  overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
}
.sa-appr[hidden]{display:none;}
.sa-appr-head{display:flex;align-items:center;justify-content:space-between;gap:9px;}
.sa-appr-count{
  font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--holo-text);
}
.sa-appr-all{
  flex:none;white-space:nowrap;
  padding:0 13px;min-height:44px;   /* 44px tap target (A11Y-M3) */
  border-radius:var(--radius-pill);
  border:1px solid var(--m-holo-line-strong);
  background:var(--bg-surface);color:var(--holo-text);
  font-size:12px;font-weight:600;
}
.sa-appr-all:hover,.sa-appr-all:focus-visible{
  border-color:var(--holo);color:var(--text-primary);outline:none;
}
.sa-appr-item{
  display:flex;flex-direction:column;gap:7px;
  padding-top:9px;border-top:1px solid var(--m-line);
}
.sa-appr-item:first-of-type{padding-top:0;border-top:0;}
.sa-appr-sum{margin:0;font-size:13.5px;line-height:1.45;color:var(--text-primary);}
.sa-appr-meta{font-size:11.5px;color:var(--m-ink-quiet);}
.sa-appr-acts{display:flex;flex-wrap:wrap;gap:8px;}
.sa-appr-ok,
.sa-appr-no{
  flex:none;
  padding:0 17px;min-height:44px;   /* 44px tap target (A11Y-M3) */
  border-radius:12px;
  font-size:13.5px;font-weight:600;
}
.sa-appr-ok{border:1px solid transparent;background:var(--accent-deep);color:var(--on-accent);}
.sa-appr-ok:hover,.sa-appr-ok:focus-visible{filter:brightness(1.1);outline:none;}
.sa-appr-no{border:1px solid var(--m-danger-line);background:transparent;color:var(--m-danger-text);}
.sa-appr-no:hover,.sa-appr-no:focus-visible{background:var(--danger-soft);outline:none;}
.sa-appr-ok:disabled,.sa-appr-no:disabled{opacity:.45;}
.sa-appr-note{margin:0;font-size:12px;line-height:1.45;color:var(--m-ink-quiet);}

/* --------------------------------------------------------------------
   23.2 screens.js — layout and states
   -------------------------------------------------------------------- */
.ms-pane{display:flex;flex-direction:column;}
.ms-section{display:flex;flex-direction:column;margin-bottom:16px;}
.ms-section:last-child{margin-bottom:0;}
.ms-stack{display:flex;flex-direction:column;}
/* These three group onto .m-note for their type and colour; only the
   centring and the breathing room are new. */
.ms-empty,.ms-loading,.ms-error,.sa-empty{
  text-align:center;font-size:13px;
  margin:6px 0;padding:14px 10px;
}
.ms-error{color:var(--m-danger-text);}

/* .m-eyebrow is outdented to sit ABOVE a group; .ms-eyebrow sits INSIDE
   a row's own column as often as it sits above one. */
.ms-eyebrow{display:block;margin:0 0 3px;}
.ms-chev svg{display:block;}
/* .ms-row-sub is a row subtitle as a <span> and a standalone note as a
   <p>. .m-item-sub truncates to one line; a note must not. */
p.ms-row-sub{
  white-space:normal;overflow:visible;
  margin:8px 4px 0;line-height:1.5;
}
/* Headings and paragraphs that group onto rules written for <div>s. */
.ms-section-title,.ms-sheet-title{margin:0;}
.ms-fence-title,.ms-draft-headline,.ms-draft-body{margin-top:0;}
.ms-card-body{
  margin:0 0 12px;
  font-size:13.5px;line-height:1.55;
  color:var(--text-secondary);
}

/* --------------------------------------------------------------------
   23.3 The SVG progress bar
   screens.js draws the bar as an <svg> because the fill's WIDTH is data
   and a width in a style="" would not survive the CSP — so it is a
   geometry ATTRIBUTE on a <rect>. That makes .m-bar unreusable: it
   colours a <div> with `background` and sizes the fill with `width`,
   and both would be the wrong property on the wrong element here. The
   viewBox (0 0 100 6) and the rx are in the markup; only the two fills
   and the box are ours. Tones ride the <svg>, per bar(fraction,tone).
   -------------------------------------------------------------------- */
.ms-bar{display:block;width:100%;height:6px;flex:none;}
.ms-bar-track{fill:var(--m-track);}
.ms-bar-fill{fill:var(--accent-bright);}
.ms-bar.cool .ms-bar-fill{fill:var(--holo);}
.ms-bar.good .ms-bar-fill{fill:var(--success);}

/* --------------------------------------------------------------------
   23.4 Inbox — bubbles, thread view
   -------------------------------------------------------------------- */
.ms-bubbles{display:flex;flex-direction:column;gap:10px;margin:12px 0;}
/* One node is the wrapper AND the bubble here, where .m-msg splits them
   in two — so the geometry groups onto .m-msg and the fill and the
   clipped corner have to live on the modifier. */
.ms-bubble{padding:9px 13px;border-radius:16px;}
.ms-bubble-text{margin:0;font-size:14px;line-height:1.45;}
.ms-bubble.them{
  align-self:flex-start;
  border-radius:16px 16px 16px 5px;
  background:var(--m-fill);color:var(--text-primary);
}
.ms-bubble.hers{
  align-self:flex-end;
  border-radius:16px 16px 5px 16px;
  background:var(--holo-tint);
  border:1px solid var(--m-holo-line);
  color:var(--text-primary);
}
.ms-bubble.hers .ms-bubble-label{color:var(--holo-text);}
.ms-bubble.yours{
  align-self:flex-end;
  border-radius:16px 16px 5px 16px;
  background:var(--accent-deep);color:var(--on-accent);
}
/* White on --accent-deep is 4.52:1 — the ramp's documented pairing, the
   same one .m-msg--mine and .m-btn--primary already use. */
.ms-bubble.yours .ms-bubble-label,
.ms-bubble.yours .ms-row-meta{color:var(--on-accent);}

/* P1-7 — the live "she is typing" line under the bubbles, hidden until
   conv_agent_thinking arrives (screens.js toggles [hidden]). */
.ms-typing{margin:2px 2px 0;font-size:12.5px;font-style:italic;color:var(--text-tertiary);}

.ms-thread-row{flex-direction:column;align-items:stretch;gap:3px;}
.ms-thread-top{display:flex;align-items:center;justify-content:space-between;gap:8px;}
/* The same class names the row title in a list and titles the thread. */
h2.ms-thread-name{
  font-family:var(--font-serif);font-size:20px;font-weight:600;
  letter-spacing:-.02em;line-height:1.2;margin:0 0 2px;
}
.ms-thread-view{display:flex;flex-direction:column;}
.ms-back{align-self:flex-start;margin-bottom:10px;}
/* screens.js reuses the forward chevron for Back. */
.ms-back svg{transform:rotate(180deg);}
.ms-composer{margin-top:12px;}
/* .ms-input also groups onto .m-field-input, whose 11px radius is right
   for a sheet field and wrong for a chat composer. */
.ms-composer-input{border-radius:22px;}
/* A live room, not a recording one — green, and it groups onto .m-dot
   for the geometry. 4.40:1 on white, 3.96:1 on the dark surface; a dot
   is graphical, so the bar is 3:1. */
.ms-live-dot{background:var(--success);animation:al-rec 1.6s ease-in-out infinite;}
.ms-actions{flex-wrap:wrap;margin-top:12px;}

/* --------------------------------------------------------------------
   23.5 Customers
   -------------------------------------------------------------------- */
/* The brief's headline is an .ms-row-title, which is a ROW title — 14.5px
   regular. Above .ms-brief-body's 15.5px serif it read as a subordinate
   lead-in to its own body. .m-brief has no title element at all (its
   design goes eyebrow → serif body), so there is nothing to group onto. */
.ms-brief .ms-row-title{font-weight:600;margin-bottom:3px;}

.ms-stage{margin-bottom:16px;}
/* Alternating avatar tint down a long list. It is her tint rather than
   a second solid fill because no blue in the ramp carries white text in
   BOTH modes — --holo-text on --holo-tint is 4.79:1 light and 6.25:1
   dark, where white on --holo-text is 5.47:1 light but 2.22:1 dark. */
.ms-av-blue{background:var(--holo-tint);color:var(--holo-text);}

/* --------------------------------------------------------------------
   23.6 Home stats
   -------------------------------------------------------------------- */
.ms-stat{text-align:left;}
.ms-stat-label,.ms-stat-value,.ms-stat-sub{display:block;}
.ms-stat-sub.good{color:var(--success);}
.ms-stat-sub.warn{color:var(--accent-text);}
.ms-brief-action:disabled{opacity:.4;}

/* --------------------------------------------------------------------
   23.7 Ads
   -------------------------------------------------------------------- */
.ms-camp-head{justify-content:space-between;}
.ms-camp-metrics{margin-top:11px;}
.ms-metric{display:flex;flex-direction:column;gap:1px;}
/* The cap row stacks title / bar / sub, where .m-cap's bar sits beside
   the label at a fixed 74px. */
.ms-cap-row .ms-bar{margin:7px 0 5px;}
/* act · ask · off. The group groups onto .m-seg, the buttons onto
   .m-seg button — but three of them plus a label on a 393px row need
   the tighter weight. Two classes, so it outranks `.m-seg button`. */
.ms-mode{flex:none;margin-bottom:0;}
.ms-mode .ms-mode-btn{padding:6px 9px;min-height:30px;font-size:12px;}
.ms-mode-btn:disabled{opacity:.4;}
.ms-fence-line{margin-bottom:6px;}
.ms-vis-row{display:flex;align-items:center;gap:10px;}
.ms-vis-row .ms-bar{flex:1;}
.ms-vis-label{flex:none;width:88px;font-size:12.5px;color:var(--text-secondary);}
.ms-vis-score{
  flex:none;min-width:32px;text-align:right;
  font-size:13px;font-weight:600;
  font-variant-numeric:tabular-nums;
}

/* --------------------------------------------------------------------
   23.8 Files
   .m-search IS the input; here the icon and the input share a bordered
   wrapper, so the border moves out to the wrapper and the input goes
   bare. The focus ring moves with it.
   -------------------------------------------------------------------- */
.ms-search{
  display:flex;align-items:center;gap:9px;
  padding:0 15px;
  border-radius:22px;
  border:1px solid var(--m-line-2);
  background:var(--bg-surface);
  margin-bottom:12px;
  transition:border-color .15s,box-shadow .15s;
}
.ms-search svg{flex:none;display:block;color:var(--m-ink-faint);}
.ms-search .ms-input{
  flex:1;min-width:0;width:auto;
  padding:11px 0;
  border:none;background:transparent;border-radius:0;
}
.ms-search .ms-input:focus{box-shadow:none;}
.ms-search:focus-within{
  border-color:var(--holo);
  box-shadow:0 0 0 3px var(--holo-soft);
}
/* The server marks matched terms; screens.js builds real <mark> nodes.
   --accent-text-soft on --accent-soft is the ramp's tinted pairing,
   4.84:1 light and 5.54:1 dark. */
.ms-file-sub mark{
  background:var(--accent-soft);color:var(--accent-text-soft);
  border-radius:3px;padding:0 2px;
}
.ms-dropzone{width:100%;}

/* --------------------------------------------------------------------
   23.9 Settings
   -------------------------------------------------------------------- */
/* .m-eyebrow sits above a group; this one is the group's first child. */
.ms-group-title{margin:0;padding:12px 14px 4px;}
.ms-role-row{text-decoration:none;color:var(--text-primary);}

/* --------------------------------------------------------------------
   23.10 Sheets and the checkbox switch
   -------------------------------------------------------------------- */
/* .m-sheet opens without a grab handle; screens.js draws one. */
.ms-sheet-grab{
  flex:none;align-self:center;
  width:36px;height:5px;margin:8px 0 2px;
  border-radius:3px;
  background:var(--m-fill-strong);
}
/* The title + provenance column has no class of its own. */
.ms-sheet-head > div{flex:1;min-width:0;}
/* .m-prov leads its block; here it trails the title. */
.ms-sheet-prov{margin:4px 0 0;}
.ms-sheet-close svg{display:block;}
.ms-field{margin-bottom:12px;}
.ms-switch-row{cursor:pointer;}
/* .m-switch is a <button> carrying .on with a knob element inside it.
   The ms- switch is a real <input type=checkbox> inside its track, so
   the track groups onto .m-switch, the input groups onto .m-switch-knob,
   and the checked state has to be read off the input.
   The :has() rules are kept SEPARATE on purpose: an engine that does
   not understand :has() drops the whole selector list a rule is
   declared with, and taking .m-switch.on down with it would break the
   switch that already ships. */
.ms-switch{appearance:none;-webkit-appearance:none;margin:0;flex:none;}
.ms-switch:focus-visible{outline:2px solid var(--holo);outline-offset:2px;}
.ms-switch-track:has(.ms-switch:checked){
  background:var(--accent-bright);
  justify-content:flex-end;
}
.ms-switch-track:has(.ms-switch:disabled){opacity:.45;}

/* --------------------------------------------------------------------
   23.11 Sub-Phase C — skeletons, gap disclosures, the briefings inbox,
   the Home rail count, the memory sheet.
   -------------------------------------------------------------------- */
/* Loading skeleton (C9): three quiet bars where rows will land, replacing
   the word "Loading…". The shimmer is one background-position sweep; the
   prefers-reduced-motion neutralizer at the top of this file already
   covers it (.m-shell * and .ms-sheet * are both possible hosts), so
   reduced motion gets a still tint, not a moving one. */
@keyframes ms-skel{0%{background-position:130% 0}100%{background-position:-30% 0}}
.ms-skel-stack{display:flex;flex-direction:column;gap:10px;margin:10px 0 14px;}
.ms-skel{
  height:44px;border-radius:12px;
  background:linear-gradient(100deg,
    var(--m-fill) 40%,var(--m-fill-strong) 50%,var(--m-fill) 60%)
    var(--m-fill);
  background-size:220% 100%;
  animation:ms-skel 1.4s ease-in-out infinite;
}
.ms-skel:nth-child(2){opacity:.72;}
.ms-skel:nth-child(3){opacity:.45;}

/* A data-gap note behind a "why?" disclosure (C9). The summary is the only
   visible line until it is asked for; the answer keeps .ms-gap's quiet
   note voice. Native <details> — no script, nothing for the CSP to mind. */
details.ms-gap summary{cursor:pointer;list-style:none;}
details.ms-gap summary::-webkit-details-marker{display:none;}
.ms-gap-why{
  font-size:11.5px;font-weight:600;color:var(--m-ink-faint);
  text-decoration:underline dotted;text-underline-offset:3px;
}
details.ms-gap[open] .ms-gap-why{color:var(--m-ink-quiet);}
.ms-gap-text{margin:6px 0 0;}

/* The Home rail item wears the unread-briefings COUNT (C1) — the Alice
   dot says "something waits", this says how much reading. Same perch as
   .m-rail-dot; white on --accent-deep is the ramp's documented pairing. */
.m-rail-badge{
  position:absolute;top:2px;left:calc(50% + 2px);
  min-width:16px;height:16px;padding:0 4px;
  border-radius:8px;
  background:var(--accent-deep);color:var(--on-accent);
  font-size:9.5px;font-weight:700;line-height:16px;
  text-align:center;
}

/* Briefings inbox cards (C1). Severity rides the left edge — the calmer
   the colour, the calmer the news — and unread carries her tint, the
   standing "she put this here" colour. Tones double as text colour on the
   small severity chip; info stays the quiet ink. */
.ms-brf{border-left:3px solid var(--m-line-2);}
.ms-brf.attention{border-left-color:var(--accent-bright);}
.ms-brf.urgent{border-left-color:var(--danger);}
.ms-brf.unread{background:var(--holo-tint);border-color:var(--m-holo-line);}
.ms-brf .ms-row-title{font-weight:600;margin-bottom:4px;}
.ms-brf-sev{
  flex:none;margin-left:auto;
  font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--m-ink-quiet);
}
.ms-brf-sev.attention{color:var(--accent-text);}
.ms-brf-sev.urgent{color:var(--m-danger-text);}

/* The Home brief card's second action (C9): same chip geometry as
   .ms-brief-action, her tint — it starts a conversation, not a cleanup. */
.ms-brief-ask{color:var(--holo-text);border-color:var(--m-holo-line-strong);}

/* The memory sheet's fact rows (C9.9): the sentence grows, Forget stays
   put beside it. */
.ms-fact{
  display:flex;align-items:center;gap:10px;
  padding:9px 2px;border-bottom:1px solid var(--m-line);
}
.ms-fact:last-of-type{border-bottom:none;}
.ms-fact-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
.ms-fact-text{font-size:13.5px;line-height:1.45;color:var(--text-primary);}
.ms-fact-when{font-size:11.5px;color:var(--text-tertiary);}


/* ====================================================================
   22b. MORE — Apple-Settings grouped lists + split view.
   One DOM at every width. Narrow: iPhone push/pop (keyframes at the top
   of the file). Wide: the 901px block turns .m-split into a persistent
   sidebar + detail and makes .m-sub-open inert. State classes live on
   .m-split and .m-back only — show() resets .m-view's className.
   ==================================================================== */
.m-split{display:grid;position:relative;overflow-x:clip;}
.m-split-list,
.m-split-detail{grid-area:1/1;min-width:0;}
/* no z-index: the detail is the LATER sibling, so it already paints
   above the list mid-flight; an explicit z-index here has been seen to
   break painting entirely inside embedded webviews. */
.m-split-detail{display:none;overflow-x:auto;}
.m-split.m-sub-open .m-split-detail{display:block;animation:m-split-in .35s cubic-bezier(0.32,0.72,0,1);}
.m-split.m-sub-open .m-split-list{display:none;animation:m-split-under .35s cubic-bezier(0.32,0.72,0,1);}
.m-split.m-split-pop .m-split-list{animation:m-split-back .35s cubic-bezier(0.32,0.72,0,1);}
.m-split.m-split-pop .m-split-detail{animation:m-split-out .35s cubic-bezier(0.32,0.72,0,1);}

/* Grouped inset card. Flat is the look: zero border, zero shadow. The
   page paper plays systemGroupedBackground; the card plays the cell. */
.m-group{
  background:var(--m-set-card);
  border-radius:10px;
  corner-shape:squircle;
  overflow:hidden;
}
/* The eyebrow aligns with the icon tiles (16px card inset). */
.m-set-eyebrow{
  margin:28px 0 7px;
  padding:0 16px;
  font-size:13px;font-weight:600;letter-spacing:.03em;
  text-transform:uppercase;
  color:var(--m-set-2nd);
}
.m-split-list .m-set-eyebrow:first-child{margin-top:4px;}
.m-set-foot{
  margin:0;
  padding:7px 16px 0;
  font-size:13px;line-height:1.45;
  color:var(--m-set-2nd);
}

/* Row anatomy: [29px tile] [title/sub … chevron]. The body wrapper
   carries the separator, so the hairline insets to the text leading
   edge (16 + 29 + 15 = 60px) and runs to the card's trailing edge. */
.m-item{
  width:100%;text-align:left;
  display:flex;align-items:stretch;
  padding:0 0 0 16px;min-height:44px;
  border:none;background:transparent;color:var(--text-primary);
  touch-action:manipulation;
  -webkit-tap-highlight-color:transparent;
  user-select:none;-webkit-user-select:none;
  transition:background-color .35s ease-out;
}
.m-item:active{background:var(--m-press);transition:none;}
.m-item:focus-visible,
.m-back:focus-visible{outline-offset:-2px;}
@media (hover:hover) and (pointer:fine){
  .m-item:not(.on):hover{background:var(--m-hover);}
}
.m-tile{
  flex:none;align-self:center;
  width:29px;height:29px;
  border-radius:6.5px;
  corner-shape:squircle;
  display:inline-flex;align-items:center;justify-content:center;
}
.m-tile svg{display:block;}
.m-tile--files{background:var(--m-tile-files);}
.m-tile--settings{background:var(--m-tile-settings);}
.m-tile--flow{background:var(--m-tile-flow);}
.m-tile--leadfinder{background:var(--m-tile-leadfinder);}
.m-tile--agent{background:var(--m-tile-agent);}
.m-tile--studio{background:var(--m-tile-studio);}
.m-tile--agency{background:var(--m-tile-agency);}
.m-tile--owner{background:var(--m-tile-owner);}
.m-tile--workspace{background:var(--m-tile-workspace);}
.m-tile--adsdesk{background:var(--m-tile-adsdesk);}
.m-tile--crmdesk{background:var(--m-tile-crmdesk);}
.m-tile--ads{background:var(--m-tile-ads);}
.m-tile--calendar{background:var(--m-tile-calendar);}
.m-tile--analytics{background:var(--m-tile-analytics);}
.m-tile--finance{background:var(--m-tile-finance);}
.m-tile--sites{background:var(--m-tile-sites);}
.m-tile--pricebook{background:var(--m-tile-pricebook);}
.m-tile--social{background:var(--m-tile-social);}
.m-item-body{
  flex:1;min-width:0;
  display:flex;align-items:center;gap:8px;
  padding:11px 16px 11px 15px;
}
.m-item + .m-item > .m-item-body{border-top:1px solid var(--m-set-sep);}
@media (min-resolution:2dppx){
  .m-item + .m-item > .m-item-body{border-top-width:.5px;}
}
.m-item-title{
  display:block;font-size:17px;font-weight:400;line-height:1.3;
  letter-spacing:-.022em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-item-sub{
  display:block;font-size:13px;line-height:1.35;color:var(--m-set-2nd);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.m-chev{flex:none;display:inline-flex;color:var(--m-set-3rd);}
.m-chev svg{display:block;}


/* ====================================================================
   24. CALENDAR — the month grid (screens.js `calendar`, 2026-08-28).

   Six fixed weeks, so paging through the year never changes the grid's
   height. A square is a button: tapping it selects the day and the list
   underneath re-reads. Three kinds ride the existing semantic ramp
   rather than a new palette — appointments wear the brand orange (a
   customer is coming), tasks wear Alice's blue (something to do), jobs
   wear the success green (money with a date on it).

   The chip label is hidden below 901px: a 50px-wide square cannot hold
   "9:30 AM Dave Mercer", so the phone shows the dots alone and the day
   list below carries the words.
   ==================================================================== */
.ms-cal-head{
  display:flex;align-items:center;gap:8px;
  flex-wrap:wrap;
  margin-bottom:12px;
}
.ms-cal-nav{display:flex;align-items:center;gap:2px;}
.ms-cal-step{
  display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;padding:0;
  border:1px solid var(--m-line-2);border-radius:8px;
  background:transparent;color:var(--m-ink-quiet);
  transition:background .16s,color .16s;
}
.ms-cal-step:hover{background:var(--m-hover);color:var(--text-primary);}
.ms-cal-step:active{background:var(--m-press);}
.ms-cal-title{
  margin:0;
  font-family:var(--font-serif);font-size:18px;font-weight:600;
  letter-spacing:-.01em;white-space:nowrap;
}
.ms-cal-spacer{flex:1;}
/* The skeletons sit where the grid will, so the head does not jump. */
.ms-cal-wait{min-height:180px;}

.ms-cal-dow{
  display:grid;grid-template-columns:repeat(7,1fr);gap:4px;
  margin-bottom:5px;
}
.ms-cal-dow-c{
  text-align:center;
  font-size:10.5px;font-weight:700;letter-spacing:.06em;
  color:var(--m-ink-faint);
}
.ms-cal-grid{
  display:grid;grid-template-columns:repeat(7,1fr);gap:4px;
}
.ms-cal-cell{
  display:flex;flex-direction:column;align-items:stretch;gap:3px;
  min-height:62px;padding:5px 5px 6px;
  border:1px solid var(--m-line);border-radius:9px;
  background:var(--m-set-card);
  text-align:left;
  transition:background .16s,border-color .16s;
}
.ms-cal-cell:hover{background:var(--m-hover);}
.ms-cal-cell.out{opacity:.42;}
.ms-cal-cell.today .ms-cal-num{
  background:var(--accent-deep);color:var(--on-accent);
  border-radius:50%;
}
.ms-cal-cell.on{border-color:var(--accent-bright);background:var(--accent-soft);}
.ms-cal-num{
  flex:none;align-self:flex-start;
  min-width:20px;height:20px;padding:0 3px;
  font-size:11.5px;font-weight:600;line-height:20px;text-align:center;
  color:var(--text-primary);
}
.ms-cal-chips{
  flex:1;min-height:0;
  display:flex;flex-direction:column;gap:2px;
  overflow:hidden;
}
.ms-cal-chip{
  display:flex;align-items:center;gap:4px;
  min-width:0;padding:1px 3px;border-radius:4px;
  font-size:10.5px;line-height:1.35;
  color:var(--m-ink-quiet);
}
.ms-cal-chip-text{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ms-cal-dot{
  flex:none;width:6px;height:6px;border-radius:50%;
  background:var(--m-ink-faint);
}
.ms-cal-chip.appt{background:var(--accent-soft);color:var(--accent-text-soft);}
.ms-cal-chip.appt .ms-cal-dot{background:var(--accent-bright);}
.ms-cal-chip.task{background:var(--holo-tint);color:var(--holo-text);}
.ms-cal-chip.task .ms-cal-dot{background:var(--holo);}
.ms-cal-chip.job{background:var(--success-soft);color:var(--success);}
.ms-cal-chip.job .ms-cal-dot{background:var(--success);}
.ms-cal-more{
  padding-left:3px;
  font-size:10px;font-weight:600;
  color:var(--m-ink-faint);
}

/* Phone: dots only. The label has nowhere to go in a 50px square, and the
   day list below is the surface that carries the words. */
@media (max-width:900px){
  .ms-cal-chip-text{display:none;}
  .ms-cal-chip{justify-content:center;padding:1px 0;background:none;}
  .ms-cal-chips{flex-direction:row;flex-wrap:wrap;gap:3px;align-content:flex-start;}
  .ms-cal-cell{min-height:52px;}
  .ms-cal-more{padding-left:0;font-size:9.5px;}
}


/* ====================================================================
   25. THE REPORTING DESKS — Analytics and Finance (screens.js
   `analytics` and `finance`, 2026-08-28).

   FIVE classes, and the smallness is the point: both desks are built
   out of the rows, cards, stat tiles, kv lines, progress bars and chip
   groups already declared above, so what is left here is only what none
   of those shapes could be.

   The table is the real one. Six panels across the two desks are a
   header row and N rows of small numbers — ROAS by platform, ROAS by
   campaign, lead sources, sources all-time, cohorts, AI usage by
   surface — and a stack of .ms-row divs would be a table pretending not
   to be one. A column header is what makes "0.42" mean cost per lead to
   a screen reader as well as to an eye.
   ==================================================================== */

/* The scroller, and it is the TABLE that scrolls, never the page: the
   shell is a fixed frame with a header and a rail, so a document-level
   horizontal scroll would drag those sideways too. */
.ms-tbl-wrap{
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  /* NO negative margin. An earlier `margin:0 -2px` bled the box to the
     edge of the card and, measured at 375px, put the wrapper 2px past
     the More detail pane on each side — so the PANE itself scrolled
     sideways, which is the exact thing the scroller exists to prevent.
     A scroller that overflows its own parent has moved the problem, not
     solved it. */
  padding-bottom:2px;
}
.ms-tbl{
  width:100%;
  border-collapse:collapse;
  font-size:13px;
  text-align:left;
}
.ms-tbl th,
.ms-tbl td{
  padding:8px 10px;
  border-bottom:1px solid var(--m-line);
  vertical-align:baseline;
  /* Wrapping a six-column table of numbers makes every row a different
     height and the columns stop reading as columns. It scrolls
     instead. */
  white-space:nowrap;
}
.ms-tbl thead th{
  font-size:11px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;
  color:var(--m-ink-quiet);
  border-bottom:1px solid var(--m-line-2);
}
.ms-tbl tbody th{font-weight:600;color:var(--text-primary);}
.ms-tbl tbody td{color:var(--text-secondary);}
.ms-tbl tbody tr:last-child th,
.ms-tbl tbody tr:last-child td{border-bottom:none;}

/* A measurement column. Right-aligned so the digits stack on the same
   place value, and tabular figures so 9 → 10 → 100 does not re-measure
   the column between one window and the next. */
.ms-num{text-align:right;font-variant-numeric:tabular-nums;}

/* The rehearsal badge. META_DRY_RUN numbers are not money, and the one
   place that must be said is beside the number itself — a footnote at
   the top of a panel is not read by someone who scrolled to a row. */
.ms-flag{
  display:inline-block;
  margin-left:6px;padding:1px 6px;
  border:1px solid var(--m-accent-line);
  border-radius:999px;
  background:var(--accent-soft);
  color:var(--accent-text);
  font-size:10px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;
  white-space:nowrap;
}

/* Two columns where two fit, one where they do not — auto-fit rather
   than a breakpoint, because these desks render inside the More split's
   detail pane, whose width is not the viewport's and does not change at
   the same place. */
.ms-grid2{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(268px,1fr));
  gap:10px;
  align-items:start;
}
/* .ms-card carries its own margin-bottom for a stack; inside the grid
   the gap does that job and the margin would double it. */
.ms-grid2>.ms-card{margin-bottom:0;}

} /* end @media all — NOT max-width:900px; see the header note */


/* ====================================================================
   23. DESKTOP — the same shell, laid out for a wide screen.

   Nothing here changes what a screen IS. The rail moves from the bottom
   edge to the left column, the content gets a readable measure instead of
   filling 1900px, and the borrowed desktop panels get room to be
   themselves. Structure, code and vocabulary are identical to the phone.
   ==================================================================== */
@media (min-width:901px){
  /* GRID, not flex-wrap. The first attempt kept the phone's flex column and
     tried to place a full-height rail beside it with wrap + margins — so the
     rail consumed the whole line box and the header, call strip and content
     wrapped onto a second row with no height left. The rail rendered; the
     entire rest of the app did not. Two axes want a two-axis layout. */
  .m-shell{
    display:grid;
    grid-template-columns:260px minmax(0,1fr);
    grid-template-rows:auto auto minmax(0,1fr);
    grid-template-areas:
      "rail header"
      "rail strip"
      "rail view";
  }

  /* The rail is the phone's idiom and stops at this breakpoint. The column
     it used to become (84px of stacked icons, 2026-08-04) is now the named
     260px sidebar in §6b: same grid area, same six destinations, same
     order — a workspace switcher, group headings, disclosure children and
     a pinned footer are what the extra 176px buys.

     .m-side is `position:relative` here and only here, because the only
     thing that needs it is the switcher's menu, which only exists at this
     width. */
  .m-rail{display:none;}
  .m-side{display:flex;grid-area:rail;position:relative;}

  .m-header{grid-area:header;}
  .m-call-host{grid-area:strip;}
  .m-view{grid-area:view;padding:22px clamp(20px,5vw,56px) 56px;}

  /* A measure, not a stretch. Long text at 1900px is unreadable; the heavy
     borrowed panels opt out because they are tables and canvases. */
  .m-view>*:not(.m-hosted):not(.m-split){max-width:920px;margin-left:auto;margin-right:auto;}
  .m-view--conv{padding:22px clamp(20px,5vw,56px) 22px;}

  .m-header-title{font-size:24px;}
  /* Sheets are centred dialogs on a wide screen, not bottom sheets. */
  .m-sheet{
    left:50%;right:auto;bottom:auto;top:50%;
    transform:translate(-50%,-50%);
    width:min(560px,92vw);max-height:78vh;border-radius:18px;
  }

  /* ── More split view: persistent 300px sidebar, detail beside it ── */
  .m-view>.m-split{
    max-width:none;margin:0;
    /* One column: the list beside this is gone (see below) and the detail
       takes the whole pane. */
    display:grid;grid-template-columns:minmax(0,1fr);gap:0;
    align-items:start;
    overflow:visible; /* restore sticky; the clip is for phone slides */
  }
  /* .m-sub-open is inert here: both panes are always visible, un-stacked
     from the phone's shared grid cell into their own columns… */
  /* ONE PLACE TO PICK A DESTINATION (owner, 2026-08-29).
     This used to be 300px of list beside the detail — which, at this width,
     sat next to a 260px sidebar that already lists every one of the same
     destinations. Two navigation columns, side by side, to choose one thing.

     Verified before removing it rather than assumed: the sidebar's top-level
     rows plus its More branch cover the More list EXACTLY, with nothing left
     only in the middle column. Below 901px the sidebar does not exist and the
     list is the only navigation there is, so it stays — this rule lives inside
     the min-width:901px block for that reason. */
  .m-split .m-split-list,
  .m-split.m-sub-open .m-split-list{display:none;}
  .m-split .m-split-detail,
  .m-split.m-sub-open .m-split-detail{display:block;grid-area:auto;grid-column:1;}
  /* …and a wide detail swap is instant — no slide, no fade, including
     the first real tap after a restore. */
  .m-split.m-sub-open .m-split-list,
  .m-split.m-sub-open .m-split-detail,
  .m-split.m-split-pop .m-split-list,
  .m-split.m-split-pop .m-split-detail{animation:none;}

  /* iPadOS sidebar idiom, not Settings' carded column: transparent rows,
     no separators, neutral selection pill, hairline column divider. */
  .m-split-list{
    position:sticky;top:0;align-self:start;
    max-height:calc(100vh - 120px);
    overflow-y:auto;overscroll-behavior:contain;
    border-right:1px solid var(--m-set-sep);
    padding-right:12px;
    scrollbar-width:none;
  }
  .m-split-list::-webkit-scrollbar{display:none;}
  .m-split-list .m-group{background:transparent;border-radius:0;overflow:visible;}
  .m-split-list .m-item{border-radius:10px;corner-shape:squircle;}
  .m-split-list .m-item + .m-item > .m-item-body{border-top:none;}
  .m-split-list .m-item.on{background:var(--m-select);}
  .m-split-list .m-chev{display:none;}

  /* the detail is right there — no back arrow at this width, ever */
  .m-back.m-back-split{display:none;}
  .m-split-detail>.ms-pane{max-width:920px;}

  /* Hosted heavy tools KEEP the sidebar (owner's call, 2026-08-09). They used
     to collapse it and hand you a back arrow, so moving between two tools was
     back-then-pick instead of one click — and the column vanishing under you
     read as a bug. The panel now lives in the detail column like every other
     selection; its own tables scroll internally, which is why it fits.
     Only the measure cap is lifted: these are tables and canvases. */
  .m-split.m-split--hosted .m-split-detail>.m-hosted{max-width:none;}
}

/* A borrowed desktop panel, living inside the shell. It keeps its own
   internal layout — these only undo the fixed-height/scroll assumptions the
   classic .view made about being the whole page. */
.m-view .m-hosted{display:none;}
.m-view .m-hosted.active{
  display:block;width:100%;max-width:none;
  padding:0;margin:0;background:transparent;overflow:visible;
}

/* Back arrow — only shown one level deep, inside More. */
.m-back{
  border:0;background:none;cursor:pointer;padding:0 8px 0 0;margin:0;
  font-size:26px;line-height:1;color:var(--accent-text);
}

/* Business: the two-way segmented control over Customers | Ads. */
.m-seg--two{display:grid;grid-template-columns:1fr 1fr;gap:3px;margin-bottom:15px;}
.m-seg-btn{
  border:0;cursor:pointer;padding:8px 12px;border-radius:8px;
  background:transparent;color:var(--text-secondary);
  font:600 13.5px/1 var(--font-sans);letter-spacing:-.01em;
}
.m-seg-btn.on{background:var(--bg-surface);color:var(--text-primary);box-shadow:var(--shadow-card);}
.m-subpane{display:block;}

/* ══════════════════════════════════════════════════════════════════════════
   ALICE COMPOSER — attach folder + growing field (2026-08-27)
   ══════════════════════════════════════════════════════════════════════════ */

/* The field is a textarea now. It must still sit on the composer row like the
   single-line input did: no resize grip, no baseline jump, scroll only once it
   has hit the ceiling JS sets (132px). */
.sa-composer-row .sa-input{
  resize:none;overflow-y:auto;min-height:40px;max-height:132px;
  line-height:1.45;padding-top:10px;padding-bottom:10px;
  font-family:inherit;
}
.sa-composer-row .sa-send:disabled{opacity:.38;cursor:default;}
.sa-file{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;}

/* ── the attach folder ──────────────────────────────────────────────────────
   Adapted from a 21st.dev folder-interaction. Same gesture — lid tipping back,
   pages lifting and fanning — at 26px, in transforms rather than a spring
   library. It opens on HOVER (and on keyboard focus, so it is not a
   mouse-only affordance); the CLICK is what opens the file picker. Preview and
   action are deliberately different events: hovering a control should never
   commit you to anything. */
.sa-attach{
  flex:none;width:38px;height:38px;display:grid;place-items:center;
  background:none;border:none;padding:0;cursor:pointer;border-radius:11px;
  transition:background .16s;
}
.sa-attach:hover,.sa-attach:focus-visible{background:var(--bg-muted);}
.sa-attach:focus-visible{outline:2px solid var(--accent-bright);outline-offset:-2px;}

.sa-folder{position:relative;display:block;width:26px;height:21px;}

/* Back wall of the folder. */
.sa-folder::before{
  content:"";position:absolute;left:0;bottom:0;width:26px;height:18px;
  background:var(--text-tertiary);border-radius:3px 5px 4px 4px;
  transition:background .16s;
}
/* The tab along the top-left, which is what makes it read as a folder. */
.sa-folder::after{
  content:"";position:absolute;left:0;bottom:15px;width:11px;height:4px;
  background:var(--text-tertiary);border-radius:3px 3px 0 0;
  transition:background .16s;
}
.sa-attach.open .sa-folder::before,.sa-attach.open .sa-folder::after{
  background:var(--text-secondary);
}

/* Three pages, stacked flat inside until the folder opens. */
.sa-folder-page{
  position:absolute;left:50%;bottom:4px;width:12px;height:15px;
  margin-left:-6px;border-radius:2px;
  background:var(--bg-surface);border:1px solid var(--border);
  transform:translateY(4px) scale(.86);opacity:0;
  transition:transform .34s cubic-bezier(.16,.84,.24,1),opacity .2s ease;
}
.sa-attach.open .sa-folder-page{opacity:1;}
.sa-attach.open .sa-folder-page.p1{transform:translate(-7px,-9px) rotate(-15deg);}
.sa-attach.open .sa-folder-page.p2{transform:translateY(-12px) rotate(1deg);}
.sa-attach.open .sa-folder-page.p3{transform:translate(7px,-9px) rotate(15deg);}
/* Stagger, so they lift as a hand of cards rather than one slab. */
.sa-attach.open .sa-folder-page.p2{transition-delay:.03s;}
.sa-attach.open .sa-folder-page.p3{transition-delay:.06s;}

/* The front lid, hinged along its bottom edge. */
.sa-folder-lid{
  position:absolute;left:0;bottom:0;width:26px;height:14px;
  background:var(--text-secondary);border-radius:3px 3px 5px 5px;
  transform-origin:bottom center;
  transform:perspective(70px) rotateX(0deg);
  transition:transform .34s cubic-bezier(.16,.84,.24,1),background .16s;
}
.sa-attach.open .sa-folder-lid{
  transform:perspective(70px) rotateX(-42deg);
  background:var(--text-primary);
}

@media(prefers-reduced-motion:reduce){
  .sa-folder-page,.sa-folder-lid{transition-duration:.01ms;transition-delay:0s;}
  .sa-attach.open .sa-folder-lid{transform:none;}
}

/* ══════════════════════════════════════════════════════════════════════════
   ALICE ORB — the WebGL disc, and its drop to the composer (2026-08-27)
   ══════════════════════════════════════════════════════════════════════════ */

/* The canvas sits OVER the CSS orb rather than replacing it. If WebGL is
   refused, the shader will not compile, the script never loads, or the context
   is lost later, the breathing gradient underneath is still there — the orb is
   an upgrade, never a dependency. .has-gl only silences the breathing
   animation, so the two are not fighting for the same 132px. */
.sa-orb.has-gl{animation:none;background:none;}
.sa-orb-gl{display:block;width:100%;height:100%;}
.sa-orb{position:relative;}

/* In flight: lifted onto a fixed layer so it can cross the screen, with its
   start rect written inline by dockOrb(). transform-origin is the centre
   because the keyframes translate centre-to-centre. */
.sa-orb-gl.flying{
  position:fixed;z-index:70;pointer-events:none;
  transform-origin:50% 50%;will-change:transform;
}

/* Where it lands: a real flex slot at the left of the composer row, sized to
   the row so it reads as part of the bar rather than floating beside it. */
.sa-orb-dock{
  flex:none;width:34px;height:34px;position:relative;
  align-self:center;margin-right:2px;
}
.sa-orb-dock[hidden]{display:none;}
.sa-orb-dock .sa-orb-gl{width:34px;height:34px;}

@media(max-width:400px){
  .sa-orb-dock{width:28px;height:28px;}
  .sa-orb-dock .sa-orb-gl{width:28px;height:28px;}
}

/* Reduced motion never sees the flight (dockOrb returns early), and the orb is
   not created at all — the CSS disc keeps its own already-neutralised
   animation. This is belt and braces for a context that somehow exists. */
@media(prefers-reduced-motion:reduce){
  .sa-orb-gl.flying{transition:none;animation:none;}
}

/* ══════════════════════════════════════════════════════════════════════════
   ALICE COMPOSER CARD — the v0 layout (2026-08-27)
   Field across the top, controls on a tools row beneath it, inside one
   border. A growing textarea on a single-row bar shoves the buttons around
   and pushes them off a phone; giving them their own row fixes that and is
   the shape the reference uses.
   ══════════════════════════════════════════════════════════════════════════ */
.sa-composer-row.sa-card{
  display:block;                       /* was a flex row */
  background:var(--bg-surface);
  border:1px solid var(--border);
  border-radius:16px;
  padding:6px 6px 6px;
  position:relative;
  transition:border-color .16s,box-shadow .16s;
}
.sa-composer-row.sa-card:focus-within{
  border-color:var(--accent-bright);
  box-shadow:0 0 0 3px var(--accent-soft);
}
.sa-composer-row.sa-card .sa-input{
  width:100%;border:none;background:none;box-shadow:none;
  padding:10px 10px 4px;min-height:44px;max-height:132px;
}
.sa-composer-row.sa-card .sa-input:focus{outline:none;box-shadow:none;}

.sa-tools{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:2px 4px 2px;}
.sa-tools-l,.sa-tools-r{display:flex;align-items:center;gap:6px;}

/* The orb's dock sits at the card's left edge, on the tools row. */
.sa-composer-row.sa-card .sa-orb-dock{position:absolute;left:10px;bottom:9px;}

/* Action pills, now BELOW the card. Horizontal scroller still — the row must
   never grow to two lines and push the card off a short screen. */
.sa-composer .sa-chips{margin-top:10px;}

@media(max-width:400px){
  .sa-composer-row.sa-card{border-radius:14px;}
  .sa-composer-row.sa-card .sa-orb-dock{left:8px;bottom:7px;}
}

/* ══════════════════════════════════════════════════════════════════════════
   ALICE ATTACHMENTS + MODEL PICKER (2026-08-27)
   Adapted from a Claude-style chat input: a tray of 125px cards above the
   field, image thumbnails, textual files showing their first lines, a big
   paste becoming a card. Colours are Loggia tokens, not the reference's zinc.
   ══════════════════════════════════════════════════════════════════════════ */

.sa-composer{position:relative;}

/* Drop zone. The overlay only appears while files are actually being dragged
   (the JS counts dragenter/dragleave depth so it cannot flicker). */
.sa-drop-hint{
  position:absolute;inset:0;z-index:5;display:none;
  align-items:center;justify-content:center;
  border:2px dashed var(--accent-bright);border-radius:18px;
  background:var(--accent-soft);pointer-events:none;
}
.sa-drop-hint span{font-size:13.5px;font-weight:600;color:var(--accent-text);}
.sa-composer.sa-dragging .sa-drop-hint{display:flex;}

/* The tray */
.sa-tray{
  display:flex;gap:10px;padding:10px 10px 4px;
  overflow-x:auto;overflow-y:hidden;
  scrollbar-width:thin;-webkit-overflow-scrolling:touch;
}
.sa-tray[hidden]{display:none;}

.sa-att{
  position:relative;flex:none;width:118px;height:118px;border-radius:12px;
  background:var(--bg-muted);border:1px solid var(--border);overflow:hidden;
}
.sa-att.is-uploading{opacity:.72;}
.sa-att.is-error{border-color:var(--danger);}
.sa-att-img{width:100%;height:100%;object-fit:cover;display:block;}
.sa-att-text{
  padding:8px;font:400 8px/1.35 var(--font-mono,ui-monospace,monospace);
  color:var(--text-secondary);white-space:pre-wrap;word-break:break-word;
  height:100%;overflow:hidden;
}
.sa-att-meta{padding:10px;display:flex;flex-direction:column;gap:3px;height:100%;justify-content:flex-end;}
.sa-att-name{font-size:11.5px;font-weight:600;color:var(--text-primary);
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;}
.sa-att-size{font-size:10.5px;color:var(--text-tertiary);}

/* The type chip, over a scrim so it stays legible on any thumbnail. */
.sa-att-badge{
  position:absolute;left:6px;bottom:6px;
  font-size:9.5px;font-weight:700;letter-spacing:.04em;
  color:var(--text-primary);background:var(--bg-surface);
  border:1px solid var(--border);border-radius:5px;padding:2px 6px;
}
.sa-att.is-image::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to bottom,transparent 55%,rgba(0,0,0,.42) 100%);
}
.sa-att.is-image .sa-att-badge{z-index:1;}
.sa-att-err{
  position:absolute;left:6px;top:6px;font-size:9.5px;font-weight:700;
  color:#fff;background:var(--danger);border-radius:5px;padding:2px 6px;
}
.sa-att-x{
  position:absolute;top:5px;right:5px;z-index:2;
  width:20px;height:20px;line-height:1;font-size:15px;
  display:grid;place-items:center;cursor:pointer;
  color:var(--text-primary);background:var(--bg-surface);
  border:1px solid var(--border);border-radius:50%;padding:0;
  opacity:0;transition:opacity .14s;
}
.sa-att:hover .sa-att-x,.sa-att-x:focus-visible{opacity:1;}
@media(hover:none){.sa-att-x{opacity:1;}}   /* touch has no hover to reveal it */

/* ── model picker ───────────────────────────────────────────────────────── */
/* The composer picker shares its name with the transcript's model badge.
   Only the badge clips long IDs; the picker must let its menu open. */
.sa-tools .sa-model{position:relative;max-width:none;overflow:visible;margin:0;padding:0;background:none;font:inherit;white-space:normal;}
.sa-model-btn{
  display:inline-flex;align-items:center;gap:4px;
  font:inherit;font-size:12.5px;font-weight:600;
  color:var(--text-secondary);background:none;border:none;
  padding:6px 8px;border-radius:8px;cursor:pointer;
}
.sa-model-btn:hover:not(:disabled){color:var(--text-primary);background:var(--bg-muted);}
.sa-model-btn:disabled{cursor:default;opacity:.75;}
.sa-model-btn:focus-visible{outline:2px solid var(--accent-bright);outline-offset:2px;}
.sa-model-name{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sa-model-chev{font-size:11px;transition:transform .16s;}
.sa-model.open .sa-model-chev{transform:rotate(180deg);}

.sa-model-menu{
  position:absolute;bottom:calc(100% + 8px);right:0;z-index:40;
  width:284px;max-width:86vw;padding:8px;
  background:var(--bg-surface);border:1px solid var(--border);
  border-radius:14px;box-shadow:var(--shadow-card);
  display:none;
}
.sa-model.open .sa-model-menu{display:block;}
.sa-model-head{font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-tertiary);padding:4px 8px 6px;}
.sa-model-opt{
  display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;
  text-align:left;font:inherit;background:none;border:none;cursor:pointer;
  padding:9px 10px;border-radius:9px;
}
.sa-model-opt:hover{background:var(--bg-muted);}
.sa-model-opt.on{background:var(--accent-soft);}
.sa-model-opt:focus-visible{outline:2px solid var(--accent-bright);outline-offset:-2px;}
.sa-model-opt-n{display:flex;align-items:center;gap:7px;font-size:13.5px;font-weight:600;color:var(--text-primary);}
.sa-model-opt-d{font-size:11.5px;color:var(--text-secondary);margin-top:2px;}
.sa-model-badge{font-size:9.5px;font-weight:700;letter-spacing:.03em;
  color:var(--accent-text);background:var(--accent-soft);border-radius:4px;padding:1.5px 5px;}
.sa-model-tick{color:var(--accent-text);font-weight:700;flex:none;}

.sa-model-note,.sa-model-byo{
  margin-top:8px;padding:9px 10px;border-radius:9px;
  background:var(--bg-canvas);border:1px solid var(--border);
  font-size:11.5px;line-height:1.5;color:var(--text-secondary);
}
.sa-model-note b{color:var(--text-primary);font-weight:600;}
.sa-model-byo-t{font-size:12.5px;font-weight:600;color:var(--text-primary);margin-bottom:3px;}
.sa-model-byo-d{font-size:11.5px;line-height:1.5;color:var(--text-secondary);}

@media(prefers-reduced-motion:reduce){
  .sa-model-chev,.sa-att-x{transition:none;}
}

/* ── composer chrome, corrected (2026-08-27) ────────────────────────────────
   The composer wrapper used to paint its own washed panel — a background, a
   square border-top and a backdrop blur. That was right when the composer was
   a bare row sitting against the transcript. It is not right now the field
   lives in a rounded card with its own surface: the wash showed up as a
   sharp-cornered grey rectangle a few pixels larger than the card, framing it
   for no reason (owner, 2026-08-27).

   Safe to drop rather than merely hide: .sa-composer is `flex:none` at the end
   of a flex column, so the transcript does not scroll underneath it — the wash
   was never separating overlapping layers, only decorating. */
.sa-composer{
  background:none;
  border-top:none;
  backdrop-filter:none;-webkit-backdrop-filter:none;
}

/* The pill row: orb pinned left, pills scrolling beside it. */
.sa-pillrow{display:flex;align-items:center;gap:10px;margin-top:10px;}
.sa-pillrow .sa-chips{flex:1;min-width:0;margin-top:0;padding-bottom:2px;}

/* The dock is a normal flex child here, not absolutely positioned in the card.
   Slightly larger than it was inside the card — it has room on this row. */
.sa-composer-row.sa-card .sa-orb-dock{position:static;}
.sa-pillrow .sa-orb-dock{
  position:relative;flex:none;width:38px;height:38px;align-self:center;margin:0;
}
.sa-pillrow .sa-orb-dock .sa-orb-gl{width:38px;height:38px;}
@media(max-width:400px){
  .sa-pillrow .sa-orb-dock{width:30px;height:30px;}
  .sa-pillrow .sa-orb-dock .sa-orb-gl{width:30px;height:30px;}
}

/* ── the first-call checklist, moved off to the side (2026-08-27) ───────────
   It used to sit directly under the Talk button, in the middle of the idle
   stage — so the first thing on the screen was a list of chores rather than
   the invitation to talk. On a screen wide enough to have margins it now
   parks in the top-right corner, out of the centred column.

   Below 900px there IS no side, so it stays exactly where it was, in flow
   under the button. A checklist tucked into a corner that does not exist
   would just be a checklist nobody sees. */
@media(min-width:900px){
  .sa-idle{position:relative;}
  .sa-vr-card{
    position:absolute;top:14px;right:14px;z-index:3;
    width:320px;max-width:34vw;margin:0;
    box-shadow:var(--shadow-card);
  }
}
/* Wider still: give it a little more room from the edge. */
@media(min-width:1280px){
  .sa-vr-card{top:20px;right:24px;}
}


/* ── Alice's dock, on the Ads screen ──────────────────────────────────────
   A small panel that can be carried anywhere (carry.js) and remembers where
   it was put, per device. It sits over the Ads screen because that is where
   the decisions are; it is deliberately NOT a chat window — a second Alice
   transcript would be exactly the duplication that collapsing three ad
   sections into one was meant to end. It shows state and hands off.

   z-index sits under the sheets (.m-sheet is 60) and over the page, so
   approving something from here still opens the real sheet on top. */
.ads-dock{
  margin:12px 0 0;border:1px solid var(--m-line);border-radius:14px;
  background:var(--m-paper);overflow:hidden;
}
.ads-dock.carry--free{
  position:fixed;right:auto;bottom:auto;margin:0;
  width:300px;max-width:calc(100vw - 16px);
  z-index:12;box-shadow:var(--m-shadow-pop);
}
.ads-dock.carry--dragging{opacity:.97;}
.ads-dock-head{
  display:flex;align-items:center;gap:6px;
  padding:0 6px 0 0;border-bottom:1px solid var(--m-line);
  background:var(--m-chrome-wash);
}
/* The grip is the ONLY drag surface, and the fold button is its SIBLING —
   never a child. A button inside the drag handle means one press is two
   actions, which is the same mistake the sidebar rows made. */
.ads-dock-grip{
  flex:1;min-width:0;display:flex;align-items:center;gap:8px;
  padding:10px 12px;min-height:44px;
  cursor:grab;touch-action:none;user-select:none;
  background:none;border:none;font:inherit;text-align:left;color:inherit;
}
.ads-dock.carry--dragging .ads-dock-grip{cursor:grabbing;}
.ads-dock-grip:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px;}
.ads-dock-dots{
  flex:none;width:12px;height:12px;color:var(--m-ink-faint);
}
.ads-dock-name{
  font-size:13px;font-weight:600;color:var(--text-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ads-dock-hint{
  font-size:11.5px;color:var(--m-ink-quiet);white-space:nowrap;
}
.ads-dock-fold{
  flex:none;width:32px;min-height:32px;padding:0;
  display:flex;align-items:center;justify-content:center;
  border:none;border-radius:8px;background:transparent;
  color:var(--m-ink-quiet);cursor:pointer;font-size:15px;line-height:1;
}
.ads-dock-fold:hover{background:var(--m-hover);color:var(--text-primary);}
.ads-dock-fold:active{background:var(--m-press);}
.ads-dock-body{padding:12px;display:flex;flex-direction:column;gap:10px;}
.ads-dock[data-folded="1"] .ads-dock-body{display:none;}
.ads-dock-row{
  display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  font-size:12.5px;
}
.ads-dock-k{color:var(--m-ink-quiet);}
.ads-dock-v{font-weight:600;color:var(--text-primary);}
.ads-dock-act{
  width:100%;min-height:44px;padding:10px 12px;
  border:1px solid var(--m-accent-line);border-radius:10px;
  background:transparent;color:var(--text-primary);
  font:inherit;font-size:13px;font-weight:600;cursor:pointer;
}
.ads-dock-act:hover{background:var(--m-hover);}
.ads-dock-act[disabled]{opacity:.5;cursor:default;}
.ads-dock-ask{display:flex;gap:6px;}
/* 16px or Mobile Safari zooms the whole page on focus. */
.ads-dock-ask input{
  flex:1;min-width:0;font-size:16px;padding:9px 10px;
  border:1px solid var(--m-line);border-radius:10px;
  background:var(--m-fill);color:var(--text-primary);
}
.ads-dock-ask button{
  flex:none;min-height:44px;padding:0 12px;
  border:none;border-radius:10px;
  background:var(--accent-deep);color:var(--on-accent);
  font:inherit;font-size:13px;font-weight:600;cursor:pointer;
}
.ads-dock-note{margin:0;font-size:11.5px;line-height:1.45;color:var(--m-ink-quiet);}
@media (prefers-reduced-motion:reduce){.ads-dock{transition:none;}}

/* Alice opened this exact settings sheet. The controls retain their normal
   approval handlers; orange identifies where the owner needs to act. */
.alice-ui-focus{outline:3px solid #f59e0b;outline-offset:3px;}
.ms-sheet.alice-ui-focus{outline-offset:-3px;}

/* One Alice instance beside the active tool. Below desktop width only the
   selected pane is visible; display:none also removes its focus targets. */
.m-alice-pane{display:none;flex:1;min-width:0;min-height:0;overflow:hidden;}
.m-shell.m-focus-alice>.m-alice-pane{display:flex;}
.m-shell.m-focus-alice>.m-view{display:none;}
.m-alice-pane .sa-tabs button:first-child{flex:1.6;}
.m-workspace-toggle{
  min-height:44px;padding:8px 12px;border:1px solid var(--m-line-2);
  border-radius:var(--radius-pill);background:var(--bg-surface);color:var(--text-primary);
  font:inherit;font-size:12px;font-weight:600;cursor:pointer;
}
.m-workspace-toggle:focus-visible{outline:2px solid var(--holo);outline-offset:2px;}
@media(min-width:901px) and (max-width:1099px){.m-alice-pane{grid-area:view;}}
@media(min-width:1100px){
  .m-shell{grid-template-columns:220px minmax(290px,1fr) minmax(0,2fr);
    grid-template-areas:"rail header header" "rail strip strip" "rail alice view";}
  .m-shell>.m-alice-pane,.m-shell.m-focus-alice>.m-alice-pane{
    display:flex;grid-area:alice;padding:16px 14px 18px;border-right:1px solid var(--m-line);
  }
  .m-shell.m-focus-alice>.m-view{display:block;}
  .m-shell>.m-view{padding:20px clamp(14px,2vw,32px) 36px;min-width:0;}
  .m-workspace-toggle{display:none;}
  .m-alice-pane .sa-tabrow{display:grid;grid-template-columns:minmax(0,1fr) auto 44px;gap:6px;}
  .m-alice-pane .sa-tabs{grid-column:1/-1;display:flex;width:100%;}
  .m-alice-pane .sa-tabs button{flex:1;min-width:0;min-height:44px;padding:8px 5px;font-size:12px;}
  .m-alice-pane .sa-running{grid-column:1;grid-row:2;}
  .m-alice-pane .sa-history{grid-column:2;grid-row:2;min-height:44px;}
  .m-alice-pane .sa-audio{grid-column:3;grid-row:2;width:44px;}
  .m-alice-pane .sa-idle{justify-content:safe center;}
  .m-alice-pane .sa-vr-card{position:static;width:100%;max-width:none;margin:12px 0 0;}
  .m-alice-pane .sa-tools,.m-alice-pane .sa-tools-l,.m-alice-pane .sa-tools-r{gap:4px;min-width:0;}
  .m-alice-pane .sa-model-btn{padding:6px 4px;}
  .m-alice-pane .sa-model-name{max-width:112px;}
  .m-alice-pane .sa-input{font-size:16px;}
  .m-alice-pane .sa-send{width:44px;height:44px;}
  .m-alice-pane .sa-model-btn,.m-alice-pane .sa-riff,.m-alice-pane .sa-attach{min-height:44px;}
  .m-shell .ads-dock{display:none;}
}

/* Phone layouts keep complete controls in reach, including hosted desks. */
@media(min-width:621px) and (max-width:900px) and (max-height:500px){
  .m-view--conv{padding-bottom:6px;}
  .sa-composer{padding:4px 0;}
  .sa-composer-row.sa-card{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;}
  .sa-tray{grid-column:1/-1;}
  .sa-composer-row.sa-card .sa-input{grid-column:1;min-height:44px;max-height:68px;font-size:16px;}
  .sa-tools{grid-column:2;}
  .sa-pillrow{margin-top:4px;}
  .sa-idle{justify-content:safe center;}
}
@media(max-width:600px){
  .m-shell{padding:env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);}
  .sa-idle{justify-content:safe center;}
  .sa-tabrow{display:grid;grid-template-columns:minmax(0,1fr) auto 44px;gap:6px;margin-bottom:8px;}
  .sa-tabrow .sa-tabs{grid-column:1/-1;display:flex;width:100%;}
  .sa-tabrow .sa-tabs button{flex:1;min-width:0;min-height:44px;padding:8px 6px;font-size:12px;}
  .sa-running{grid-column:1;grid-row:2;}
  .sa-history{grid-column:2;grid-row:2;justify-self:end;min-height:44px;}
  .sa-audio{grid-column:3;grid-row:2;width:44px;}
  .sa-tools{gap:4px;}
  .sa-tools-l,.sa-tools-r{gap:4px;min-width:0;}
  .sa-model-btn,.sa-riff,.sa-attach{min-height:44px;}
  .sa-send{width:44px;height:44px;}
  .sa-model-btn{padding:6px 4px;}
  .sa-model-name{max-width:112px;}
  .sa-model-menu{max-height:min(420px,65dvh);overflow-y:auto;overscroll-behavior:contain;}
  .m-view input:not([type=checkbox]):not([type=radio]):not([type=range]),
  .m-view select,.m-view textarea,
  .m-alice-pane input:not([type=checkbox]):not([type=radio]):not([type=range]),
  .m-alice-pane select,.m-alice-pane textarea,
  .sheet input:not([type=checkbox]):not([type=radio]):not([type=range]),.sheet select,.sheet textarea,
  .m-sheet input:not([type=checkbox]):not([type=radio]):not([type=range]),.m-sheet select,.m-sheet textarea,
  .ms-sheet input:not([type=checkbox]):not([type=radio]):not([type=range]),.ms-sheet select,.ms-sheet textarea{font-size:16px;}
  .sheet-head{padding-top:max(12px,env(safe-area-inset-top));}
  .sheet-body{padding:16px 16px max(24px,env(safe-area-inset-bottom));}
  .sheet-close,.m-sheet-close,.ms-sheet-close{width:44px;height:44px;flex:none;}
  .sheet .btn,.modal .btn{min-height:44px;}
  .member-row{flex-wrap:wrap;}
  .member-row .grow{flex-basis:100%;overflow-wrap:anywhere;}
  .member-ai-controls label{flex:1;min-width:150px;}
  .member-ai-controls input{width:100%;min-height:44px;}
  .invite-box{flex-wrap:wrap;}
  #settingsView .set-card{padding:18px 16px;}
  .dash-tabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));margin:12px 14px 0;}
  .dash-tabs button{min-height:44px;padding:8px;}
  .dash-window button{min-height:38px;}
  .m-rail{padding-bottom:max(6px,env(safe-area-inset-bottom));}
}
