/* ============================================================
   Clerim — app shell composition (structure ONLY — no appearance
   decisions beyond tokens). One rule: size classes come from
   CONTAINER queries against the shell, never viewport media
   queries. A 400px pane and a 400px window resolve identically.
   compact <720 · regular 720–1119 · wide 1120–1599 · expanded ≥1600
   Ported from the v1.0 prototype's responsive/shell.css.
   ============================================================ */

/* a page-level shell fills the viewport */
html, body { height: 100%; }
body { margin: 0; }
body > .app-shell { height: 100dvh; }

.app-shell {
  container-type: inline-size;
  container-name: shell;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  min-width: 360px;
  overflow: hidden;
  background: var(--page);
  background-image: var(--page-gradient);
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.app-shell *, .app-shell *::before, .app-shell *::after { box-sizing: border-box; }
.app-shell :focus-visible { outline: 2px solid var(--action); outline-offset: 2px; border-radius: 4px; }

/* ---------- optional FX layers (ADR-0015 D23.8) ----------
   Structure is stamped once by AppShell; every visual decision stays here.
   Aurora is the accepted seed-1337 export. It is dark-only, never catches a
   pointer and sits behind the same semantic panes the static token ground
   supports when canvas, motion or transparency is unavailable. */
.app-shell[data-fx~="ambient"] {
  /* A transparent shell lets the two negative stack levels sit between its
     stacking-context ground and every GUI child. The pseudo-element is the
     CURRENT Clerim token backdrop; Aurora is only a translucent light field
     over it, never a replacement page and never a foreground scrim. */
  isolation: isolate;
  background: transparent;
  background-image: none;
}
.app-shell[data-fx~="ambient"]::before {
  content: ""; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background: var(--page); background-image: var(--page-gradient);
}
.fx-aurora {
  display: none; position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%;
  /* Deliberately without an alpha rule of its own: the renderer's MCAP
     (68/255) is the ceiling, as the 00.01 canvas composites it (D49 Phase 1). */
  pointer-events: none; mix-blend-mode: screen; filter: blur(4px) saturate(112%);
}
.app-shell[data-amb="aurora"] .fx-aurora { display: block; }
/* Light theme paints the curtain's own hue INTO the pale ground (founder,
   2026-09-04): the renderer multiplies, so the stylesheet composites as multiply. */
[data-theme="light"] .app-shell[data-amb="aurora"] .fx-aurora { mix-blend-mode: multiply; opacity: .9; filter: blur(5px) saturate(104%); }
/* THE TWO CSS GROUNDS the 00.01 canvas draws with the tokens' own colours
   (founder, 2026-09-04). Both stay well under a third of full alpha at their
   brightest and blur hard; the contrast guard measures the brightest pixel. */
[data-fx-orbs] { display: none; position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.app-shell[data-amb="blob"] [data-fx-orbs], .app-shell[data-amb="lights"] [data-fx-orbs] { display: block; }
[data-fx-orb] { position: absolute; border-radius: 999px; filter: blur(30px); }
/* blob — three drifting orbs, full-bleed. */
[data-fx-orb="a"] { width: 520px; height: 520px; left: -90px; top: -140px; background: radial-gradient(circle, color-mix(in srgb, var(--action) 26%, transparent) 0%, transparent 68%); animation: fx-drift-a 26s ease-in-out infinite; }
[data-fx-orb="b"] { width: 460px; height: 460px; right: -60px; top: 180px; background: radial-gradient(circle, color-mix(in srgb, var(--ok-fg) 18%, transparent) 0%, transparent 68%); animation: fx-drift-b 31s ease-in-out infinite; }
[data-fx-orb="c"] { width: 400px; height: 400px; left: 38%; bottom: -160px; background: radial-gradient(circle, color-mix(in srgb, var(--wait-fg) 16%, transparent) 0%, transparent 68%); animation: fx-drift-c 23s ease-in-out infinite; }
@keyframes fx-drift-a { 0%, 100% { transform: none; } 50% { transform: translate(70px, 50px) scale(1.08); } }
@keyframes fx-drift-b { 0%, 100% { transform: none; } 50% { transform: translate(-80px, -60px) scale(1.06); } }
@keyframes fx-drift-c { 0%, 100% { transform: none; } 50% { transform: translate(50px, -70px) scale(1.1); } }
/* lights — one broad band across the top and nothing else: four very large,
   very soft ellipses, one dominant green with a teal support and a violet
   whisper, drifting a few percent over one to three minutes, masked out of
   the lower half so the working set sits on clean ground. */
.app-shell[data-amb="lights"] [data-fx-orbs] {
  opacity: .9;
  -webkit-mask-image: linear-gradient(180deg, black 0%, black 34%, color-mix(in srgb, black 35%, transparent) 62%, transparent 84%);
  mask-image: linear-gradient(180deg, black 0%, black 34%, color-mix(in srgb, black 35%, transparent) 62%, transparent 84%);
}
.app-shell[data-amb="lights"] [data-fx-orb] { border-radius: 0; mix-blend-mode: screen; will-change: transform, opacity; }
.app-shell[data-amb="lights"] [data-fx-orb="a"] { width: 150%; height: 90%; left: -25%; top: -22%; right: auto; bottom: auto; background: radial-gradient(ellipse 46% 52% at 44% 52%, color-mix(in srgb, var(--ok-fg) 34%, transparent) 0%, color-mix(in srgb, var(--ok-fg) 14%, transparent) 44%, transparent 74%); filter: blur(90px); animation: fx-aur-a 118s ease-in-out infinite; }
.app-shell[data-amb="lights"] [data-fx-orb="b"] { width: 170%; height: 100%; left: -35%; top: -14%; right: auto; bottom: auto; background: radial-gradient(ellipse 52% 46% at 62% 46%, color-mix(in srgb, var(--action) 26%, transparent) 0%, transparent 70%); filter: blur(110px); animation: fx-aur-b 143s ease-in-out infinite; }
.app-shell[data-amb="lights"] [data-fx-orb="c"] { width: 90%; height: 64%; left: 12%; top: -16%; right: auto; bottom: auto; background: radial-gradient(ellipse 40% 56% at 50% 48%, color-mix(in srgb, var(--ok-fg) 30%, transparent) 0%, transparent 66%); filter: blur(72px); animation: fx-aur-c 97s ease-in-out infinite; }
.app-shell[data-amb="lights"] [data-fx-orb="d"] { width: 160%; height: 58%; left: -30%; top: -26%; border-radius: 0; background: radial-gradient(ellipse 50% 60% at 46% 60%, color-mix(in srgb, var(--violet-fg) 22%, transparent) 0%, transparent 72%); filter: blur(120px); animation: fx-aur-d 161s ease-in-out infinite; }
[data-theme="light"] .app-shell[data-amb="lights"] [data-fx-orb] { mix-blend-mode: multiply; opacity: .4; }
[data-theme="light"] .app-shell[data-amb="lights"] [data-fx-orb="d"] { opacity: .35; }
@keyframes fx-aur-a { 0%, 100% { transform: translate(-3%, 0) scale(1); opacity: .85; } 50% { transform: translate(4%, 1.5%) scale(1.07); opacity: 1; } }
@keyframes fx-aur-b { 0%, 100% { transform: translate(3%, 1%) scale(1.04); opacity: .7; } 50% { transform: translate(-4%, -1%) scale(1); opacity: .95; } }
@keyframes fx-aur-c { 0%, 100% { transform: translate(2%, 0) scale(1.02); opacity: .6; } 50% { transform: translate(-5%, 2%) scale(1.1); opacity: .9; } }
@keyframes fx-aur-d { 0%, 100% { transform: translate(0, 0) scale(1); opacity: .7; } 50% { transform: translate(-4%, 1%) scale(1.08); opacity: .95; } }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .app-shell[data-amb="aurora"] .fx-aurora { mix-blend-mode: multiply; opacity: .9; filter: blur(5px) saturate(104%); }
  :root:not([data-theme="dark"]) .app-shell[data-amb="lights"] [data-fx-orb] { mix-blend-mode: multiply; opacity: .4; }
  :root:not([data-theme="dark"]) .app-shell[data-amb="lights"] [data-fx-orb="d"] { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .app-shell[data-amb="lights"] [data-fx-orb], .app-shell[data-amb="blob"] [data-fx-orb] { animation: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .app-shell .fx-aurora, .app-shell [data-fx-orbs] { display: none; }
}

/* The depth ladder reads semantic column identity, never DOM position. In
   compact mode the staged panes need their existing opaque page ground so a
   hidden navigator cannot show through the active working set. */
@container shell (min-width: 720px) {
  .app-shell[data-fx~="depth"] .pane-navigator { background: var(--wa03); }
  .app-shell[data-fx~="depth"] .pane-working-set { background: var(--wa05); box-shadow: inset 1px 0 var(--wa06); }
  .app-shell[data-fx~="depth"] .pane-record { background: var(--wa08); box-shadow: inset 1px 0 var(--wa10); }
}

/* Chain index numbers the actual universal rows in each independently
   scrolling pane. CSS counters mean insertion/removal cannot stale an index. */
.app-shell[data-fx~="chain-index"] [data-chain-pane] { counter-reset: clerim-chain; }
.app-shell[data-fx~="chain-index"] [data-chain-pane] [data-chain-anchor] {
  counter-increment: clerim-chain; position: relative; padding-right: 32px;
}
.app-shell[data-fx~="chain-index"] [data-chain-pane] [data-chain-anchor]::after {
  content: counter(clerim-chain, decimal-leading-zero); position: absolute; right: 7px; top: 50%; pointer-events: none;
  transform: translateY(-50%); font: 600 calc(8px * var(--ts))/1 var(--font-mono);
  color: var(--text-muted); opacity: .62;
}

/* Real unit refs, emitted only by Directory's unit result set. */
[data-unit-strip] { display: none; }
.app-shell[data-fx~="unit-strip"] [data-unit-strip] {
  display: flex; gap: 4px; overflow-x: auto; padding: 4px 0 9px; margin-bottom: 8px;
  border-bottom: .5px solid var(--border);
}
.app-shell[data-fx~="unit-strip"] [data-unit-strip-item] {
  flex: 0 0 auto; min-width: 34px; padding: 5px 7px; text-align: center;
  border: .5px solid var(--ok-fg); border-radius: var(--r-sm); background: var(--ok-bg);
  color: var(--ok-fg); font: 650 calc(9px * var(--ts))/1.2 var(--font-mono);
}

@media (prefers-reduced-motion: reduce) {
  .app-shell[data-fx~="chain-index"] [data-chain-pane] [data-chain-anchor]::after { transform: translateY(-50%); }
}

/* ---------- header (compact: one 44px line) ---------- */
.shell-header {
  flex: none;
  display: flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 10px;
  /* overflow is VISIBLE, where it used to be hidden: a header button's name
     drops out from under it on hover (.reveal, primitives.css) and a clipped
     bar would cut the pill in half. Nothing else in here overflows — the
     building label and the workspace switcher each clip and ellipsis
     themselves, which is where that belonged in the first place. */
  min-width: 0; overflow: visible;
  background: var(--glass);
  -webkit-backdrop-filter: blur(20px) saturate(180%); backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 0.5px solid var(--glass-border);
  box-shadow: inset 0 1px 0 var(--glass-highlight);
}

[data-context-row] { transition: background 120ms ease, border-color 120ms ease; }
[data-context-row]:hover:not([aria-disabled='true']) { background: var(--wa12) !important; }

/* ---------- universal search (header slot, after the brand) ---------- */
[data-shell-search] input::placeholder { color: var(--text-muted); }
[data-search-result-item]:hover { background: var(--wa08) !important; }
@container shell (max-width: 719px) { [data-shell-search] { display: none; } }
[data-search-results] {
  -webkit-backdrop-filter: blur(24px) saturate(200%);
  backdrop-filter: blur(24px) saturate(200%);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.34);
}

/* counter chips — always visible, the wedge's vital signs */
.chip {
  font: inherit; font-size: calc(11px * var(--ts)); font-weight: 600; white-space: nowrap;
  position: relative;
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 28px; padding: 2px 9px;
  border-radius: var(--r-pill); border: 0.5px solid var(--wa12);
  background: var(--wa06); color: var(--text-secondary); cursor: pointer;
}
.chip .n { font-weight: 700; color: var(--text); }
.chip.warn { background: var(--warn-bg); border-color: transparent; } .chip.warn .n { color: var(--warn-fg); }
.chip.wait { background: var(--wait-bg); border-color: transparent; } .chip.wait .n { color: var(--wait-fg); }
.chip .chip-label { display: none; }
/* One button shape across the whole chrome: a 16px glyph at reading size, in a
   30px button, with the word revealed on hover. The target is the invisible
   ::after below — the trick .chip and .top-pills [data-nav-item] already use. */
.icon-btn {
  font: inherit; width: 30px; height: 30px; min-width: 30px; position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--text-secondary); font-size: calc(16px * var(--ts)); cursor: pointer;
}
.icon-btn:hover, [data-icon-btn]:hover { background: var(--wa08); }
.shell-header .icon-btn svg, .shell-header [data-icon-btn] svg { width: 16px; height: 16px; }
/* The way into the nav drawer. Mobile only: a wide shell has the rail, a
   regular one has the pills, and neither has a drawer to open. Its reveal
   drops from the LEFT edge — it is the one header button with nothing to its
   left, so a right-anchored pill would hang off the shell.

   It also needs pages to open onto: [data-nav-pages] is set by renderNav when
   a nav has more than one entry, and a single-page role (the unit owner's
   portal) gets no glyph rather than one that opens an empty drawer. */
.shell-header .drawer-btn { display: none; }
.app-shell[data-mode="mobile"][data-nav-pages] .shell-header .drawer-btn { display: inline-flex; }
.shell-header .drawer-btn > .reveal { right: auto; left: 0; transform-origin: top left; }

/* ---------- offline banner ---------- */
.offline-banner {
  flex: none; display: flex; align-items: center; gap: 8px;
  min-height: 32px; padding: 4px 12px;
  font-size: calc(11.5px * var(--ts)); font-weight: 600; color: var(--wait-fg, var(--text));
  background: var(--wait-bg, var(--wa08));
  border-bottom: 0.5px solid var(--border);
}
.offline-banner .dot { width: 7px; height: 7px; border-radius: 999px; background: currentColor; flex: none; }
.offline-banner .spacer { flex: 1; }
.offline-banner button {
  font: inherit; font-size: calc(11px * var(--ts)); font-weight: 700; color: inherit;
  min-height: 24px; padding: 2px 12px; border-radius: 999px;
  border: 0.5px solid currentColor; background: transparent; cursor: pointer;
  position: relative;
}
.offline-banner button::after { content: ""; position: absolute; inset: -10px; }

/* ---------- body grid ---------- */
/* ONE ROW, AND EVERY CHILD IN IT.

   FOUND IN PRODUCTION, and it is the worst kind of CSS defect: nothing was
   wrong with any single rule. The collapse controls (A20) are grid items
   pinned to `grid-row: 1`, which is the only way to place a control that has
   eight possible corners. The PANES had no row of their own, so
   auto-placement found row 1 already occupied across columns 2–4 and put
   them in an implicit SECOND row — and a grid whose rows are all `auto`
   distributes free space between them, so the shell rendered its columns
   starting halfway down the screen.

   The fix is to say what was always meant: this grid has ONE row, it is the
   whole height, and everything in the body shares it. `> *` rather than a
   list of the four children, because a surface PORTALS its panes in here and
   a rule that names them would have to name the next one too.

   `minmax(0, 1fr)` and not `1fr`: a `1fr` row floors at its content's
   min-content height, which for a pane full of cards is taller than the
   shell — and the body would scroll instead of the panes. */
.shell-body {
  flex: 1; min-height: 0; position: relative;
  display: grid; grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
}
.shell-body > * { grid-row: 1; }
.pane { container-type: inline-size; min-width: 0; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 12px; }
[data-navigator-compact] { display: none; }

/* ---------- the column handles (ADR-0015 D2) ----------
   Two resize controls, absolutely positioned on their column boundaries and
   OUTSIDE the grid flow. Their offsets are computed from the SAME tokens the
   grid templates read, so a handle cannot drift off its boundary however the
   widths change — drag, preference or token. Hidden at rest; each @container
   block that adds a column shows its handle, below. The box, the cursor and
   the pointer contract are the component's (ColumnHandle.tsx, RN-authored);
   this file owns placement, the ladder and the states RN cannot spell.
   The 10px box starts AT the boundary and extends into the column to the
   right (its own padding), never across it: the pane to the left ends in a
   scrollbar on platforms that draw one, and a box centred on the boundary
   covered that scrollbar's last pixels. */
[data-col-handle] { display: none; }
/* The collapse controls hide at rest for the same reason and in the same
   place: each @container step below shows the ones whose column it adds.
   Declared HERE rather than with the rest of their rules at the foot of
   this file, because a `display: none` AFTER the ladder would win over it
   and hide every control at every width. Their placement, states and strip
   are with the folded-column block. */
[data-col-controls] { display: none; }
[data-col-handle="navigator"] { left: calc(var(--rail-w) + var(--navigator-w)); }
[data-col-handle="record"] { right: calc(var(--record-w) - 10px); }
[data-col-handle]:hover [data-col-line],
[data-col-handle]:focus-visible [data-col-line],
[data-col-handle][data-drag="1"] [data-col-line] { background: var(--action); }
[data-col-handle]:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
[data-col-handle] [data-col-line] { transition: background 120ms ease; }

/* detail header back control — renders only when the pane is stacked */
.detail-back, [data-back="1"] {
  font: inherit; font-size: calc(13px * var(--ts)); font-weight: 600;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap); padding: 0 12px 0 6px; margin: 0 0 6px -6px;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--action-tint-text); cursor: pointer;
}

/* ---------- the two nav renderings (one model) ----------
   The rail at wide, the pills at regular, and the drawer on a phone. There
   used to be a fourth — a tab bar carrying only the active group — and it was
   a second, smaller copy of the drawer, which already lists every page with
   its name in full. It went out with the chrome redesign (2026-08-13). */
.nav-rail {
  display: none;
  /* gap 4 on a 44px button is a 48px pitch (the mockup rail: 44px buttons,
     gap 4), which is what makes the 44px hit area below real instead of
     clipped by its neighbour. */
  flex-direction: column; align-items: center; gap: 4px;
  width: var(--rail-w); padding: 8px 0;
  border-right: 0.5px solid var(--border);
}
/* Icon-only, at the same reading size as the header and the dock. The label
   is still emitted — it is a .reveal now (primitives.css), positioned rather
   than stacked under the glyph — so nothing was removed from the accessibility
   tree, and font-size:0 only stops the word taking layout. */
.nav-rail [data-nav-item] {
  font: inherit; width: 44px; min-height: 44px; height: 44px; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0;
  border: 0; border-radius: 14px; background: transparent;
  color: var(--text-muted); cursor: pointer; font-size: 0; font-weight: 600;
}
.nav-rail [data-nav-item] .glyph { font-size: calc(20px * var(--ts)); line-height: 1; }
.nav-rail [data-nav-item] .glyph svg { width: 20px; height: 20px; }
.nav-rail [data-nav-item][aria-current="page"] { background: var(--wa12); color: var(--text); box-shadow: inset 0 1px 0 var(--wa18); }

/* ---------- the rail's FULL mode (ADR-0015 D2) ----------
   The same rail, widened, with the same label laid out inline instead of
   floating. Nothing is added to the markup and nothing is taken away: the
   word is on the button in both modes (Nav.tsx emits one `[data-reveal]`
   either way), which is why switching moves no node and loses no word. What
   changes is presentation, and presentation is this file's.

   `--rail-w` is overridden rather than hard-coded, so everything that reads
   it follows for free — the grid template above, and the column handles,
   whose offsets and room arithmetic are computed from the same token. */
.app-shell[data-nav-mode="full"] .nav-rail { align-items: stretch; padding: 8px; }
.app-shell[data-nav-mode="full"] .nav-rail [data-nav-item] {
  width: auto; flex-direction: row; justify-content: flex-start; gap: 10px;
  padding: 0 10px; font-size: calc(13px * var(--ts));
}
/* The label stops being a pill and becomes type in a row: static, opaque,
   no glass, no shadow, no transform. Everything [data-reveal] does to float
   is undone here rather than made conditional there, because the pill is the
   default and this is the exception. */
.app-shell[data-nav-mode="full"] .nav-rail [data-reveal] {
  position: static; opacity: 1; transform: none; pointer-events: auto;
  padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  font-size: inherit; font-weight: inherit; letter-spacing: inherit;
  overflow: hidden; text-overflow: ellipsis;
}
.app-shell[data-nav-mode="full"] .nav-rail [data-rail-rule] { width: auto; align-self: stretch; }
/* ---------- the 44px target, MEASURED rather than declared ----------
   A 44x44 ::after on a 34px button is only 44px of TARGET if the next button
   is 44px away. It was not. The rail sat at gap 2 (a 36px pitch) and the
   header at gap 8 (38px), so adjacent hit areas OVERLAPPED and the later
   sibling won the overlap — what a thumb actually got was the pitch, not the
   rectangle. Measured on the built shell: rail 35px, dock 41px, header 37px,
   against a 44px invariant that three comments claimed was intact.

   So the PITCH is what gets set, in all three. The rail spaces at gap 10 and
   the dock at gap 8 (34 + 10, 36 + 8). The header could not afford it while it
   carried nine controls — nine at a 44px pitch need 396px of a 430px phone
   before the brand and the building name — and it does not carry nine any
   more: the theme toggle and sign-out became rows in Settings (founder,
   2026-08-13), which is what bought the six pixels. `.icon-btn + .icon-btn`
   adds them to the header's own 8px gap, so ADJACENT chrome buttons sit 44
   apart while the brand and the building label keep the tighter rhythm.

   test/icon-chrome.structural.test.ts computes button + gap rather than
   reading the declaration, because the declaration is what hid this. */
.icon-btn::after, [data-icon-btn]::after, .nav-rail [data-nav-item]::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: var(--tap); height: var(--tap); transform: translate(-50%, -50%);
}
.shell-header .icon-btn + .icon-btn,
.shell-header .icon-btn + [data-icon-btn],
.shell-header [data-icon-btn] + .icon-btn,
.shell-header [data-icon-btn] + [data-icon-btn] { margin-left: 6px; }
/* Groups are a hairline in the rail, where an uppercase caption over a 34px
   glyph was more type than the thing it divided. The drawer keeps its words —
   it has the width, and a phone's only nav should never be a rebus. */
.nav-rail [data-rail-rule] { flex: none; width: 20px; height: 0; border-top: 0.5px solid var(--wa12); margin: 0; }

.top-pills {
  display: none;
  flex-wrap: wrap; gap: 4px; margin: 12px auto 0; padding: 4px;
  width: calc(100% - 24px); max-width: var(--col-max);
  background: var(--wa05); border: 0.5px solid var(--wa08); border-radius: var(--r-lg);
}
/* THE LABEL INHERITS ITS TYPE, and this rule is load-bearing rather than
   tidy. react-native-web's Text base class sets the `font` SHORTHAND
   (`font: 14px System`), which does not just set a family — it resets SIZE
   and LINE-HEIGHT too, and 14px is a constant. The item above sizes itself in
   `calc(Npx * var(--ts))`, so without this the nav's words stopped growing
   with the reader's text-size preference while everything around them grew:
   a real accessibility regression, invisible in a screenshot, caught by the
   a11y scan's scale pass (7 of 107 elements pinned at 14px). Specificity, not
   order, is what settles it — 0,3,0 against RNW's atomic 0,1,0. */
.top-pills [data-nav-item] [data-nav-label],
.drawer-panel [data-nav-item] [data-nav-label] { font: inherit; }
.top-pills [data-nav-item] {
  font: inherit; font-size: calc(12.5px * var(--ts)); font-weight: 500; position: relative;
  min-height: 32px; padding: 5px 14px; border: 0; border-radius: var(--r-md);
  background: transparent; color: var(--text-muted); cursor: pointer;
  /* renderNav paints every nav entry as an <a>, and only the drawer said so —
     the pills have been underlined since they were ported. PRE-EXISTING, and
     visible enough to fix while the pills are the operator console's phone nav
     (they were previously never seen below 720px, which is why nobody had). */
  text-decoration: none;
}
/* compact visuals, full-size touch targets: invisible expanded hit area */
.chip::after, .top-pills [data-nav-item]::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: var(--tap); transform: translateY(-50%);
}
.top-pills [data-nav-item][aria-current="page"] { background: var(--wa12); color: var(--text); }

/* ---------- collapsed companion strip (56px) ---------- */
.collapse-strip { display: none; }
.app-shell[data-collapsed="1"] { height: 56px !important; min-height: 56px; }
.app-shell[data-collapsed="1"] .shell-header,
.app-shell[data-collapsed="1"] .shell-body,
.app-shell[data-collapsed="1"] .top-pills { display: none; }
.app-shell[data-collapsed="1"] .collapse-strip {
  display: flex; align-items: center; gap: 6px; height: 56px; padding: 0 10px; overflow-x: auto;
}
.collapse-strip .chip { flex: none; }
.collapse-strip .chip .chip-label { display: inline; font-weight: 500; color: var(--text-muted); }

/* ---------- size-class badge (dev chrome) ---------- */
.size-badge {
  position: absolute; right: 10px; bottom: 64px; z-index: 8;
  font-size: calc(10px * var(--ts)); font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 3px 10px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 0.5px solid var(--glass-border); color: var(--text-secondary);
  pointer-events: none;
}
.size-badge span { display: none; }
.size-badge .b-compact { display: inline; }
.app-shell[data-collapsed="1"] .size-badge { display: none; }

/* very narrow companion: numbers-only chips, mark-only brand */
@container shell (max-width: 479px) {
  [data-counter-chips] .chip .chip-short { display: none; }
  [data-shell-brand-word] { display: none; }
  [data-shell-building] { max-width: 90px; }
}

/* ---------- the navigator, as the mockup draws it (visual pass, 2026-09-03) ----------
   A 17px title, 11px uppercase group captions, flat 34px rows carrying the
   count in mono at the right edge, the current row on a wa12 tint. The DOM is
   the host's — same words, same counts, same order, same hooks — and only the
   presentation keys on the pane. Specificity 0,3,0 over react-native-web's
   atomic 0,1,0 on purpose, the trick the label rules below already use. */
.pane-navigator [data-navigator-header] [data-page-header] [role="heading"] {
  font-size: calc(17px * var(--ts)); font-weight: 600; letter-spacing: -0.02em;
}
.pane-navigator [data-card]:has(> [data-section]) {
  background: transparent; border: 0; box-shadow: none; padding: 0 2px; margin: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.pane-navigator [data-card]:has(> [data-section]) [role="heading"] {
  font-size: calc(11px * var(--ts)); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted); margin: 16px 0 6px 10px; line-height: 1.4;
}
.pane-navigator [data-card]:has(> [data-section]) > [role="heading"]:first-child { margin-top: 4px; }
.pane-navigator [data-section] {
  /* The canvas draws a 34px row. The tap floor is the design system's one
     invariant that density may not spend (tokens.css, DENSITY), so the row
     keeps the canvas's type and tint at the density target's height. */
  min-height: var(--tap); padding: 5px 10px; margin-bottom: 0; gap: 8px;
  background: transparent; border: 0; border-radius: 10px; color: var(--text-secondary);
}
.pane-navigator [data-section]:hover { background: var(--wa06); color: var(--text); }
.pane-navigator [data-section][aria-current="true"] { background: var(--wa12); color: var(--text); }
.pane-navigator [data-section] [data-section-label] {
  font-size: calc(12.5px * var(--ts)); font-weight: 500; color: inherit; line-height: 1.4;
}
.pane-navigator [data-section][aria-current="true"] [data-section-label] { font-weight: 600; }
.pane-navigator [data-section] [data-section-count] {
  font-family: var(--font-mono); font-size: calc(10.5px * var(--ts)); font-weight: 500;
  color: var(--text-muted); background: transparent; padding: 0; line-height: 1.4;
}
/* Chat's mode control rides the navigator as the mockup's segmented pair. */
.pane-navigator [data-modes] { margin: 2px 0 8px; }

/* ---------- the working set, as the mockup draws it (visual pass, 2026-09-03) ----------
   A 16px title beside the counter band, then rows straight on the pane: a
   card whose body is rows loses its frame and its heading becomes a group
   caption. Forms, editors and records keep their card. Filter and mode
   choices read as the mockup's pill row rather than a segmented block. */
.pane-working-set [data-detail-header] [role="heading"] {
  font-size: calc(16px * var(--ts)); font-weight: 600; letter-spacing: -0.02em;
}
.pane-working-set [data-card]:has(> [data-fx-row]),
.pane-working-set [data-card]:has(> [data-section]) {
  background: transparent; border: 0; box-shadow: none; border-radius: 0;
  padding: 0; margin: 0 0 14px;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.pane-working-set [data-card]:has(> [data-fx-row]) > [role="heading"],
.pane-working-set [data-card]:has(> [data-section]) > [role="heading"] {
  font-size: calc(11px * var(--ts)); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted); margin: 6px 0 8px; line-height: 1.4;
}
.pane-working-set [data-modes] {
  display: flex; flex-wrap: wrap; gap: 6px; padding: 0; margin: 0 0 10px;
  background: transparent; border: 0; box-shadow: none;
}
.pane-working-set [data-modes] > [role="button"] {
  flex: 0 0 auto; min-height: 28px; padding: 3px 10px; border-radius: var(--r-pill);
  background: var(--wa08); box-shadow: none;
}
.pane-working-set [data-modes] > [role="button"] > div { font-size: calc(11px * var(--ts)); font-weight: 600; }
.pane-working-set [data-modes] > [role="button"][aria-current="true"],
.pane-working-set [data-modes] > [role="button"][aria-pressed="true"] { background: var(--action-tint); }
.pane-working-set [data-modes] > [role="button"][aria-current="true"] > div,
.pane-working-set [data-modes] > [role="button"][aria-pressed="true"] > div { color: var(--action-tint-text); }

/* ---------- a narrow working set (the 720 px reading) ----------
   At 720 the working set is what the outer pair leaves it — about 108 px
   — and a row's title column collapses to nothing beside its action, which
   makes the row a thousand pixels tall. Below 240 px a row wraps its action
   under its title, and the counter band reserves the control cluster
   VERTICALLY rather than sideways, so neither can sit under the controls. */
@container (max-width: 240px) {
  .pane-working-set [data-fx-row] { flex-wrap: wrap; }
  .pane-working-set > [data-workset-header] { padding-right: 0; padding-top: var(--tap); }
}

/* ---------- column 3's header band ----------
   The counters, as the mockup's 28px chips: a bold number, its label, on the
   working set's first row. A linked counter is a button wearing the chip. */
/* A 28px chip, as the canvas draws it, with the same invisible expander the
   header chips carry, and a row pitch of one tap so neighbours do not share
   the target. */
.pane-working-set > [data-workset-header] [data-counter-strip] { gap: 6px; row-gap: calc(var(--tap) - 28px); }
.pane-working-set > [data-workset-header] [data-counter-link] { position: relative; }
.pane-working-set > [data-workset-header] [data-counter-link]::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: var(--tap); transform: translateY(-50%);
}
/* Column four's caption, as the canvas draws it: THE RECORD over the record's
   own top, decoration only — the pane's accessible name already says it. */
.shell-body > .pane-record:has([data-record-top], [data-detail-header])::before {
  content: "The record"; display: block; margin: 0 0 6px;
  font-size: calc(10.5px * var(--ts)); font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-muted); line-height: 1.4;
}
.pane-working-set > [data-workset-header] [data-counter-link] { cursor: pointer; }
.pane-working-set > [data-workset-header] [data-counter-link]:hover { background: var(--wa10); }
.pane-working-set > [data-workset-header] [data-counter-link]:focus-visible { outline: 2px solid var(--action); outline-offset: 2px; }

/* ============ semantic four-column ladder ============ */
.shell-body > .pane-navigator { grid-column: 1; }
.shell-body > .pane-working-set {
  position: absolute; inset: 0; z-index: 3;
  background: var(--page); background-image: var(--page-gradient);
  transform: translateX(8%); opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform .18s ease, opacity .18s ease;
}
.shell-body > .pane-record {
  position: absolute; inset: 0; z-index: 4;
  background: var(--page); background-image: var(--page-gradient);
  transform: translateX(8%); opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform .18s ease, opacity .18s ease;
}
.shell-body > .pane-working-set[data-full="1"] {
  position: static; inset: auto; grid-column: 1 / -1; z-index: auto;
  transform: none; opacity: 1; visibility: visible; pointer-events: auto;
}
.app-shell[data-stack="workset"] .shell-body > .pane-working-set,
.app-shell[data-stack="record"] .shell-body > .pane-working-set,
.app-shell[data-stack="record"] .shell-body > .pane-record {
  transform: none; opacity: 1; visibility: visible; pointer-events: auto;
}

@container shell (min-width: 720px) {
  .size-badge { bottom: 10px; }
  /* THE HARD FLOOR (founder, 2026-09-03): the working set's track never
     drops under --workset-min; when the row cannot fit, the body scrolls
     sideways rather than folding the pane. */
  .shell-body { grid-template-columns: var(--rail-w) var(--navigator-w) minmax(var(--workset-min), 1fr); overflow-x: auto; }
  .nav-rail { display: flex; grid-column: 1; }
  .shell-body > .pane-navigator {
    position: static; grid-column: 2; display: block;
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    border-right: 0.5px solid var(--border);
  }
  .shell-body > .pane-working-set {
    position: static; grid-column: 3; z-index: auto; display: block;
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    background: transparent; background-image: none;
  }
  .shell-body > .pane-record {
    position: static; grid-column: 4; z-index: auto; display: block;
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    background: transparent; background-image: none;
    border-left: 0.5px solid var(--border);
  }
  .shell-body > .pane-working-set[data-full="1"] { grid-column: 2 / -1; }
  .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--navigator-w) minmax(var(--workset-min), 1fr) var(--record-w);
  }
  /* Chat's floor is wider: eleven filter counters in two rows. Two rules,
     not a property — shell.css defines no token. */
  .shell-body:has(> .pane-navigator [data-tabs="chat"]) {
    grid-template-columns: var(--rail-w) var(--navigator-w) minmax(var(--workset-min-chat), 1fr);
  }
  .shell-body:has(> .pane-navigator [data-tabs="chat"]):has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--navigator-w) minmax(var(--workset-min-chat), 1fr) var(--record-w);
  }
  .shell-body:has(> .pane-record) [data-col-controls="record"] { display: flex; }
  [data-col-controls="navigator"], [data-col-controls="workset"] { display: flex; }
  [data-col-handle="navigator"] { display: block; }
  .shell-body:has(> .pane-record) [data-col-handle="record"] { display: block; }
  .detail-back, [data-back="1"] { display: none; }

  .app-shell[data-col-navigator="collapsed"] .shell-body {
    grid-template-columns: var(--rail-w) var(--strip-w) minmax(var(--workset-min), 1fr);
  }
  .app-shell[data-col-navigator="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--strip-w) minmax(var(--workset-min), 1fr) var(--record-w);
  }
  .app-shell[data-col-workset="collapsed"] .shell-body {
    grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--strip-w);
  }
  .app-shell[data-col-workset="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--navigator-w) var(--strip-w) minmax(0, 1fr);
  }
  .app-shell[data-col-record="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--navigator-w) minmax(var(--workset-min), 1fr) var(--strip-w);
  }
  .app-shell[data-col-navigator="collapsed"][data-col-workset="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--strip-w) var(--strip-w) minmax(0, 1fr);
  }
  .app-shell[data-col-navigator="collapsed"][data-col-record="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) var(--strip-w) minmax(var(--workset-min), 1fr) var(--strip-w);
  }
  .app-shell[data-col-workset="collapsed"][data-col-record="collapsed"] .shell-body:has(> .pane-record) {
    grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--strip-w) var(--strip-w);
  }
  .app-shell[data-col-navigator="collapsed"] .shell-body > .pane-navigator,
  .app-shell[data-col-workset="collapsed"] .shell-body > .pane-working-set,
  .app-shell[data-col-record="collapsed"] .shell-body > .pane-record { display: none; }
  .app-shell[data-col-workset="collapsed"]:not([data-col-record="collapsed"]) .shell-body > .pane-record {
    display: block;
  }
  /* A collapsed workspace navigator remains a navigator, not an empty
     recovery strip. Its real destinations stay in reading order as glyphs;
     the expanded heading/groups leave layout and each glyph reveals its
     accessible name into the working set. */
  .app-shell[data-col-navigator="collapsed"] .shell-body > .pane-navigator:has([data-navigator-compact]) {
    display: block; position: relative; overflow: visible; padding-left: calc((var(--tap) - 30px) / 2);
    padding-right: calc((var(--tap) - 30px) / 2);
    /* Below the recovery control (20), above ordinary pane content. Equal
       stacking levels let this later, portaled pane intercept Expand. */
    background: var(--surface-1); z-index: 19;
  }
  .app-shell[data-col-navigator="collapsed"] .pane-navigator [data-navigator-expanded] { display: none; }
  .app-shell[data-col-navigator="collapsed"] .pane-navigator [data-navigator-compact] {
    display: flex; position: absolute; inset: calc(12px + var(--tap)) 0 12px;
    overflow-y: auto; overflow-x: hidden; scrollbar-width: thin;
  }
  .pane-navigator [data-navigator-destination] { flex: none; }
  .pane-navigator [data-navigator-destination] svg { width: 16px; height: 16px; }
  [data-reveal][data-navigator-floating-reveal] {
    position: absolute; z-index: 20; opacity: 1;
    transform: translateY(-50%) scale(1); transition: none;
  }
  /* A divider stays captured while its pane crosses the automatic fold line.
     The pane may leave layout; the separator may not. Reversing the same
     gesture therefore restores the pane without a second pointerdown. */
  .app-shell [data-col-handle][data-drag="1"] { display: block; }
  /* A folded pane keeps its divider on the recovery strip. It is both the
     visible seam and the drag target that reopens the pane; hiding it made a
     drag-collapse a one-way operation. */
  .app-shell[data-col-navigator="collapsed"] [data-col-handle="navigator"] {
    display: block; left: calc(var(--rail-w) + var(--strip-w));
  }
  .app-shell[data-col-workset="collapsed"] [data-col-handle="navigator"],
  .app-shell[data-col-workset="collapsed"] .shell-body:has(> .pane-record) [data-col-handle="record"],
  .app-shell[data-col-record="collapsed"] .shell-body:has(> .pane-record) [data-col-handle="record"] {
    display: block;
  }
  /* On a two-pane shell the flexible navigator consumes the vacated working
     set. Its recovery seam therefore belongs at the strip on the far edge,
     not at the navigator token's former position inside that flexible pane. */
  .app-shell[data-col-workset="collapsed"]
    .shell-body:not(:has(> .pane-record)) [data-col-handle="navigator"] {
    left: auto; right: calc(var(--strip-w) - 10px);
  }
  .app-shell[data-col-workset="collapsed"]:not([data-col-record="collapsed"])
    .shell-body:has(> .pane-record) [data-col-handle="record"] {
    right: auto;
    left: calc(var(--rail-w) + var(--navigator-w) + var(--strip-w));
  }
  .app-shell[data-col-navigator="collapsed"][data-col-workset="collapsed"]:not([data-col-record="collapsed"])
    .shell-body:has(> .pane-record) [data-col-handle="record"] {
    left: calc(var(--rail-w) + var(--strip-w) + var(--strip-w));
  }
  .app-shell[data-col-workset="collapsed"][data-col-record="collapsed"] [data-col-handle="navigator"] {
    left: auto; right: calc(var(--strip-w) + var(--strip-w) - 10px);
  }
  .app-shell[data-col-record="collapsed"] [data-col-handle="record"] {
    left: auto; right: calc(var(--strip-w) - 10px);
  }
  .app-shell[data-col-navigator="collapsed"] [data-col-handle="navigator"] [data-col-line],
  .app-shell[data-col-workset="collapsed"] [data-col-handle] [data-col-line],
  .app-shell[data-col-record="collapsed"] [data-col-handle="record"] [data-col-line] {
    background: var(--border-strong);
  }
  .app-shell[data-col-navigator="collapsed"] [data-col-handle="navigator"],
  .app-shell[data-col-workset="collapsed"] [data-col-handle],
  .app-shell[data-col-record="collapsed"] [data-col-handle="record"] {
    /* The collapsed control fills its strip at 20. The 10px seam must win a
       fresh pointerdown, while remaining below every blocking scrim at 30+. */
    z-index: 21;
  }

  /* Controls own a density-sized top band. The panes begin below it, so
     neither the visible button nor its complete target can cover a heading, row or field.
     The cluster sits above ordinary pane chrome so its revealed name cannot
     be cut by an adjacent pane, sticky band or rail. Twenty is deliberately
     below the first blocking overlay plane (.scrim at 30): a fold target must
     never pierce an emergency notice, sheet, settings scrim or palette. */
  [data-col-controls] {
    align-self: start; justify-self: end; z-index: 20; margin: 0;
    min-width: var(--tap); min-height: var(--tap); overflow: visible;
  }
  [data-col-controls][data-col-state] {
    gap: calc(var(--tap) - 30px);
    padding: calc((var(--tap) - 30px) / 2);
  }
  /* The controls share the FIRST ROW with the pane's own heading (founder,
     2026-09-03: inline with the title row, not a band above it). A pane whose
     first row is a heading — the navigator's header, the working set's
     counter band, the record's top or its detail header — shortens that row
     by the cluster's width, and content starts at the mockup's 12px. A pane
     that opens with anything else keeps the reserved band, so the cluster
     still never covers a row, a card corner or a field (D34 item 1). */
  .shell-body > .pane-navigator:not(:has([data-navigator-header])),
  .shell-body > .pane-working-set:not([data-full="1"]):not(:has(> [data-workset-header])),
  .shell-body > .pane-record:not(:has([data-record-top], [data-detail-header])) { padding-top: calc(12px + var(--tap)); }
  .pane-navigator [data-navigator-header] { padding-left: var(--tap); }
  [data-col-controls="navigator"] { justify-self: start; }
  [data-col-controls="navigator"] [data-reveal] { right: auto; left: 0; transform-origin: top left; }
  .pane-working-set > [data-workset-header] { padding-right: calc(2 * var(--tap)); }
  .pane-record [data-record-top], .pane-record [data-detail-header] { padding-right: calc(2 * var(--tap)); }
  [data-col-controls="navigator"] { grid-column: 2; }
  [data-col-controls="workset"] { grid-column: 3; }
  [data-col-controls="record"] { grid-column: 4; }

  .size-badge .b-compact { display: none; }
  .size-badge .b-regular { display: inline; }
}

@container shell (min-width: 1120px) {
  .size-badge .b-regular { display: none; }
  .size-badge .b-wide { display: inline; }
}

@container shell (min-width: 1600px) {
  .size-badge .b-wide { display: none; }
  .size-badge .b-expanded { display: inline; }
}

.shell-body:has(> .pane-working-set[data-full="1"]) [data-col-handle],
.shell-body:has(> .pane-working-set[data-full="1"]) [data-col-controls] { display: none; }
.app-shell[data-col-workset="collapsed"] .shell-body > .pane-working-set[data-full="1"] {
  display: block;
}

/* ---------- the column controls ---------- */
[data-col-controls] [role="button"] {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--r-sm);
  color: var(--text-muted); background: transparent; border: 0; cursor: pointer;
}
[data-col-controls] [role="button"]:hover { background: var(--wa08); color: var(--text); }
[data-col-controls] [role="button"]:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
[data-col-controls] [role="button"][aria-disabled="true"] { opacity: 0.4; cursor: default; }
[data-col-controls] [role="button"][aria-disabled="true"]:hover { background: transparent; color: var(--text-muted); }
[data-col-controls] .glyph svg { width: 16px; height: 16px; }
[data-col-controls] [role="button"]::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: var(--tap); height: var(--tap); transform: translate(-50%, -50%);
}
[data-col-controls] [data-reveal] {
  top: calc(100% + 5px); right: 0;
  transform-origin: top right; transform: translateY(-6px) scale(.92);
}
[data-col-controls] [data-has-reveal]:hover > [data-reveal],
[data-col-controls] [data-has-reveal]:focus-visible > [data-reveal] {
  opacity: 1; transform: none; transition-delay: .1s;
}
[data-col-controls][data-col-state="collapsed"] {
  align-self: stretch; justify-self: stretch; margin: 0;
  align-items: center; padding-top: 8px;
  background: var(--surface-1); border-right: 0.5px solid var(--border);
}
[data-col-controls="record"][data-col-state="collapsed"] {
  border-right: 0; border-left: 0.5px solid var(--border);
}

/* ---------- two sub-columns in the column that got the room ----------
   The founder's items 5 and 6: "When column 3 is collapsed, give column 4 two
   sub columns", and the mirror. Expressed as a CONTAINER query on the pane
   rather than on the fold state — the same behaviour, arrived at more
   honestly: a pane wide enough for two columns of cards gets them, and folding
   a neighbour is simply how a pane becomes that wide. It cannot go stale when
   the widths move, and it needs no rule per combination.

   At rest nothing reaches 940px — at 1600 the working set is 672 and the
   record column 560 — so this fires only in a folded state, which is also why
   the committed baseline does not move for it.

   THE CARDS ARE THE GRID ITEMS, NOT THE PANE'S CHILDREN. A HomeHost detail
   pane holds one `[data-panel]` wrapper per section with only the open one
   shown, so a two-column grid on the pane itself would put that single panel
   in the left column and leave the right one empty. The grid belongs on
   whatever element holds the cards, found with `:has()` — the technique this
   file already uses for the full-span list pane. */
@container (min-width: 940px) {
  /* Never inside the Chat workbench: it divides its own width into 3a and
     3b, and a 3a laid out as two columns wrapped every row letter by letter
     beside an empty second column (founder, 2026-09-06). */
  .pane :has(> [data-card]):not([data-chat-workbench] *) {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px; align-items: start;
  }
  /* A page or section heading spans both, or the first record starts beside
     the title of the thing it is inside. */
  .pane [data-page-header], .pane [data-detail-header] { grid-column: 1 / -1; }
}

/* ---------- a11y / OS preferences ---------- */
@media (prefers-reduced-motion: reduce) {
  .app-shell *, .app-shell { transition: none !important; animation: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
  .shell-header { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--surface-2); }
}

/* ---------- rail group divisions (two-level nav, PORT-PLAN Phase B) ----------
   The rail's uppercase captions became .rail-rule above. The drawer's captions
   are down with the drawer, where they still read. */
.nav-rail [data-nav-hatch] { margin-top: auto; color: var(--text-muted); }

/* ---------- mobile nav drawer (thumb-swipe page list) ----------
   The tab bar carries only the active group; a rightward thumb-swipe slides
   the WHOLE nav in from the left (clerim.js builds it and paints it through
   renderNav like every other holder). Mobile only — wider shells already
   have the rail or the pills. Positioned by insets; the transform is state,
   not position (the .pane-record pattern above). */
.nav-drawer { display: none; }
.app-shell[data-mode="mobile"] .nav-drawer {
  display: block; position: absolute; inset: 0; z-index: 40; pointer-events: none;
}
.nav-drawer .drawer-scrim {
  position: absolute; inset: 0;
  background: rgba(10,12,18,0.55);
  opacity: 0; transition: opacity 0.18s ease;
}
[data-theme="light"] .nav-drawer .drawer-scrim { background: rgba(40,44,52,0.35); }
.nav-drawer .drawer-panel {
  position: absolute; top: 0; bottom: 0; left: 0; width: min(80%, 300px);
  display: flex; flex-direction: column; gap: 2px; overflow-y: auto;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 10px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--surface-2); border-right: 0.5px solid var(--border);
  transform: translateX(-102%); visibility: hidden;
  transition: transform 0.22s ease, visibility 0.22s;
}
.app-shell[data-drawer="open"] .nav-drawer { pointer-events: auto; }
.app-shell[data-drawer="open"] .drawer-scrim { opacity: 1; }
.app-shell[data-drawer="open"] .drawer-panel { transform: none; visibility: visible; }
.drawer-panel [data-nav-item] {
  font: inherit; font-size: calc(13px * var(--ts)); font-weight: 600;
  display: flex; align-items: center; gap: 10px; flex: none;
  min-height: var(--tap); padding: 4px 10px;
  border: 0; border-radius: var(--r-md); background: transparent;
  color: var(--text-secondary); cursor: pointer; text-decoration: none;
}
.drawer-panel [data-nav-item] .glyph { font-size: calc(18px * var(--ts)); line-height: 1; width: 22px; text-align: center; }
.drawer-panel [data-nav-item][aria-current="page"] { background: var(--wa12); color: var(--text); }
.drawer-panel [data-rail-group] {
  /* `font: inherit` FIRST, then this caption's own type over it. The shorthand
     is what react-native-web's Text base class sets, and it carries family AND
     line-height as well as size — so a rule that declares only size and weight
     leaves the caption in -apple-system at line-height normal. The a11y scan
     caught the family; nothing would have caught the line-height, which is the
     same wave of this trap Annex B 1.79.0 records. */
  font: inherit;
  font-size: calc(9px * var(--ts)); font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-muted); opacity: 0.7; margin: 10px 4px 2px;
}
.drawer-panel [data-rail-group]:first-child { margin-top: 0; }
@media (prefers-reduced-motion: reduce) {
  .nav-drawer .drawer-panel, .nav-drawer .drawer-scrim { transition: none; }
}
