/* OxOnOS Design System v2 — the component layer. Mason, 2026-10-04.
   Spec:   pod/dev/specs/oxonos-gui-v2.md (UX-Contract-Hash sha256:5fad1565…)
   Source: pod/qa/ux-review/oxonos-gui-v2/prototype/components.css (Iris)

   HOW THIS FILE WORKS. It loads AFTER app.css. It does two things:
     1. re-skins the v1 primitives every screen already emits (.page, .h1, .sub,
        .sect, .btn, .tag, .dot, .pill, .card, .setrow, .notice, .state-panel,
        .band/.stat, .chip, form controls) to the v2 geometry — so every screen
        changes at once and no screen's behaviour is touched;
     2. adds the v2 components (.ph, .greeting, .list/.lrow, .state, .banner,
        .empty, .tiles/.tile, .truth, .split, .seg, .switch …).
   ⛔ Tokens only below this line. Not one raw hex: a hard-coded light ground is
   the P0 dark-mode catastrophe class (Iris closed list). The ONLY literal
   colours are #fff on the switch knob and the QR quiet-zone, which must be
   white in both themes to be read by a camera.
   ⚠️ v2 list rows are `.lrow`, not the spec's `.row`: `.row` is already the
   Activity timeline row in app.css and the two cannot share a name. */

/* ── base ─────────────────────────────────────────────────────────────── */
html { background: var(--bg); }
body {
  font-family: var(--font); font-size: var(--t-body); line-height: var(--lh-body);
  color: var(--text); background: var(--bg);
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* ⭐ A FOCUS RING IS NEVER CUT OFF (tests/ui/controls-geometry.test.mjs (g)).
   · Moving focus scrolls a control to the scroller's edge, and the ring that
     hangs 5 px outside it was clipped — so leave room for it.
   · A row flush to the edge of a rounded, clipping group (.list, .setgroup,
     .rows, a table's scroller, the Settings rail) has nowhere outside to draw:
     its ring is drawn INSIDE. */
:where(a, button, input, select, textarea, summary, [tabindex]) { scroll-margin: var(--s-3); }
:is(.list, .setgroup, .rows, .tablewrap, .ss-side) :focus-visible { outline-offset: -3px; }
/* and a whole row's inset ring is rounded, so the group's own corner does not shave it */
:is(.list, .setgroup, .rows) > :focus-visible { border-radius: var(--r-lg); }
::selection { background: var(--info-bg); color: var(--text); }

/* ⛔ THE !important OVERRIDE BLOCK THAT USED TO LIVE HERE IS GONE (UI phase 1,
   audit S-03). It neutralised a hand-kept list of 33 v1 uppercase-tracked
   classes and missed the rest. app.css itself no longer sets uppercase or
   tracking anywhere except the pairing code, so there is nothing to override.

   ONE WEIGHT SCALE (RULEBOOK R-T2): the browser draws <b>, <strong>, <th> and
   headings at 700, which is off-scale; they take the scale's bold. */
b, strong, th, h1, h2, h3, h4, h5, h6 { font-weight: var(--w-bold); }
small { font-size: var(--t-small); }
code, kbd, samp, pre { font-family: var(--mono); font-size: var(--t-small); }

/* ── shell ────────────────────────────────────────────────────────────── */
.rail {
  width: var(--rail-w); padding: var(--s-6) var(--s-4) var(--s-5);
  border-right: 1px solid var(--border); background: var(--bg);
}
.brand { padding: 0 var(--s-3); }
.brand img { width: 148px; height: auto; }
.brand-tag { padding: var(--s-2) var(--s-3) 0; font-size: var(--t-caption); color: var(--text-3); font-weight: var(--w-medium); letter-spacing: 0; text-transform: none; }
.nav { margin-top: var(--s-8); gap: var(--s-1); }
.nav a {
  min-height: var(--hit); padding: 0 var(--s-3); gap: var(--s-3);
  border-radius: var(--r-md); border: 1px solid transparent;
  font-size: var(--t-body); font-weight: var(--w-medium); color: var(--text-2);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.nav a svg { width: 22px; height: 22px; opacity: 1; }
.nav a:hover { background: var(--surface-2); color: var(--text); }
.nav a[aria-current="page"] {
  background: var(--surface); color: var(--text); font-weight: var(--w-bold);
  border-color: var(--border); box-shadow: var(--shadow-1);
}
.nav a[hidden] { display: none; }
.nav.nav-foot { margin-top: auto; padding-top: var(--s-4); }
.rail-foot { margin-top: var(--s-3); }
.rail-meta { padding: 0 var(--s-3); font-size: var(--t-caption); color: var(--text-3); }

.topbar {
  min-height: 64px; padding: 0 var(--page-pad); gap: var(--s-3);
  background: var(--surface-glass);
  -webkit-backdrop-filter: saturate(1.4) blur(16px); backdrop-filter: saturate(1.4) blur(16px);
  border-bottom: 1px solid var(--hairline);
}
.crumb { font-size: var(--t-small); color: var(--text-3); }
.crumb b { color: var(--text); font-weight: var(--w-bold); }
.clock { font-size: var(--t-small); color: var(--text-3); }
.boxbadge { background: var(--accent); color: var(--on-accent); font-size: var(--t-small); min-height: 28px; }

/* The icon button (theme). Exactly one glyph shows: the current choice. */
.iconbtn {
  display: inline-grid; place-items: center; width: var(--hit); height: var(--hit);
  border-radius: var(--r-pill); border: 0; background: transparent; color: var(--text-2);
  cursor: pointer; flex: 0 0 auto; padding: 0;
}
.iconbtn:hover { background: var(--surface-2); color: var(--text); }
.iconbtn .ti { display: none; line-height: 0; }
html:not([data-theme]) .iconbtn .ti-auto,
html[data-theme="light"] .iconbtn .ti-light,
html[data-theme="dark"] .iconbtn .ti-dark { display: block; }

/* ── the privacy pill: one chip, a word for each of the box's four states ── */
.topbar { position: sticky; }
button.pill { font: inherit; cursor: pointer; }
.pill {
  min-height: 32px; padding: 0 var(--s-3); gap: var(--s-2);
  font-size: var(--t-small); font-weight: var(--w-bold); letter-spacing: 0;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface); color: var(--text-2);
}
.topbar .pill { min-height: var(--hit); }
.pill.local     { color: var(--ok);   background: var(--ok-bg);   border-color: transparent; }
.pill.connected { color: var(--info); background: var(--info-bg); border-color: transparent; }
.pill.warn      { color: var(--warn); background: var(--warn-bg); border-color: transparent; }
.pill.remote    { color: var(--bad);  background: var(--bad-bg);  border-color: transparent; }
.pill.unknown   { color: var(--idle); background: var(--idle-bg); border-color: transparent; }
.pill.offbox    { color: var(--info); background: var(--info-bg); border-color: transparent; }
.dot.info { background: var(--info); }
.dot.bad  { background: var(--bad); }

.pill-sheet {
  animation: ui-rise var(--motion-sheet) both;
  position: absolute; top: calc(100% + var(--s-2)); right: var(--page-pad); z-index: 20;
  width: min(420px, calc(100vw - 2 * var(--s-4)));
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-xl); box-shadow: var(--shadow-3);
  padding: var(--s-6);
}
.pill-sheet[hidden] { display: none; }
.pill-sheet-head { display: flex; align-items: center; gap: var(--s-3); margin: calc(-1 * var(--s-2)) calc(-1 * var(--s-2)) var(--s-2) 0; }
.pill-sheet-head .iconbtn svg { width: 20px; height: 20px; }
.pill-sheet h2 { flex: 1; font-size: var(--t-title-3); font-weight: var(--w-bold); margin: 0; }
/* legend: chip column max-content so every description starts on one x (D-05) */
.pill-sheet li .state { justify-self: start; align-self: start; }
.pill-sheet .now { color: var(--text-2); font-size: var(--t-small); margin: 0 0 var(--s-4); }
.pill-sheet ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--s-3); }
.pill-sheet li { display: contents; }
.pill-sheet li .state { flex: 0 0 auto; }
.pill-sheet li p { margin: 0; font-size: var(--t-small); color: var(--text-2); }
.pill-sheet li[aria-current="true"] p { color: var(--text); }
.pill-sheet .close { margin-top: var(--s-4); }

/* ── phones: the rail becomes a tab bar ───────────────────────────────── */
.tabbar { display: none; }
@media (max-width: 760px) {
  .shell { display: block; }
  .rail { display: none; }
  .topbar { min-height: 56px; padding: 0 var(--page-pad); }
  .topbar .clock { display: none; }
  .main { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px)); }
  .tabbar {
    display: grid; grid-template-columns: repeat(5, 1fr);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 15;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--surface-glass);
    -webkit-backdrop-filter: saturate(1.4) blur(16px); backdrop-filter: saturate(1.4) blur(16px);
    border-top: 1px solid var(--hairline);
  }
  .tabbar a {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    text-decoration: none; color: var(--text-3); font-size: var(--t-caption); font-weight: var(--w-medium);
    min-height: var(--hit);
  }
  .tabbar a svg { width: 24px; height: 24px; }
  .tabbar a[aria-current="page"] { color: var(--text); font-weight: var(--w-bold); }
  .pill-sheet { right: var(--s-4); left: var(--s-4); width: auto; }
  /* ⛔ THE TOPBAR MUST NOT WIDEN THE PAGE (Clyde screen v2, 2026-10-04).
     Box badge + privacy pill + Glance/Desk + theme measured 486 px wide on a
     390 px phone, so the whole page laid out at 486: the browser zoomed out,
     and a screen pinned to the viewport (Clyde's composer) fell below the
     fold. The switch gets narrower, and anything still too wide scrolls
     inside the bar instead of overflowing the page. */
  .topbar-right { flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
  .topbar-right::-webkit-scrollbar { display: none; }
  .topbar .modeswitch button { min-width: 0; padding: 0 var(--s-3); }
  .topbar .modeswitch kbd { display: none; }
}

/* ⛔ THE PHONE TOP BAR FITS, OR IT IS BROKEN (re-audit N-02). Measured at
   390×844 on cabc37d: the Glance switch clipped at x 349–393, Appearance at
   403–443 and Control Centre at 453–497 — wholly off the screen, hidden inside
   a bar that scrolled sideways with no scrollbar. Nobody swipes a top bar.
   So on a phone the bar carries exactly: the box's name (the elastic member —
   it ellipsises, its full name is in its title), search, the privacy pill's
   ANSWER, the one Glance/Desk icon button, Control Centre and the bell. The
   theme cycler goes, because Control Centre already has the Appearance tile;
   the pill's subject ("AI processing ·") steps out of sight but not out of the
   accessibility tree. Nothing scrolls inside the bar any more, so a control
   that ever stops fitting shows up as page overflow in tests/ui instead of
   disappearing. tests/ui/reaudit-fixes.test.mjs, tests/ui/touch-targets.test.mjs. */
@media (max-width: 640px) {
  .topbar { gap: var(--s-2); padding-left: var(--s-4); padding-right: var(--s-2); }
  .topbar-right { flex: 0 0 auto; min-width: auto; overflow: visible; gap: 0; }
  .topbar-right .os-topbar { gap: 0; }
  .topbar #theme-btn { display: none; }
  .topbar .boxbadge { flex: 0 1 auto; min-width: 0; max-width: 100%; overflow: hidden; }
  .topbar .boxbadge .bb-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  /* js/app.js fitTopbar(): too little room beside the controls → the badge steps aside */
  .topbar .boxbadge.is-squeezed { display: none; }
  .topbar-right .pill { padding-left: var(--s-3); padding-right: var(--s-3); }
  .topbar-right .pill .pill-who {
    display: inline; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
  }
}

/* ── page scaffolding ─────────────────────────────────────────────────── */
.page {
  max-width: var(--page-max); margin: 0 auto;
  padding: var(--s-10) var(--page-pad) var(--s-16);
}
@media (max-width: 760px) { .page { padding-top: var(--s-6); } }
.h1 {
  font-size: var(--t-title-1); font-weight: var(--w-bold); line-height: var(--lh-tight);
  letter-spacing: var(--track-title); margin: 0;
}
.sub { margin-top: var(--s-2); font-size: var(--t-lead); color: var(--text-2); max-width: var(--read-max); line-height: var(--lh-snug); }
.sect { margin-top: var(--s-10); gap: var(--s-3); }
.sect.first { margin-top: var(--s-8); }
.sect h2 { font-size: var(--t-title-3); font-weight: var(--w-bold); letter-spacing: var(--track-title); }
.sect .line { background: transparent; }
.sect .more { font-size: var(--t-body); color: var(--text-2); min-height: var(--hit); display: inline-flex; align-items: center; }

/* page header (spec §3.6) */
.ph { display: flex; align-items: flex-end; gap: var(--s-4); margin-bottom: var(--s-8); flex-wrap: wrap; }
.ph .titles { flex: 1; min-width: 240px; }
.ph .eyebrow { font-size: var(--t-small); color: var(--text-3); font-weight: var(--w-medium); margin: 0 0 var(--s-1); }
.ph .lede { font-size: var(--t-lead); color: var(--text-2); margin: var(--s-2) 0 0; max-width: var(--read-max); line-height: var(--lh-snug); }
.ph .back { display: inline-flex; align-items: center; gap: var(--s-1); color: var(--text-2); text-decoration: none; min-height: var(--hit); }
.greeting .h1, .h1.display { font-size: var(--t-display); font-weight: var(--w-display); letter-spacing: var(--track-display); }
.greeting .lede { font-size: var(--t-lead); color: var(--text-2); margin: var(--s-3) 0 0; max-width: var(--read-max); }

.section { margin-top: var(--s-10); }
.section > header { display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-4); }
.section > header h2 { font-size: var(--t-title-3); font-weight: var(--w-bold); letter-spacing: var(--track-title); margin: 0; flex: 1; }
.section > header a { color: var(--text-2); text-decoration: none; min-height: var(--hit); display: inline-flex; align-items: center; }
.foot { font-size: var(--t-small); color: var(--text-3); }

/* ── buttons: ONE shape, four kinds, ONE height (RULEBOOK §4, audit S-04) ──
   primary (accent fill) · secondary (surface + control border) · plain (text)
   · destructive (red text). Height --hit everywhere; --hit-lg ONLY for the
   single CTA of a full-screen step (.lg). Hover changes the BACKGROUND, never
   the opacity. Weight 500: 600 drew as Bold and was the heaviest thing on
   every card. The v1 names (ghost, quiet, danger, sm) are aliases for ONE
   release so no screen breaks while its stream migrates; do not write them. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: var(--hit); padding: 0 var(--s-5); border-radius: var(--r-md);
  font: inherit; font-size: var(--t-body); font-weight: var(--w-medium); line-height: 1.2;
  letter-spacing: 0; text-transform: none;
  background: var(--accent); color: var(--on-accent); border: 1px solid transparent;
  cursor: pointer; text-decoration: none; white-space: nowrap; opacity: 1;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.btn svg { width: 18px; height: 18px; flex: none; }
.btn:hover { opacity: 1; background: color-mix(in srgb, var(--accent) 84%, var(--bg)); }
.btn:active { transform: scale(.98); }
.btn.primary { background: var(--accent); color: var(--on-accent); }
.btn.primary:hover { background: color-mix(in srgb, var(--accent) 84%, var(--bg)); }
.btn.secondary, .btn.ghost { background: var(--surface); color: var(--text); border-color: var(--control-border); }
.btn.secondary:hover, .btn.ghost:hover { background: var(--surface-2); }
.btn.plain, .btn.quiet, .btn--quiet { background: transparent; color: var(--text); border-color: transparent; }
.btn.plain:hover, .btn.quiet:hover, .btn--quiet:hover { background: var(--surface-2); color: var(--text); }
.btn.destructive, .btn.danger { background: transparent; color: var(--bad); border-color: transparent; }
.btn.destructive:hover, .btn.danger:hover { background: var(--bad-bg); color: var(--bad); }
.btn.secondary.destructive { border-color: var(--control-border); background: var(--surface); }
.btn.sm { min-height: var(--hit); padding: 0 var(--s-4); font-size: var(--t-body); }
.btn.lg { min-height: var(--hit-lg); padding: 0 var(--s-6); font-size: var(--t-body); }
/* ⛔ DISABLED LOOKS DISABLED, FOR EVERY KIND (re-audit N-01). This rule sat
   ABOVE the kinds at the same specificity (0,2,0), so .btn.primary and
   .btn.secondary won and a button that could not act was drawn as one that
   could — Updates "Tonight" was the one Juno caught. It now comes AFTER every
   kind and outranks them (.btn.btn.btn = 0,4,0; with :hover 0,5,0 — above the
   0,4,0 sheet hovers in os.css, which load later), so neither the
   kind nor a hover can repaint it. tests/ui/reaudit-fixes.test.mjs. */
.btn.btn.btn:disabled, .btn.btn.btn[disabled], .btn.btn.btn[aria-disabled="true"],
.btn.btn.btn:disabled:hover, .btn.btn.btn[disabled]:hover, .btn.btn.btn[aria-disabled="true"]:hover,
.btn.btn.btn:disabled:active, .btn.btn.btn[disabled]:active {
  opacity: 1; background: var(--surface-2); color: var(--text-3); border-color: transparent;
  cursor: not-allowed; transform: none; box-shadow: none;
}
.btn[hidden] { display: none; }
.actions { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }

/* ── dots and tags: the tag IS the v2 state chip ──────────────────────── */
.dot { width: 8px; height: 8px; }
/* ⛔ could-not-look is HOLLOW. It can never be mistaken for green (rule 4). */
.dot.idle { background: transparent; box-shadow: inset 0 0 0 2px currentColor; color: var(--idle); }
.tag {
  min-height: 28px; padding: 0 var(--s-3); gap: 6px; border-radius: var(--r-pill);
  font-size: var(--t-small); font-weight: var(--w-bold);
  background: var(--idle-bg); color: var(--idle);
}
.tag.ok   { background: var(--ok-bg);   color: var(--ok); }
.tag.warn { background: var(--warn-bg); color: var(--warn); }
.tag.bad  { background: var(--bad-bg);  color: var(--bad); }
.tag.mute { background: var(--idle-bg); color: var(--idle); }
.tag .dot { background: currentColor; }
.tag .dot.idle { background: transparent; box-shadow: inset 0 0 0 2px currentColor; }

.state {
  display: inline-flex; align-items: center; gap: 6px; min-height: 28px; padding: 0 var(--s-3);
  border-radius: var(--r-pill); font-size: var(--t-small); font-weight: var(--w-bold); white-space: nowrap;
  background: var(--idle-bg); color: var(--idle);
}
.state::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.state.ok   { background: var(--ok-bg);   color: var(--ok); }
.state.warn { background: var(--warn-bg); color: var(--warn); }
.state.bad  { background: var(--bad-bg);  color: var(--bad); }
.state.info { background: var(--info-bg); color: var(--info); }
.state.idle::before { background: transparent; border: 2px solid currentColor; width: 8px; height: 8px; box-sizing: border-box; }
.state.busy::before { animation: v2pulse 1.4s var(--ease-in-out) infinite; }
@keyframes v2pulse { 50% { opacity: .3; } }

/* ── cards ────────────────────────────────────────────────────────────── */
.cards { gap: var(--s-4); }
.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1); padding: var(--s-6);
}
.card.hero { border-radius: var(--r-xl); padding: var(--s-8); box-shadow: var(--shadow-2); }
.card h3 { font-size: var(--t-title-3); font-weight: var(--w-bold); letter-spacing: var(--track-title); line-height: var(--lh-snug); }
.card .desc { font-size: var(--t-body); color: var(--text-2); }
.card .meta { font-size: var(--t-small); color: var(--text-3); }
.card.sel { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.grid { display: grid; gap: var(--s-4); }
.grid.c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.c4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1100px) { .grid.c3, .grid.c4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) { .grid.c2, .grid.c3, .grid.c4 { grid-template-columns: minmax(0, 1fr); } }

/* ── setting rows: the grouped list ───────────────────────────────────── */
.setgroup {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1); overflow: hidden; margin-top: var(--s-4);
}
.setrow { min-height: 64px; padding: var(--s-3) var(--s-5); gap: var(--s-4); border-bottom: 1px solid var(--hairline); }
.setrow:last-child { border-bottom: 0; }
.setrow .info > b { font-size: var(--t-lead); font-weight: var(--w-medium); }
.setrow .info span { font-size: var(--t-small); color: var(--text-2); }
.setrow .val { font-size: var(--t-small); color: var(--text-3); }

/* ── v2 list rows (`.lrow`) ───────────────────────────────────────────── */
.list { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-1); }
.lrow { display: flex; align-items: center; gap: var(--s-4); min-height: 64px; padding: var(--s-3) var(--s-5); text-decoration: none; color: inherit; background: none; border: 0; width: 100%; font: inherit; text-align: left; }
.lrow + .lrow { border-top: 1px solid var(--hairline); }
a.lrow:hover, button.lrow:hover { background: var(--surface-2); }
.lrow[aria-current="page"] { background: var(--surface-2); }
.lrow .lead { flex: none; width: 36px; height: 36px; display: grid; place-items: center; border-radius: var(--r-sm); background: var(--surface-2); color: var(--text-2); }
.lrow .lead svg { width: 20px; height: 20px; }
.lrow .body { flex: 1; min-width: 0; }
.lrow .title, .lrow .desc { display: block; }
.lrow .title { font-size: var(--t-lead); font-weight: var(--w-medium); line-height: var(--lh-snug); }
.lrow .desc { font-size: var(--t-small); color: var(--text-2); line-height: var(--lh-snug); margin-top: 2px; }
.lrow .trail { flex: none; display: flex; align-items: center; gap: var(--s-3); color: var(--text-3); font-size: var(--t-small); }
.lrow .chev { width: 16px; height: 16px; color: var(--text-3); flex: none; }
@media (max-width: 760px) {
  .lrow { flex-wrap: wrap; row-gap: var(--s-2); }
  .lrow .body { flex: 1 1 180px; }
  .lrow .trail { margin-left: auto; flex-wrap: wrap; }
}

/* ── notices, banners, the five states ────────────────────────────────── */
.notice {
  border-radius: var(--r-lg); padding: var(--s-4) var(--s-5);
  background: var(--warn-bg); border: 0; color: var(--text); font-size: var(--t-body);
}
.notice b { color: var(--warn); }
.banner { display: flex; gap: var(--s-3); align-items: flex-start; padding: var(--s-4) var(--s-5); border-radius: var(--r-lg); background: var(--warn-bg); color: var(--text); }
.banner.bad { background: var(--bad-bg); }
.banner.info { background: var(--info-bg); }
.banner .glyph { flex: none; width: 20px; height: 20px; color: var(--warn); margin-top: 2px; }
.banner .glyph svg { width: 20px; height: 20px; display: block; }
.banner.bad .glyph { color: var(--bad); }
.banner.info .glyph { color: var(--info); }
.banner > div { flex: 1; min-width: 0; }
.banner b { display: block; font-weight: var(--w-bold); }
.banner p { margin: 2px 0 0; color: var(--text-2); font-size: var(--t-small); }
.banner details { margin-top: var(--s-2); font-size: var(--t-small); color: var(--text-2); }
.banner summary { cursor: pointer; font-weight: var(--w-bold); color: var(--text); min-height: 32px; display: inline-flex; align-items: center; }

.state-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1); color: var(--text);
}
.state-panel h2 { font-size: var(--t-title-3); font-weight: var(--w-bold); }
.state-panel p { color: var(--text-2); font-size: var(--t-body); }
.state-panel.empty { border-style: dashed; box-shadow: none; background: transparent; }
/* The error panel: the v2 bad banner, still centred enough to read as the
   screen's answer and not as a footnote. */
.state-panel.state-error, .state-panel.banner.bad {
  display: block; text-align: left; background: var(--bad-bg); border: 0; box-shadow: none;
  padding: var(--s-5) var(--s-6);
}
.state-panel.state-error h2 { color: var(--bad); }
.state-panel.state-error p { margin: var(--s-2) 0 var(--s-4); max-width: var(--read-max); }

.empty { text-align: center; padding: var(--s-12) var(--s-6); border: 1px dashed var(--border); border-radius: var(--r-lg); }
.empty .art { width: 56px; height: 56px; margin: 0 auto var(--s-4); border-radius: var(--r-lg); background: var(--surface-2); display: grid; place-items: center; color: var(--text-3); }
.empty .art svg { width: 28px; height: 28px; }
.empty h3 { font-size: var(--t-title-3); margin: 0; font-weight: var(--w-bold); }
.empty p { color: var(--text-2); margin: var(--s-2) auto var(--s-5); max-width: 44ch; }

.sk { border-radius: var(--r-sm); background: linear-gradient(90deg, var(--surface-2), var(--hairline), var(--surface-2)); background-size: 200% 100%; animation: v2sk 1.6s linear infinite; }
@keyframes v2sk { to { background-position: -200% 0; } }

/* ── stat band ────────────────────────────────────────────────────────── */
.band { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--shadow-1); overflow: hidden; }
.stat { border-color: var(--hairline); }
.stat .label { color: var(--text-2); }
.stat .value { font-weight: var(--w-display); letter-spacing: var(--track-display); font-variant-numeric: tabular-nums; }
.stat .value.idle { font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--idle); letter-spacing: 0; }
.meter { height: 6px; border-radius: var(--r-pill); background: var(--surface-2); overflow: hidden; }

/* ── choice chips ─────────────────────────────────────────────────────── */
.chip {
  min-height: var(--hit); padding: 0 var(--s-4); border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font: inherit; font-size: var(--t-body);
}
.chip:hover { border-color: var(--control-border); color: var(--text); }
.chip[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.chip:disabled { color: var(--text-3); background: transparent; }
.chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* ── form controls: explicit ground and ink, or dark mode paints white boxes ── */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea {
  background: var(--surface); color: var(--text); border: 1px solid var(--control-border);
  border-radius: var(--r-sm); font: inherit;
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not(.composer input), select {
  min-height: var(--hit);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input[type="checkbox"], input[type="radio"], input[type="range"] { accent-color: var(--ok); }
.input { min-height: var(--hit); padding: 0 var(--s-4); border-radius: var(--r-md); width: 100%; max-width: 280px; }

/* ── tables and the engineers' disclosure ─────────────────────────────── */
th { background: var(--surface-2); color: var(--text-2); }
details.tech summary { min-height: var(--hit); display: flex; align-items: center; cursor: pointer; color: var(--text-2); }

/* ── app tiles (extends Juno's family) ────────────────────────────────── */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: var(--s-6) var(--s-4); }
.tile { display: flex; flex-direction: column; align-items: center; gap: var(--s-2); text-decoration: none; color: var(--text); text-align: center; padding: var(--s-2); border-radius: var(--r-lg); background: none; border: 0; font: inherit; cursor: pointer; }
.tile:hover { background: var(--surface-2); }
.tile .icon { width: 88px; height: 88px; border-radius: var(--r-icon); box-shadow: var(--shadow-2); overflow: hidden; display: grid; place-items: center; }
.tile .icon img, .tile .icon svg { width: 100%; height: 100%; display: block; }
.tile .icon.letter { background: var(--surface-2); border: 1px solid var(--border); font-size: var(--t-title-1); font-weight: var(--w-display); color: var(--text-2); }
.tile .icon.add { background: transparent; border: 2px dashed var(--control-border); box-shadow: none; color: var(--text-2); }
.tile .icon.add svg { width: 36px; height: 36px; }
.tile .name { font-weight: var(--w-medium); font-size: var(--t-body); line-height: var(--lh-snug); }
.tile .where { font-size: var(--t-caption); color: var(--text-3); line-height: var(--lh-snug); margin-top: -4px; }
.tile.away .icon { filter: grayscale(1); opacity: .55; }
@media (max-width: 760px) { .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5) var(--s-2); } .tile .icon { width: 68px; height: 68px; } }

/* ── truth strip: where the AI runs (rules 3/4) ───────────────────────── */
.truth { display: flex; align-items: center; gap: var(--s-4); padding: var(--s-5) var(--s-6); border-radius: var(--r-xl); background: var(--ok-bg); }
.truth.warn { background: var(--warn-bg); }
.truth.idle { background: var(--idle-bg); }
.truth.info { background: var(--info-bg); }
.truth.bad  { background: var(--bad-bg); }
.truth .mark { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; background: var(--surface); color: var(--ok); flex: none; }
.truth.warn .mark { color: var(--warn); } .truth.idle .mark { color: var(--idle); }
.truth.info .mark { color: var(--info); } .truth.bad .mark { color: var(--bad); }
.truth .mark svg { width: 24px; height: 24px; }
.truth b { display: block; font-size: var(--t-lead); }
.truth span { color: var(--text-2); font-size: var(--t-small); }
.truth .grow { flex: 1; min-width: 0; }
@media (max-width: 760px) {
  .truth { flex-wrap: wrap; padding: var(--s-4) var(--s-5); }
  .truth .grow { flex-basis: calc(100% - 60px); }
  .truth > .state { margin-left: 60px; }
}

/* ── composer ─────────────────────────────────────────────────────────── */
.composer { border-radius: var(--r-xl); background: var(--surface); border: 1px solid var(--control-border); box-shadow: var(--shadow-2); }
.composer:focus-within { outline: 3px solid var(--focus); outline-offset: 2px; }
.composer textarea, .composer input { border: 0; background: transparent; color: var(--text); }

/* ── settings split view ──────────────────────────────────────────────── */
.split { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: var(--s-8); align-items: start; }
.split > * { min-width: 0; }
.split .side { position: sticky; top: 88px; }
.split .side .lrow { min-height: 52px; }
.split .pane .page { padding: 0; max-width: none; margin: 0; }
.split .pane .page .page { padding: 0; }
.split .pane .h1 { font-size: var(--t-title-1); }
@media (max-width: 1100px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .split .side { position: static; }
  .split.has-section .side { display: none; }
  .split.is-index .pane { display: none; }
  .settings-page.has-section > .ph { display: none; }
}
@media (min-width: 1101px) { .split .pane .back-settings { display: none; } }
.back-settings { display: inline-flex; align-items: center; gap: var(--s-1); min-height: var(--hit); color: var(--text-2); text-decoration: none; margin-bottom: var(--s-2); }

/* segmented control: see PHASE 1 COMPONENTS below */

.switch { position: relative; width: 52px; height: 32px; border-radius: var(--r-pill); background: var(--control-border); border: 0; cursor: pointer; flex: none; transition: background var(--dur-2) var(--ease); }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 26px; height: 26px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-1); transition: transform var(--dur-2) var(--ease); }
.switch[aria-checked="true"] { background: var(--ok); }
.switch[aria-checked="true"]::after { transform: translateX(20px); }

/* ⭐ COMPACT TO LOOK AT, FINGER-SIZED TO HIT (re-audit N-03, RULEBOOK R-K2).
   The controls below keep the size they are drawn at; an invisible overhang
   (a pseudo-element the control owns, so a press on it IS a press on the
   control) carries the target to --hit: 44 px, 48 on the glass. Measured by
   tests/ui/touch-targets.test.mjs the way a finger meets it — elementFromPoint
   round a --hit circle — not by the drawn box.
   · the on/off switch (52×32): ::before, because ::after is the knob */
.switch::before { content: ""; position: absolute; left: calc((52px - max(52px, var(--hit))) / 2);
  right: calc((52px - max(52px, var(--hit))) / 2); top: calc((32px - var(--hit)) / 2); bottom: calc((32px - var(--hit)) / 2); }
/* · text links that are the only route somewhere: a --hit box centred on the
     words, at least as wide as the words. inline-block so a link never splits
     across two lines (a split link has no centre to aim at). */
.v2-foot a, .help-keys a, .v2-files td > a[data-go] { display: inline-block; }
.v2-foot a, .help-keys a, .v2-files td > a[data-go], .home .home-ask-open { position: relative; }
.v2-foot a::after, .help-keys a::after, .v2-files td > a[data-go]::after, .home .home-ask-open::after {
  content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: max(100%, var(--hit)); height: max(100%, var(--hit));
}
/* · a disclosure inside a banner ("What still protects them") */
.banner details > summary { cursor: pointer; min-height: var(--hit); display: flex; align-items: center; gap: var(--s-2); list-style: none; }
.banner details > summary::-webkit-details-marker { display: none; }
.banner details > summary::before { content: "\25B8"; }
.banner details[open] > summary::before { content: "\25BE"; }

/* ══════════════════════════════════════════════════════════════════════════
   PHASE 1 COMPONENTS (UI rebuild stream 1 — foundations, 2026-10-04).
   The CSS half of the component API in ui/js/ui.js. RULEBOOK §4.
   ══════════════════════════════════════════════════════════════════════════ */

/* status chip: NEVER stretches (audit S-05 — "Newer version" drew 590px wide) */
.state, .tag { width: max-content; max-width: 100%; flex: none; align-self: center; white-space: nowrap; font-weight: var(--w-medium); }
.state.neutral { background: var(--idle-bg); color: var(--text-2); }
.state.neutral::before { background: var(--text-3); }
/* ⛔ could-not-check is grey and HOLLOW whatever else is on it (rule 4) */
.state.idle { background: var(--idle-bg); color: var(--idle); }

/* section header */
.sh { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-10) 0 var(--s-3); min-height: var(--hit); }
.sh:first-child { margin-top: 0; }
.sh-title { flex: 1; min-width: 0; margin: 0; font-size: var(--t-title-2); font-weight: var(--w-bold); line-height: var(--lh-tight); letter-spacing: var(--track-title); }
h3.sh-title { font-size: var(--t-title-3); }
.sh-action { flex: none; display: flex; gap: var(--s-2); align-items: center; }

/* setting row (v2): icon? · title + description · control column, right */
.setrow.v2 { display: flex; align-items: center; gap: var(--s-4); }
.setrow.v2 .lead { flex: none; width: 36px; height: 36px; display: grid; place-items: center; border-radius: var(--r-sm); background: var(--surface-2); color: var(--text-2); }
.setrow.v2 .lead svg { width: 20px; height: 20px; }
.setrow.v2 .info { flex: 1; min-width: 0; }
.setrow.v2 .info > b { display: block; font-size: var(--t-lead); font-weight: var(--w-medium); line-height: var(--lh-snug); }
.setrow.v2 .info > span { display: block; font-size: var(--t-small); color: var(--text-2); line-height: var(--lh-snug); margin-top: 2px; max-width: var(--read-max); }
.setrow.v2 .ctl { flex: 0 0 auto; display: flex; justify-content: flex-end; align-items: center; gap: var(--s-2); min-width: 0; max-width: 280px; }
.setrow.v2 .ctl .input, .setrow.v2 .ctl select { width: 280px; max-width: 100%; }
/* a segmented control is as wide as its words — the 280 px cap pushed
   Display › "Return to Glance" 36 px out of its .ctl on the glass */
.setrow.v2 .ctl:has(> .seg) { max-width: none; }
.setrow.v2.locked .locked-note { font-size: var(--t-small); color: var(--idle); }
@media (max-width: 760px) {
  .setrow.v2 { flex-wrap: wrap; }
  .setrow.v2 .ctl { flex-basis: 100%; max-width: none; justify-content: flex-start; }
}
.lrow .val { color: var(--text-2); font-size: var(--t-small); }
.lrow .lead { border-radius: var(--r-sm); }

/* back link: chevron + the parent's NAME, 44px target, on the page edge */
.nav-back {
  display: inline-flex; align-items: center; gap: var(--s-1); min-height: var(--hit);
  margin-left: calc(-1 * var(--s-1)); padding-right: var(--s-2);
  /* a hair of air above the title: flush against it, the title's line box
     took the bottom edge of the back link's target (re-audit N-03) */
  margin-bottom: var(--s-1);
  color: var(--text-2); text-decoration: none; font-size: var(--t-body); font-weight: var(--w-medium);
  border-radius: var(--r-md);
}
.nav-back svg { width: 20px; height: 20px; }
.nav-back:hover { color: var(--text); }

/* empty / loading / error: ONE layout, full content width (R-S5, R-O9) */
.ui-state {
  display: flex; flex-direction: column; align-items: center; text-align: center; width: 100%;
  padding: var(--s-12) var(--s-6); border-radius: var(--r-lg); background: var(--surface);
  border: 1px solid var(--border); box-sizing: border-box;
}
.ui-state .art { width: 48px; height: 48px; display: grid; place-items: center; border-radius: var(--r-md); background: var(--surface-2); color: var(--text-2); margin-bottom: var(--s-4); }
.ui-state .art svg { width: 24px; height: 24px; }
.ui-state h2 { margin: 0; font-size: var(--t-title-3); font-weight: var(--w-bold); }
.ui-state p { margin: var(--s-2) 0 0; color: var(--text-2); max-width: 52ch; }
.ui-state .actions { margin-top: var(--s-5); justify-content: center; }
.empty-state { background: transparent; border-style: dashed; }
.error-state { background: var(--bad-bg); border-color: transparent; }
.error-state .art { background: var(--surface); color: var(--bad); }
.loading-state { align-items: stretch; text-align: left; background: transparent; border: 0; padding: 0; }
.sk-title { height: 32px; width: 40%; margin-bottom: var(--s-6); }
.sk-list { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; }
.sk-row { display: flex; align-items: center; gap: var(--s-4); min-height: 64px; padding: var(--s-3) var(--s-5); }
.sk-row + .sk-row { border-top: 1px solid var(--hairline); }
.sk-dot { width: 36px; height: 36px; flex: none; }
.sk-lines { flex: 1; display: flex; flex-direction: column; gap: var(--s-2); }
.sk-line { height: 12px; width: 60%; }
.sk-line.short { width: 35%; }

/* segmented control — one height with every other control */
.seg { display: inline-flex; padding: 3px; border-radius: var(--r-md); background: var(--surface-2); gap: 2px; }
/* white-space: nowrap — "10 min" on two lines is a broken control (re-audit N-05).
   The button is drawn 6 px short of --hit inside the 3 px track; its ::after
   reaches the track's outer edge, so the TARGET is the full --hit (N-03). */
.seg button { position: relative; white-space: nowrap; min-height: calc(var(--hit) - 6px); padding: 0 var(--s-4); border: 0; border-radius: var(--r-sm); background: transparent; color: var(--text-2); font: inherit; font-size: var(--t-body); font-weight: var(--w-medium); cursor: pointer; transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease); }
.seg button::after { content: ""; position: absolute; left: -1px; right: -1px; top: -3px; bottom: -3px; }
.seg button:hover { color: var(--text); }
.seg button[aria-pressed="true"], .seg button[aria-checked="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }
.seg button:disabled { color: var(--text-3); cursor: not-allowed; }
/* fill: the track spans its container and the segments share it equally
   (ui.js segmented({ fill: true })). min-width 0 + flex-basis 0 so every
   segment is the same width whatever its word. */
.seg.seg--fill { display: flex; width: 100%; }
.seg--fill button { flex: 1 1 0; min-width: 0; padding: 0 var(--s-2); }
/* a segmented control never runs out of the box it sits in: on a phone the
   segments give back padding before the track can overhang the page */
.seg { max-width: 100%; }
@media (max-width: 760px) { .seg button { padding: 0 var(--s-3); } .seg--fill button { padding: 0 var(--s-2); } }
/* a suggestion chip that wraps on a phone is a rounded rectangle, not a pill —
   two lines in a pill put the words on the curve (#/ask, 390 px) */
@media (max-width: 760px) { .chips .chip { border-radius: var(--r-lg); } }

/* the sheet primitive (S-10) + confirm sheet (S-07) */
#ui-layer { position: relative; z-index: 60; }
.ui-sheet-wrap { position: fixed; inset: 0; display: grid; place-items: center; padding: var(--s-4); z-index: 60; }
.ui-scrim { position: absolute; inset: 0; background: color-mix(in srgb, var(--oxon-black) 32%, transparent); animation: ui-fade var(--motion-sheet) both; }
.ui-sheet {
  position: relative; width: min(480px, 100%); max-height: calc(100vh - 2 * var(--s-8)); overflow: auto;
  background: var(--surface); color: var(--text); border-radius: var(--r-xl); box-shadow: var(--shadow-3);
  padding: var(--s-6); animation: ui-rise var(--motion-sheet) both;
}
.ui-sheet-head { display: flex; align-items: center; gap: var(--s-3); margin: calc(-1 * var(--s-2)) calc(-1 * var(--s-2)) var(--s-3) 0; }
.ui-sheet-head h2 { flex: 1; margin: 0; font-size: var(--t-title-3); font-weight: var(--w-bold); line-height: var(--lh-snug); }
.ui-sheet-head .iconbtn svg { width: 20px; height: 20px; }
.ui-sheet-text { margin: 0; color: var(--text-2); max-width: var(--read-max); }
.ui-sheet-actions { display: flex; justify-content: flex-end; gap: var(--s-3); margin-top: var(--s-6); flex-wrap: wrap; }
.ui-sheet-wrap.leaving .ui-sheet { animation: ui-sink var(--motion-exit) both; }
.ui-sheet-wrap.leaving .ui-scrim { animation: ui-fade-out var(--motion-exit) both; }
@media (max-width: 760px) {
  .ui-sheet-wrap { place-items: end stretch; padding: 0; }
  .ui-sheet { width: 100%; border-radius: var(--r-xl) var(--r-xl) 0 0; padding-bottom: calc(var(--s-6) + env(safe-area-inset-bottom, 0px)); }
  .ui-sheet-actions .btn { flex: 1; }
}

/* the toast (S-08): one region, bottom-centre, above the tab bar */
.toast-region { position: fixed; left: 50%; bottom: var(--s-6); transform: translateX(-50%); z-index: 70; display: flex; flex-direction: column; align-items: center; gap: var(--s-2); pointer-events: none; width: max-content; max-width: calc(100vw - 2 * var(--s-4)); }
.toast {
  pointer-events: auto; display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--hit); padding: var(--s-1) var(--s-2) var(--s-1) var(--s-5);
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-3); font-size: var(--t-body);
  animation: ui-rise var(--motion-banner) both;
}
.toast:not(:has(.btn)) { padding-right: var(--s-5); }
.toast.leaving { animation: ui-fade-out var(--motion-exit) both; }
@media (max-width: 760px) { .toast-region { bottom: calc(var(--tabbar-h) + var(--s-3) + env(safe-area-inset-bottom, 0px)); } }

/* topbar OS slots (stream 6 mounts search / control / bell here) */
.topbar-slot { display: inline-flex; align-items: center; }
.topbar-slot[hidden] { display: none; }

/* ── motion: FOUR named transitions (Atlas item 4; RULEBOOK R-O8) ──────────
   screen   — page change, 200ms cross-fade + 4px settle on #main
   sheet    — sheets and popovers rise, 320ms
   app      — an app opening from its icon, 320ms scale-in
   banner   — banners and toasts in/out, 200ms
   All four are tokens in tokens.css and go to 0 under reduced motion. */
@keyframes ui-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ui-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes ui-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes ui-sink { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(12px); } }
@keyframes ui-screen { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes ui-app-open { from { opacity: 0; transform: scale(.94); } to { opacity: 1; transform: none; } }
.main.screen-enter > * { animation: ui-screen var(--motion-screen) both; }
.motion-app-open { animation: ui-app-open var(--motion-app) both; transform-origin: var(--app-origin, 50% 50%); }
.motion-banner { animation: ui-rise var(--motion-banner) both; }
@media (prefers-reduced-motion: reduce) {
  .main.screen-enter > *, .motion-app-open, .motion-banner, .ui-sheet, .ui-scrim, .toast,
  .ui-sheet-wrap.leaving .ui-sheet, .ui-sheet-wrap.leaving .ui-scrim, .toast.leaving { animation: none; }
}

/* ── utilities ────────────────────────────────────────────────────────── */
.muted { color: var(--text-2); }
.faint { color: var(--text-3); }
.hstack { display: flex; align-items: center; gap: var(--s-3); }
.vstack { display: flex; flex-direction: column; gap: var(--s-3); }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }
.mt-4 { margin-top: var(--s-4); } .mt-6 { margin-top: var(--s-6); } .mt-8 { margin-top: var(--s-8); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@media (max-width: 760px) { .hide-phone { display: none !important; } }

/* ── reduced motion: everything still works, nothing moves ───────────── */
@media (prefers-reduced-motion: reduce) {
  .state.busy::before, .sk { animation: none; }
  .btn:active { transform: none; }
}

/* ── the glass ────────────────────────────────────────────────────────── */
@media (min-width: 1700px) {
  .tile .icon { width: 104px; height: 104px; }
}

/* "This computer was updated — Refresh" (js/release-refresh.js). Non-blocking:
   top-centre, small, one button; never a scrim, never over the composer. */
.release-bar {
  position: fixed; top: var(--s-3); left: 50%; transform: translateX(-50%); z-index: 71;
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--hit); padding: var(--s-1) var(--s-2) var(--s-1) var(--s-5);
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-3); font-size: var(--t-body);
  max-width: calc(100vw - 2 * var(--s-4));
}
