/* Sinteric Console — design tokens.

   TRUE NEUTRAL, MONOCHROME. Calm substrate: opaque surfaces separated by tone
   and hairlines, never blur. Ink/graphite is the brand — there is no chromatic
   primary and no hue knob. Three text steps (--ink · --ink-soft · --ink-mute)
   carry every word; --ink-faint is non-text only; the --status-* hues are dots
   and glyphs only.

   Rebased 2026-08-16 (Stage S batch P1, docs/plans/surface-port-plan.md) off
   the inherited warm-cream palette this file used to carry. Until 2026-08-16
   the console was ruled exempt from the design system; that ruling is overturned
   in docs/product/platform-experience-and-assurance.md §2, which also states
   the authority order (ruling wins over design).

   D2 (2026-05-23) pairs each Latin face with a Hangul one per
   [[client_design_locked]] memory: Pretendard with Geist for UI sans,
   Noto Serif KR with Spectral for the editorial serif, D2 Coding with
   Geist Mono for mono. All six binaries are committed under src/fonts/
   and @font-face'd below; licences and pinned upstream sources live in
   fonts/MANIFEST.md, which is their one authority — restating them here
   is how this comment came to claim the wrong licence for one of them.
   The platform-Hangul fallback that used to trail these stacks is gone
   with the binaries' arrival: see the :root type block for why an
   unshipped family in a stack is a per-machine type system. */

/* Self-hosted Hangul fonts. The .woff2 files live in src/fonts/; Vite
   inlines them into dist/assets/ at build time with content-hashed
   names, served by client.AssetServer at /app/assets/* with immutable
   cache. font-display: swap so first paint never blocks on a font
   fetch — Latin glyphs render in Geist immediately, then the page
   swaps to the Hangul font once it's loaded. */

@font-face {
  font-family: "Pretendard Variable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations");
}

@font-face {
  font-family: "Noto Serif KR";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/NotoSerifKR-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "Noto Serif KR";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/NotoSerifKR-SemiBold.woff2") format("woff2");
}

@font-face {
  font-family: "D2 Coding";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/D2Coding-Regular.woff2") format("woff2");
}

/* Self-hosted Latin fonts (console, 2026-05-30). The Claude Design console
   leans on an editorial serif + Geist (UI sans) + Geist Mono; these were
   previously only platform-fallbacks. Self-hosted (latin subset) so first
   paint matches the design with no third-party request — same font-src
   'self' posture as the Hangul fonts above. See fonts/MANIFEST.md for
   licenses + pinned sources.

   THE SERIF IS SPECTRAL, not Newsreader (swapped 2026-08-17, Stage S batch
   P2c). Newsreader was the console's inherited serif from 2026-05-30, before
   the console was in scope for the design system at all; the system rules
   Spectral, and the client has shipped it since. Two serifs across two
   surfaces of one product is the per-machine type system the client's own
   tokens header argues against, one level up. Spectral ships as three STATIC
   faces (400/500/600) rather than a variable axis — the same three files the
   client pins — and the console asks for no serif weight outside them
   (.wordmark 500; .page-title/.gv-layer-title/.serif 400; the rest inherit,
   and Tailwind preflight resets <h1> to the body weight). */
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/Spectral-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/Spectral-Medium.woff2") format("woff2");
}

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/Spectral-SemiBold.woff2") format("woff2");
}

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/Geist-Variable.woff2") format("woff2-variations");
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/GeistMono-Variable.woff2") format("woff2-variations");
}

:root {
  /* type — ONE shape per stack: [shipped Latin face] → [shipped Hangul
     face] → generics, and NOTHING this file does not @font-face. The
     three stacks below are this surface's whole type system.

     Korean is selected by PER-CHARACTER fallback, not by `:lang()`. A
     Korean name inside an English console is still Korean, and a
     `:lang(ko)` scope would leave exactly that on the Latin face.

     WHAT THE PREVIOUS STACKS GOT WRONG was the second half of that rule.
     They named families the console does not ship — "Söhne" second in
     sans, "JetBrains Mono" second in mono, and "Pretendard" / "Noto Sans
     KR" / "Apple SD Gothic Neo" / "Malgun Gothic" trailing behind the
     generics — and a family we do not ship renders only for whoever
     happens to have it installed. A designer with Söhne licensed saw a UI
     nobody else saw. ("Newsreader", "Source Serif 4" and "Nanum Myeongjo"
     left the serif on 2026-08-17 for the same reason; this completes the
     pass P2c named and deliberately did not take, because rewriting these
     two is a change to what every word in the console is set in.)

     MEASURED, not assumed, before the rewrite: nothing here moves what a
     machine without Söhne installed renders. Geist / Geist Mono lead both
     stacks before and after, so Latin is untouched; the four trailing
     Hangul names sat BEHIND the generics and were already unreachable;
     and Hangul already resolved to "Pretendard Variable" / "D2 Coding" —
     the client measured (Chromium/macOS, 2026-08-02) that a generic ahead
     of Pretendard does not steal Hangul, because the engine walks the
     declared list per character first. Promoting Pretendard ahead of the
     generics is kept because it is cheap and does not depend on one
     engine's fallback behaviour, not because it repairs a visible defect.
     The one rendering that DOES change is the machine with Söhne
     installed: glyphs outside Geist's Latin subset used to land there and
     now land on the platform sans. That is the ruling, not a regression. */
  --font-serif: "Spectral", "Noto Serif KR", serif;
  --font-sans: "Geist", "Pretendard Variable", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "Geist Mono", "D2 Coding", ui-monospace, monospace;

  /* scale (comfortable default) */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  --radius-1: 4px;
  --radius-2: 8px;
  --radius-3: 12px;
  --radius-4: 16px;

  /* light theme — TRUE NEUTRAL, monochrome accent (Stage S batch P1).
     Replaces the inherited warm-cream palette, which the design project's own
     integration record says "arrived broken": it was a different design system
     wearing the same token names, and it is why the console never read as the
     same product as the client. Values are design_handoff/tokens.css verbatim,
     with the two places that file lags its own rulings taken from the ruling
     (its header names both).

     Surface ladder, recessed → raised:
       app-bg (the ground)  ·  bg-sunken (sidebar)  ·  bg (raised reading
       surface)  ·  bg-card (cards and dialogs that float)
     FOUR surfaces (the retired --bg-elev duplicated --bg-card). */

  /* The UNIVERSAL CONTENT GROUND. Not a gutter: route content sits directly on
     it, and only deliberate components such as the sidebar and cards rise. tokens.css in
     the handoff still carries the pre-rethink #E4E4E4; rethink.css overrides it
     to #F0F0EF and changes what it means, and that is the value to copy — the
     shipped client already has it. It is also the ground the three text steps
     were MEASURED against: ink 15.26:1 · ink-soft 8.15:1 · ink-mute 4.54:1 ·
     ink-faint 3.03:1. */
  --app-bg: #f0f0ef;
  --bg-sunken: #f1f1f0; /* sidebar fill — recessed                    */
  --bg: #fafafa; /* raised reading surface                     */
  --bg-card: #fefefe; /* cards & dialogs — float on top, off-white  */

  --ink: #1a1a1a; /* 15.26:1 on --app-bg                        */
  --ink-soft: #474747; /* 8.15:1 — page lead and ALL body prose      */
  --ink-mute: #6d6d6d; /* 4.54:1 — labels, meta, placeholders        */
  /* 3.03:1 — NON-TEXT ONLY. Dots, rules, disabled marks. Never a word: at
     3.03:1 it is a UI-component contrast, not a text one. */
  --ink-faint: #8a8a8a;

  --rule: rgba(0, 0, 0, 0.08);
  --rule-strong: rgba(0, 0, 0, 0.14);
  --rule-faint: rgba(0, 0, 0, 0.04);

  --hover: rgba(0, 0, 0, 0.045);
  --press: rgba(0, 0, 0, 0.08);

  /* PII — neutral and quiet, no hue. The marker is a behaviour signal, not
     chrome, so it must not borrow the accent's identity (and under a
     monochrome accent, "derived from the accent hue" meant nothing anyway). */
  --pii-bg: rgba(0, 0, 0, 0.06);
  --pii-line: rgba(0, 0, 0, 0.34);

  /* accent — MONOCHROME: ink/graphite IS the brand. There is no chromatic
     primary, so there is no hue knob: --accent-h is gone rather than pinned,
     because a hue variable that nothing can move is an invitation to move it.
     (The client keeps its own --accent-h only so a legacy runtime setter stays
     a harmless no-op; this console has no such setter — grep is clean.) */
  --accent: #3f3f3f;
  --accent-soft: #eaeaea;
  --accent-line: #c2c2c2;
  --accent-ink: #2b2b2b;

  /* status colors — kept calm, share chroma. THESE ARE DOTS, NEVER WORDS.
     Measured against --app-bg: open 3.42:1 · review 3.80:1 · blocked 3.93:1 ·
     done 4.14:1 — right for a 6px dot (UI-component floor 3:1) and wrong for
     any label (text floor 4.5:1). Put the hue on the dot or the glyph and the
     word in --ink. */
  --status-open: oklch(60% 0.08 240);
  --status-blocked: oklch(58% 0.1 30);
  --status-review: oklch(58% 0.1 80);
  --status-done: oklch(55% 0.06 150);

  /* control sizing — single source for .btn / .chip / .field heights so
     the vertical rhythm holds across surfaces. Density toggle below
     scales these alongside --space-*. */
  --ctl-h-sm: 28px;
  --ctl-h-md: 36px;
  --ctl-h-lg: 44px;
  --chip-h: 24px;
  --field-h: 40px;

  /* hairline border — 0.5px renders as a true sub-pixel line on HiDPI
     and the nearest 1px on standard displays. Used wherever the design
     calls for "0.5px solid". */
  --rule-w-hair: 0.5px;

  /* motion — snap = control transitions; tap = active-press settle. */
  --motion-snap: 0.12s;
  --motion-tap: 0.04s;
}

[data-theme="dark"] {
  /* dark mirrors the light ladder with even value steps, so ground / sidebar /
     panel / card stay distinct instead of clustering into one muddy tone. */
  --app-bg: #0d0d0e;
  --bg-sunken: #161617;
  --bg: #1c1c1e;
  /* Card ruled down from #28282b on 2026-08-08 — the ruled ink ramp was
     measured against --app-bg and did not hold on the raised card in dark
     (--ink-mute 4.26 vs a 4.5 text floor, --ink-faint 2.88 vs a 3.0 mark
     floor). Identical in both SPAs, so it was the design system's values, not
     this console's. Now 4.54 / 3.07. The +3 blue is deliberate and matches the
     rest of the ladder — see the client's tokens.css for the cast ruling. */
  --bg-card: #232326;

  --ink: #ececec;
  --ink-soft: #b9b9b9;
  --ink-mute: #8a8a8a;
  --ink-faint: #6e6e6e; /* non-text only; ≥3:1 on --app-bg */

  --rule: rgba(255, 255, 255, 0.09);
  --rule-strong: rgba(255, 255, 255, 0.16);
  --rule-faint: rgba(255, 255, 255, 0.045);

  --hover: rgba(255, 255, 255, 0.055);
  --press: rgba(255, 255, 255, 0.11);

  --pii-bg: rgba(255, 255, 255, 0.08);
  --pii-line: rgba(255, 255, 255, 0.42);

  --accent: #cacaca;
  --accent-soft: #242427;
  --accent-line: #3e3e40;
  --accent-ink: #dadada;
}



/* base */
* {
  box-sizing: border-box;
}
html,
body,
#root {
  height: 100%;
}
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Hangul does not have word-spacing; mid-word breaks at narrow
     widths are jarring. keep-all preserves syllable integrity for
     Korean paragraphs while leaving Latin word-wrap untouched. Set
     on body so every descendant inherits unless overridden. */
  word-break: keep-all;
  overflow-wrap: break-word;
  /* Geist alt-glyph features per design package (Latin); falls back
     no-op on non-Geist fonts. cv11 = 6/9 with single-storey g; ss01 =
     small-cap-friendly numerals. */
  font-feature-settings: "cv11", "ss01";
}

button {
  font: inherit;
  color: inherit;
}

/* PII span — driven by [data-pii] on root, derived from accent */
.pii {
  font-style: normal;
}
[data-pii="highlight"] .pii {
  background: var(--pii-bg);
  border-bottom: 1px dotted var(--pii-line);
  padding: 0 2px;
  border-radius: 2px;
}
[data-pii="underline"] .pii {
  border-bottom: 1px dotted var(--pii-line);
}
[data-pii="dot"] .pii::before {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--pii-line);
  margin-right: 4px;
  vertical-align: 0.18em;
}
[data-pii="bracket"] .pii {
  background: var(--pii-bg);
  border-radius: 3px;
  padding: 0 4px;
  box-shadow:
    inset 1px 0 0 var(--pii-line),
    inset -1px 0 0 var(--pii-line);
}

/* focus grammar — ONE ring, everywhere, keyboard-only: a 1px INSET hairline in
   --ink. The EDGE goes to ink and THE FILL DOES NOT MOVE, because a grey wash
   on focus reads as `disabled`. That is precisely what the ring here used to
   be: 2px of --accent-line, which under the monochrome palette resolves to the
   grey #c2c2c2 (#3e3e40 dark) — a focus indicator the eye reads as "this
   control is off". Inset, so no surface needs a bespoke offset and no ring is
   clipped by a container that rounds or scrolls.

   THE DOUBLED PSEUDO-CLASS IS DELIBERATE, and it is prophylactic here rather
   than corrective: `styles.css` emits `@tailwind utilities` AFTER this file, so
   a `shadow-*` utility would set the same property at the same specificity and
   erase the ring on whatever it lands on. The client already lost the ring that
   way on every card with a resting shadow. Measured in this console on
   2026-08-16: ZERO Tailwind utility classes in `cp/console/src` (Tailwind is
   configured but only ever emits preflight here), so nothing is erasing it
   today — the hazard is structural and one character cheaper to close now than
   to re-diagnose the first time somebody reaches for a utility.

   `outline: none` is only safe because of the forced-colors block below:
   box-shadow is not painted in Windows High Contrast, so without it keyboard
   focus would vanish for the users who most depend on it. */
:focus-visible:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--ink);
  border-radius: 4px;
}
/* A control that is the INTERIOR of something already presenting as the field
   does not draw a second edge inside the first. `:not(.field)` can never match
   a `.field`, so this rule and the one above never race on specificity. */
input:not(.field):not([type="checkbox"]):not([type="radio"]):focus-visible,
textarea:not(.field):focus-visible,
select:not(.field):not(.selectish):focus-visible {
  box-shadow: none;
}
/* menus: the roving --hover wash IS the focus. A second mark inside an open
   menu reads as two selections. */
[role="menu"] :focus-visible,
[role="menuitem"]:focus-visible {
  outline: none;
  box-shadow: none;
}
@media (forced-colors: active) {
  :focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: 1px;
  }
}

/* Skip-to-content link (WCAG 2.4.1 Bypass Blocks). Visually hidden
   until focused; jumps the keyboard user past the sidebar to the
   route content. */
.skip-link {
  position: absolute;
  left: var(--space-3);
  top: var(--space-3);
  transform: translateY(-200%);
  z-index: 100;
  background: var(--bg-card);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-2);
  font-weight: 500;
  text-decoration: none;
  transition: transform 120ms ease;
}
.skip-link:focus,
.skip-link:focus-visible {
  transform: translateY(0);
}


/* button — small set of variants; classes are referenced across pages
   (SignIn / Agenda / AllWorks / Connect / ConnectModal / Work). Sizing
   ladder uses fixed heights (design `tokens.css` does 28/36/44) so
   buttons share a vertical rhythm with chips and inputs. Density toggle
   still scales the --space-* padding tokens around them. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--ctl-h-md);
  padding: 0 var(--space-4);
  border-radius: var(--radius-2);
  border: var(--rule-w-hair) solid var(--rule-strong);
  background: var(--bg-card);
  color: var(--ink);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.2;
  cursor: pointer;
  transition:
    background var(--motion-snap),
    border-color var(--motion-snap),
    transform var(--motion-tap);
  white-space: nowrap;
}
.btn:hover {
  background: var(--hover);
}
.btn:active {
  transform: translateY(0.5px);
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn-sm {
  height: var(--ctl-h-sm);
  padding: 0 var(--space-3);
  font-size: 13px;
}
.btn-lg {
  height: var(--ctl-h-lg);
  padding: 0 var(--space-5);
  font-size: 15px;
}
.btn-ghost {
  background: transparent;
  border-color: transparent;
}
.btn-ghost:hover {
  background: var(--hover);
}
.btn-primary {
  background: var(--ink);
  color: var(--bg-card);
  border-color: var(--ink);
}
.btn-primary:hover {
  background: var(--ink-soft);
  border-color: var(--ink-soft);
}
/* The one place a status hue carries a word, and it is KEPT rather than
   rebased: the shipped client's `.btn-danger` is byte-identical, and forking
   the destructive button between the two planes to satisfy this batch would
   trade one inconsistency for a worse one. It IS a measured near-miss —
   #fefefe on --status-blocked is 4.45:1 against a 4.5:1 floor at 14px/500 —
   and it belongs to a change that moves both surfaces together, not here. */
.btn-danger {
  background: var(--status-blocked);
  color: #fff;
  border-color: var(--status-blocked);
}
.btn-danger:hover {
  background: var(--status-blocked);
  border-color: var(--status-blocked);
  filter: brightness(0.92);
}

/* chip — small inline state/status label. Fixed height for vertical
   rhythm with .btn and .field. `.chip-dot` is the colored leading dot;
   color flows from the parent's `color` so callers can recolor the chip
   via inline style. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: var(--chip-h);
  padding: 0 var(--space-2);
  border-radius: 999px;
  background: var(--bg-sunken);
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 500;
  border: var(--rule-w-hair) solid transparent;
  white-space: nowrap;
}
.chip-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

/* kbd — keyboard-shortcut hint rendered next to nav items + menu rows */
.kbd {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 2px 5px;
  border-radius: var(--radius-1);
  background: var(--bg-sunken);
  border: var(--rule-w-hair) solid var(--rule);
  color: var(--ink-soft);
  line-height: 1;
}

/* typography helpers — straight from design tokens.css. Tailwind's
   font-serif/font-mono only set font-family, so these layer the
   weight + letter-spacing the design assumes everywhere it uses the
   serif (h1s, section titles, dialog titles, etc.). */
.mono {
  font-family: var(--font-mono);
}
.tabular {
  font-variant-numeric: tabular-nums;
}

/* Shared text-input primitive. Fixed height matches .btn so forms and dialog
   action rows stay aligned. */
.field {
  width: 100%;
  height: var(--field-h);
  padding: 0 var(--space-4);
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-2);
  background: var(--bg-card);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  outline: none;
  transition:
    border-color var(--motion-snap),
    box-shadow var(--motion-snap);
}
/* A focused field speaks the same grammar as every other control: the edge goes
   to ink, the fill does not move. This replaces an --accent-line border plus a
   3px --accent-soft halo — under the monochrome palette both are grey, so a
   focused field read as a DISABLED one and the halo shoved the surround. It has
   to be stated here rather than left to `:focus-visible:focus-visible` because
   `.field:focus` carries equal specificity and lands later in the file: without
   this rule the grey halo simply wins. */
.field:focus,
.field:focus-visible {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* subtle scrollbars — design package convention; only paints on
   WebKit/Blink + Firefox (scrollbar-* properties). */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--rule-strong) transparent;
}
*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background: var(--rule-strong);
  border-radius: 6px;
  border: 3px solid transparent;
  background-clip: content-box;
}

/* animations — fade-up for newly-streamed Section blocks + Plan items,
   fade-in for dialog backdrops + toasts. */
@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
.fade-up {
  animation: fadeUp 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
.fade-in {
  animation: fadeIn 0.35s ease both;
}

/* toast — one pill, rendered by the Toast portal. The design's console pill:
   ink fill, card-coloured word, fully round.

   THE GLYPH IS THE ONLY TINT. The design artifact paints the whole error pill
   `--status-blocked` and leaves the word near-white on it, which measures
   4.45:1 — under the 4.5:1 floor, and a word in a status hue either way. The
   ruling (status hues are dots, never words) wins over the artifact, and the
   shipped client already ships the ruled form under the same sentence: "one
   pill, glyph is the only tint (no tinted borders or text)". Success and error
   stay distinguishable by the glyph the Toast component already renders
   (check / alert) — colour was never carrying that on its own. */
.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px var(--space-4);
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg-card);
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}
.toast svg {
  flex-shrink: 0;
  color: var(--bg-card);
  opacity: 0.7;
}
.toast-error svg {
  color: var(--status-blocked);
  opacity: 1;
}

/* Sinteric Console — chassis.

   Rebased 2026-08-16 (Stage S batch P1) onto the console's own stylesheet
   from the design project of that date. Geometry mirrors the
   client shell so the two planes read as one product: an --app-bg ground, a
   floating 240px sidebar card, and a content panel rounded at its top-left
   corner. Builds on tokens.css custom properties ONLY.

   Three things this file is holding that are easy to undo by accident:

   1. THE SUBSTRATE IS CALM. Opaque surfaces separated by tone and hairlines.
      No `backdrop-filter`, no translucent chrome — the two `saturate(1.2)
      blur(10px)` bars this file used to carry are gone and must not come back.

   2. STATUS HUES ARE DOTS AND GLYPHS, NEVER WORDS. Measured against --app-bg
      they are 3.42–4.13:1: right for a 6px dot, under the floor for a label.
      Every place this file used to tint a word (`.chip-allow`, `.chip-deny`,
      `.verdict`, `.placeholder.is-error`, `.field-error`, `.gw-check.bad`,
      `.bar-*`, `.spark .seg-*`) now puts the hue on a dot or a glyph and the
      word in --ink. Where the design artifact still tints the word, the ruling
      wins — see the note on each.

   3. THE WORDMARK IS NOT APP CHROME. It lives on sign-in only. The top-left of
      the app is the org switcher.

   Where the artifact and a ruling disagree, the ruling wins, and the
   disagreement is named at the rule rather than resolved silently. */

:root {
  color-scheme: light;
}
[data-theme="dark"] {
  color-scheme: dark;
}

/* The shell owns the viewport and each pane owns its own scroll region — which
   is what lets a pane carry a sticky toolbar. This file used to undo
   tokens.css's `html,body,#root{height:100%}` and hand scrolling back to the
   document; that override is gone, so the 100% pin from tokens.css stands. */
html,
body {
  background: var(--app-bg);
}

a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 2px;
}
a:hover {
  color: var(--accent-ink);
  text-decoration-color: var(--ink-mute);
}
/* structural links (nav rows, cards) are UI, not prose — no underline */
a.navtab,
a.card,
a.chip {
  text-decoration: none;
}

/* ---------- app frame ---------- */
/* The application ground is the content surface. Route pages sit directly on
   it; only deliberate components such as the sidebar, cards and dialogs rise
   above it. */
.app {
  display: flex;
  flex-direction: column;
  height: 100vh;
  width: 100%;
  background: var(--app-bg);
  position: relative;
  overflow: hidden;
}
.app.layout-sidebar {
  flex-direction: row;
}
.app.layout-orgs {
  flex-direction: column;
}
.app > main {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
}


/* container widths */
.wrap {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-7) var(--space-6) var(--space-8);
}
.wrap-narrow {
  max-width: 720px;
}

/* ---------- sidebar: flush navigation rail (Flutter workspace sidebar) ---------- */
.sidebar {
  width: 248px;
  flex-shrink: 0;
  margin: 0;
  border: 0;
  border-right: var(--rule-w-hair) solid var(--rule);
  border-radius: 0;
  box-shadow: none;
  background: var(--bg);
  padding: 49px 12px 22px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 45;
}
.sidebar-titlebar-toggle {
  position: absolute;
  left: 12px;
  top: 8px;
  z-index: 50;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  border-radius: 7px;
  cursor: pointer;
  padding: 0;
}
.sidebar-titlebar-toggle:hover {
  background: var(--hover);
}
.app.is-sidebar-collapsed .sidebar {
  display: none;
}
/* Close button for the mobile drawer only. Desktop collapse lives in the
   titlebar toggle, which stays put when the rail is gone. */
.sidebar-brand {
  display: none;
}
.sidebar-close {
  display: none;
}
.sidebar-org {
  padding-bottom: var(--space-4);
}
.sidebar-tier {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-mute);
  padding: 0 7px 3px;
}
.sidebar .sidebar-close {
  display: none;
}

/* org switcher — the top-left of the app */
.orgswitch {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 7px;
  border: 0;
  background: transparent;
  cursor: pointer;
  border-radius: 7px;
  padding: 6px 7px;
  color: var(--ink);
  text-align: left;
}
.orgswitch:hover {
  background: var(--hover);
}
.orgswitch .nm {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.orgswitch .car {
  flex-shrink: 0;
  color: var(--ink-mute);
  transition: transform var(--motion-snap);
}
.orgswitch[aria-expanded="true"] .car {
  transform: rotate(180deg);
}

/* org switcher menu */
.orgmenu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 240px;
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-3);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: var(--space-1);
  z-index: 50;
  transform-origin: top left;
  animation: orgmenu-settle var(--motion-snap) ease-out;
}
@keyframes orgmenu-settle {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(0.99);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .orgmenu {
    animation: none;
  }
  .orgswitch .car {
    transition: none;
  }
}

/* icon button (theme / locale) */
.iconbtn {
  width: 28px;
  height: 28px;
  border-radius: 7px;
  border: 0;
  cursor: pointer;
  flex-shrink: 0;
  background: transparent;
  color: var(--ink-mute);
  display: grid;
  place-items: center;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--motion-snap);
}
.iconbtn:hover {
  background: var(--hover);
  color: var(--ink-soft);
}
.iconbtn svg {
  width: 15px;
  height: 15px;
  display: block;
}
/* An icon button that carries a WORD instead of a glyph (both sign-outs). The
   square is sized for a 15px glyph; without this the label overflows it. One
   modifier rather than the design's `.sidebar-signout`, because the org-list
   bar needs exactly the same thing and two names for one rule is how one of
   them goes stale. */
.iconbtn.has-label {
  width: auto;
  height: 24px;
  padding: 0 7px;
}

/* ---------- nav ---------- */
.sidenav {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.sidenav-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-mute);
  padding: 0 10px var(--space-2);
}
.navtab {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 10px;
  border-radius: 6px;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-size: 13.5px;
  cursor: pointer;
  text-align: left;
  width: 100%;
  text-decoration: none;
}
.navtab:hover {
  background: var(--hover);
  color: var(--ink);
}
/* The active row is marked by tone, not by the accent: there is no chromatic
   primary to mark it with. */
.navtab[aria-current="page"] {
  background: var(--hover);
  color: var(--ink);
  font-weight: 500;
}
.nav-ico {
  color: var(--ink-mute);
  flex-shrink: 0;
}
.navtab[aria-current="page"] .nav-ico {
  color: var(--ink-soft);
}
.nav-label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* kbd hint on hover only — a persistent number on the active row reads as an
   unread badge, which is inbox semantics and retired product-wide. */
.navtab .kbd {
  margin-left: auto;
  opacity: 0;
  background: transparent;
  border-color: transparent;
  transition: opacity 0.1s;
}
.navtab:hover .kbd {
  opacity: 1;
  background: var(--bg-card);
  border-color: var(--rule);
}

/* footer: tools + account */
.sidebar-foot {
  border-top: var(--rule-w-hair) solid var(--rule);
  margin-top: auto;
  padding-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sidebar-tools {
  display: flex;
  align-items: center;
  gap: 4px;
}
.sidebar-account {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.sidebar-email {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  color: var(--ink-mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}



/* ---------- legacy top bar (orgs route, which has no org to switch) ---------- */
.topbar {
  border-bottom: var(--rule-w-hair) solid var(--rule);
  background: var(--bg-card);
  flex-shrink: 0;
}
.topbar .wrap {
  max-width: none;
  padding: 0 var(--space-6);
}
.topbar-inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 48px;
  padding-top: 0;
  padding-bottom: 0;
}
.topbar-spacer {
  flex: 1 1 auto;
}
.topbar-tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.topbar-email {
  font-size: 13px;
  color: var(--ink-mute);
  white-space: nowrap;
}
@media (max-width: 720px) {
  .topbar-email {
    display: none;
  }
}
.mobile-topbar {
  display: none;
}
.sidebar-scrim {
  display: none;
}

/* wordmark — sign-in only, never app chrome */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
/* The mark (static/sinteric-mark.svg, written by docs/design/logos.py) is a
   mask, so it paints in --ink in both themes. */
.wordmark .glyph {
  width: 22px;
  height: 22px;
  background: var(--ink);
  -webkit-mask: url("sinteric-mark.svg") center / contain no-repeat;
  mask: url("sinteric-mark.svg") center / contain no-repeat;
  flex-shrink: 0;
}

/* ---------- responsive: sidebar -> drawer ---------- */
@media (max-width: 900px) {
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 50;
    transform: translateX(-100%);
    transition: transform 0.2s;
    padding-top: 16px;
  }
  .sidebar.is-open {
    transform: none;
  }
  .sidebar-brand {
    display: flex;
    justify-content: flex-end;
    padding-bottom: var(--space-2);
  }
  .sidebar-close {
    display: grid;
  }
  .sidebar-titlebar-toggle {
    display: none;
  }
  .app.is-sidebar-collapsed .sidebar {
    display: flex;
  }
  .sidebar-scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.28);
    z-index: 45;
  }
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    height: 48px;
    padding: 0 var(--space-3);
    border-bottom: var(--rule-w-hair) solid var(--rule);
    background: var(--bg-card);
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 30;
  }
  .app.layout-sidebar {
    padding-top: 48px;
  }
  .mt-org {
    font-size: 13px;
    color: var(--ink-soft);
    margin-left: auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 50%;
  }
}

/* ---------- page / section scaffolding ---------- */
.sr-only {
  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 {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.page-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.page-head-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
}
.page-head-actions > .page-head { flex: 1; min-width: 240px; }
.page-head-actions > form { margin: 0; }
.page-title {
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-weight: 400;
  margin: 0;
}
/* The page lead is body prose, so it is --ink-soft (8.15:1), not --ink-mute. */
.page-intro {
  color: var(--ink-soft);
  font-size: 14.5px;
  line-height: 1.55;
  margin: 0;
  max-width: 640px;
  text-wrap: pretty;
}
.section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.section-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.section-title {
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
}
/* Compact taxonomies inside a card remain eyebrows; section-title is reserved
   for headings that structure a pane. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 0;
}

/* ---------- layout helpers ---------- */
.grow {
  flex: 1;
}
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.row-wrap {
  flex-wrap: wrap;
}
.between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.col {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.form-grid {
  display: grid;
  gap: var(--space-3);
}
.span2 {
  grid-column: 1 / -1;
}
.right {
  text-align: right;
}

/* ---------- text ---------- */
.hint {
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1.5;
  text-wrap: pretty;
}
.tiny {
  font-size: 12px;
}
.muted {
  color: var(--ink-mute);
}
.label,
.lbl {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-soft);
  margin-bottom: 6px;
}
.k {
  color: var(--ink-soft);
  font-size: 13px;
}
.v {
  color: var(--ink);
  font-size: 13px;
  font-weight: 500;
}
.idchip {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--ink-mute);
  background: var(--bg-sunken);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-1);
  padding: 1px 5px;
  white-space: nowrap;
}
.cur {
  color: var(--ink-mute);
  font-size: 13px;
  flex-shrink: 0;
}

/* ---------- cards & tables ---------- */
.card {
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-3);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  overflow: hidden;
}
a.card.row {
  overflow: visible;
  padding: var(--space-4) var(--space-5);
  gap: var(--space-4);
  color: var(--ink);
  min-width: 0;
}
a.card.row .grow {
  min-width: 0;
}
a.card.row .idchip {
  align-self: flex-start;
  width: max-content;
}
.org-initial {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-2);
  background: var(--bg);
  color: var(--ink-soft);
  display: grid;
  place-items: center;
  flex: none;
  font-family: var(--font-serif);
  font-size: 17px;
}
.card-pad {
  padding: var(--space-4) var(--space-5);
}
.table-scroll {
  overflow-x: auto;
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}
.table th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding: 10px var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule);
  white-space: nowrap;
  background: var(--bg-sunken);
}
.table td {
  padding: 11px var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-faint);
  color: var(--ink-soft);
  vertical-align: middle;
}
/* atomic values (dates, ids, versions) never break mid-value — the region
   already scrolls, so a wrapped date buys nothing and costs a row's height */
.table td.mono,
.table td .idchip {
  white-space: nowrap;
}
.table tbody tr:last-child td {
  border-bottom: 0;
}
.table tbody tr:hover td {
  background: var(--hover);
}
.table td:first-child {
  color: var(--ink);
  font-weight: 500;
}
.table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.table .right {
  text-align: right;
}
.cell-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: flex-end;
}
.cell-actions > form { margin: 0; }

/* ---------- chips ---------- */
.chip {
  white-space: nowrap;
}
.chip.is-status {
  background: var(--bg-sunken);
  border-color: var(--rule);
}
.chip.is-warn {
  background: var(--bg-sunken);
  color: var(--ink);
  border-color: var(--rule-strong);
}
.dot-green {
  color: var(--status-done);
}
.dot-yellow {
  color: var(--status-review);
}
.dot-red {
  color: var(--status-blocked);
}
.dot-blue {
  color: var(--status-open);
}
.dot-mute {
  color: var(--ink-mute);
}
/* effect chips: allow is the resting state, deny is the heavier one. Both are
   neutral — these carry a WORD ("Allow" / "Deny"), and a word never takes a
   status hue. Weight and the --pii-bg wash do the work colour used to. */
.chip-allow {
  background: var(--bg-sunken);
  color: var(--ink-soft);
  border-color: var(--rule);
}
.chip-deny {
  background: var(--pii-bg);
  color: var(--ink);
  border-color: var(--rule-strong);
  font-weight: 600;
}

/* ---------- forms ---------- */
/* The sentence is --ink and the ALERT GLYPH carries the hue. The artifact tints
   the whole line --status-blocked, which measures 4.30:1 on --bg at 12.5px —
   under the text floor, and a word in a status hue. The field's own `.is-bad`
   edge plus the glyph carry the signal. */
.field-error {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--ink);
  margin-top: var(--space-2);
}
.field-error svg {
  flex: none;
  color: var(--status-blocked);
}
.field.is-bad {
  border-color: var(--status-blocked);
}
textarea.field {
  height: auto;
  padding: var(--space-3) var(--space-4);
  line-height: 1.5;
  resize: vertical;
  min-height: 64px;
}
textarea.field.mono {
  font-family: var(--font-mono);
  font-size: 13px;
}
.selectish {
  height: 32px;
  padding: 0 28px 0 var(--space-3);
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-2);
  background: var(--bg-card);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236d6d6d' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
/* The chevron is a data URI, so it cannot read currentColor — it needs the dark
   theme's --ink-mute (#8a8a8a) spelled out, or it stays at the light value and
   dims to roughly half the intended contrast on a dark field. */
[data-theme="dark"] .selectish {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238a8a8a' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
}
.segmented {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border-radius: var(--radius-2);
  background: var(--bg-sunken);
  border: var(--rule-w-hair) solid var(--rule);
}
.segmented button {
  border: 0;
  background: transparent;
  cursor: pointer;
  height: 24px;
  padding: 0 8px;
  border-radius: 6px;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-mute);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}
.segmented button:hover {
  color: var(--ink-soft);
}
.segmented button[aria-pressed="true"] {
  background: var(--bg-card);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--ink);
}

/* ---------- empty / error / loading ---------- */
.placeholder {
  padding: var(--space-7) var(--space-5);
  text-align: center;
  color: var(--ink-mute);
  font-size: 13.5px;
  background: var(--bg-card);
  border: var(--rule-w-hair) dashed var(--rule-strong);
  border-radius: var(--radius-3);
}
.ph-title {
  font-size: 14.5px;
  font-weight: 500;
  color: var(--ink-soft);
  margin-bottom: 6px;
}
/* Same rule as .field-error: the glyph is tinted, the sentence is not. */
.placeholder.is-error {
  border-style: solid;
  border-color: var(--rule-strong);
  color: var(--ink);
}
.placeholder.is-error svg {
  color: var(--status-blocked);
}
.skel {
  background: linear-gradient(
    90deg,
    var(--bg-sunken) 25%,
    var(--hover) 50%,
    var(--bg-sunken) 75%
  );
  background-size: 400px 100%;
  border-radius: var(--radius-1);
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer {
  0% {
    background-position: -200px 0;
  }
  100% {
    background-position: 200px 0;
  }
}
.banner {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-2);
  background: var(--bg-sunken);
  border: var(--rule-w-hair) solid var(--rule);
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: var(--space-4);
}
.banner svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--ink-mute);
}
/* A warning banner lifts by SURFACE and weight, not by a colour wash: it comes
   forward onto --bg-card with a stronger rule and full-ink text. */
.banner.is-warn {
  background: var(--bg-card);
  border-color: var(--rule-strong);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.banner.is-warn svg {
  color: var(--ink-soft);
}
/* The HONEST-ABSENCE banner (components/primitives.tsx :: NotWired). Dashed,
   because that is the one border treatment nothing else in this console uses,
   and the whole point of this instrument is that a reader can tell it apart at
   a glance from an empty table (.placeholder) and from a failed read
   (.placeholder.is-error). Three different facts, three different shapes. */
.banner.is-absent {
  background: transparent;
  border-style: dashed;
  border-color: var(--rule-strong);
  color: var(--ink-soft);
  align-items: flex-start;
}
.absent-title {
  color: var(--ink);
  font-weight: 600;
}
/* One absent FIGURE, inline. The dash alone reads as zero; the title/aria say
   which it is. Dotted underline so it does not pass for a value. */
.nofigure {
  color: var(--ink-mute);
  border-bottom: 1px dotted var(--rule-strong);
  cursor: help;
}

/* ---------- configuration table (pages/Dataplanes.tsx) ---------- */
/* One version IN FORCE. Tabular figures so a column of versions lines up and a
   v7 among v8s is visible as a shape before it is read.

   `is-behind` weights the NUMBER and adds the word "behind"; it takes no status
   hue, per the .chip-deny rule above — a word never does. Behind is also not an
   alarm state: a plane one version back for a minute is normal, and only a plane
   that STAYS behind is a finding, which is a judgement a colour cannot make. */
.dp-ver {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.dp-ver.is-behind > span:first-child {
  font-weight: 600;
}

/* ---------- publish bar (components/PublishBar.tsx) ---------- */
.pubbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-3);
  background: var(--bg-card);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.pubbar-chips {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.pubbar-say {
  flex: 1 1 240px;
  max-width: 340px;
  margin: 0 0 0 auto;
  text-align: right;
}
.pubbar-go {
  width: auto;
  justify-content: center;
  flex-shrink: 0;
}
.pubbar-note {
  flex-basis: 100%;
  text-align: right;
  margin: 0;
}
.pubbar-ro {
  flex: 1 1 280px;
  margin: 0;
}
/* Models embeds the same publish semantics inside its narrow review card. The
   card is already the surrounding surface, so it keeps the compact vertical
   treatment while Policy uses the full horizontal action bar above. */
.gw-review .pubbar {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.gw-review .pubbar-say {
  max-width: none;
  margin: 0 0 var(--space-2);
  text-align: left;
}
.gw-review .pubbar-go {
  width: 100%;
}
.gw-review .pubbar-note {
  text-align: center;
  margin-top: var(--space-2);
}

/* A long policy document must not make its commit action discoverable only by
   reaching the final pixel. The bar settles against the pane's scroll edge;
   Models stays exempt because its own review card is already sticky. */
.page.has-pinned-publish .pubbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  flex-wrap: nowrap;
  box-shadow:
    0 -1px 0 var(--rule),
    0 -8px 24px -12px rgba(0, 0, 0, 0.28);
}
@media (max-width: 1180px) {
  .page.has-pinned-publish .pubbar-say {
    display: none;
  }
}
@media (max-width: 720px) {
  .pubbar,
  .page.has-pinned-publish .pubbar {
    position: static;
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
  }
  .pubbar-say {
    display: block;
    max-width: none;
    margin: 0;
    text-align: left;
  }
  .pubbar-go {
    width: 100%;
  }
}

/* ---------- long lists ---------- */
/* Searchable lists keep their controls above the scrolling table. */
.listbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}
.listbar-field {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  max-width: 300px;
}
.listbar-field .field {
  width: 100%;
  min-width: 0;
  height: 32px;
  padding: 0 var(--space-3) 0 36px;
  font-size: 13.5px;
}
.listbar-field > svg {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-mute);
  pointer-events: none;
}
.is-tall {
  max-height: min(56vh, 540px);
  overflow-y: auto;
}
.is-tall .table thead th,
.is-tall .gw-model-head {
  position: sticky;
  top: 0;
  z-index: 2;
}

/* ---------- one-time secret ---------- */
.secret {
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-3);
  background: var(--bg-sunken);
  padding: var(--space-4);
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.secret-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.secret-hint {
  font-size: 12.5px;
  color: var(--ink-mute);
  line-height: 1.5;
  text-wrap: pretty;
}
.secret-value {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-2);
  padding: var(--space-3);
}
.secret-value code {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink);
  word-break: break-all;
  line-height: 1.6;
}
.secret-meta {
  font-size: 11.5px;
  color: var(--ink-mute);
  font-family: var(--font-mono);
}

/* ---------- toasts & dialog ---------- */
/* Bottom-centre, one pill (the pill itself is in tokens.css). */
.toast-stack {
  position: fixed;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  z-index: 80;
  align-items: center;
}
.dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.32);
  z-index: 60;
}
[data-theme="dark"] .dialog-overlay {
  background: rgba(0, 0, 0, 0.55);
}
.dialog-positioner {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 60;
  padding: var(--space-5);
}
.dialog {
  width: 100%;
  max-width: 420px;
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-4);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: var(--space-5);
}
/* A dialog title is not a large title — sans, 17px, semibold. The serif stays
   with the wordmark and the page/stat titles. */
.dialog-title {
  font-size: 17px;
  font-weight: 600;
  margin: 0 0 var(--space-2);
  letter-spacing: -0.01em;
}
.dialog-body {
  color: var(--ink-soft);
  font-size: 14px;
  line-height: 1.55;
  text-wrap: pretty;
}
.dialog-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-5);
}

/* ---------- policy editor ---------- */
/* A rule card. Deny reads heavier than allow — on a screen whose semantics are
   "deny overrides allow", the effect has to be visible before you read it. It
   used to be a 3px chromatic spine on each; the weight now comes from the rule
   colour and the --pii-bg header wash, and allow (the resting state) is plain. */
.rule {
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-3);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  overflow: hidden;
  margin-bottom: var(--space-3);
}
.rule.is-deny {
  border-color: var(--rule-strong);
}
.rule-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule);
  background: var(--bg-sunken);
}
.rule.is-deny .rule-head {
  background: var(--pii-bg);
}
.rule-body {
  padding: var(--space-4);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
.rule-body .span2 {
  grid-column: span 2;
}
.hints-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
}
.policy-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--space-4);
  align-items: start;
}
.sim {
  position: sticky;
  top: calc(46px + var(--space-4));
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
/* The verdict is the one place the old file painted a whole panel in a status
   hue. Same treatment as the rule card it is simulating: neutral panel, the
   deny head carrying the --pii-bg wash, and the glyph the only tint. */
.verdict {
  border-radius: var(--radius-2);
  border: var(--rule-w-hair) solid var(--rule-strong);
  overflow: hidden;
  margin-top: var(--space-4);
}
.verdict-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
  background: var(--bg-sunken);
  border-bottom: var(--rule-w-hair) solid var(--rule);
}
.verdict.deny .verdict-head {
  background: var(--pii-bg);
}
/* The tint the comment above promised. It was only ever applied inline from
   `Policy.tsx`, over the WHOLE head — so the rule said "the glyph is the only
   tint" while the shipped surface tinted the word. Here it is a glyph rule, so
   the word stays --ink and the promise is kept by the stylesheet that makes
   it. */
.verdict.allow .verdict-head svg {
  color: var(--status-done);
}
.verdict.deny .verdict-head svg {
  color: var(--status-blocked);
}

/* ---------- activity charts ---------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
}
.stat {
  background: var(--bg-card);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-3);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.stat-num {
  font-family: var(--font-serif);
  font-size: 30px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stat-label {
  color: var(--ink-mute);
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Allow/deny in a bar is a QUANTITY, not a status: the two shares separate by
   value (--ink-faint against --ink), which survives both themes and colour
   vision deficiency without asking a hue to mean anything. */
.bar-track {
  height: 6px;
  border-radius: 999px;
  background: var(--bg-sunken);
  overflow: hidden;
  display: flex;
}
.bar-allow {
  background: var(--ink-faint);
  height: 100%;
}
.bar-deny {
  background: var(--ink);
  height: 100%;
}
.activity-person-table th:first-child,
.activity-person-table td:first-child {
  min-width: 180px;
}
.activity-person-table th:last-child,
.activity-person-table td:last-child {
  min-width: 190px;
}
.activity-person-unavailable {
  color: var(--ink-mute);
  font-size: 12px;
  font-weight: 400;
}
.activity-person-share-unavailable {
  display: grid;
  grid-template-columns: minmax(80px, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
}
.spark {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 110px;
}
.spark .col {
  flex: 1 1 0;
  min-width: 0;
  gap: 0;
  justify-content: flex-end;
}
.spark .seg-allow {
  display: block;
  background: var(--ink-faint);
  border-radius: 1px;
  min-height: 1px;
}
.spark .seg-deny {
  display: block;
  background: var(--ink);
  border-radius: 1px;
  min-height: 1px;
}
.spark .lbl {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-mute);
  text-align: center;
  margin: 0;
  font-weight: 400;
  letter-spacing: 0;
}

/* ---------- models and limits ---------- */
.gw-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--space-4);
  align-items: start;
}
.gw-money {
  display: flex;
  align-items: center;
  gap: 8px;
}
.gw-money .cur {
  font-size: 16px;
  color: var(--ink-mute);
  line-height: 1;
}
.gw-cap {
  max-width: 160px;
  font-variant-numeric: tabular-nums;
  font-size: 16px;
  font-weight: 500;
}
.gw-cap.is-bad {
  border-color: var(--status-blocked);
}
.gw-cap-ro {
  font-size: 16px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.gw-per {
  font-size: 12px;
  color: var(--ink-mute);
  white-space: nowrap;
}
.gw-capin {
  width: 108px;
  min-width: 88px;
  font-variant-numeric: tabular-nums;
}
.models-ceilings-table th:nth-child(2),
.models-ceilings-table td:nth-child(2) {
  width: 150px;
}
.models-ceilings-table th:last-child,
.models-ceilings-table td:last-child {
  width: 132px;
}
.models-ceilings-unavailable {
  color: var(--ink-mute);
  font-size: 12px;
  font-weight: 400;
}
.models-ceilings-default td {
  background: var(--bg-sunken);
}
.models-ceilings-absence {
  padding-top: var(--space-3);
  border-top: var(--rule-w-hair) solid var(--rule);
}

/* model rows: checkbox | model meta | default | input rate | output rate */
.gw-models {
  border-top: var(--rule-w-hair) solid var(--rule);
}
.gw-model {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 62px 100px 100px;
  gap: var(--space-2);
  align-items: center;
  padding: 9px var(--space-5) 9px var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-faint);
}
.gw-model:last-child {
  border-bottom: 0;
}
.gw-model-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-mute);
  background: var(--bg-sunken);
  border-bottom: var(--rule-w-hair) solid var(--rule);
  padding-top: 8px;
  padding-bottom: 8px;
}
.gw-model-head span {
  font-size: 11px;
  color: var(--ink-mute);
}
.gw-model-head .right {
  text-align: right;
}
.gw-model.is-denied {
  opacity: 0.5;
}
.gw-check-box {
  display: grid;
  place-items: center;
}
.gw-check-box input {
  width: 15px;
  height: 15px;
  accent-color: var(--ink);
  cursor: pointer;
}
.gw-check-box input[type="radio"] {
  width: 14px;
  height: 14px;
}
.gw-model-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.gw-model-id-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
}
.gw-model-name {
  font-weight: 600;
  color: var(--ink);
}
.gw-model-id {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--ink-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gw-default {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--ink-soft);
  font-size: 13px;
}
.gw-default input:disabled {
  cursor: not-allowed;
}
.gw-price {
  display: flex;
  align-items: center;
  gap: 4px;
  position: relative;
}
.gw-price .cur {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-mute);
  font-size: 12px;
  pointer-events: none;
}
.gw-price .field {
  height: 30px;
  padding: 0 8px 0 20px;
  font-size: 13px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.gw-price .field:read-only,
.gw-price .field:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* A price is a FACT, not a field. The provider's rate card is what the DP
   metered with and what every figure on Activity is computed from, so an
   editable box here would let an admin restate history — and the server
   discards the value anyway (`stamp_platform_rates` overwrites `rate_cards`
   from the CP model catalog on every publish). It renders as text because that
   is what it is. */
.gw-price-ro {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  width: 100%;
  text-align: right;
  padding-right: 8px;
}
.gw-rate-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  max-width: 320px;
}

/* review panel (sticky) */
.gw-review {
  position: sticky;
  top: var(--space-4);
}
.gw-sum {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-2);
  overflow: hidden;
  margin-bottom: var(--space-4);
}
.gw-sum-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 9px var(--space-3);
  border-bottom: var(--rule-w-hair) solid var(--rule-faint);
  font-size: 13px;
}
.gw-sum-row:last-child {
  border-bottom: 0;
}
.gw-sum-row .v {
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.gw-checks {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* The check glyph is tinted; the sentence beside it is not. */
.gw-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--ink-soft);
}
.gw-check svg {
  flex: none;
}
.gw-check.ok svg {
  color: var(--status-done);
}
.gw-check.bad {
  color: var(--ink);
}
.gw-check.bad svg {
  color: var(--status-blocked);
}
.gw-forbid {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: var(--space-4);
  padding: var(--space-3);
  border-radius: var(--radius-2);
  background: var(--bg-sunken);
  font-size: 12.5px;
  color: var(--ink-soft);
  line-height: 1.5;
}
.gw-forbid svg {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--ink-mute);
}

/* ---------- billing ---------- */
.bill-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  align-items: start;
}
.bill-lines {
  margin-top: var(--space-3);
  border-top: var(--rule-w-hair) solid var(--rule);
}
.bill-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
}
.bill-row + .bill-row {
  border-top: var(--rule-w-hair) solid var(--rule-faint);
}
.bill-row.is-total {
  border-top: var(--rule-w-hair) solid var(--rule-strong);
  background: var(--bg-sunken);
}
.bill-k {
  font-size: 14px;
  color: var(--ink-soft);
}
.bill-k-strong {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.bill-v {
  font-size: 15px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.bill-v-strong {
  font-family: var(--font-serif);
  font-size: 26px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.bill-locked {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  max-width: 560px;
}
.billing-invoice-table th:first-child,
.billing-invoice-table td:first-child {
  min-width: 170px;
}
.billing-invoice-table th:last-child,
.billing-invoice-table td:last-child {
  min-width: 118px;
}
.billing-invoice-unavailable {
  color: var(--ink-mute);
  font-size: 12px;
  font-weight: 400;
}
/* The two-up fact pair Billing uses for contact / registration number. */
.gv-two {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-4);
}
.gv-a {
  margin: 0;
  color: var(--ink-soft);
  font-size: 13.5px;
  line-height: 1.55;
  max-width: 660px;
  text-wrap: pretty;
}

/* ---------- the organization-policy rung (Policy, upper layer) ----------
   A LAYER, not a section: `.gv-layer-title` is the serif title that says "you
   are looking at a different altitude of the same rule", and `.gv-divider`
   below it is the seam where the pane hands over to the expert layer. Ported
   from the design project's console stylesheet, with its `0.5px` hairlines swapped
   for this file's `--rule-w-hair` so a single token still moves every rule in
   the console.

   The organization-policy object is still absent. Its controls are therefore
   preserved as disabled anatomy: enough shape to communicate the intended
   interaction, with no selected value or active publish path. */
.gv-layer {
  gap: var(--space-3);
}
.gv-head {
  margin-bottom: var(--space-1);
}
.gv-layer-title {
  font-family: var(--font-serif);
  font-size: 22px;
  line-height: 1.15;
  letter-spacing: -0.015em;
  font-weight: 400;
  margin: 0;
}
.gv-q {
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.gv-q-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}
.gv-q-title {
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--ink);
}
.gv-q-head .chip {
  flex-shrink: 0;
}
.gv-ctl {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: var(--rule-w-hair) solid var(--rule-faint);
}
.gv-control-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.gv-control-stack > .label {
  margin: 0;
}
.gv-ctl-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.gv-unavailable-chip {
  border-style: dashed;
  color: var(--ink-mute);
}
.gv-add {
  width: min(180px, 100%);
  height: 30px;
}
.gv-days {
  width: 72px;
  height: 32px;
  text-align: right;
}
.gv-q .segmented button:disabled,
.gv-q .field:disabled,
.gv-q .selectish:disabled,
.gv-publish .btn:disabled {
  cursor: not-allowed;
  opacity: 0.58;
}
.gv-q .segmented button:disabled:hover {
  color: var(--ink-mute);
}
.gv-publish {
  margin-top: var(--space-1);
}
/* Policy has two publish-shaped rows. Only the live Rules bar is pinned; the
   unavailable organization-policy anatomy belongs to the upper layer and must
   scroll away with that layer. */
.page.has-pinned-publish .pubbar.gv-publish {
  position: static;
  z-index: auto;
  flex-wrap: wrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.gv-divider {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: var(--space-6) 0 var(--space-4);
  padding-top: var(--space-5);
  border-top: var(--rule-w-hair) solid var(--rule-strong);
}

/* ---------- sign-in ---------- */
.signin-screen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}
.signin-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
}
.signin-center {
  flex: 1;
  display: grid;
  place-items: center;
  padding: var(--space-5);
}
.signin-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: stretch;
}
.signin-card > .chip {
  align-self: flex-start;
}
.signin-tagline {
  font-family: var(--font-serif);
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0;
}
.signin-foot {
  padding: var(--space-5);
  text-align: center;
  font-size: 12px;
  color: var(--ink-mute);
}
/* The "or" between the Google button and the password form, on BOTH public
   screens. It was an inline style duplicated in SignIn.tsx and
   InviteAccept.tsx, and both copies asked for `var(--muted)` — a custom
   property no stylesheet in this SPA defines. An undefined var() with no
   fallback makes the whole declaration invalid at computed-value time, so
   `color` fell back to the inherited ink and the separator rendered at FULL
   strength: the loudest word on a sign-in card was "or". A class means the
   two screens cannot drift apart again. */
/* Shared sign-in separator: one muted word inset between two hairlines. */
.signin-or {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--ink-mute);
  font-size: 12px;
}
.signin-or::before,
.signin-or::after {
  content: "";
  flex: 1;
  height: 0;
  border-top: var(--rule-w-hair) solid var(--rule);
}
/* A join page's list of what is set up, and a new authenticator app's QR. */
.signin-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
  color: var(--ink-soft);
}
.signin-qr {
  display: flex;
  justify-content: center;
}
.signin-qr svg {
  width: 200px;
  height: 200px;
  border-radius: 6px;
}
/* The two quiet lines on the sign-in card: why Google is first, and the one
   that says signing in is not access. 13px per the design spec — a step above
   the 12px chrome, because both are meant to be read, not skimmed past. */
.signin-hint {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-mute);
  text-wrap: pretty;
}
.signin-gate {
  margin-top: var(--space-2);
  padding-top: var(--space-4);
  border-top: var(--rule-w-hair) solid var(--rule);
}

/* ---------- connector-permission grid (org-permission-system) ----------
   Modeled on .gw-models: a repeating [action | admin 96px | member 96px] grid
   with collapsible per-connector groups. A denied cell is a sunken "hole"
   (--bg-sunken) so an off toggle reads as a recess against the card-bg allowed
   cells — scannable at a glance, and the recess is a tone rather than a hue. */
.conn-perms {
  border-top: var(--rule-w-hair) solid var(--rule);
}
.conn-perm-colhead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px 96px;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule);
  background: var(--bg-sunken);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.conn-perm-colhead .rolecol {
  display: flex;
  justify-content: center;
}
.conn-perm-group {
  border-bottom: var(--rule-w-hair) solid var(--rule);
}
.conn-perm-group:last-child {
  border-bottom: 0;
}
.conn-perm-grouphead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px 96px;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-4);
  background: var(--bg-sunken);
}
.conn-perm-grouptitle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--ink);
  font: inherit;
  padding: var(--space-2) 0;
  text-align: left;
}
.conn-perm-grouptitle .chev {
  color: var(--ink-mute);
  flex: none;
  transition: transform var(--motion-snap);
}
.conn-perm-grouptitle.is-collapsed .chev {
  transform: rotate(-90deg);
}
.conn-perm-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conn-perm-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px 96px;
  gap: var(--space-3);
  align-items: stretch;
  padding: 0 var(--space-4);
  border-top: var(--rule-w-hair) solid var(--rule-faint);
}
.conn-perm-label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-3) 0;
}
.conn-perm-actname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
}
.conn-perm-empty {
  padding: var(--space-3) 0;
}
.conn-perm-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: var(--space-2) 0;
}
.conn-perm-cell.is-denied {
  background: var(--bg-sunken);
}
.conn-perm-cell input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
}
.conn-perm-cell input[type="checkbox"]:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}
.cperm-count {
  font-size: 10.5px;
  padding: 0 5px;
  height: auto;
  line-height: 1.5;
}

/* Forced colors suppresses the shared box-shadow ring. Keep this scroll
   region's replacement outline inside its clipped viewport. */
@media (forced-colors: active) {
  #main:focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: -2px;
  }
}

/* Focus a pointer navigation moved to main (console.js `focusMain`): it keeps
   keyboard scrolling working but is not a keyboard focus, so no ring. */
#main[data-quiet-focus]:focus-visible {
  box-shadow: none;
  outline: none;
}

/* ---------- narrow ---------- */
@media (max-width: 1100px) {
  .gw-grid,
  .policy-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .gw-review,
  .sim {
    position: static;
  }
}
@media (max-width: 900px) {
  .rule-body {
    grid-template-columns: 1fr;
  }
  .rule-body .span2 {
    grid-column: span 1;
  }
  .hints-grid,
  .gw-rate-grid {
    grid-template-columns: 1fr;
  }
  .wrap {
    padding: var(--space-5) var(--space-4) var(--space-7);
  }
}

/* "Deny overrides allow." — the resolution rule, stated inline at the end of
   the Rules intro. Ink weight rather than a status hue: it is a fact about how
   the evaluator resolves, not a warning, and the four --status-* colours are
   dots and glyphs only on this surface. */
.policy-denywins {
  font-weight: 500;
  color: var(--ink);
}

/* ---------- Connectors: request lane, browsable catalogue, publish state ----
   The console decides what an organization allows. Cards make connector
   identity, connection shape and current allow state readable as one unit;
   workspace installation and personal authorization stay on the data plane. */
.connector-request-lane,
.connector-market,
.connector-market-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.connector-market-section-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding-bottom: 2px;
}
.connector-market-count {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-mute);
}
.connector-market-section-copy {
  max-width: 720px;
  margin: -4px 0 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-mute);
  text-wrap: pretty;
}
/* THE REQUEST LANE, DRAWN WITHOUT ITS DATA. The lane's four columns and its
   Allow action are the feature; the control plane receives no request
   aggregate, so every VALUE is a `.nofigure` and the action is disabled.

   ⚠ It is a real `.table`, in the same grammar as the Models per-person
   ceilings, and NOT a dashed placeholder box. The box this replaced said "there
   is a hole here" where the design says "these are the four things a request
   tells you"; a reader could not learn from it that the lane names a workspace
   and never a person, which is the lane's whole privacy contract. A dashed
   border belongs on a slot nobody has designed — this one is designed and
   unbacked, which is a different claim. */
.connector-request-table {
  table-layout: auto;
}
.connector-request-table td .nofigure {
  font-family: var(--font-mono);
}
/* The row-source cell: a sentence, not a name, so the row cannot be skimmed as
   a request from a workspace called something. */
.connector-request-rows-absent {
  color: var(--ink-mute);
  font-size: 12.5px;
}
.connector-market-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-top: var(--space-2);
  border-top: var(--rule-w-hair) solid var(--rule);
}
.connector-market-toolbar .listbar {
  flex: 1 1 390px;
  margin: 0;
}
.connector-market-toolbar .listbar-field {
  max-width: 430px;
}
.connector-market-facets {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow-x: auto;
  padding: 1px 1px var(--space-2);
  scrollbar-width: thin;
}
.connector-market-facet {
  flex: none;
  min-height: 29px;
  padding: 4px 11px;
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-mute);
  font: inherit;
  font-size: 11.5px;
  cursor: pointer;
}
.connector-market-facet:hover {
  background: var(--hover);
  color: var(--ink-soft);
}
.connector-market-facet[aria-pressed="true"] {
  border-color: var(--rule-strong);
  background: var(--bg-card);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.connector-market-groups {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding-top: var(--space-2);
}
.connector-market-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}
.connector-market-card {
  display: flex;
  min-height: 158px;
  min-width: 0;
  flex-direction: column;
  padding: var(--space-4);
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: var(--radius-3);
  background: var(--bg-card);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.035);
}
.connector-market-card.is-allowed {
  border-color: var(--rule-strong);
}
.connector-market-card-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}
.connector-market-mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border: var(--rule-w-hair) solid var(--rule);
  border-radius: 9px;
  background: var(--bg-sunken);
  color: var(--ink-soft);
}
.connector-market-mark svg {
  width: 18px;
  height: 18px;
}
/* The mark for a connector whose author declared no category. Dashed and faint
   so it reads as an empty slot rather than as a sixth family's glyph — the same
   register `.nofigure` uses for a fact nobody stated. */
.connector-market-mark.is-unfiled {
  border-style: dashed;
  background: none;
  color: var(--ink-mute);
}
.connector-market-card-id {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 1px;
}
.connector-market-card-id strong {
  overflow: hidden;
  color: var(--ink);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.connector-market-card-id small {
  color: var(--ink-mute);
  font-size: 10.5px;
  line-height: 1.4;
}
.connector-market-description {
  display: -webkit-box;
  min-height: 34px;
  margin: var(--space-3) 0;
  overflow: hidden;
  color: var(--ink-soft);
  font-size: 12px;
  line-height: 1.5;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.connector-market-card-foot {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: var(--rule-w-hair) solid var(--rule-faint);
}
.connector-market-card-foot .btn {
  margin-left: auto;
}
.connector-market-state {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--ink-soft);
  font-size: 11.5px;
  white-space: nowrap;
}
.connector-market-state.is-muted {
  color: var(--ink-mute);
}
.connector-market-explainer {
  margin-top: var(--space-2);
}
.connector-observed-list {
  overflow: hidden;
}
.connector-observed-row {
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--rule-w-hair) solid var(--rule-faint);
}
.connector-observed-row:last-child {
  border-bottom: 0;
}
.connector-observed-meta {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}
.connector-observed-name {
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
}
.connector-market-status {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: var(--rule-w-hair) solid var(--rule-strong);
  border-radius: var(--radius-3);
  background: var(--bg-card);
  box-shadow:
    0 -1px 0 var(--rule),
    0 -10px 26px -16px rgba(0, 0, 0, 0.3);
}
.connector-market-status-spacer {
  flex: 1;
}
.connector-market-status-note {
  max-width: 360px;
  color: var(--ink-mute);
  font-size: 11.5px;
  line-height: 1.45;
  text-align: right;
}
@media (max-width: 940px) {
  .connector-market-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .connector-market-status-note {
    display: none;
  }
}
@media (max-width: 720px) {
  .connector-market-toolbar {
    align-items: stretch;
    flex-direction: column;
  }
  .connector-market-toolbar .listbar,
  .connector-market-toolbar .listbar-field {
    max-width: none;
    flex-basis: auto;
  }
  .connector-market-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .connector-market-status {
    position: static;
    align-items: stretch;
    flex-direction: column;
  }
  .connector-market-status-spacer {
    display: none;
  }
  .connector-market-status .btn {
    justify-content: center;
    width: 100%;
  }
}

/* The skip link — absent from this console entirely until 2026-08-08, and the
   design has carried it all along. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 99;
  padding: 8px 14px;
  border-radius: var(--radius-2);
  background: var(--ink);
  color: var(--bg-card);
  font-size: 13px;
}
.skip-link:focus {
  left: 12px;
}


/* Native <dialog> host — same overlay/surface as Kobalte's portal. */
dialog.dialog-host {
  border: 0;
  padding: var(--space-4);
  background: transparent;
  max-width: none;
  max-height: none;
  color: inherit;
  white-space: normal;
  text-align: left;
}
dialog.dialog-host::backdrop {
  background: rgba(0, 0, 0, 0.32);
}
[data-theme="dark"] dialog.dialog-host::backdrop {
  background: rgba(0, 0, 0, 0.55);
}
dialog.dialog-host[open] {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100dvh;
  background: transparent;
}
dialog.dialog-host > .dialog { max-height: calc(100dvh - 2 * var(--space-4)); overflow-y: auto; }
.field[type="file"] { height: auto; min-height: var(--field-h); padding: var(--space-2); font-size: 13px; }
.field::file-selector-button { font: inherit; color: var(--ink); background: var(--bg-sunken); border: var(--rule-w-hair) solid var(--rule); border-radius: var(--radius-2); padding: 5px 9px; margin-right: var(--space-2); cursor: pointer; }
.invite-form { display: flex; align-items: end; gap: var(--space-3); }
.invite-form > .grow { min-width: 0; }
.invite-form > .btn { min-height: var(--field-h); white-space: nowrap; }
.is-behind { color: var(--ink-soft); }
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-4);
  margin: var(--space-4) 0;
}
.stat-label { font-size: 12px; color: var(--ink-mute); }
.stat-num { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.bar { width: 72px; height: 6px; }
.bar-wide { width: 100%; }
.no-figure { color: var(--ink-mute); font-style: italic; }

/* Factor sign-in (identity.md §F6 R38). Classes rather than style
   attributes: the console's CSP is style-src 'self', which refuses inline
   styles. */
.btn-wide { width: 100%; justify-content: center; }
.btn-wide.btn-gap { margin-top: var(--space-2); }
.signin-chip { margin-bottom: var(--space-3); }

/* Visibility attributes also apply to components with an explicit display. */
[hidden] { display: none !important; }
.orgswitch-wrap { position: relative; }
.preview-note { padding: 9px 12px; margin: -20px 0 24px; border: var(--rule-w-hair) dashed var(--rule); border-radius: var(--radius-2); font-size: 11px; color: var(--ink-mute); }

/* Provider catalog with model groups and independently allowed options. */
.models-page { gap: 28px; }
.model-settings { display: flex; flex-direction: column; gap: 28px; }
.model-overview {
  display: grid; grid-template-columns: 1fr .7fr 1.7fr;
  border-bottom: var(--rule-w-hair) solid var(--rule); padding-bottom: 22px;
}
.model-overview-item { padding: 0 24px; min-width: 0; }
.model-overview-item:first-child { padding-left: 0; }
.model-overview-item:last-child { padding-right: 0; }
.model-overview .label { color: var(--ink-soft); font-size: 12px; margin-bottom: 12px; }
.model-overview .hint { font-size: 12px; margin: 10px 0 0; }
.model-overview-value { font-size: 22px; line-height: 32px; font-variant-numeric: tabular-nums; letter-spacing: -.035em; }
.select-wrap { display: inline-block; position: relative; max-width: 100%; }
.select-wrap > svg { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-soft); pointer-events: none; }
.select-wrap > .selectish { max-width: 100%; padding-right: 32px; background-image: none; text-overflow: ellipsis; }
.model-default-picker { display: block; }
.model-default-picker .model-default-select { width: 100%; min-width: 0; height: 38px; }
.model-default-select:disabled { color: var(--ink-mute); cursor: default; }
.model-budget .gw-money { max-width: 200px; }
.model-budget .gw-cap { width: 112px; font-size: 25px; height: 38px; font-variant-numeric: tabular-nums; }
.model-budget .gw-per { font-size: 11px; }
.catalog-heading { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 18px; }
.catalog-heading .section-title { margin: 0; font-size: 16px; }
.catalog-tools { display: flex; align-items: center; gap: 24px; margin-bottom: 16px; }
.catalog-search { flex: 1; max-width: 480px; }
.catalog-search .field { width: 100%; }
.catalog-only, .model-allow { display: inline-flex; align-items: center; gap: 9px; font-size: 13px; cursor: pointer; }
.catalog-only input, .model-allow input { accent-color: var(--ink); width: 15px; height: 15px; margin: 0; }
.catalog-navigation { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding-bottom: 0; border-bottom: var(--rule-w-hair) solid var(--rule); }
.provider-filters { display: flex; gap: 12px; overflow-x: auto; padding: 0; }
.provider-filter { display: inline-flex; align-items: center; gap: 9px; border: 0; border-bottom: 2px solid transparent; background: transparent; color: var(--ink-soft); border-radius: 0; padding: 10px 0; font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap; }
.provider-filter span { color: var(--ink-mute); font-size: 10px; font-variant-numeric: tabular-nums; }
.provider-filter:hover { color: var(--ink); }
.provider-filter[aria-pressed="true"] { color: var(--ink); border-bottom-color: var(--ink-soft); }
.provider-groups { display: flex; flex-direction: column; gap: 30px; padding-top: 24px; }
.provider-heading { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.provider-heading h3 { font-size: 15px; font-weight: 550; margin: 0; }
.provider-heading > .tiny { margin-left: 4px; }
.catalog-option-hint { color: var(--ink-mute); font-size: 12px; line-height: 1.6; margin: -6px 0 18px; }
.model-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 13px; }
.model-col-allow { width: 7%; }
.model-col-context { width: 23%; }
.model-col-endpoint { width: 15%; }
.model-col-region { width: 29%; }
.model-col-price { width: 13%; }
.model-table th, .model-table td { text-align: left; padding: 10px 12px; overflow-wrap: anywhere; }
.model-table thead th { color: var(--ink-mute); font-size: 11px; font-weight: 450; padding-top: 0; padding-bottom: 10px; border-bottom: var(--rule-w-hair) solid var(--rule); }
.model-table .model-group-row > th { padding: 24px 0 12px; }
.model-group-heading { display: flex; align-items: center; gap: 12px; }
.model-name { color: var(--ink); font-size: 15px; font-weight: 500; line-height: 1.5; }
.model-option-count { margin-left: auto; color: var(--ink-mute); font-size: 11px; font-weight: 400; font-variant-numeric: tabular-nums; }
.model-default-badge { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 450; color: var(--ink-soft); }
.model-option-row > th, .model-option-row > td { border-bottom: var(--rule-w-hair) solid var(--rule); vertical-align: middle; }
.model-option-row:hover { background: var(--bg-card); }
.model-option-row .model-context-cell { font-weight: 400; }
.model-context-cell > span, .model-region-cell > span { display: block; }
.model-output-limit, .model-region-code { color: var(--ink-mute); font-size: 10px; margin-top: 4px; }
.model-region-code { font-size: 11px; }
.model-endpoint-cell, .model-region-cell { color: var(--ink-soft); }
.model-table .model-select-cell { padding: 0; }
.model-select-cell .model-allow { display: flex; justify-content: center; min-height: 48px; }
.model-table .model-price { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.model-option-row .model-price { font-size: 13px; color: var(--ink); }
.mobile-option-label { display: none; }
.catalog-empty { padding: 52px 16px; text-align: center; border-bottom: var(--rule-w-hair) solid var(--rule); }
.catalog-empty > span { color: var(--ink-mute); }
.catalog-empty h3 { font-size: 18px; font-weight: 500; margin: 12px 0 8px; }
.catalog-empty .btn { margin-top: 12px; }
.model-limits { background: transparent; border-radius: 0; border-width: 0 0 1px; }
.model-limits summary { cursor: pointer; display: flex; align-items: center; gap: 12px; font-size: 13px; font-weight: 500; padding: 17px 20px; }
.model-limits summary::before { content: '+'; color: var(--ink-mute); font-size: 16px; font-weight: 400; }
.model-limits[open] summary::before { content: '−'; }
.model-limits summary .tiny { margin-left: auto; font-weight: 400; }
.model-limits-body { padding: 0 20px 18px; }
.model-limit-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; max-width: 500px; }
.model-limit-fields .field { width: 100%; }
.model-person-note { display: flex; align-items: center; gap: 8px; margin: -12px 0 0; font-size: 12px; }
.model-person-note svg { flex-shrink: 0; }
.catalog-publish { position: sticky; bottom: 16px; z-index: 5; padding: 16px 20px; box-shadow: 0 5px 22px rgba(0,0,0,.12); flex-wrap: nowrap; }
.catalog-publish-state { flex: 1; min-width: 0; }
.catalog-publish-state > .hint { font-size: 11px; margin: 8px 0 0; }
.catalog-publish-actions { display: flex; align-items: center; gap: 8px; }
.catalog-publish .chip { font-size: 10px; }
.catalog-publish .btn { white-space: nowrap; }
@media (max-width: 900px) { .sidebar .sidebar-close { display: grid; } }
@media (max-width: 680px) {
  .page-head-actions { align-items: start; flex-direction: column; }
  .page-head-actions > .page-head { min-width: 0; }
  .invite-form { align-items: stretch; flex-direction: column; }
  .bill-grid { grid-template-columns: minmax(0, 1fr); }
  .model-overview { grid-template-columns: 1fr 1fr; }
  .model-overview-item { padding: 12px 0; }
  .model-overview-item + .model-overview-item { border-top: var(--rule-w-hair) solid var(--rule); }
  .model-budget { grid-column: 1; }
  .model-overview-item:nth-child(2) { border-top: 0; padding-left: 20px; }
  .model-default-setting { grid-column: 1 / -1; }
  .model-overview-value { font-size: 22px; }
  .catalog-heading { align-items: start; flex-direction: column; gap: 8px; }
  .catalog-tools { gap: 12px; flex-wrap: wrap; }
  .catalog-search { flex-basis: 100%; max-width: none; }
  .catalog-navigation { align-items: start; flex-direction: column; gap: 12px; }
  .provider-filters { max-width: 100%; }
  .model-table, .model-entry { display: block; }
  .model-table colgroup, .model-table thead { display: none; }
  .model-group-row, .model-group-row > th { display: block; }
  .model-table .model-group-row > th { padding-top: 22px; }
  .model-group-heading { flex-wrap: wrap; gap: 6px 12px; }
  .model-group-heading .model-default-badge { font-size: 9px; }
  .model-option-row { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 10px 20px; padding: 12px 0; border-bottom: var(--rule-w-hair) solid var(--rule); }
  .model-option-row > th, .model-option-row > td { border-bottom: 0; padding: 0; }
  .model-option-row > [data-label]::before { content: attr(data-label); display: block; color: var(--ink-mute); font-size: 10px; margin-bottom: 3px; font-weight: 450; }
  .model-option-row .model-select-cell { grid-column: 1; grid-row: 1; }
  .mobile-option-label { display: inline; font-size: 12px; }
  .model-select-cell .model-allow { min-height: 32px; justify-content: start; }
  .model-context-cell { grid-column: 1; grid-row: 2; }
  .model-endpoint-cell { grid-column: 2; grid-row: 2; }
  .model-region-cell { grid-column: 1 / -1; grid-row: 3; }
  .model-region-cell > span { display: inline; }
  .model-region-code { margin-left: 8px; }
  .model-option-row > .model-price { text-align: left; grid-row: 4; }
  .catalog-publish { align-items: stretch; flex-direction: column; padding: 14px; bottom: 10px; gap: 12px; }
  .catalog-publish-actions { justify-content: flex-end; }
  .catalog-publish-state > .hint { display: none; }
  .model-limit-fields { grid-template-columns: 1fr; }
}

/* Console template details live here so the shell CSP can forbid inline styles. */
.orgmenu .navtab.orgmenu-item {
  width: 100%;
  justify-content: space-between;
  height: 38px;
  border-radius: var(--radius-2);
}
.orgmenu .row.orgmenu-label {
  gap: 8px;
}
.orgmenu-check-space {
  width: 14px;
}
.orgmenu-separator {
  border-top: var(--rule-w-hair) solid var(--rule);
  margin: var(--space-1) 0;
}
.orgmenu .navtab.orgmenu-back {
  width: 100%;
  justify-content: flex-start;
  gap: 8px;
  height: 36px;
  border-radius: var(--radius-2);
  color: var(--ink-soft);
}
.enrollment-command {
  white-space: pre-wrap;
}
.table-identity {
  font-weight: 500;
}
.row.restore-backup-label {
  gap: var(--space-2);
  margin-top: var(--space-4);
  align-items: center;
  cursor: pointer;
}
.restore-backup-text {
  font-size: 14px;
  color: var(--ink-soft);
}
.restore-actions {
  margin-top: var(--space-4);
}
.hint.config-hint {
  margin: 0;
  max-width: 660px;
}
.banner.billing-notice {
  margin-top: var(--space-3);
}
.stack.absent-copy {
  gap: 3px;
}
.chip.signin-brand {
  margin-bottom: var(--space-3);
}
.wrap.orgs-wrap {
  margin: 0 auto;
}
.stack.orgs-list {
  gap: var(--space-2);
}
.orgs-name {
  font-weight: 600;
  font-size: 15px;
}
.hint.orgs-hint {
  max-width: 620px;
  margin-top: var(--space-3);
}
