/* Readiness Simulation — the platform VERSION 2 unified shell.
 *
 * WHERE THIS COMES FROM. Every value below is the v2 unified shell the
 * lakehouse migrated to: edgentx/vforce-lakehouse
 * frontend/app/(platform)/v2.css, itself a 1:1 port of that repository's
 * v2-ui-spec.html. The grid, the 212px rail, the 46px bands, the 30px row, the
 * 8px radius, the ink ramp, the flat --panel card, the mono uppercase badge,
 * the 38px tab with its 2px underline and the --raise floating surface are all
 * taken from there rather than approximated, because the point of this change
 * is that the Readiness Simulation reads as a sibling of the v2 estate at a
 * glance. What preceded it here was the OLD v1 blade style — glass surfaces
 * over a radial-gradient ground, 14px radii, backdrop blur, 999px pills —
 * copied from vforce-flow before the migration.
 *
 * WHAT IS DELIBERATELY DIFFERENT FROM THE LAKEHOUSE, AND WHY.
 *
 * 1. THE ACCENT. The lakehouse identity is cyan (#35c6e0). This product gets
 *    its own so that a screenshot is attributable to the right instance rather
 *    than mistaken for the data platform. The whole identity is one token and
 *    its derivations, at the top of each theme block — see THE ACCENT below.
 *
 * 2. A LIGHT THEME. v2.css is dark-only; it never needed anything else. This
 *    instance is delivered under Section 508 and is demonstrated on projectors
 *    in rooms nobody controls the lighting of, so both themes have to hold
 *    WCAG 2.1 AA and axe checks both in CI. The light theme redefines TOKENS
 *    ONLY — not one component rule below knows which theme it is in — and it
 *    mirrors the v2 elevation order rather than inverting it arbitrarily: the
 *    page ground is the recessed surface, the panel is the raised one.
 *
 * 3. NO TRANSLUCENCY IN THE CHROME. The v1 style leaned on backdrop-filter and
 *    rgba(255,255,255,.04) glass. v2 is flat and opaque, which is both the
 *    house look now and one fewer way for a floating surface to show whatever
 *    scrolls underneath it.
 *
 * 4. ICON-FIRST ACTIONS WITH REAL TOOLTIPS. A title attribute alone appears on
 *    hover and never on keyboard focus, so a keyboard user meets an unlabeled
 *    glyph. .iconBtn draws its own tooltip on :hover AND :focus-visible from
 *    the same string that is its aria-label.
 */

/* ── Tokens: dark (the house default) ─────────────────────────────────────── */
:root {
  color-scheme: dark;

  /* THE ACCENT — the product's identity, and the ONE place to change it.
     Everything tinted, filled or underlined in the identity color derives from
     --accent by color-mix, so a sibling product built on this shell swaps two
     literals (--accent and --on-accent) per theme and nothing else. Scattering
     the hex through component rules is what makes a rebrand a search-and-
     replace with a long tail of misses.

     Violet is chosen because it is unclaimed in the estate: the lakehouse is
     cyan, Flow is amber. Measured against the grounds it is actually drawn on
     (WCAG 2.1 contrast, both themes, stated in the pull request):
       #a78bfa on --bg  #0a0f14 = 7.07:1   text, AA and AAA
       #a78bfa on --panel #0f1820 = 6.59:1 text, AA and AAA
       #a78bfa on --raise #13202a = 6.09:1 text, AA
       --on-accent #120a2b on #a78bfa = 6.99:1 text on the filled control
     THE FAVICON CANNOT REACH THIS TOKEN. It is a data-URI SVG in index.html
     and carries the accent as a literal; change it in the same commit. */
  --accent: #a78bfa;
  --on-accent: #120a2b;
  --accent-hover: color-mix(in srgb, var(--accent) 84%, #ffffff);
  --accent-dim: color-mix(in srgb, var(--accent) 14%, transparent);
  --accent-soft: color-mix(in srgb, var(--accent) 22%, transparent);
  --accent-line: color-mix(in srgb, var(--accent) 55%, transparent);
  --accent-wash: color-mix(in srgb, var(--accent) 5%, transparent);
  /* Ink for text sitting on an --accent-dim ground — the selected nav row, the
     selected table row. Derived so it tracks the accent through a swap. */
  --accent-ink: color-mix(in srgb, var(--accent) 30%, var(--text));

  /* Grounds, in v2's elevation order: page, rail, panel, raised. */
  --bg: #0a0f14;
  --rail: #0c141b;
  --panel: #0f1820;
  --raise: #13202a;
  /* The ground a row or a control takes on hover. It is --raise in the dark
     theme, where "one step up" is also "one step lighter" — but in the light
     theme the raised surface IS the card, so a hover that reused it would be
     white on white. It is its own token for that reason alone. */
  --hover: #13202a;

  --border: #1d2b36;
  --border-soft: #16212a;
  --hair: #1a2731;
  /* Aliases consumed by chart chrome and callouts; an undefined token paints
     SVG text black, which the tokens spec now refuses. */
  --line: var(--border);
  --line-soft: var(--hair);
  --glass: var(--panel);
  --warn-bg: rgba(230, 178, 74, 0.12);
  /* WCAG 1.4.11 wants 3:1 for the boundary of a control a reader has to find.
     --border is the hairline between surfaces; this is the edge of an input. */
  --control-border: #4d5f6d;

  /* Ink ramp, from v2. --faint is the floor: it is used at 10.5–11px, which is
     not "large text", so it has to clear 4.5:1 rather than 3:1. */
  --text: #d6e1ea;
  --muted: #8598a6;
  --faint: #7a8894;

  --ok: #39e08a;
  --warn: #e6b24a;
  --err: #ff8f87;
  --info: #79c0e8;

  --code-bg: rgba(0, 0, 0, 0.3);
  --shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  --shadow-sm: 0 10px 26px rgba(0, 0, 0, 0.4);

  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, system-ui, sans-serif;
  --mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;

  --rail-w: 212px;
  --rail-w-min: 52px;
  --top-h: 46px;
  --row-h: 30px;
  --radius: 8px;

  /* CHART TOKENS, DEFINED IN BOTH THEMES. The interface draws its own SVG, and
     a hard-coded chart color is a color that was measured in exactly one
     theme — always the one that gets demonstrated, never the one that gets
     projected in a lit room. Six series is the ceiling on purpose: the
     comparison screen accepts at most six runs, and a seventh category nobody
     can tell from the first is not a category. */
  --chart-1: #38bdf8;
  --chart-2: #34d399;
  --chart-3: #a78bfa;
  --chart-4: #f5b301;
  --chart-5: #fb7185;
  --chart-6: #94a3b8;
  --chart-grid: rgba(255, 255, 255, 0.09);
  --chart-axis: rgba(255, 255, 255, 0.30);
  --chart-band-opacity: 0.24;
  /* The hatch a withheld slot is filled with. It has to read as "deliberately
     absent" at a glance and never as an empty bar, which is a zero. */
  --chart-withheld: rgba(255, 255, 255, 0.05);
  --chart-withheld-line: rgba(251, 191, 36, 0.55);
}

/* ── Tokens: light. Only the tokens change. ───────────────────────────────── */
:root[data-theme='light'] {
  color-scheme: light;

  /* THE ACCENT, light. Measured on the grounds it is drawn on:
       #5b46c8 on --panel #ffffff = 6.66:1  text, AA and AAA
       #5b46c8 on --bg    #f2f5f7 = 6.08:1  text, AA and AAA
       #5b46c8 on --rail  #f7f9fa = 6.30:1  text, AA and AAA
       --on-accent #ffffff on #5b46c8 = 6.66:1 text on the filled control */
  --accent: #5b46c8;
  --on-accent: #ffffff;
  --accent-hover: color-mix(in srgb, var(--accent) 86%, #000000);

  --bg: #f2f5f7;
  --rail: #f7f9fa;
  --panel: #ffffff;
  --raise: #ffffff;
  --hover: #e9edf1;

  --border: #d2dae1;
  --border-soft: #e4e9ee;
  --hair: #dde4ea;
  --line: var(--border);
  --line-soft: var(--hair);
  --glass: var(--panel);
  --warn-bg: rgba(138, 83, 0, 0.08);
  --control-border: #6b7681;

  --text: #0f1a22;         /* 17.6:1 on --panel */
  --muted: #4a5b68;        /* 7.03:1 on --panel, 6.42:1 on --bg */
  --faint: #54646f;        /* 6.12:1 on --panel, 5.59:1 on --bg */

  --ok: #0b6b4a;           /* 6.53:1 on --panel */
  --warn: #8a5300;         /* 6.33:1 */
  --err: #b3261e;          /* 6.54:1 */
  --info: #0b5f8a;         /* 6.96:1 */

  --code-bg: rgba(15, 26, 34, 0.05);
  --shadow: 0 20px 50px rgba(15, 26, 34, 0.16);
  --shadow-sm: 0 8px 22px rgba(15, 26, 34, 0.12);
  /* The same six series, darkened for a white ground. Same names, same order,
     so a chart's fourth series is the fourth color in either theme and a
     reader who has learned the legend keeps it. */
  --chart-1: #0b6ea8;
  --chart-2: #0f766e;
  --chart-3: #6d4bd6;
  --chart-4: #8a5300;
  --chart-5: #b3261e;
  --chart-6: #475569;
  --chart-grid: rgba(13, 21, 38, 0.11);
  --chart-axis: rgba(13, 21, 38, 0.34);
  --chart-band-opacity: 0.20;
  --chart-withheld: rgba(13, 21, 38, 0.04);
  --chart-withheld-line: rgba(138, 83, 0, 0.55);
}

/* The reader's explicit choice is the only thing that switches the theme. The
   house portals are dark-first and this instance is meant to read as one of
   them at first glance, so the dark palette is the default and the light one is
   a deliberate switch that persists. */

* { box-sizing: border-box; }
* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 7px;
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-corner { background: transparent; }

/* The shell owns the viewport and nothing outside .mainBody scrolls. Two
   scrollbars on one screen is the state where a reader cannot tell what a
   wheel gesture will move. */
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
body {
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
::selection { background: var(--accent-soft); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.mono { font-family: var(--mono); }

/* Skip link — first tab stop, invisible until focused. */
.skipLink {
  position: absolute; left: 8px; top: -3rem; z-index: 2000;
  background: var(--raise); color: var(--text);
  border: 1px solid var(--border); border-radius: 7px;
  padding: 8px 12px; font-weight: 700; font-size: 12.5px;
  box-shadow: var(--shadow-sm);
  transition: top 0.12s ease;
}
.skipLink:focus { top: 8px; }

/* Available to a screen reader, absent from the page. Used for a column header
   whose cells are icon actions: the column still needs a name, and drawing one
   would be a header over nothing. */
.visually-hidden {
  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;
}

/* ── The v2 app grid: rail + main ─────────────────────────────────────────── */
/* grid-template-columns is `auto 1fr` rather than v2's `var(--rail-w) 1fr` so
   the collapsed width lives on .rail itself. v2 puts the collapsed class on the
   grid and reads it from the column template; doing it that way here would mean
   either :has() or a second class on a second element for one animated width. */
/* grid-template-rows is minmax(0,1fr) and not the implicit `auto`: an auto row
   sizes to its content, so the main column would grow past the viewport and
   .mainBody — a flex child with overflow-y:auto — would never have a height to
   scroll inside. The document would scroll instead, which is the one thing this
   shell must not do. */
.app {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: minmax(0, 1fr);
  height: 100vh; overflow: hidden;
}

.rail {
  width: var(--rail-w);
  transition: width 0.18s ease;
  background: var(--rail);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
}
.rail.collapsed { width: var(--rail-w-min); }

.brandBand {
  flex: 0 0 auto; display: flex; align-items: center;
  height: var(--top-h); padding: 0 12px;
  border-bottom: 1px solid var(--border-soft);
}
.rail.collapsed .brandBand { padding: 0; justify-content: center; }
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  color: var(--text); min-width: 0;
}
.brand:hover { text-decoration: none; }
.brandMark {
  width: 24px; height: 24px; border-radius: 7px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--accent); color: var(--on-accent);
  font-size: 10px; font-weight: 800; letter-spacing: 0.02em;
}
.brandName {
  font-weight: 700; letter-spacing: -0.01em; font-size: 13.5px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.brandSub { color: var(--muted); font-weight: 500; }

.navScroll { flex: 1; overflow-y: auto; padding: 8px 8px 12px; }
.navGroup + .navGroup { margin-top: 8px; }
/* The group heading is a LABEL, not a control. v2 draws a chevron here and
   collapses the group; that is state this restyle has no mandate to invent, and
   a chevron that does nothing is worse than no chevron. */
.ghead {
  display: flex; align-items: center; height: 24px; padding: 0 8px; margin: 0;
  color: var(--faint); font-size: 10.5px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; white-space: nowrap; user-select: none;
}
/* COLLAPSED, THE HEADING IS HIDDEN RATHER THAN NARROWED — and that distinction
   is the whole defect (#60). Centering the text in a 36px box does not shorten
   it: "UNDERSTAND" is 98px of nowrap uppercase, so the rail cut it down to
   "DERSTA", a word sliced through the letterforms at both ends, which reads as
   a rendering fault rather than as a label. A collapsed rail is an ICONS-ONLY
   rail; the labels are carried by each item's tooltip.
   VISUALLY HIDDEN AND NOT display:none, deliberately: the heading is what tells
   a screen reader that four links are three groups, and collapsing the rail is
   a sighted reader's space decision rather than a change of structure. Same
   technique as .visually-hidden above — restated rather than shared, because a
   class on the element would have to be added and removed by the renderer for
   a state the stylesheet already knows about. */
.rail.collapsed .ghead {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); border: 0;
}
/* The groups must still read as groups once their headings are gone. A hairline
   says what the heading said, in the space a 52px rail actually has. */
.rail.collapsed .navGroup + .navGroup {
  margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--border-soft);
}
.items { display: flex; flex-direction: column; gap: 1px; margin-top: 1px; }
.navItem {
  display: flex; align-items: center; gap: 9px;
  height: var(--row-h); padding: 0 9px; border-radius: 6px;
  color: var(--muted); font-size: 13px; white-space: nowrap; text-decoration: none;
}
.navItem:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.navItem[aria-current='page'] { background: var(--accent-dim); color: var(--accent-ink); }
.navIcon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex: 0 0 auto; opacity: 0.9;
}
.navItem[aria-current='page'] .navIcon { opacity: 1; }
.navLabel { overflow: hidden; text-overflow: ellipsis; }
.rail.collapsed .navItem { justify-content: center; padding: 0; }
.rail.collapsed .navLabel { display: none; }

/* Identity and secondary items live in the rail foot in v2. Here it carries the
   build, which is the one thing a reader needs when reporting what they saw. */
.railFoot {
  flex: 0 0 auto; border-top: 1px solid var(--border-soft);
  padding: 7px 9px 10px; color: var(--faint);
  font-family: var(--mono); font-size: 10.5px; white-space: nowrap; overflow: hidden;
}
.rail.collapsed .railFoot { display: none; }

/* ── Main column: topbar + the one scroll region ──────────────────────────── */
.main { display: flex; flex-direction: column; min-width: 0; background: var(--bg); }
.topbar {
  flex: 0 0 auto; height: var(--top-h);
  display: flex; align-items: center; gap: 10px;
  padding: 0 12px; border-bottom: 1px solid var(--border); background: var(--panel);
}
.topbar .spacer { flex: 1 1 auto; }
.topleft, .topright { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.mainBody { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 22px 40px; }

/* Breadcrumb — on every screen, every crumb a deep link. v2 separates with a
   slash and sets the current segment in the full ink. */
.crumb { margin: 0; min-width: 0; }
.crumb ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0; font-size: 12.5px;
}
.crumb li { display: inline-flex; align-items: center; }
.crumbLink { color: var(--muted); font-weight: 500; }
.crumbLink:hover { color: var(--text); }
.crumbCurrent { color: var(--text); font-weight: 600; }
.crumbSep { color: var(--faint); opacity: 0.6; margin: 0 6px; }

/* ── Page head, cards ─────────────────────────────────────────────────────── */
.pageHead { display: flex; align-items: flex-start; gap: 12px; margin: 0 0 16px; }
.pageHead h1 {
  margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -0.015em;
}
.pageHead p { margin: 6px 0 0; color: var(--muted); font-size: 12.5px; max-width: 70ch; }
.pageActions { margin-left: auto; display: flex; align-items: center; gap: 7px; flex: 0 0 auto; }

.card {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 15px;
}
.card + .card { margin-top: 14px; }
.cardHead { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
.cardHead h2 { margin: 0; font-size: 13px; font-weight: 700; letter-spacing: -0.01em; }
.cardHead .spacer { flex: 1 1 auto; }

/* ── Controls ─────────────────────────────────────────────────────────────── */
button.primary, button.secondary {
  display: inline-flex; align-items: center; gap: 7px;
  height: var(--row-h); padding: 0 12px; border-radius: 7px;
  font: inherit; font-size: 12.5px; font-weight: 600; line-height: 1;
  cursor: pointer; white-space: nowrap;
}
button.primary { background: var(--accent); color: var(--on-accent); border: 1px solid var(--accent); }
button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.secondary { background: transparent; color: var(--text); border: 1px solid var(--control-border); }
button.secondary:hover { background: var(--hover); border-color: var(--accent-line); }
button.primary:disabled, button.secondary:disabled { opacity: 0.45; cursor: not-allowed; }
button.primary:disabled:hover { background: var(--accent); border-color: var(--accent); }
button.secondary:disabled:hover { background: transparent; border-color: var(--control-border); }

input[type='text'], input[type='search'], input[type='number'], select, textarea {
  height: var(--row-h); padding: 0 10px;
  border: 1px solid var(--control-border); border-radius: 7px;
  font: inherit; font-size: 12.5px; width: 100%;
  background: var(--bg); color: var(--text);
}
textarea { height: auto; padding: 8px 10px; }
input:focus-within, input:focus, select:focus, textarea:focus { border-color: var(--accent-line); }
input::placeholder { color: var(--faint); }
select option { background: var(--raise); color: var(--text); }

/* Icon-first action with a tooltip that a keyboard reaches. aria-label and
   data-tip carry the same string — the tooltip a mouse sees and the name a
   screen reader announces must not be two different claims. */
.iconBtn {
  position: relative; display: inline-grid; place-items: center;
  width: var(--row-h); height: var(--row-h); flex: none;
  border: 1px solid var(--border); border-radius: 7px;
  background: transparent; color: var(--muted);
  font-size: 13px; line-height: 1; cursor: pointer; text-decoration: none;
}
.iconBtn > span { display: inline-flex; align-items: center; justify-content: center; }
.iconBtn:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.iconBtn:disabled { opacity: 0.45; cursor: not-allowed; }
.iconBtn[data-tip]::after {
  content: attr(data-tip);
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 1200;
  background: var(--raise); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; box-shadow: var(--shadow-sm);
  padding: 4px 8px; font-size: 11.5px; font-weight: 500; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.1s ease;
}
.iconBtn[data-tip]:hover::after,
.iconBtn[data-tip]:focus-visible::after { opacity: 1; }

/* v2's badge: mono, uppercase, small radius, tinted ground, colored border.
   Not the v1 999px lozenge — a status here is a machine-readable label rather
   than a decoration, and the mono face is what says so. */
.pill {
  display: inline-block; font-family: var(--mono);
  font-size: 10px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 5px; border: 1px solid var(--border); color: var(--muted);
}
.pill.ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); background: color-mix(in srgb, var(--ok) 12%, transparent); }
.pill.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.pill.err { color: var(--err); border-color: color-mix(in srgb, var(--err) 40%, transparent); background: color-mix(in srgb, var(--err) 12%, transparent); }
.pill.info { color: var(--info); border-color: color-mix(in srgb, var(--info) 40%, transparent); background: color-mix(in srgb, var(--info) 12%, transparent); }
.pill.neutral { color: var(--muted); border-color: var(--border); background: var(--hover); }

/* ── Lists ────────────────────────────────────────────────────────────────── */
.listTable { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.listTable caption {
  text-align: left; color: var(--faint); font-size: 11px;
  padding: 0 0 10px; letter-spacing: 0.02em;
}
.listTable th {
  text-align: left; color: var(--faint); font-weight: 600; font-size: 11px;
  letter-spacing: 0.04em; text-transform: uppercase;
  padding: 0 12px 8px 0; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.listTable td {
  padding: 8px 12px 8px 0; border-bottom: 1px solid var(--border-soft); vertical-align: top;
}
.listTable tbody tr:hover td { background: var(--accent-wash); }
.listTable td.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--mono); font-size: 11.5px; }
.tableScroll { overflow-x: auto; }

.pager { display: flex; align-items: center; gap: 7px; margin-top: 12px; }
.pagerText { color: var(--faint); font-size: 11.5px; }

/* Empty and failed states say what happened rather than showing nothing. */
.emptyState { text-align: center; padding: 34px 16px; color: var(--muted); }
.emptyState .glyph { font-size: 22px; display: block; margin-bottom: 8px; opacity: 0.8; }
.emptyState h3 { margin: 0 0 4px; font-size: 13.5px; font-weight: 700; color: var(--text); }
.emptyState p { margin: 0 auto; font-size: 12.5px; max-width: 52ch; }
.errorState { border-color: color-mix(in srgb, var(--err) 45%, transparent); }
.errorState h3 { color: var(--err); }

/* Skeletons. Same shape as the rows they stand in for, so nothing jumps. */
.skeletonRow { display: flex; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--border-soft); }
.skeletonBar {
  height: 10px; border-radius: 5px; flex: 1 1 auto; align-self: center;
  background: linear-gradient(90deg, var(--border-soft) 25%, var(--border) 37%, var(--border-soft) 63%);
  background-size: 400% 100%;
  animation: skeletonShimmer 1.4s ease infinite;
}
.skeletonBar.w-sm { flex: 0 0 18%; }
.skeletonBar.w-md { flex: 0 0 32%; }
@keyframes skeletonShimmer {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

/* ── Blade stack ──────────────────────────────────────────────────────────── */
/* Blades are the v2 detail panel (.colRight in the lakehouse catalog surface)
   given the whole viewport height and stacked: opaque --raise ground, a single
   hairline against the content, no blur and no glass. */
.bladeStack {
  position: fixed; top: 0; right: 0; bottom: 0;
  display: flex; justify-content: flex-end; z-index: 950;
  overflow-x: auto; max-width: calc(100vw - var(--rail-w));
  box-shadow: var(--shadow);
}
.blade {
  background: var(--raise);
  border-left: 1px solid var(--border);
  display: flex; flex-direction: column; height: 100%;
  flex: 0 0 auto; animation: bladeIn 0.16s ease-out;
}
@keyframes bladeIn {
  from { transform: translateX(24px); opacity: 0.4; }
  to { transform: translateX(0); opacity: 1; }
}
.blade.w-narrow { width: 360px; }
.blade.w-default { width: 440px; }
.blade.w-wide { width: 640px; max-width: 90vw; }
.bladeHead {
  flex: 0 0 auto; display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--panel);
}
.bladeTitles { flex: 1; min-width: 0; }
.bladeTitles h2 { margin: 0; font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.bladeTitles span { display: block; margin-top: 2px; font-size: 11.5px; color: var(--muted); }
.bladeBody { flex: 1; overflow-y: auto; padding: 14px 16px 24px; }

/* The v2 tab: 38px tall, no chrome of its own, a 2px accent underline inset
   from the label so the marker reads as belonging to the word and not to a box. */
.bladeTabs .tabRow { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.bladeTabs .tab {
  position: relative; height: 38px; padding: 0 12px;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 500; color: var(--muted);
}
.bladeTabs .tab:hover { color: var(--text); }
.bladeTabs .tab[aria-selected='true'] { color: var(--text); }
.bladeTabs .tab[aria-selected='true']::after {
  content: ''; position: absolute; left: 8px; right: 8px; bottom: -1px;
  height: 2px; border-radius: 2px; background: var(--accent);
}

/* ── Key/value and parameter presentation ─────────────────────────────────── */
.kv { display: grid; grid-template-columns: minmax(7rem, auto) 1fr; gap: 0; font-size: 12.5px; }
.kv dt { color: var(--muted); padding: 9px 12px 9px 0; border-bottom: 1px solid var(--border-soft); }
.kv dd { margin: 0; padding: 9px 0; border-bottom: 1px solid var(--border-soft); color: var(--text); }
.kv code { font-family: var(--mono); font-size: 11.5px; }

.paramCard { border: 1px solid var(--border); border-radius: 7px; padding: 10px 12px; background: var(--bg); }
.paramCard + .paramCard { margin-top: 8px; }
.paramHead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.paramName { font-weight: 600; font-size: 13px; }
.paramShape { font-family: var(--mono); font-size: 11.5px; color: var(--accent); }
.paramNote { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.paramNote b { color: var(--text); font-weight: 600; }

/* ── Run watch ────────────────────────────────────────────────────────────── */
.runLog { min-height: 6rem; }
.runConsole {
  background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius);
  max-height: 22rem; overflow: auto; padding: 6px 10px;
  font-family: var(--mono); font-size: 11.5px;
}
.logLine { display: flex; gap: 12px; align-items: baseline; padding: 3px 0; line-height: 1.5; }
.logLine + .logLine { border-top: 1px solid var(--border-soft); }
.logTime { color: var(--faint); flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.logLevel { flex: 0 0 9rem; font-weight: 600; letter-spacing: 0.02em; }
.logLevel.level-info { color: var(--accent); }
.logLevel.level-warn { color: var(--warn); }
.logMessage { color: var(--muted); white-space: pre-wrap; word-break: break-word; }
.launchForm label { color: var(--text); }

/* ── Editing: forms, refusals, switches, history, the document pane ───────── */
/* Every color below is a token. The refusal and accepted panels are the two
   places this interface uses a semantic color as a GROUND rather than as ink,
   so both are stated as a color-mix wash under a border of the same hue and the
   text on them stays --text: a paragraph set in --err on an --err wash is the
   combination that fails contrast in one theme and passes in the other. */

.rowActions { display: inline-flex; gap: 5px; }

.editForm { display: block; }
.formField { margin-bottom: 11px; }
.formField > label {
  display: block; font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 3px;
}
.fieldHint { margin: 3px 0 0; color: var(--muted); font-size: 11.5px; line-height: 1.45; }
/* Empty until a refusal names this field, so it takes no space in the layout
   and does not leave a gap that reads as a missing message. */
.fieldError:empty { display: none; }
.fieldError {
  margin: 4px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--err); font-weight: 600;
}
.fieldError span { display: block; }
/* The border is the SECOND signal, never the only one. aria-invalid is what a
   screen reader acts on and the message under the box is what says why; this is
   for the reader scanning a form for the box that went wrong. */
input[aria-invalid='true'], textarea[aria-invalid='true'], select[aria-invalid='true'] {
  border-color: var(--err);
}
.formGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: 10px; }
.formActions { display: flex; gap: 7px; margin-top: 12px; flex-wrap: wrap; }
.formRule { border: 0; border-top: 1px solid var(--border-soft); margin: 14px 0; }
.formStatus { margin: 0 0 10px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.cardNote { margin: 0 0 10px; color: var(--muted); font-size: 12px; line-height: 1.5; max-width: 74ch; }
.bladeNote { margin: 0 0 12px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.bladeActions { display: flex; gap: 6px; margin-bottom: 12px; }

.refusals, .accepted {
  border-radius: 7px; padding: 10px 12px; margin: 0 0 12px;
}
.refusals {
  border: 1px solid color-mix(in srgb, var(--err) 45%, transparent);
  background: color-mix(in srgb, var(--err) 9%, transparent);
}
.accepted {
  border: 1px solid color-mix(in srgb, var(--ok) 45%, transparent);
  background: color-mix(in srgb, var(--ok) 9%, transparent);
}
.refusals h3 { margin: 0 0 6px; font-size: 12.5px; font-weight: 700; color: var(--err); }
.accepted h3 { margin: 0 0 6px; font-size: 12.5px; font-weight: 700; color: var(--ok); }
.accepted p { margin: 0; font-size: 12px; line-height: 1.5; color: var(--text); }
.refusals ul { margin: 0; padding-left: 17px; }
.refusals li { margin: 0 0 5px; font-size: 12px; line-height: 1.5; color: var(--text); }
.refusals li:last-child { margin-bottom: 0; }
/* The field or the line a reason is about, set in the mono face so a key and a
   line number are scannable down the left of the list rather than buried in the
   sentence that follows them. */
.refusalWhere {
  display: inline-block; margin-right: 7px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--err);
}
.refusals:focus-visible, .accepted:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.policyRows { display: flex; flex-direction: column; gap: 6px; }
.policyRow { display: flex; align-items: center; gap: 9px; }
.policyName { font-size: 12.5px; color: var(--text); }
.iconBtn[aria-pressed='true'] {
  color: var(--accent); border-color: var(--accent-line); background: var(--accent-dim);
}

.historyList { list-style: none; margin: 0; padding: 0; }
.historyEntry { padding: 9px 0; border-bottom: 1px solid var(--border-soft); }
.historyEntry:last-child { border-bottom: 0; padding-bottom: 0; }
.historyHead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* A button rather than an anchor: it opens a panel on this screen, and an
   anchor that navigates nowhere is a promise to the middle mouse button that
   the interface cannot keep. It is styled as a link because that is what it
   behaves like to a reader. */
.versionLink {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
}
.versionLink:hover { text-decoration: underline; }
.historyAt { color: var(--faint); font-family: var(--mono); font-size: 10.5px; margin-left: auto; }
.historyDetail { margin: 4px 0 0; font-size: 12px; color: var(--muted); line-height: 1.5; }
.historyRationale { margin: 4px 0 0; font-size: 12px; color: var(--muted); line-height: 1.5; }
.historyRationale b { color: var(--text); font-weight: 600; }

.paneSection { margin-top: 18px; }
.paneSection > h3 { margin: 0 0 6px; font-size: 12.5px; font-weight: 700; }
.paneHeading { font-size: 12.5px; font-weight: 700; margin: 14px 0 4px; }
.paneProse { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.paneWarning { margin: 14px 0 0; font-size: 12px; line-height: 1.5; color: var(--warn); }

/* The document pane. A textarea and not a custom editor: see yamlPane() in
   screens/editing.js for why the coloring is worth less than the behavior. */
.yamlEditor {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55;
  background: var(--code-bg); resize: vertical; min-height: 16rem; white-space: pre;
}
.yamlEditor:disabled { color: var(--muted); }
input[type='file'] {
  width: 100%; font: inherit; font-size: 12px; color: var(--text);
  border: 1px solid var(--control-border); border-radius: 7px; padding: 6px 8px;
  background: var(--bg);
}

.versionScope { margin-top: 14px; }
.versionScope > h3 { margin: 0 0 5px; font-size: 12.5px; font-weight: 700; }
.versionParams { list-style: none; margin: 0; padding: 0; }
.versionParams li {
  display: flex; gap: 10px; align-items: baseline; justify-content: space-between;
  padding: 5px 0; border-bottom: 1px solid var(--border-soft); font-size: 12px;
}
.versionKey { font-family: var(--mono); font-size: 11px; color: var(--muted); }

/* ── Documentation area ───────────────────────────────────────────────────── */
.prose { max-width: 78ch; font-size: 13px; line-height: 1.6; color: var(--text); }
.prose h1 { font-size: 17px; font-weight: 700; letter-spacing: -0.015em; margin: 22px 0 8px; }
.prose h2 { font-size: 14px; font-weight: 700; margin: 20px 0 8px; }
.prose h3 { font-size: 12.5px; font-weight: 700; margin: 16px 0 6px; }
.prose p { margin: 0 0 11px; }
.prose ul, .prose ol { margin: 0 0 11px; padding-left: 20px; }
.prose li { margin: 0 0 5px; }
.prose code { font-family: var(--mono); font-size: 0.9em; background: var(--code-bg); padding: 1px 5px; border-radius: 4px; }
.prose pre {
  background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px 12px; overflow-x: auto; font-size: 11.5px;
}
.prose pre code { background: none; padding: 0; font-size: inherit; }
.prose table { border-collapse: collapse; width: 100%; margin: 0 0 11px; font-size: 12.5px; }
.prose th, .prose td { border-bottom: 1px solid var(--border-soft); padding: 7px 12px 7px 0; text-align: left; vertical-align: top; }
.prose th { color: var(--faint); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; border-bottom-color: var(--border); }
.prose blockquote { margin: 0 0 11px; padding-left: 12px; border-left: 2px solid var(--accent-line); color: var(--muted); }
.prose hr { border: 0; border-top: 1px solid var(--border-soft); margin: 18px 0; }
.prose a { text-decoration: underline; text-underline-offset: 2px; }
summary { cursor: pointer; }

.diagram {
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px; overflow-x: auto;
  /* The card is a preview: a pointer anywhere on it opens the viewer. The
     keyboard equivalent is the named expand control in the card head, so this
     is a second way in for a mouse rather than the only one. */
  cursor: zoom-in;
}
.diagram img { display: block; width: 100%; height: auto; min-width: 640px; }
.diagramNote { color: var(--faint); font-size: 12px; margin: 10px 0 0; line-height: 1.5; }

/* ── The diagram viewer ───────────────────────────────────────────────────── */
/* A full-screen port over one C4 render. Above the blade stack (950) because it
   is modal and there is nothing to compare it against; the tooltip on its own
   controls sits at 1200 inside this stacking context, so it still shows. */
.viewer {
  position: fixed; inset: 0; z-index: 1100;
  display: flex; flex-direction: column;
  background: var(--bg);
}
.viewerHead {
  flex: 0 0 auto; display: flex; align-items: center; gap: 7px;
  height: var(--top-h); padding: 0 12px;
  border-bottom: 1px solid var(--border); background: var(--panel);
}
.viewer .spacer { flex: 1 1 auto; }
.viewerTitles { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
.viewerTitles h2 {
  margin: 0; font-size: 13px; font-weight: 700; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.viewerTitles span {
  font-family: var(--mono); font-size: 10.5px; color: var(--faint); white-space: nowrap;
}
/* The magnification, in the mono face and tabular figures so the number does
   not shuffle the controls sideways every time it changes. */
.viewerZoom {
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  min-width: 5ch; text-align: right; font-variant-numeric: tabular-nums;
  margin-right: 3px;
}
/* The port. It CLIPS and never scrolls — the canvas inside it is transformed —
   so there is no scrollable region for a keyboard reader to be stranded in and
   no browser scroll competing with the pan. */
.viewerStage {
  flex: 1 1 auto; position: relative; overflow: hidden;
  background: var(--bg); touch-action: none; cursor: grab;
}
.viewerStage.isPanning { cursor: grabbing; }
.viewerStage:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; border-radius: 0; }
.viewerCanvas {
  position: absolute; top: 0; left: 0;
  transform-origin: 0 0; will-change: transform;
}
.viewerCanvas svg { display: block; }
.viewerMessage {
  position: absolute; inset: 0; margin: 0; display: grid; place-items: center;
  padding: 20px; text-align: center; color: var(--muted); font-size: 12.5px;
}

.docGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 14px; }
.docTile {
  display: block; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); padding: 14px 15px; color: var(--text);
}
.docTile:hover { border-color: var(--accent-line); background: var(--hover); text-decoration: none; }
.docTile h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 5px; font-size: 13px; font-weight: 700; }
.tileIcon { display: inline-flex; color: var(--accent); flex: 0 0 auto; }
.docTile:hover .tileIcon { color: var(--accent-hover); }
.docTile p { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.5; }

/* Gherkin rendering — the specification in the words it was written in.
   Each line is one block, and the block is what ends the line: the renderer
   emits no newline of its own, because inside a <pre> that would be a second
   break on top of this one and the whole specification would be double-spaced.
   min-height is what keeps a blank line in the source a blank line on screen
   now that it carries no character to give it height.

   The identity color marks the STRUCTURE keywords, which is the v2 convention
   for the token a reader scans for; steps take the positive color and tags the
   amber, so the three layers of a feature file separate without any of them
   competing with the accent. */
.gherkin { font-family: var(--mono); font-size: 11.5px; line-height: 1.6; }
.gherkin .gLine { display: block; white-space: pre-wrap; min-height: 1.6em; }
.gherkin .gTag { color: var(--warn); font-weight: 600; }
.gherkin .gKeyword { color: var(--accent); font-weight: 700; }
.gherkin .gStep { color: var(--text); }
.gherkin .gStepKeyword { color: var(--ok); font-weight: 700; }
.gherkin .gComment { color: var(--faint); font-style: italic; }
.gherkin .gNarrative { color: var(--muted); }
.gherkin .gTable { color: var(--text); }
/* A fenced Gherkin block in a rendered document is the same renderer's output
   inside .prose, so it inherits the code-block box and needs nothing but to
   stop inheriting .prose's own monospace size. */
.prose pre.gherkin { font-size: 11.5px; }
.featureItem + .featureItem { margin-top: 14px; }

/* ── The YAML formats page ────────────────────────────────────────────────────
   The same line-per-block arrangement the Gherkin renderer uses, and for the
   same reason: the block ends the line, so the renderer emits no newline of its
   own and min-height is what keeps a blank source line a blank rendered one.

   The document scrolls SIDEWAYS rather than wrapping. A YAML file's indentation
   is its structure, and a soft-wrapped derivation that reflows under its own key
   makes a nested map look like a sibling — which is the one thing an example of
   a format must not do. The box is focusable and named for the same reason the
   diagram is: a scroll container has to be reachable from the keyboard. */
.yamlDoc {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.6;
  background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 11px 13px; margin: 0; overflow-x: auto;
}
.yamlDoc:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.yamlDoc .yLine { display: block; white-space: pre; min-height: 1.6em; }
.yamlDoc .yComment { color: var(--faint); font-style: italic; }
.yamlDoc .yKey { color: var(--accent); font-weight: 700; }
.yamlDoc .yValue { color: var(--text); }
.yamlDoc .yItem { color: var(--ok); font-weight: 700; }
/* The elision is not part of the file and must never be mistaken for a comment
   in it: it is the page speaking, so it is marked rather than colored like one. */
.yamlDoc .yElision {
  display: block; white-space: pre-wrap; color: var(--muted);
  border-top: 1px dashed var(--border); margin-top: 6px; padding-top: 6px;
}

/* The callout marker: a link to the note, because a callout a keyboard reader
   cannot reach is a callout for pointer users only, and this is a Section 508
   delivery. Sized to clear the surrounding line without pushing it. */
.yMark {
  display: inline-block; margin-left: 8px; min-width: 15px; padding: 0 4px;
  border-radius: 999px; background: var(--accent); color: var(--on-accent);
  font-family: var(--sans); font-size: 10px; font-weight: 700; line-height: 15px;
  text-align: center; vertical-align: 1px; text-decoration: none;
}
.yMark:hover, .yMark:focus-visible { background: var(--accent-hover); text-decoration: none; }

.yamlNotes {
  margin: 12px 0 0; padding-left: 20px; max-width: 84ch;
  font-size: 12.5px; line-height: 1.6; color: var(--muted);
}
.yamlNotes li { margin: 0 0 7px; }
.yamlNotes li b { color: var(--text); }
/* A note jumped to from its marker is highlighted, so a reader who followed the
   link does not have to count list items to find where they landed. */
.yamlNotes li:target { color: var(--text); }
.yamlNotes li:target b { color: var(--accent); }

.formatNote {
  margin: 0 0 10px; max-width: 84ch; color: var(--muted);
  font-size: 12.5px; line-height: 1.55;
}
.downloadList { list-style: none; margin: 0; padding: 0; }
.downloadList li {
  display: flex; gap: 10px; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; padding: 7px 0; border-bottom: 1px solid var(--border-soft);
  font-size: 12.5px;
}
.downloadList a { font-weight: 600; }
.downloadMeta { color: var(--faint); font-family: var(--mono); font-size: 10.5px; }

/* ── Responsive: the blade becomes a full sheet, the rail a strip ─────────── */
/* ── Analytics: charts, stat tiles and the withheld state ─────────────────── */

/* A chart is responsive through its viewBox rather than through a measured
   width. Nothing here is sized in pixels on the page, so a chart in a blade,
   in a card and on a projector are the same drawing at three sizes. */
.chartFig { margin: 0 0 0.9rem; }
.chartFig + .chartFig { border-top: 1px solid var(--line-soft); padding-top: 0.7rem; }
.chartTitle { font-size: 0.8rem; font-weight: 800; color: var(--text); margin: 0 0 0.3rem; }
.chartCanvas { width: 100%; }
.chartCanvas svg { display: block; width: 100%; height: auto; }
.chartNote { margin: 0.3rem 0 0; color: var(--muted); font-size: 0.75rem; line-height: 1.45; }

.chartLegend {
  list-style: none; margin: 0.2rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.25rem 0.9rem;
  font-size: 0.76rem; color: var(--muted);
}
.chartLegend li { display: inline-flex; align-items: center; gap: 0.35rem; }
.legendSwatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; flex: none; }

/* SVG paints with fill and stroke rather than color, so every chart part gets
   its token here rather than an attribute at the call site. */
.gridLine { stroke: var(--chart-grid); stroke-width: 1; }
.axisLine { stroke: var(--chart-axis); stroke-width: 1; }
.chartTick { fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.chartLabel { fill: var(--text); font-size: 11px; }
.chartValue { fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.axisLabel { fill: var(--muted); font-size: 10px; font-weight: 700; letter-spacing: 0.03em; }
.bandArea { fill-opacity: var(--chart-band-opacity); stroke: none; }
.bandLine { stroke-width: 1.8; fill: none; stroke-linejoin: round; }
.bar { rx: 2; }
.rangeLine { stroke: var(--chart-axis); stroke-width: 1.6; }
.rangeCap { stroke: var(--chart-axis); stroke-width: 1.6; }
.rangeDot { fill: var(--chart-1); }
.emptyPlot { fill: var(--chart-withheld); stroke: var(--line); }
.emptyPlotText { fill: var(--muted); font-size: 12px; font-weight: 700; }

/* WITHHELD IS DRAWN. A suppressed measure that simply left a bar out is
   indistinguishable from a zero at a glance, and one of those is a claim about
   the world that nobody made. */
.withheldGround { fill: var(--chart-withheld); }
.withheldHatch { stroke: var(--chart-withheld-line); stroke-width: 1.4; }
.gapBand { stroke: none; }
.withheldBar { stroke: none; }
.withheldText { fill: var(--warn); color: var(--warn); font-size: 10px; font-weight: 800; }
.undefinedText { color: var(--muted); font-style: italic; }
.refText { color: var(--muted); }
.deltaUp, .deltaDown { color: var(--text); font-weight: 700; }
.deltaFlat { color: var(--muted); }

.statGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.5rem; }
.statTile {
  border: 1px solid var(--line); border-radius: 12px; background: var(--glass);
  padding: 0.55rem 0.7rem; display: flex; flex-direction: column; gap: 0.1rem;
}
.statTile.withheld { border-color: var(--warn); }
.statLabel { font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.statValue { font-size: 1.15rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.statTile.withheld .statValue { color: var(--warn); font-size: 0.95rem; }
.statTile.tone-warn .statValue { color: var(--warn); }
.statDetail { font-size: 0.72rem; color: var(--muted); line-height: 1.4; }

.runHeader { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.exportRow { display: inline-flex; gap: 0.3rem; align-items: center; }
.cardNote { margin: 0 0 0.6rem; color: var(--muted); font-size: 0.78rem; line-height: 1.5; max-width: 82ch; }
.cardNote.caveat { border-left: 3px solid var(--warn); padding-left: 0.6rem; color: var(--text); }

.selectorRow {
  display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
  margin: 0 0 0.75rem; font-size: 0.8rem;
}
.selectorRow label { font-weight: 700; color: var(--muted); }
.selectorRow select { width: auto; max-width: 100%; min-width: 12rem; }

.runChips { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; }
.runChip { display: inline-flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.runChooser {
  border: 1px solid var(--line); border-radius: 10px; margin: 0.6rem 0 0; padding: 0.4rem 0.7rem 0.6rem;
  display: flex; flex-wrap: wrap; gap: 0.3rem 0.9rem;
}
.runChooser legend { font-size: 0.72rem; font-weight: 800; color: var(--muted); padding: 0 0.3rem; }
.runCheck { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; }
.runCheck input { width: auto; }

.rowLabel { font-weight: 600; }
.runLink { display: block; font-size: 0.74rem; }
.withheldReason {
  display: flex; align-items: baseline; gap: 0.35rem; flex-wrap: wrap;
  margin-top: 0.15rem; color: var(--muted); font-size: 0.74rem; line-height: 1.4; max-width: 44ch;
}
.withheldBanner {
  border: 1px solid var(--warn); border-radius: 10px; background: var(--warn-bg);
  padding: 0.45rem 0.6rem; margin: 0 0 0.7rem;
}
.withheldBanner p { margin: 0.25rem 0 0; font-size: 0.78rem; line-height: 1.45; color: var(--text); }

/* ── Responsive: the blade becomes a full sheet, the nav a rail ───────────── */
@media (max-width: 900px) {
  .rail { width: var(--rail-w-min); }
  .rail .brandBand { padding: 0; justify-content: center; }
  .rail .brandName, .rail .navLabel, .rail .railFoot { display: none; }
  .rail .ghead { justify-content: center; padding: 0; }
  .rail .navItem { justify-content: center; padding: 0; }
  .mainBody { padding: 12px 14px 32px; }
  .bladeStack { max-width: 100vw; }
  .blade, .blade.w-narrow, .blade.w-default, .blade.w-wide { width: 100vw; max-width: 100vw; }
  .diagram img { min-width: 520px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .skeletonBar, .blade { animation: none; }
}
