/* ============================================================================
   suite.css — the Structural Suite component layer.        Brand v3.6, Rule H.

   v3.6 (2026-08-25) added the "Apple-premium" screen layer proven out on the
   masonry app: .ui-icon-seg/.ui-icon-btn (momentary icon buttons), a suite-wide
   input[type=range] retheme, .ui-modal-* (quiet read-off overlay — replaces a
   solid --accent-deep header bar on in-canvas explainer popovers), and
   .ui-cell-tint/.ui-cell-pass/-warn/-fail (tinted status cells). All four
   existed as one-off, per-app code before; see STYLE_GUIDE.md → "Component
   patterns — the Apple-premium screen layer" for the governing rule (one
   resolved accent, state via label/shape/position, never a second hue) and
   masonry-wall-design.html as the reference implementation.

   WHY THIS FILE EXISTS
   Before it, the suite had no stylesheet at all. 34 pages carried 5,364 hard-coded
   hex values across 203 unique colours (the brand defines 14) and 4,815 inline
   style= attributes, so 41% of every colour decision bypassed :root entirely and
   no amount of token editing could reach it. There was nothing to restyle.

   This is not a new design invented from scratch. It is `Sign In.html`'s vocabulary
   — .field / .ctrl / .btn / .tabs / .meter — promoted to a shared file and extended.
   Sign In was the one page in the suite auditing at zero B-FONT and zero B-HEX, so
   it was already the house style; it just had no way to leave its own <head>.

   HARD RULES FOR EDITING THIS FILE
   1. NO hex literals. Every colour comes from a brand token, or the file has
      defeated its own purpose. `branding_audit.py` blocks on B-HEX.
   2. NO font-size below 11px, and no font-weight 700 (v3.5 deprecated it).
   3. A NUMBER IS ALWAYS MONO. --font-ui carries labels and prose; --font-mono
      carries every value, unit, clause tag and ID. See `.num`.
   4. NOTHING here may touch report content (.a4p / .rp / .sh-*). The calc sheet
      has its own palette, its own face and its own flow pagination, and it is the
      one artefact a client keeps. Screen chrome only.
   5. All motion is screen-only, dead in print, and dead under reduced-motion.

   LOAD ORDER MATTERS. This must come AFTER <style id="arcos-branding"> so the
   tokens exist, and BEFORE the page's own <style> so a page can still override.
   ========================================================================== */

/* ── Density ────────────────────────────────────────────────────────────────
   The concrete wall app's input rail holds 34 controls inside a
   `height:100vh; overflow:hidden` shell. Inflating every control from ~22px to
   ~34px to hit modern hit-target sizes adds roughly 400px of scrolling to the
   primary work surface — prettier and slower. So control sizing is a variable,
   not a constant, and the engineer picks.

   Comfortable is the default. Compact keeps the roomier type and shape but pulls
   the vertical rhythm back to something close to the old density, for a laptop
   or a 12-column DCR table. Set data-density="compact" on <html>.            */
:root{
  --ui-ctl-h:34px;      /* control height          */
  --ui-ctl-px:11px;     /* control inner padding-x */
  --ui-gap:12px;        /* field-to-field gap      */
  --ui-pad:16px;        /* card padding            */
  --ui-fs-ctl:14px;     /* value text in a control */
  --ui-fs-lbl:11.5px;   /* field label             */
  --ui-fs-sm:11.5px;    /* secondary / hint text   */
  --ui-row-py:7px;      /* table cell padding-y    */

  /* Motion. One duration scale and one easing so nothing feels hand-timed.
     --ui-ease is a decelerate curve: fast out of the gate, gentle into rest. */
  --ui-fast:.16s;
  --ui-mid:.28s;
  --ui-slow:2.4s;
  --ui-ease:cubic-bezier(.22,1,.36,1);
  --ui-ease-io:cubic-bezier(.4,0,.2,1);
}
:root[data-density="compact"]{
  --ui-ctl-h:27px; --ui-ctl-px:8px; --ui-gap:7px; --ui-pad:11px;
  --ui-fs-ctl:12.5px; --ui-fs-lbl:11px; --ui-fs-sm:11px; --ui-row-py:4px;
}

/* ── Type utilities ─────────────────────────────────────────────────────────
   .num is the load-bearing one. --font-ui is proportional, so ANY figure left
   to inherit from it loses column alignment and stops reading as a solver
   readout. tabular-nums is what keeps a DCR column vertically aligned when the
   digits change.                                                             */
.num,.ui-num{
  font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}
.ui-lbl{
  display:block;font-family:var(--font-ui);font-size:var(--ui-fs-lbl);
  font-weight:500;color:var(--ink-muted);margin-bottom:5px;letter-spacing:0;
}
.ui-hint{font-size:var(--ui-fs-sm);color:var(--ink-faint);margin-top:5px;line-height:1.45}
.ui-err{font-size:var(--ui-fs-sm);color:var(--force);margin-top:5px;display:none}
.ui-err.show{display:block}
/* Section divider. UPPERCASE survives here on purpose — as engineering furniture
   marking a boundary, not as a label shouting a field name. */
.ui-div{
  display:flex;align-items:center;gap:10px;margin:18px 0 11px;
  font-family:var(--font-ui);font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:.09em;color:var(--accent-strong);
}
.ui-div::before,.ui-div::after{content:"";flex:1;height:1px;background:var(--report-border-soft)}
.ui-div::before{flex:0 0 0}

/* ── Fields ─────────────────────────────────────────────────────────────────
   Promoted from Sign In's .field/.ctrl. The unit chip is the addition: carrying
   "(mm)" inside the control instead of in the label buys back a whole line per
   field, which is most of what pays for the taller control.                   */
.ui-field{margin-bottom:var(--ui-gap)}
.ui-field-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--ui-gap);margin-bottom:var(--ui-gap)}
.ui-ctl-wrap{position:relative;display:flex;align-items:center}
.ui-ctl{
  width:100%;height:var(--ui-ctl-h);
  font-family:var(--font-ui);font-size:var(--ui-fs-ctl);color:var(--ink);
  background:var(--surface);border:1px solid var(--report-border);
  border-radius:var(--r-md);padding:0 var(--ui-ctl-px);
  box-shadow:var(--shadow-inset);
  transition:border-color var(--ui-fast),box-shadow var(--ui-fast);
}
/* A control holding a VALUE is mono; a control holding a name or a sentence is not. */
.ui-ctl[type=number],.ui-ctl.num,input.ui-ctl[inputmode=decimal]{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
textarea.ui-ctl{height:auto;padding:8px var(--ui-ctl-px);line-height:1.5}
select.ui-ctl{cursor:pointer}
.ui-ctl::placeholder{color:var(--ink-faint)}
.ui-ctl:focus{outline:none;border-color:var(--accent);box-shadow:var(--focus-ring)}
.ui-ctl:disabled,.ui-ctl[readonly]{background:var(--glass-panel);color:var(--ink-muted);cursor:default}
.ui-ctl.invalid{border-color:var(--force);box-shadow:0 0 0 3.5px var(--force-soft)}
.ui-ctl-wrap .ui-unit{
  position:absolute;right:7px;pointer-events:none;
  font-family:var(--font-ui);font-size:11px;font-weight:500;color:var(--ink-faint);
  background:var(--surface-alt);border-radius:var(--r-sm);padding:2px 6px;
}
.ui-ctl-wrap:has(.ui-unit) .ui-ctl{padding-right:46px}
/* Number spinners stay suppressed suite-wide: the arrows steal the click target in
   a narrow cell and a stray scroll over a focused field silently alters a design
   action. Typing remains the only way in. */
.ui-ctl[type=number]::-webkit-outer-spin-button,
.ui-ctl[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}
.ui-ctl[type=number]{-moz-appearance:textfield;appearance:textfield}

.ui-check{display:flex;align-items:center;gap:7px;font-family:var(--font-ui);
  font-size:var(--ui-fs-sm);color:var(--structure);cursor:pointer;margin-bottom:6px}
.ui-check input{accent-color:var(--accent);width:15px;height:15px;flex-shrink:0;margin:0}

/* ── Range slider ───────────────────────────────────────────────────────────
   Native inputs render with the OS accent (bold Windows blue) no matter what
   the brand says — the one control that was silently breaking the "one
   resolved accent" rule on every dₙ / depth / angle slider in the suite.
   Retheme every <input type=range> here so it never has to be done per-app.  */
input[type=range]{-webkit-appearance:none;appearance:none;height:4px;border-radius:99px;
  background:color-mix(in srgb,var(--ink) 8%,var(--surface));outline:none;cursor:pointer;
  vertical-align:middle}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:15px;height:15px;border-radius:50%;background:var(--accent-deep);
  box-shadow:var(--shadow-md);border:2px solid var(--surface);cursor:pointer;
  transition:transform var(--ui-fast)}
input[type=range]::-webkit-slider-thumb:hover{transform:scale(1.15)}
input[type=range]::-moz-range-track{height:4px;border-radius:99px;
  background:color-mix(in srgb,var(--ink) 8%,var(--surface))}
input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;
  background:var(--accent-deep);box-shadow:var(--shadow-md);border:2px solid var(--surface);cursor:pointer}
input[type=range]:focus-visible::-webkit-slider-thumb,
input[type=range]:focus-visible::-moz-range-thumb{box-shadow:var(--focus-ring)}

/* ── Buttons ────────────────────────────────────────────────────────────────
   v3.4 primaries were uppercase/700. v3.5 is sentence case at 600: the shout was
   doing the work that hierarchy should do.                                    */
.ui-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  height:var(--ui-ctl-h);padding:0 16px;
  font-family:var(--font-ui);font-size:13px;font-weight:600;letter-spacing:-.005em;
  border:1px solid transparent;border-radius:var(--r-md);cursor:pointer;
  transition:background var(--ui-fast),box-shadow var(--ui-fast),transform var(--ui-fast);
  white-space:nowrap;
}
.ui-btn:active{transform:scale(.985)}
.ui-btn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.ui-btn[disabled],.ui-btn.is-disabled{opacity:.5;cursor:not-allowed;transform:none}

.ui-btn-primary{
  background:var(--accent);color:var(--accent-ink);
  box-shadow:var(--shadow-sm);
}
.ui-btn-primary:hover:not([disabled]){background:var(--accent-strong)}

/* Secondary is the brand's frosted glass (v3.2), referenced through the tokens —
   never re-typed as rgba, or a brand edit stops reaching it. */
.ui-btn-glass{
  background:var(--glass-bg);
  -webkit-backdrop-filter:var(--glass-filter);backdrop-filter:var(--glass-filter);
  border-color:var(--border-focus);color:var(--accent);
  box-shadow:var(--glass-shadow);
}
.ui-btn-glass:hover:not([disabled]){background:var(--accent-soft);color:var(--accent-strong)}

.ui-btn-ghost{background:transparent;color:var(--ink-muted)}
.ui-btn-ghost:hover:not([disabled]){background:var(--surface-alt);color:var(--ink)}
.ui-btn-danger{background:var(--force);color:var(--accent-ink)}
.ui-btn-sm{height:auto;padding:5px 11px;font-size:12px}

/* ── Segmented control ──────────────────────────────────────────────────────
   Replaces the flat 9-tab strip. The indicator is a real element so it can
   TRANSLATE between positions; a moving highlight tells the engineer which way
   through the workflow they just went, which an instant repaint cannot.
   Drive it from JS: position .ui-seg-ind over the active button.              */
.ui-seg{position:relative;display:flex;gap:2px;padding:4px;background:var(--surface-alt);
  border-radius:var(--r-lg);overflow-x:auto;scrollbar-width:none}
.ui-seg::-webkit-scrollbar{display:none}
.ui-seg-ind{
  position:absolute;top:4px;left:0;height:calc(100% - 8px);
  background:var(--surface);border-radius:var(--r-md);
  box-shadow:var(--shadow-md);
  transition:transform var(--ui-mid) var(--ui-ease-io),width var(--ui-mid) var(--ui-ease-io);
  pointer-events:none;z-index:0;
}
.ui-seg-btn{
  position:relative;z-index:1;flex:0 0 auto;
  padding:7px 14px;border:0;background:none;cursor:pointer;
  font-family:var(--font-ui);font-size:12.5px;font-weight:500;letter-spacing:-.005em;
  color:var(--ink-muted);white-space:nowrap;transition:color var(--ui-fast);border-radius:var(--r-md);
}
.ui-seg-btn:hover{color:var(--structure)}
.ui-seg-btn.active{color:var(--ink);font-weight:600}
/* A stage you have not reached yet is NOT forbidden — the padlock read as a
   permission error. Muted + a quiet dot says "not yet", which is the truth. */
.ui-seg-btn.pending{color:var(--ink-faint);cursor:not-allowed}
.ui-seg-btn.pending::after{
  content:"";display:inline-block;width:4px;height:4px;border-radius:50%;
  background:currentColor;margin-left:7px;vertical-align:middle;opacity:.6;
}

/* ── Icon-button group ──────────────────────────────────────────────────────
   Momentary actions (zoom, jump-to, view step) are not a persisted selection
   like .ui-seg, so there is no .active state: every button looks the same at
   rest and only ghost-hovers on its own. Replaces the old per-app zoom/jump
   button rows that each hand-picked their own border/background.            */
.ui-icon-seg{display:inline-flex;gap:2px;padding:2px;
  background:color-mix(in srgb,var(--ink) 5%,var(--surface));border-radius:var(--r-md)}
.ui-icon-btn{width:24px;height:22px;display:inline-flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:600;font-family:var(--font-ui);border:none;background:none;
  border-radius:var(--r-sm);color:var(--ink-muted);cursor:pointer;padding:0;line-height:1;
  transition:background-color var(--ui-fast),color var(--ui-fast)}
.ui-icon-btn:hover{background:var(--surface);color:var(--ink)}
.ui-icon-btn[disabled]{opacity:.4;cursor:not-allowed}

/* ── Chart / table card ────────────────────────────────────────────────────
   A Plotly chart or data table sat in a flat box: hard 1px grey border, no
   shadow. Same "elevated, not boxed" treatment as .ui-card, sized for a
   chart or table container rather than a form card (no padding — the
   Plotly canvas or <table> fills it edge to edge). */
.nm-chart-card{position:relative;background:var(--surface);border:1px solid var(--report-border-soft);
  border-radius:var(--r-lg);box-shadow:var(--shadow-md);overflow:hidden;flex-shrink:0}

/* ── Cards & panels ─────────────────────────────────────────────────────────*/
.ui-card{
  background:var(--surface);border:1px solid var(--report-border-soft);
  border-radius:var(--r-lg);padding:var(--ui-pad);
  box-shadow:var(--shadow-sm);
}
.ui-card-glass{
  background:var(--glass-bg);
  -webkit-backdrop-filter:var(--glass-filter);backdrop-filter:var(--glass-filter);
  border:1px solid var(--glass-edge);border-radius:var(--r-lg);
  box-shadow:var(--glass-shadow);position:relative;
}
.ui-card-glass::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:var(--glass-sheen);border-radius:inherit;z-index:0}
.ui-card-glass>*{position:relative;z-index:1}
.ui-card-hd{display:flex;align-items:baseline;gap:9px;margin-bottom:11px;
  font-family:var(--font-ui);font-size:13px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
.ui-card-hd .sub{font-size:11.5px;font-weight:400;color:var(--ink-faint);margin-left:auto}

/* ── Modal / read-off overlay ──────────────────────────────────────────────
   The "explain this calculation" popover (click a capacity cell, see the
   step-by-step) used to open on a solid --accent-deep header bar with a white
   title — the same treatment as the page masthead. On a small in-canvas
   popover that reads as an alert, not an explanation. The masthead keeps its
   bar; this component is quiet instead: a hairline card on --surface, no
   colour-block header. z-index sits above Plotly's own layered elements
   (modebar, hover layer), which is why it is a specific large constant and
   not a token — nothing else in the suite needs to out-stack it.            */
.ui-modal-scrim{position:fixed;inset:0;background:rgba(15,23,42,.45);
  z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:16px}
.ui-modal-card{background:var(--surface);border:1px solid var(--report-border-soft);
  border-radius:var(--r-lg);max-width:var(--ui-modal-w,560px);width:94%;max-height:88vh;
  overflow:auto;box-shadow:var(--shadow-lg)}
.ui-modal-hd{display:flex;align-items:flex-start;gap:12px;padding:15px 20px;
  background:var(--surface-alt);border-bottom:1px solid var(--report-border-soft)}
.ui-modal-title{font-family:var(--font-ui);font-size:14px;font-weight:600;
  color:var(--ink);letter-spacing:-.005em;flex:1}
.ui-modal-sub{font-family:var(--font-ui);font-size:11.5px;color:var(--ink-muted);margin-top:2px}
.ui-modal-close{width:26px;height:26px;flex-shrink:0;border-radius:50%;border:none;
  background:transparent;color:var(--ink-muted);display:inline-flex;align-items:center;
  justify-content:center;cursor:pointer;font-size:15px;line-height:1;
  transition:background var(--ui-fast),color var(--ui-fast)}
.ui-modal-close:hover{background:var(--report-border-soft);color:var(--ink)}
.ui-modal-body{padding:18px 20px;font-family:var(--font-ui);font-size:13px;
  line-height:1.6;color:var(--structure)}
/* The one accent-tinted callout inside a modal body — a formula highlight, an
   advisory note. Same "one resolved accent, not a rainbow" rule as diagrams. */
.ui-modal-note{background:var(--accent-soft);border-left:3px solid var(--accent-deep);
  border-radius:var(--r-sm);padding:10px 12px;margin:10px 0}

/* ── Tinted status cell ────────────────────────────────────────────────────
   A DCR/ratio table cell reading pass/warn/fail entirely from an inline
   hand-picked 3-colour band, copy-pasted per app. One resolved token, tinted
   12% into the surface for the fill, full-strength for the text — same
   token, two weights, never a second hue.                                   */
.ui-cell-tint{padding:2px 7px;border-radius:var(--r-sm);font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;font-weight:500;display:inline-block}
.ui-cell-pass{background:color-mix(in srgb,var(--reaction) 12%,var(--surface));color:var(--reaction)}
.ui-cell-warn{background:color-mix(in srgb,var(--warn) 12%,var(--surface));color:var(--warn)}
.ui-cell-fail{background:color-mix(in srgb,var(--force) 12%,var(--surface));color:var(--force)}

/* ── Check-row list ─────────────────────────────────────────────────────────
   A pre-qualifying/detailing checklist as flat cards, not a <table> — an icon,
   the name + provided value as a headline, then clause/formula/required as a
   quiet subtitle line. Replaces a <tr>-per-check table whose only way to show
   state was tinting the whole row background (or, worse, not tinting it at
   all and leaving every row the same colour). Ported from masonry-wall-design.html's
   buildChecks()/.ck-row. .ck-i's background was a one-off hardcoded blue
   (#1d9bf0) in the app it was ported from — resolved to --accent-deep here so
   the promotion doesn't carry the violation forward. */
.ck-list{border:1px solid var(--report-border-soft);border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
.ck-grp{padding:7px 14px;font-family:var(--font-ui);font-size:10.5px;font-weight:600;color:var(--ink-muted);background:var(--surface-alt);border-bottom:1px solid var(--report-border-soft)}
.ck-grp:not(:first-child){border-top:1px solid var(--report-border-soft)}
.ck-row{display:flex;align-items:flex-start;gap:10px;padding:10px 14px;border-bottom:1px solid var(--report-border-soft)}
.ck-list>.ck-row:last-child{border-bottom:none}
.ck-ico{font-size:13px;font-weight:700;width:16px;text-align:center;display:inline-block;flex:none;margin-top:1px}
.ck-ico.ok{color:var(--reaction)}.ck-ico.warn{color:var(--warn)}.ck-ico.no{color:var(--force)}.ck-ico.info{color:var(--accent)}
.ck-main{flex:1;min-width:0}
.ck-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.ck-name{font-family:var(--font-ui);font-size:12.5px;font-weight:500;color:var(--ink)}
.ck-val{font-family:var(--font-ui);font-variant-numeric:tabular-nums;font-size:12px;font-weight:600;color:var(--ink);white-space:nowrap;flex:none}
.ck-sub{font-family:var(--font-ui);font-size:11px;color:var(--ink-muted);margin-top:2px;line-height:1.5}
.clref{font-family:var(--font-mono);color:var(--warn);font-size:10px;font-weight:600;white-space:nowrap}
.ck-formula{font-family:var(--font-mono)}
.ck-req{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--ink)}
.ck-bar{margin-top:7px;max-width:220px;display:flex;align-items:center;gap:5px}
.ck-bar .ui-bar-trk{flex:1}
.ck-tight{flex:none;display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--warn)}
.ck-i{cursor:pointer;background:var(--accent-deep);color:var(--accent-ink);border:none;border-radius:50%;width:15px;height:15px;font-size:11px;font-weight:600;font-style:normal;text-transform:none;letter-spacing:0;font-family:Georgia,'Times New Roman',serif;line-height:1;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;vertical-align:middle;margin-top:1px}
.ck-foot{margin-top:8px;display:flex;gap:18px;font-size:11.5px;font-weight:600;padding:7px 4px;border-top:1px solid var(--report-border)}
.ck-foot .p{color:var(--reaction)}.ck-foot .f{color:var(--force)}.ck-foot .w{color:var(--warn)}

/* ── Stat tile ──────────────────────────────────────────────────────────────*/
.ui-stat{background:var(--surface-alt);border-radius:var(--r-md);padding:10px 12px}
.ui-stat-v{font-family:var(--font-mono);font-variant-numeric:tabular-nums;
  font-size:19px;font-weight:500;color:var(--ink);letter-spacing:-.02em;line-height:1.1}
.ui-stat-l{font-family:var(--font-ui);font-size:11px;color:var(--ink-muted);margin-top:4px}

/* ── Verdict ────────────────────────────────────────────────────────────────
   The governing DCR used to be an 11px cell somewhere in a 12-column table,
   with pass/fail carried by TEXT COLOUR ALONE — which fails a greyscale print
   and a colour-blind reviewer, and is a correctness problem before it is a
   styling one. .ui-pill therefore always pairs the colour with a glyph AND a
   word; never ship one without both.                                          */
.ui-verdict{display:flex;gap:18px;align-items:center;flex-wrap:wrap}
.ui-pill{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--font-ui);font-size:12px;font-weight:600;
  padding:4px 11px;border-radius:999px;border:1px solid;
}
.ui-pill-pass{background:var(--reaction-soft);border-color:var(--reaction);color:var(--reaction)}
.ui-pill-fail{background:var(--force-soft);border-color:var(--force);color:var(--force)}
.ui-pill-warn{background:var(--warn-soft);border-color:var(--warn);color:var(--warn)}

/* Ring gauge. Reads the MARGIN, which is the thing the table never showed: 0.93
   and 0.41 are both "pass" and are not remotely the same result. Set
   --ui-arc (0..1) inline; stroke-dasharray 264 ≈ 2πr for r=42. */
.ui-gauge{position:relative;width:104px;height:104px;flex-shrink:0}
.ui-gauge svg{width:100%;height:100%;transform:rotate(-90deg)}
.ui-gauge-trk{fill:none;stroke:var(--surface-alt);stroke-width:9}
.ui-gauge-arc{
  fill:none;stroke-width:9;stroke-linecap:round;stroke:var(--accent);
  stroke-dasharray:264;stroke-dashoffset:264;
}
.ui-gauge.run .ui-gauge-arc{
  animation:ui-sweep var(--ui-slow) var(--ui-ease) forwards;
  --ui-off:calc(264 - 264 * var(--ui-arc,0));
}
.ui-gauge-arc.pass{stroke:var(--reaction)}
.ui-gauge-arc.warn{stroke:var(--warn)}
.ui-gauge-arc.fail{stroke:var(--force)}
.ui-gauge-c{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center}
.ui-gauge-v{font-family:var(--font-mono);font-variant-numeric:tabular-nums;
  font-size:25px;font-weight:500;letter-spacing:-.02em;line-height:1;color:var(--ink)}
.ui-gauge-k{font-family:var(--font-ui);font-size:11px;font-weight:600;
  letter-spacing:.08em;color:var(--ink-faint);margin-top:4px;text-transform:uppercase}
@keyframes ui-sweep{to{stroke-dashoffset:var(--ui-off)}}

/* Per-check margin bars. Set --ui-w inline. */
.ui-bars{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px 18px}
/* min-width:0 is load-bearing on a grid child: without it a long limit-state name
   sets the track's minimum and the row stops fitting its column. */
.ui-bar{min-width:0}
.ui-bar-hd{display:flex;align-items:baseline;gap:8px;font-family:var(--font-ui);
  font-size:11.5px;color:var(--ink-muted)}
.ui-bar-hd>span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ui-bar-hd .v{margin-left:auto;flex:none;font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;font-weight:500;color:var(--structure)}
.ui-bar-trk{height:5px;background:var(--surface-alt);border-radius:3px;margin-top:5px;overflow:hidden}
.ui-bar-fil{height:100%;border-radius:3px;width:0;background:var(--accent)}
.ui-bar-fil.pass{background:var(--reaction)}
.ui-bar-fil.warn{background:var(--warn)}
.ui-bar-fil.fail{background:var(--force)}
.run .ui-bar-fil{animation:ui-grow 1.5s var(--ui-ease) forwards}
@keyframes ui-grow{to{width:var(--ui-w,0)}}

/* ── Tables ─────────────────────────────────────────────────────────────────
   Sticky cells REQUIRE border-collapse:separate — Chromium silently ignores
   position:sticky on a th/td inside a collapsed table, which reads as a
   half-broken freeze rather than a whole-table one. This is opt-in via .ui-tbl
   so it never converts a table whose row separators live on the <tr> (those do
   not paint in separate mode and would vanish).                               */
.ui-tbl{width:100%;border-collapse:separate;border-spacing:0;
  font-family:var(--font-ui);font-size:12.5px}
.ui-tbl th{
  position:sticky;top:0;z-index:2;text-align:left;
  padding:var(--ui-row-py) 9px;background:var(--surface-alt);color:var(--ink-muted);
  font-size:11px;font-weight:600;white-space:nowrap;
  border-bottom:1px solid var(--report-border);
}
.ui-tbl td{padding:var(--ui-row-py) 9px;border-bottom:1px solid var(--report-border-soft);
  vertical-align:middle;color:var(--structure)}
/* Any cell holding a figure. Right-aligned + mono is what makes a column scannable. */
.ui-tbl td.num,.ui-tbl th.num{text-align:right;font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;color:var(--ink)}
.ui-tbl tbody tr{transition:background var(--ui-fast)}
.ui-tbl tbody tr:hover{background:var(--accent-soft)}
.ui-tbl tr.gov>td{background:var(--accent-soft);font-weight:600}

/* ── Stale results ──────────────────────────────────────────────────────────
   Replaces the full-width red "OUTDATED — RE-RUN" banner. Desaturating the old
   answer says the same thing without an alarm: the number is still there, it has
   simply stopped being current. Alarm colour is reserved for a FAILED check, so
   that it still means something when it appears.                              */
.ui-stale{filter:grayscale(1);opacity:.45;transition:filter var(--ui-mid),opacity var(--ui-mid)}
.ui-stale-tag{font-family:var(--font-ui);font-size:11.5px;font-weight:500;
  color:var(--ink-faint);display:inline-flex;align-items:center;gap:6px}

/* ── Entrance ───────────────────────────────────────────────────────────────
   6px, once, on a panel becoming visible. Never on report content, and never on
   a Plotly container: a transform on a plot's parent corrupts the layout metrics
   Plotly measured, so charts must appear without moving.                       */
.ui-rise{animation:ui-rise var(--ui-mid) var(--ui-ease) backwards}
.ui-rise-2{animation-delay:.06s}
.ui-rise-3{animation-delay:.12s}
@keyframes ui-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* ── Accessibility & print ──────────────────────────────────────────────────*/
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.ui-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
  /* The gauge and bars are STATE, not decoration — if the animation is suppressed
     they must still show their final value, not sit empty at zero. */
  .ui-gauge.run .ui-gauge-arc{stroke-dashoffset:var(--ui-off)}
  .run .ui-bar-fil{width:var(--ui-w,0)}
}

@media print{
  /* Glass, shadow and motion are screen-only; panels fall back to solid surface
     with a hairline rule so PDF fidelity is preserved. */
  .ui-card,.ui-card-glass,.ui-btn-glass{
    -webkit-backdrop-filter:none!important;backdrop-filter:none!important;
    background:var(--surface)!important;border:1px solid var(--report-border-soft)!important;
    box-shadow:none!important;
  }
  .ui-card-glass::before{display:none}
  .ui-rise,.run .ui-bar-fil,.ui-gauge.run .ui-gauge-arc{animation:none!important}
  .run .ui-bar-fil{width:var(--ui-w,0)}
  .ui-gauge.run .ui-gauge-arc{stroke-dashoffset:var(--ui-off)}
  .ui-stale{filter:none;opacity:1}
  .ui-seg,.ui-seg-ind{background:transparent;box-shadow:none}
  /* Ephemeral, JS-opened chrome — never part of an issued document. */
  .ui-modal-scrim{display:none!important}
}
