/* Type: Geist for display (500; 300 for captions and big numbers) + Inter for body. Both are
   self-hosted variable latin subsets under the SIL OFL (fonts/LICENSE-Geist.txt,
   fonts/LICENSE-Inter.txt). Geist is the Google Fonts file, served from here because the CSP is
   font-src 'self' and the house rule is zero external requests. */
@font-face{font-family:"Geist";src:url(/fonts/geist-var.woff2) format("woff2");font-weight:100 900;font-style:normal;font-display:swap}
@font-face{font-family:"Inter";src:url(/fonts/inter-var.woff2) format("woff2");font-weight:100 900;font-style:normal;font-display:swap}
@font-face{font-family:"Inter";src:url(/fonts/inter-var-italic.woff2) format("woff2");font-weight:100 900;font-style:italic;font-display:swap}
/* MeriTec design system: C3, "Live on white" (approved by the product owner, 2026-10-04).
   A pure white ground, ink #0b1220, and the brand navy #0d2b6e sampled from the logo. Red is the
   live signal: mic open, live. It also stays on failure lines and destructive confirms, where it
   is a warning, not a color choice. Lots of air; hairline dividers at about 6% black; one soft,
   large-radius shadow level (--lift) for the things that float (menus, modals, the composer, the
   dial's disc); no boxes around everything. Sentence case everywhere: no uppercase, letter-spaced
   eyebrows, no accent stripes on cards. Async still has three visible states (idle, in flight and
   inert, settled), and nothing may double-fire.
   Superseded: the 2026-08-10 royal-blue rail, the frosted glass, the #f7f8f9 canvas. */
:root{
  --canvas:#fff; --canvas-alt:#f4f5f8; --card:#fff;
  --ink:#0b1220; --ink-2:#344054; --muted:#667085; --ahead:#7f899b; --tick:#b9c1cf; --pale:#d3d9e3;
  --navy:#0d2b6e; --navy-2:#0d2b6e; --navy-3:#1b3f8c;
  --red:#d92d20; --red-deep:#b42318; --ok:#1a9d52;
  /* Red, white and blue. The chrome (the top bar) is brand navy with white type. Red is the
     classroom's action and attention color (primary buttons, unread and needs-action counts),
     and it is still the live signal: on the tutor, the only page that opens a microphone, red
     appears only while the microphone is open, as the breathing ring and the Live mark, and always
     with a word. body.focus (the tutor) puts actions back on navy so nothing red sits there at rest. */
  --on-chrome:#fff; --chrome-line:rgba(255,255,255,.16);
  /* The chrome blue, lighter than the logo navy. Everything on it derives from it: --chrome-deep
     (an 18% shade) fills the chip, the avatar and the phone's active-tab pill; --chrome-deeper (30%)
     is their hover. --on-chrome-2 is the dimmest white that still holds 4.5:1 on this blue.
     --chrome-rule is the red that reads best against it. */
  --chrome:#1b48c7; --chrome-deep:#163ba3; --chrome-deeper:#13328b;
  --on-chrome-2:rgba(255,255,255,.80); --chrome-rule:var(--red);
  --act:var(--red); --act-hover:var(--red-deep); --attn:var(--red-deep);
  --claude:#dd7a45; --gpt:#13a07d;
  --hairline:rgba(11,18,32,.06); --hair-2:rgba(11,18,32,.12);
  --border:#e2e5ea; --track:#e8eaef;
  --lift:0 30px 60px -30px rgba(11,18,32,.22),0 10px 24px -14px rgba(11,18,32,.10);
  --shadow:none;
  --shadow-lift:var(--lift);
  --r:16px; --r-sm:10px;
  /* Material tokens kept for page styles that still name them: everything is solid white now. */
  --glass:#fff; --glass-edge:var(--hairline); --glass-top:0 0 0 transparent; --blur:0px;
  --rail-grad:#fff;
  /* Motion: one strong ease-out for entrances + transforms; plain `ease` for color-only hovers. */
  --ease-out:cubic-bezier(.23,1,.32,1);
  --font-display:"Geist","Inter",system-ui,sans-serif;
  --font-body:"Inter",system-ui,sans-serif;
  /* Page measure: content centers in this width; the hairlines above it run edge to edge. */
  --measure:1200px; --gutter:56px;
}
*{box-sizing:border-box;margin:0;padding:0}
html{color-scheme:light}
body{font-family:var(--font-body);color:var(--ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  background:var(--canvas);min-height:100vh;font-size:15px}
h1,h2,h3,.disp{font-family:var(--font-display);font-weight:500;letter-spacing:-.02em}
.num{font-variant-numeric:tabular-nums}
:focus-visible{outline:2px solid var(--navy);outline-offset:2px;border-radius:4px}
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Scrollbars: a floating pill, quiet until the pointer is in the scrolling region. */
*{scrollbar-width:thin;scrollbar-color:rgba(11,18,32,.16) transparent}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track,::-webkit-scrollbar-corner{background:transparent}
::-webkit-scrollbar-thumb{background-color:rgba(11,18,32,.16);border-radius:99px;
  border:3px solid transparent;background-clip:content-box;transition:background-color .15s ease}
@media (hover:hover) and (pointer:fine){
  ::-webkit-scrollbar-thumb:hover{background-color:rgba(11,18,32,.32)}
}

/* ---- Shell: a white top bar (brand · nav · identity) over the page. The window never scrolls;
   .page scrolls. The bar is the #rail element (the body contract every page already authors),
   laid out as a row. One hairline under it, nothing else. ---- */
.app{display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden}
.rail{position:relative;z-index:20;flex-shrink:0;display:flex;align-items:center;gap:24px;height:76px;padding:0 var(--gutter);
  background:var(--chrome);color:var(--on-chrome)}
/* On the navy bar the ink tokens turn to white, so everything the bar holds (nav, identity, the
   tutor's lesson header) follows without its own rules. Menus that drop from the bar are white
   surfaces again and take the light values back. */
.rail,#phonebar,.pagehead,.tabbar{--ink:var(--on-chrome);--ink-2:var(--on-chrome);--muted:var(--on-chrome-2);--hairline:var(--chrome-line);
  --hair-2:rgba(255,255,255,.24);--canvas-alt:var(--chrome-deep);--navy:#fff}
.rail .menu,#phonebar .menu,.pagehead .menu{--ink:#0b1220;--ink-2:#344054;--muted:#667085;--hairline:rgba(11,18,32,.06);
  --hair-2:rgba(11,18,32,.12);--canvas-alt:#f4f5f8;--navy:#0d2b6e;color:var(--ink)}
/* The live mark on navy: red on navy is 2.7:1, so it rides a white pill (red on white, 4.8:1).
   It is the only white-filled thing in the bar, which is the point. */
.rail .live{background:#fff;border-radius:99px;padding:6px 13px 6px 11px;font-size:14px}
.rail .live i{width:7px;height:7px}
body.focus{--act:var(--navy);--act-hover:var(--navy-3);--attn:var(--ink)}
.railbrand{flex:1;display:flex;align-items:center;min-width:0}
/* The logo at about 1.5x (46px) inside the same 76px bar. It steps down where the nav compacts. */
.railbrand img{height:46px;width:auto;display:block}
.nav{display:flex;align-items:center;gap:34px}
.nav a{position:relative;display:inline-flex;align-items:center;gap:7px;padding:8px 0;color:var(--muted);text-decoration:none;
  font-size:15.5px;font-weight:500;white-space:nowrap;transition:color .15s ease}
.nav a svg{display:none}
.nav a.active{color:var(--ink)}
/* The current page carries a 2px white underline, so "where am I" never rests on the dimmed white
   alone (on the lightest blue there is no dimmed white: every label is full white). */
.nav a::after{content:"";position:absolute;left:0;right:0;bottom:1px;height:2px;border-radius:1px;background:currentColor;
  opacity:0;transition:opacity .15s ease}
.nav a.active::after{opacity:1}
.nav a:active{opacity:.7}
.nav .navsep{width:1px;height:18px;background:var(--hair-2)}
/* Unread count pill (Board/Messages/Home alerts), driven by the shell's 20s notification poll. Always
   in the DOM so appearance can transition; visibility keeps the off state out of the a11y tree.
   Red: an unread count is attention. (The tutor, where red is the live signal, has no nav.) */
.nav a .navbadge,.tabbar .navbadge{min-width:19px;height:19px;padding:0 5px;border-radius:10px;background:var(--act);color:#fff;
  font-size:11.5px;font-weight:600;line-height:19px;text-align:center;visibility:hidden;opacity:0;transform:scale(.95);
  transition:opacity .18s var(--ease-out),transform .18s var(--ease-out),visibility .18s}
.nav a .navbadge.on,.tabbar .navbadge.on{visibility:visible;opacity:1;transform:scale(1)}
/* Off, the pill takes no room in the row (it would read as a gap after the label). */
.nav a .navbadge:not(.on){position:absolute;left:100%}
.railright{flex:1;display:flex;justify-content:flex-end;align-items:center;gap:12px;min-width:0}
.railswitch{position:relative;min-width:0}
.railswitch .chip{max-width:260px;cursor:pointer}
.railswitch .chip b{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.railswitch .chip svg{opacity:.55}
.railfoot{position:relative}
.railfoot .userbtn{display:flex;align-items:center;gap:12px;border:0;background:none;color:var(--ink-2);font:inherit;font-size:15px;
  cursor:pointer;padding:0;border-radius:99px;transition:opacity .15s ease}
.railfoot .userbtn:active{opacity:.7}
.railfoot .av,#phonebar .userbtn .av{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:50%;
  background:var(--canvas-alt);color:var(--ink);font-size:13.5px;font-weight:600;flex-shrink:0}
.railfoot .userbtn[aria-expanded="true"] .av{box-shadow:0 0 0 2px var(--chrome),0 0 0 3px var(--hair-2)}
.railfoot .pn{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px}
/* Demo door: the switcher chip already names who is acting, so the user button is just the avatar. */
.railswitch~.railfoot .pn{display:none}
.railfoot .menu{right:0;left:auto;top:calc(100% + 10px)}
@media (max-width:1100px){
  .rail{--gutter:28px;gap:18px}
  .nav{gap:22px}
  .nav a{font-size:14.5px}
  .railfoot .pn{display:none}
  .railswitch .chip{max-width:150px}
  .railbrand img{height:40px}
}
@media (max-width:900px){
  .rail{gap:14px}
  .railbrand img{height:36px}
  .nav{gap:14px}
  .nav a{font-size:13.5px}
  .railswitch .chip{padding:7px 11px}
  .railswitch .chip b{max-width:70px}
}
/* The focused bar (the tutor): back to Home, the brand, then the page's own slot. No nav, no tab
   bar on a phone either; a voice conversation gets the whole screen. */
.rail.focus{gap:28px;padding:0 40px}
.rail.focus .railbrand{flex:0 0 auto}
.fback{display:inline-flex;align-items:center;gap:6px;font-size:15.5px;font-weight:500;color:var(--muted);text-decoration:none;
  padding:8px 4px 8px 0;flex-shrink:0;transition:color .15s ease}
.fback svg{width:18px;height:18px}
.fback:active{opacity:.7}
.fslot{flex:1;min-width:0;display:flex;align-items:center}

.field{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;background:#fff;position:relative}
/* Header row: title + sub on the left, page chips on the right, centered in the page measure. */
/* The masthead: the page title sits on the chrome blue under the bar, and the two read as one
   field. One thin red rule runs the full width where the masthead meets the white page (a rule
   across the page, not a stripe on a card). The tutor hides its page header, so the rule never
   reaches the page where red means live. */
.pagehead{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-shrink:0;
  padding:18px max(var(--gutter),calc((100% - var(--measure)) / 2)) 38px;
  background:var(--chrome);color:var(--on-chrome);border-bottom:3px solid var(--chrome-rule);margin-bottom:28px}
.pagehead h1{font-size:30px;line-height:1.1;letter-spacing:-.03em}
.pagehead .topsub{font-size:15px;color:var(--muted);margin-top:6px}
.pagehead .topslot{display:flex;align-items:center;gap:10px;flex-shrink:0}
.page{flex:1;min-height:0;overflow-y:auto;padding:6px max(var(--gutter),calc((100% - var(--measure)) / 2)) 64px}

/* ---- Components ---- */
/* A card is a quiet hairline-edged white region now: grouping, not a box with a shadow. */
.card{background:#fff;border:1px solid var(--hairline);border-radius:var(--r);padding:22px}
.card--flag{border-color:var(--hair-2)}
/* Feature surface: was the view's one navy slab. On white it is a card with a firmer edge and
   navy type; nothing ships filled navy any more. */
.card--feature{position:relative;background:#fff;color:var(--ink);border:1px solid var(--hair-2)}
.card--cta{border-color:var(--hair-2)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border:1px solid transparent;border-radius:99px;padding:10px 20px;
  font:inherit;font-size:14.5px;font-weight:500;cursor:pointer;text-decoration:none;white-space:nowrap;
  transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .12s var(--ease-out)}
.btn:active{transform:scale(.97)}
.btn:disabled{opacity:.5;cursor:default}
.btn:disabled:active{transform:none}
.btn--primary{background:var(--act);color:#fff}
.btn--ghost{background:#fff;color:var(--ink-2);border-color:var(--hair-2)}
.btn--ondark{background:var(--navy);color:#fff}
.btn--ondark-ghost{background:#fff;color:var(--navy);border-color:var(--hair-2)}
.btn--danger{background:var(--red-deep);color:#fff}
@media (hover:hover) and (pointer:fine){
  .nav a:hover{color:var(--ink)}
  .nav a:not(.active):hover::after{opacity:.4}
  .rail .chip:hover,#phonebar .chip:hover,.rail .userbtn:hover .av,#phonebar .userbtn:hover .av{background:var(--chrome-deeper)}
  .fback:hover{color:var(--ink)}
  .btn--primary:hover{background:var(--act-hover)}
  .btn--ondark:hover{background:var(--navy-3)}
  .btn--ghost:hover,.btn--ondark-ghost:hover{border-color:rgba(11,18,32,.24);color:var(--ink)}
  .btn--danger:hover{filter:brightness(1.08)}
}
.chip{display:inline-flex;align-items:center;gap:7px;background:var(--canvas-alt);border:1px solid transparent;border-radius:99px;
  padding:7px 14px;font-size:14px;font-weight:500;color:var(--ink-2);font-family:inherit}
.chip svg{width:14px;height:14px;flex-shrink:0;opacity:.7}
/* Small section label: sentence case, quiet. (It was the uppercase, letter-spaced eyebrow.) */
.label{font-size:14px;font-weight:500;color:var(--muted);letter-spacing:0}
.table{width:100%;border-collapse:collapse}
.table th{text-align:left;font-size:13.5px;font-weight:500;color:var(--muted);padding:0 10px 12px;border-bottom:1px solid var(--hairline)}
.table td{padding:14px 10px;font-size:14.5px;border-bottom:1px solid var(--hairline)}
.table tr:last-child td{border-bottom:0}
@media (hover:hover) and (pointer:fine){.table tbody tr:hover td{background:#fafbfc}}
/* Progress: a fine track with a slightly heavier navy fill riding on it. */
.bar{height:4px;border-radius:2px;background:var(--track);overflow:hidden}
.bar>i{display:block;height:100%;border-radius:2px;background:var(--navy);transition:width .3s var(--ease-out)}
.bar>i.warn{background:var(--ink)}
.stat .v{font-family:var(--font-display);font-size:34px;font-weight:300;letter-spacing:-.04em;line-height:1.1}

/* ---- Feedback ---- */
.skeleton{position:relative;overflow:hidden;background:var(--canvas-alt);border-radius:8px;min-height:14px}
.skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,rgba(255,255,255,.8),transparent);animation:shimmer 1.4s ease-out infinite}
@keyframes shimmer{to{transform:translateX(100%)}}
.empty{color:var(--muted);font-size:14.5px;text-align:center;padding:30px 12px}
.errorbox{background:#fff;border:1px solid rgba(180,35,24,.22);color:var(--red-deep);border-radius:12px;padding:12px 14px;font-size:14px;font-weight:500}
.toast{position:fixed;left:50%;bottom:26px;transform:translate(-50%,8px);opacity:0;background:var(--ink);color:#fff;border-radius:99px;padding:11px 20px;font-size:14px;font-weight:500;box-shadow:var(--lift);transition:opacity .2s var(--ease-out),transform .2s var(--ease-out);pointer-events:none;z-index:60}
.toast.show{opacity:1;transform:translate(-50%,0)}
.toast.err{background:var(--red-deep)}
.toast.act{pointer-events:auto;cursor:pointer}
.toast.act:focus-visible{outline:2px solid #fff;outline-offset:2px}
@media (hover:hover) and (pointer:fine){.toast.act.show:hover{transform:translate(-50%,-2px)}}
.toast.act.show:active{transform:translate(-50%,0) scale(.98)}

/* ---- Menus (acting-user switcher, user menu) ---- */
.switcher{position:relative}
.menu{position:absolute;right:0;top:calc(100% + 8px);min-width:232px;background:#fff;border:1px solid var(--hairline);border-radius:16px;
  box-shadow:var(--lift);padding:8px;z-index:50;transform-origin:top right;transform:scale(.97) translateY(-3px);opacity:0;pointer-events:none;
  transition:transform .18s var(--ease-out),opacity .18s var(--ease-out)}
.menu.open{transform:scale(1) translateY(0);opacity:1;pointer-events:auto}
.menu--up{top:auto;bottom:calc(100% + 6px);left:0;right:0;min-width:0;transform-origin:bottom left;transform:scale(.97) translateY(3px)}
.railswitch .menu{left:auto;right:0;min-width:240px}
.menu .grouplabel{font-size:12.5px;color:var(--muted);font-weight:500;margin:10px 10px 4px}
.menu button,.menu a{display:flex;width:100%;align-items:center;gap:10px;border:0;background:none;font:inherit;font-size:14.5px;font-weight:400;
  color:var(--ink);text-decoration:none;border-radius:10px;padding:9px 10px;cursor:pointer;text-align:left;
  transition:background-color .12s ease,transform .1s var(--ease-out)}
@media (hover:hover) and (pointer:fine){.menu button:hover,.menu a:hover{background:var(--canvas-alt)}}
.menu button:active,.menu a:active{transform:scale(.98)}
.menu button:disabled{opacity:.6;cursor:default}
.menu button:disabled:active{transform:none}
.menu [role="menuitem"] svg{width:16px;height:16px;opacity:.6;flex-shrink:0}
.menu button[aria-current="true"]{color:var(--navy);background:var(--canvas-alt);font-weight:500}

/* ---- Settings modal (shell-owned, opened from the user menu on every page) ----
   Centered dialog over a dimming scrim. Materializes: scale + blur settle with the fade, 220ms
   ease-out in, faster out. Left pane = section nav, right pane = rows (label left, control right). */
.smodal-wrap{position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;padding:24px}
.smodal-scrim{position:absolute;inset:0;background:rgba(11,18,32,.32);opacity:0;transition:opacity .2s var(--ease-out)}
.smodal{position:relative;display:flex;width:min(880px,100%);height:min(620px,100%);background:#fff;border:1px solid var(--hairline);
  border-radius:24px;box-shadow:var(--lift);overflow:hidden;opacity:0;transform:scale(.97);filter:blur(6px);
  transition:opacity .22s var(--ease-out),transform .22s var(--ease-out),filter .22s var(--ease-out)}
.smodal-wrap.open .smodal-scrim{opacity:1}
.smodal-wrap.open .smodal{opacity:1;transform:scale(1);filter:blur(0)}
.smodal-wrap.closing{pointer-events:none}
.smodal-wrap.closing .smodal-scrim{opacity:0;transition-duration:.16s}
.smodal-wrap.closing .smodal{opacity:0;transform:scale(.98);filter:blur(4px);transition-duration:.16s}
.smodal-x{position:absolute;top:16px;right:16px;width:32px;height:32px;border-radius:50%;border:0;background:none;color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .15s ease,color .15s ease,transform .12s var(--ease-out)}
.smodal-x svg{width:15px;height:15px}
.smodal-x:active{transform:scale(.94)}
.smodal-nav{width:200px;flex-shrink:0;display:flex;flex-direction:column;gap:2px;padding:20px 12px;border-right:1px solid var(--hairline);background:#fff;overflow-y:auto}
.smodal-nav .grouplabel{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;color:var(--ink);margin:0 10px 12px}
.smodal-nav button{display:flex;align-items:center;gap:10px;width:100%;border:0;background:none;font:inherit;font-size:14.5px;color:var(--ink-2);border-radius:10px;padding:9px 10px;cursor:pointer;text-align:left;transition:background-color .12s ease}
.smodal-nav button svg{width:16px;height:16px;opacity:.6;flex-shrink:0}
.smodal-nav button.active{background:var(--canvas-alt);color:var(--ink);font-weight:500}
.smodal-nav button.active svg{opacity:1;color:var(--navy)}
.smodal-body{flex:1;min-width:0;overflow-y:auto;padding:26px 30px 32px}
.smodal-body h2{font-size:20px;margin-bottom:6px}
.smodal-body .subnote{font-size:14px;color:var(--muted);line-height:1.55;margin-bottom:10px}
.smodal-body .trow{display:flex;align-items:center;gap:14px;padding:15px 2px}
.smodal-body .trow+.trow{border-top:1px solid var(--hairline)}
.smodal-body .trow .tmain{flex:1;min-width:0}
.smodal-body .trow .tlabel{display:block;font-size:14.5px;font-weight:500;color:var(--ink)}
.smodal-body .trow .terr{font-size:13px;color:var(--red-deep);font-weight:500;margin-top:3px}
.smodal-body .tnote{font-size:13px;color:var(--muted);line-height:1.5;margin-top:3px}
.smodal-body .mline+.tnote{margin-top:7px}
/* The switch: navy when on (red is the live signal, not a state), white knob, ease-out travel. */
.switch{position:relative;flex-shrink:0;width:42px;height:26px;border-radius:99px;border:0;background:var(--track);padding:0;cursor:pointer;transition:background-color .18s ease}
.switch::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(11,18,32,.25);transition:transform .18s var(--ease-out)}
.switch[aria-checked="true"]{background:var(--navy)}
.switch[aria-checked="true"]::after{transform:translateX(16px)}
.switch:disabled{opacity:.55;cursor:default}
.switch:active:not(:disabled)::after{transform:scale(.94)}
.switch[aria-checked="true"]:active:not(:disabled)::after{transform:translateX(16px) scale(.94)}
.smodal-body .thnum{width:92px;font:inherit;font-size:14.5px;text-align:right;border:1px solid var(--hair-2);border-radius:10px;padding:9px 11px;background:#fff;color:var(--ink);transition:border-color .15s ease,box-shadow .15s ease}
.smodal-body .thnum:focus{outline:none;border-color:rgba(13,43,110,.45);box-shadow:0 0 0 3px rgba(13,43,110,.10)}
.smodal-body .thline{display:flex;align-items:center;gap:12px;margin-top:14px}
.smodal-body .krow{padding:16px 2px}
.smodal-body .krow+.krow{border-top:1px solid var(--hairline)}
.smodal-body .krow label{display:flex;align-items:center;gap:8px;font-size:14.5px;font-weight:500;color:var(--ink);margin-bottom:8px}
.smodal-body .dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.smodal-body .dot--claude{background:var(--claude)}
.smodal-body .dot--gpt{background:var(--gpt)}
.smodal-body .kline,.smodal-body .mline{display:flex;gap:10px}
.smodal-body .kline input[type="password"],.smodal-body .mline select{flex:1;min-width:0;font:inherit;font-size:14.5px;border:1px solid var(--hair-2);border-radius:10px;padding:10px 12px;background:#fff;color:var(--ink);transition:border-color .15s ease,box-shadow .15s ease}
.smodal-body .mline select{cursor:pointer}
.smodal-body .kline input[type="password"]::placeholder{color:var(--muted)}
.smodal-body .kline input[type="password"]:focus,.smodal-body .mline select:focus{outline:none;border-color:rgba(13,43,110,.45);box-shadow:0 0 0 3px rgba(13,43,110,.10)}
.smodal-body .key-status{font-size:13px;color:var(--muted);margin-top:7px;min-height:16px}
.smodal-body .key-status.set{color:var(--ok);font-weight:500}
.smodal-body .thline .key-status{margin-top:0}
/* The settings modal's confirm (the zero-backstop question), shell-owned. Sits over the smodal. */
.sdlg-wrap{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:24px}
.sdlg-scrim{position:absolute;inset:0;background:rgba(11,18,32,.32);opacity:0;transition:opacity .2s var(--ease-out)}
.sdlg{position:relative;width:min(440px,100%);background:#fff;border:1px solid var(--hairline);border-radius:22px;box-shadow:var(--lift);padding:24px;opacity:0;transform:scale(.97);filter:blur(6px);transition:opacity .22s var(--ease-out),transform .22s var(--ease-out),filter .22s var(--ease-out)}
.sdlg-wrap.open .sdlg-scrim{opacity:1}
.sdlg-wrap.open .sdlg{opacity:1;transform:scale(1);filter:blur(0)}
.sdlg-wrap.closing{pointer-events:none}
.sdlg-wrap.closing .sdlg-scrim{opacity:0;transition-duration:.16s}
.sdlg-wrap.closing .sdlg{opacity:0;transform:scale(.98);filter:blur(4px);transition-duration:.16s}
.sdlg h3{font-size:18px;margin-bottom:8px}
.sdlg p{font-size:14.5px;color:var(--ink-2);line-height:1.55}
.sdlg .acts{display:flex;justify-content:flex-end;gap:9px;margin-top:20px}
.sdlg .btn{font-size:14px;padding:9px 18px}
@media (hover:hover) and (pointer:fine){
  .smodal-x:hover{background:var(--canvas-alt);color:var(--ink)}
  .smodal-nav button:hover{background:var(--canvas-alt)}
  .switch:hover:not(:disabled){background:#dde0e6}
  .switch[aria-checked="true"]:hover:not(:disabled){background:var(--navy-3)}
  .smodal-body .thnum:hover,.smodal-body .kline input[type="password"]:hover,.smodal-body .mline select:hover{border-color:rgba(11,18,32,.24)}
}

/* ---- The voice dial: the hero of home and of the tutor's voice mode ----
   One element, four states on data-state. Idle: a perfect hairline ring around a soft-shadowed
   white disc. Listening: a smooth red ring breathing inward, with a faint red bloom and a hairline
   halo just outside it. Speaking: a steady navy hairline with three translucent navy ripples
   radiating outward and a low navy bloom. Paused: the navy ring at rest. The motion spec (periods,
   speeds, easing) and its reduced-motion fallback are written up in
   docs/design-refs/c3-build/NOTES.md. Size comes from --d; every layer is a percentage of it, so the
   same dial works at 232px on a phone and 430px on the tutor stage. Rings are bordered divs, not
   SVG: transform + opacity is all that animates, which the compositor handles alone. */
.dial{--d:300px;--glow:rgba(13,43,110,.55);position:relative;display:block;width:var(--d);height:var(--d);flex-shrink:0;
  border:0;padding:0;background:none;border-radius:50%;cursor:pointer;color:var(--navy);font:inherit;text-decoration:none;
  -webkit-tap-highlight-color:transparent;transition:width .3s var(--ease-out),height .3s var(--ease-out)}
.dial:focus-visible{outline:2px solid var(--navy);outline-offset:4px;border-radius:50%}
.dial__ring,.dial__halo,.dial__bloom,.dial__rip{position:absolute;inset:7.5%;border-radius:50%;pointer-events:none}
.dial__ring{border:1.1px solid var(--pale);
  transition:border-color .24s var(--ease-out),border-width .24s var(--ease-out),transform .3s var(--ease-out)}
.dial__halo{inset:4.25%;border:1px solid var(--red);opacity:0;transition:opacity .24s var(--ease-out)}
.dial__bloom{box-shadow:0 0 calc(var(--d)*.07) calc(var(--d)*.006) var(--glow),inset 0 0 calc(var(--d)*.06) calc(var(--d)*-.01) var(--glow);
  opacity:0;transition:opacity .3s var(--ease-out)}
.dial__rip{border:1px solid var(--navy);opacity:0;box-shadow:0 0 calc(var(--d)*.025) rgba(13,43,110,.16)}
.dial__disc{position:absolute;left:50%;top:50%;width:var(--disc,50%);height:var(--disc,50%);transform:translate(-50%,-50%);border-radius:50%;
  background:#fff;box-shadow:var(--lift);transition:transform .14s var(--ease-out),box-shadow .2s ease}
.dial__face{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
  padding:0 12%;opacity:0;transition:opacity .18s var(--ease-out);pointer-events:none}
.dial__face svg{width:31%;height:31%}
.dial__face b{font-family:var(--font-display);font-weight:500;font-size:calc(var(--d)*.06);letter-spacing:-.02em;color:var(--ink);line-height:1.15}
.dial__face span{font-size:max(13px,calc(var(--d)*.034));color:var(--muted);margin-top:6px;line-height:1.35}
.dial[data-state="idle"] .dial__face[data-face="idle"],
.dial[data-state="off"] .dial__face[data-face="idle"],
.dial[data-state="listening"] .dial__face[data-face="listening"],
.dial[data-state="speaking"] .dial__face[data-face="speaking"],
.dial[data-state="thinking"] .dial__face[data-face="thinking"],
.dial[data-state="paused"] .dial__face[data-face="paused"]{opacity:1}
.dial:active .dial__disc{transform:translate(-50%,-50%) scale(.97)}
.dial[data-state="off"]{cursor:default}
.dial[data-state="off"] .dial__ring{border-style:dashed}
/* Off dims the idle face only; every other face stays invisible (they are stacked in the disc). */
.dial[data-state="off"] .dial__face[data-face="idle"]{opacity:.45}
.dial[data-state="off"] .dial__face[data-face="idle"] span{visibility:hidden}   /* no "Tap to talk" on a control that cannot be tapped */
.dial[data-state="off"]:active .dial__disc{transform:translate(-50%,-50%)}
@media (hover:hover) and (pointer:fine){
  .dial[data-state="idle"]:hover .dial__ring{border-color:var(--tick)}
  .dial[data-state="idle"]:hover .dial__bloom{opacity:.22}
  .dial[data-state="idle"]:hover .dial__disc{box-shadow:0 34px 64px -30px rgba(11,18,32,.28),0 12px 26px -14px rgba(11,18,32,.12)}
}
/* Listening: red, breathing inward. Ring, bloom and halo share one 3.2s cycle. */
.dial[data-state="listening"]{--glow:rgba(217,45,32,.5)}
.dial[data-state="listening"] .dial__ring{border-color:var(--red);border-width:max(2px,calc(var(--d)*.0072));animation:dial-breathe 3.2s cubic-bezier(.45,0,.55,1) infinite}
.dial[data-state="listening"] .dial__bloom{animation:dial-breathe-bloom 3.2s cubic-bezier(.45,0,.55,1) infinite}
.dial[data-state="listening"] .dial__halo{animation:dial-halo 3.2s cubic-bezier(.45,0,.55,1) infinite}
@keyframes dial-breathe{0%,100%{transform:scale(1)}50%{transform:scale(.966)}}
@keyframes dial-breathe-bloom{0%,100%{transform:scale(1);opacity:.32}50%{transform:scale(.966);opacity:.62}}
@keyframes dial-halo{0%,100%{transform:scale(1);opacity:.22}50%{transform:scale(.982);opacity:.07}}
/* Speaking: navy hairline, ripples out every 0.8s, each one fading as it travels. */
.dial[data-state="speaking"] .dial__ring{border-color:var(--navy);border-width:1.3px}
.dial[data-state="speaking"] .dial__bloom{animation:dial-glow 2.4s ease-in-out infinite}
.dial[data-state="speaking"] .dial__rip{animation:dial-ripple 2.4s cubic-bezier(.22,.61,.36,1) infinite}
.dial[data-state="speaking"] .dial__rip.r2{animation-delay:-.8s}
.dial[data-state="speaking"] .dial__rip.r3{animation-delay:-1.6s}
@keyframes dial-ripple{0%{transform:scale(1);opacity:.42}100%{transform:scale(1.16);opacity:0}}
@keyframes dial-glow{0%,100%{opacity:.22}50%{opacity:.38}}
/* Thinking: the listening breath in navy, because the question is with the tutor now. */
.dial[data-state="thinking"] .dial__ring{border-color:var(--navy);border-width:1.3px;animation:dial-breathe 3.2s cubic-bezier(.45,0,.55,1) infinite}
.dial[data-state="thinking"] .dial__bloom{animation:dial-breathe-bloom 3.2s cubic-bezier(.45,0,.55,1) infinite}
.dial[data-state="paused"] .dial__ring{border-color:rgba(13,43,110,.42);border-width:1.3px}

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
  .menu{transform:none}
  .smodal,.sdlg{transform:none;filter:none}
  /* The dial holds still and says the same thing: the static C3 frames. */
  .dial .dial__ring,.dial .dial__bloom,.dial .dial__halo,.dial .dial__rip{animation:none !important}
  .dial[data-state="listening"] .dial__bloom{opacity:.42}
  .dial[data-state="listening"] .dial__halo{opacity:.2}
  .dial[data-state="thinking"] .dial__bloom{opacity:.42}
  .dial[data-state="speaking"] .dial__bloom{opacity:.28}
  .dial[data-state="speaking"] .dial__rip.r1{transform:scale(1.053);opacity:.4}
  .dial[data-state="speaking"] .dial__rip.r2{transform:scale(1.106);opacity:.2}
  .dial[data-state="speaking"] .dial__rip.r3{transform:scale(1.159);opacity:.08}
}

/* The live mark: red, and only ever red, because it means the microphone is open. */
.live{display:inline-flex;align-items:center;gap:8px;color:var(--red);font-weight:600;font-size:15px;white-space:nowrap}
.live i{width:8px;height:8px;border-radius:50%;background:var(--red);display:block}

/* ---- Phone (≤767px): brand + identity in a slim top bar, nav in a fixed bottom tab bar; six-plus
   nav items overflow into a bottom More sheet (admins). The ONE width breakpoint in the shell, per
   docs/superpowers/specs/2026-08-15-responsive-design.md. shell.js renders one chrome at a time
   (matchMedia), so the bar and the phone bars never coexist in the DOM. ---- */
#phonebar,.tabbar,.sheetwrap{display:none}
@media (max-width:767px){
  :root{--gutter:20px}
  body{font-size:16px}
  .rail{display:none}
  .app{padding:64px 0 calc(72px + env(safe-area-inset-bottom,0px))}
  .pagehead{padding:12px var(--gutter) 26px;flex-wrap:wrap;align-items:flex-start;margin-bottom:18px}
  .pagehead h1{font-size:26px}
  .page{padding:4px var(--gutter) 32px}
  .card{padding:18px}
  /* iOS focus-zoom: a control under 16px zooms the page inside an overflow:hidden frame. */
  input,select,textarea{font-size:16px}
  #phonebar{display:flex;align-items:center;justify-content:space-between;gap:10px;position:fixed;top:0;left:0;right:0;height:64px;
    padding:0 16px 0 20px;z-index:40;background:var(--chrome);color:var(--on-chrome)}
  #phonebar .pbbrand{display:flex;align-items:center;flex-shrink:0}
  #phonebar .pbbrand img{height:36px;width:auto;display:block}
  #phonebar .pbright{display:flex;align-items:center;gap:8px;min-width:0}
  #phonebar .chip{max-width:170px;cursor:pointer;padding:6px 12px}
  #phonebar .chip b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}
  #phonebar .userbtn{display:flex;align-items:center;border:0;background:none;padding:0;border-radius:50%;cursor:pointer}
  #phonebar .userbtn .av{width:36px;height:36px}
  /* The chrome blue at the bottom too: the phone's chrome is one color at both ends, the page white between. */
  .tabbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:40;background:var(--chrome);
    padding:8px 6px calc(10px + env(safe-area-inset-bottom,0px))}
  .tabbar a,.tabbar button{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:5px;min-height:54px;border:0;background:none;font:inherit;border-radius:12px;color:var(--muted);text-decoration:none;
    font-size:12.5px;font-weight:500;cursor:pointer;transition:color .15s ease,transform .12s var(--ease-out)}
  .tabbar a svg,.tabbar button svg{width:22px;height:22px;position:relative;stroke-width:1.7}
  .tabbar a span,.tabbar button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
  .tabbar a:active,.tabbar button:active{transform:scale(.96)}
  .tabbar a.active{color:var(--navy)}
  /* The current tab sits on a pill in the deeper shade of the chrome blue: on the lightest blue
     every tab is full white, so the pill is what says "you are here". */
  .tabbar a.active::before{content:"";position:absolute;top:2px;left:50%;width:56px;height:30px;margin-left:-28px;
    border-radius:99px;background:var(--chrome-deep)}
  .tabbar .navbadge{position:absolute;top:0;left:calc(50% + 5px);margin:0}
  /* More sheet: parked on <body>, class-revealed (forced reflow, not rAF: the row-menu lesson). */
  .sheetwrap{position:fixed;inset:0;z-index:58;display:block;visibility:hidden;pointer-events:none;transition:visibility .26s}
  .sheetwrap .scrim{position:absolute;inset:0;background:rgba(11,18,32,.32);opacity:0;transition:opacity .2s var(--ease-out)}
  .sheetwrap .sheet{position:absolute;left:0;right:0;bottom:0;background:#fff;border-top-left-radius:24px;border-top-right-radius:24px;
    box-shadow:var(--lift);padding:8px 10px calc(14px + env(safe-area-inset-bottom,0px));transform:translateY(100%);transition:transform .26s var(--ease-out)}
  .sheetwrap .sheet::before{content:"";display:block;width:36px;height:4px;border-radius:2px;background:rgba(11,18,32,.16);margin:2px auto 8px}
  .sheetwrap.open{visibility:visible;pointer-events:auto}
  .sheetwrap.open .scrim{opacity:1}
  .sheetwrap.open .sheet{transform:translateY(0)}
  .sheet a{display:flex;width:100%;align-items:center;gap:12px;border:0;background:none;font:inherit;font-size:16px;color:var(--ink);text-decoration:none;border-radius:12px;padding:13px 12px;cursor:pointer}
  .sheet a svg{width:20px;height:20px;opacity:.7;flex-shrink:0}
  .sheet a:active{transform:scale(.98)}
  /* The settings modal and wide dialogs take the whole screen. Small confirms stay centered cards. */
  .smodal-wrap{padding:0}
  .smodal{width:100%;height:100%;border-radius:0;border:0;flex-direction:column}
  .smodal-nav{width:100%;flex-direction:row;overflow-x:auto;gap:4px;padding:52px 12px 10px;border-right:0;border-bottom:1px solid var(--hairline)}
  .smodal-nav .grouplabel{display:none}
  .smodal-nav button{width:auto;white-space:nowrap;flex-shrink:0}
  .smodal-body{padding:18px 20px 28px}
  .sdlg-wrap{padding:12px}
  .sdlg{max-height:100%;overflow-y:auto}
  .sdlg-wrap:has(.sdlg--wide){padding:0}
  .sdlg--wide{width:100%;height:100%;max-height:none;border-radius:0;border:0}
  /* The focused bar stays on a phone (it is the tutor's only chrome) and nothing pads for tab bars. */
  body.focus .rail{display:flex;height:auto;min-height:64px;padding:10px 16px 10px 10px;gap:8px}
  body.focus .rail .railbrand{display:none}
  body.focus .fback span{display:none}
  body.focus .app{padding:0}
}
@media (prefers-reduced-motion: reduce){
  .sheetwrap,.sheetwrap .scrim,.sheetwrap .sheet{transition:none}
}
