/* Generated from the <style> block of Pages/Calculators.cshtml by client/tools/build-calculator-page-css.mjs; do not edit. */
        :root {
            --bg:#f4f5f8; --surface:#fff; --ink:#10131c; --muted:#606a7b;
            --line:#e7e9ef; --line-strong:#d5d9e2;
            --required-ink:#b42318;
            /* Schema 0.19 conditional tables use the page palette rather than the
               workbook's advisory hex values. Light mode matches Excel's familiar
               three-colour scale; the dark tokens preserve the meaning and contrast. */
            --conditional-worse:#F8696B; --conditional-middle:#FFEB84;
            --conditional-better:#63BE7B; --conditional-cell-ink:#10131c;
            --radius:16px; --radius-sm:11px;
            /* ONE VERTICAL RHYTHM FOR THE WHOLE CALCULATOR COLUMN (#378).
               Item 4 replaced four literals with TWO TOKENS, and two tokens is how the
               numbers disagree in a way that survives a grep: --block-gap spaced the
               results region while --tile-gap spaced the grids INSIDE it, so one page
               ran at 18px between the cards and the chart and 14px between the hero card
               and the detail row beneath it — measured on
               activated-sludge-fm-ratio-calculator, filed as #378, reported twice.
               There is now ONE number, and a second one cannot be introduced without
               introducing a second token, which The_calculator_column_has_one_vertical_
               rhythm refuses. Horizontal spacing is deliberately NOT this token: a
               two-column workspace gap is not a rhythm the eye reads down the page. */
            --block-gap:18px;
            --shadow-sm:0 1px 2px rgba(16,19,28,.04), 0 2px 6px rgba(16,19,28,.05);
            --shadow-md:0 12px 34px -14px rgba(16,19,28,.28);
            --font-ui:"Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
            --font-display:"Fraunces", Georgia, "Times New Roman", serif;
        }
        * { box-sizing:border-box; }
        html { -webkit-text-size-adjust:100%; }
        /* The homepage's white ground (editorial restyle, Ugur 2026-10-20); --bg stays the
           tint the workspaces and code use. */
        body { margin:0; font-family:var(--font-ui); color:var(--ink); background:var(--surface);
               line-height:1.5; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }

        /* ---- Hero / character block ---- */
        .hero { position:relative; overflow:hidden; color:var(--hero-ink);
                background:linear-gradient(140deg, var(--accent), var(--accent-strong));
                padding:44px 20px 92px; }
        /* Editorial restyle (Ugur, 2026-10-20): no motif layer and no emoji icon tile. */
        .hero-inner { position:relative; z-index:2; max-width:1040px; margin:0 auto; display:flex; gap:22px; align-items:center; }
        /* The breadcrumb, as on the category page (category.css .crumbs). */
        .crumbs { max-width:1180px; margin:0 auto; padding:22px 20px 0; font-size:.82rem; color:var(--muted); }
        .crumbs ol { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:6px; }
        .crumbs li { min-width:0; overflow-wrap:anywhere; }
        .crumbs li + li::before { content:"\203A"; margin-right:6px; color:var(--line-strong); }
        .crumbs a { color:var(--muted); text-decoration:none; }
        .crumbs a:hover { color:var(--ink); text-decoration:underline; }
        .crumbs a:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:4px; }
        .crumbs [aria-current] { color:var(--ink); font-weight:600; }
        .hero-eyebrow { text-transform:uppercase; letter-spacing:.14em; font-size:.7rem; font-weight:600; opacity:.85; }
        .hero h1 { font-family:var(--font-display); font-weight:600; font-optical-sizing:auto;
                   font-size:clamp(1.75rem, 3.4vw, 2.55rem); line-height:1.04; letter-spacing:-.015em; margin:.2rem 0 .4rem; }
        .hero-tagline { margin:0; font-size:1.03rem; max-width:60ch; opacity:.94; }

        /* ---- Layout ---- */
        main { max-width:1040px; margin:-58px auto 72px; padding:0 20px; position:relative; z-index:3; }

        /* ---- Calculator controls: reset + worked examples ----
           One toolbar, built once by the island and dropped into the first card of every
           layout. The server-rendered hero h1 is the page's sole calculator title. */
        .calc-head { display:flex; align-items:center; justify-content:flex-end;
                     gap:14px; flex-wrap:wrap; margin:0 0 14px; }
        /* `.form` is a two-column grid, so the head has to span it; the grid's own row gap
           then supplies the spacing and the margin above would double it. */
        .form > .calc-head { grid-column:1 / -1; margin-bottom:0; }
        /* Secondary by design: reset throws away everything the reader has typed, so it
           must never read as the primary action next to the inputs. Same neutral treatment
           as .seg / .tab-btn, accent only on hover and focus. */
        .reset-btn { display:inline-flex; align-items:center; gap:6px; font:inherit; font-size:.85rem; font-weight:600; color:var(--muted);
                     background:var(--surface); border:1px solid var(--line-strong);
                     border-radius:var(--radius-sm); padding:7px 15px; cursor:pointer;
                     transition:border-color .12s, color .12s; }
        .reset-btn:hover { border-color:var(--accent); color:var(--accent); }
        .reset-btn:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
        .reset-icon { flex:none; }
        /* The compact head (Ugur, 2026-10-28). With worked examples it is ONE row: the label inline,
           the select flexing, Reset at the end; on a phone the row wraps. Without them the head row
           exists only on a form whose inputs have no group title, and holds the legend and Reset. */
        .calc-head:has(> .preset-bar) { justify-content:flex-start; gap:10px 12px; }
        .calc-head:has(> .preset-bar) .reset-btn { min-height:42px; margin-left:auto; }
        .calc-head .required-legend { flex:none; }
        /* Only schema 0.18 emits these elements. */
        .required-marker { color:var(--required-ink); font-weight:600; }
        .required-legend { margin:0; color:var(--muted); font-size:.82rem; }
        /* The required-inputs status line reserves NO space: while it is empty it is a visually
           hidden live region (still announced when it fills), and it is drawn only with a message,
           in the Run bar or after the last input, directly above the results. */
        .required-status { grid-column:1 / -1; margin:12px 0 0; color:var(--muted); font-size:.82rem; }
        .required-status:not(.has-note) { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
                                          overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
        .run-bar > .required-status { margin:0; }
        .required-field-error { display:grid; margin:4px 0 0; min-height:1.5em; color:var(--required-ink); font-size:.8rem; }
        .required-field-error::before { content:attr(data-validation-space); visibility:hidden; grid-area:1 / 1; }
        .required-field-error > span { grid-area:1 / 1; }
        [aria-invalid="true"] { border-color:var(--required-ink); }
        /* P-1: the Run bar of a form page that calculates on demand (ui.json calculation.mode
           "on_demand"). The BOTTOM of the input card, after the last input section (Ugur,
           2026-10-08), set off from the fields by a rule: the Run button
           (primary, the canvas Calculate button's treatment), the declared copy actions and,
           where ui.json opts in, Copy results (secondary, Reset's), then the status line saying
           what the result on screen answers. It spans its card's grid where the card is one
           (`.form`), whose row gap then replaces the margin. No other page draws any of these. */
        .run-bar { grid-column:1 / -1; display:grid; gap:8px; margin-top:20px; padding-top:16px;
                   border-top:1px solid var(--line); }
        .form > .run-bar { margin-top:0; }
        .run-controls { display:flex; flex-wrap:wrap; align-items:center; gap:10px; }
        .run-btn { -webkit-appearance:none; appearance:none; min-height:44px; padding:0 24px;
                   border:1px solid var(--accent-strong); border-radius:var(--radius-sm);
                   background:var(--accent-strong); color:var(--hero-ink); font:inherit; font-weight:700;
                   font-variant-numeric:tabular-nums; cursor:pointer; }
        .run-btn:hover:not([aria-disabled="true"]):not(:disabled) { background:var(--accent); border-color:var(--accent); }
        .run-btn[aria-disabled="true"], .run-btn:disabled { background:var(--surface); color:var(--muted);
                                                            border-color:var(--line-strong); cursor:default; }
        .run-btn:focus-visible { outline:3px solid color-mix(in srgb, var(--accent) 45%, white); outline-offset:2px; }
        .run-action, .run-copy { font:inherit; font-size:.85rem; font-weight:600; color:var(--muted);
                                 background:var(--surface); border:1px solid var(--line-strong);
                                 border-radius:var(--radius-sm); min-height:44px; padding:0 15px; cursor:pointer; }
        .run-action:hover:not(:disabled), .run-copy:hover:not(:disabled) { border-color:var(--accent); color:var(--accent); }
        .run-action:disabled, .run-copy:disabled { opacity:.55; cursor:default; }
        .run-action:focus-visible, .run-copy:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
        .run-status, .run-copy-note { margin:0; color:var(--muted); font-size:.88rem; }
        .run-copy-note:empty { display:none; }
        /* A value outside the contract's declared bounds, said under its field before Run. */
        .range-flag { margin:4px 0 0; color:var(--required-ink); font-size:.8rem; }
        /* A stale result stays readable and says so on the Run bar: its figures in muted italic
           ink (4.5:1 kept), its charts, tables, gauges, model check and diagram faded, as on the
           canvas. */
        .run-page.results-stale [data-output] { color:var(--muted); font-style:italic; }
        .run-page.results-stale .chart-card, .run-page.results-stale .grid-out,
        .run-page.results-stale .gauge, .run-page.results-stale .model-check,
        .run-page.results-stale .calc-figure { opacity:.6; }

        /* ---- The examples menu (Ugur item 2, issue #225; compact head 2026-10-28) ----
           The first item of the one-row head (`.calc-head:has(> .preset-bar)`): "Worked examples" inline, then
           the select taking the free width, then Reset at the row's end. On a narrow screen the
           row wraps and the menu takes the full width.

           Only rendered when the version actually attests presets, so these rules style
           nothing on a calculator without them. */
        .preset-bar { flex:1 1 320px; min-width:0; display:flex; align-items:center; gap:12px;
                      padding:0; border-top:0; box-sizing:border-box; }
        .preset-bar .preset-heading { flex:none; }
        .preset-bar .preset-label { white-space:nowrap; }
        .preset-bar .preset-select { flex:1 1 200px; min-width:0; width:auto; min-height:42px; }
        /* The disclosure is anchored to the WHOLE heading row, not to the 14px icon.
           Scenario Explorer and Decision Canvas both put this card beside a results
           panel. Anchoring a 360px panel to the icon let it cross that column boundary,
           where the later sticky/result surface painted over it. The heading already
           spans the input card, so it is the honest clipping/placement boundary at both
           desktop and the one-column breakpoints. */
        .preset-heading { position:relative; display:flex; align-items:center; gap:6px; min-width:0; }
        .preset-label { font-size:.82rem; font-weight:600; color:color-mix(in srgb, var(--ink) 88%, var(--muted)); }
        .preset-select { font:inherit; font-size:.85rem; color:var(--ink); background:var(--surface);
                         border:1px solid var(--line-strong); border-radius:var(--radius-sm);
                         padding:7px 32px 7px 11px; cursor:pointer; width:100%; max-width:100%; box-sizing:border-box;
                         text-align:left; }
        .preset-select:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
        .preset-info { position:static; }
        .preset-info > summary { list-style:none; }
        .preset-info > summary::-webkit-details-marker { display:none; }
        .preset-info-button { display:inline; border:0; padding:0; color:var(--muted); background:none;
                              font-size:.9rem; line-height:1; cursor:pointer; }
        .preset-info-button:hover, .preset-info-button:focus-visible { color:var(--accent); outline:none; }
        .preset-info-body { position:absolute; z-index:45; top:calc(100% + 8px); left:0; right:0; width:auto; max-width:360px;
                            box-sizing:border-box; padding:12px 14px;
                            border:1px solid var(--line); border-radius:12px; background:var(--surface); box-shadow:0 14px 40px -12px rgba(16,19,28,.4);
                            color:var(--muted); font-size:.82rem; line-height:1.5; }
        /* Worked-examples (i): the shared popup, opened from the heading's left edge (stays in a 320px rail). */
        .preset-heading .out-info { position:static; margin-left:0; }
        .preset-heading .out-info-body { left:0; right:auto; top:calc(100% + 8px); }

        /* ---- ONE vertical rhythm for the results region (Ugur item 4) ----
           The three results regions each stacked their blocks a different way and none of
           them agreed: `.results-live` relied on `.cards`' 20px top margin and `.banner`'s
           18px, while `.chart-card` carries NO margin at all, so charts sat flush against
           the cards above them and against each other; `panel` put the breakdown (margin 0)
           and the charts in a bare div, so a chart touched the last row of the breakdown;
           `dashboard` put the banner, the KPI row and the metric tiles in a bare div, so
           `.dash-top` and `.dash-metrics` touched.

           The gap now lives on the CONTAINER and the ad-hoc margins inside it are zeroed,
           so there is one number and it cannot be half-applied. Margins are zeroed rather
           than deleted because `.cards`, `.banner` and `.primary-figure` all render outside
           these containers too. */
        /* The seam INTO the results region on `stacked` (Ugur, 2026-08-24). Item 4 above
           gave the region one internal rhythm and stopped at its edge: the wrapper holding
           the input card and the results was classless, so the first result block sat flush
           against the card. Measured before changing anything — 0px seam against an 18px
           rhythm below it on both wire-size (segmented control last) and eoq (plain field
           last), so it is structural to the layout and not the control's padding.
           EXTENDED rather than patched with a margin: the same container-gap treatment and
           the same token, because 2d4234b's whole argument was that four literals is how
           the numbers come to disagree, and a fifth would be that again.

           #378 FINISHED THE ARGUMENT. That commit exempted `.workspace` AND `.dashboard`
           at 22px on the grounds that they "place a card BESIDE its results" — true of
           `.workspace`, and NOT true of `.dashboard`, which is `flex-direction:column`
           and therefore stacks the inputs card ABOVE the results in one column, at 22px,
           over a results region running at 18. Nor of `.workspace` below 900px, where the
           grid collapses to one column and its 22px column gap becomes the vertical seam.
           Both now take the token; `.workspace` keeps 22px only where it is genuinely a
           HORIZONTAL gap between two side-by-side columns. */
        .calc-stack { display:flex; flex-direction:column; gap:var(--block-gap); }
        /* Before the first calculation returns there is nothing to separate: the region
           renders as an empty div, and a gap above nothing is a stray 18px under the card.
           display:none takes it out of flex layout entirely, so the gap appears exactly
           when a result does. */
        .calc-stack > .results-live:empty { display:none; }
        .results-live, .results-flow { display:flex; flex-direction:column; gap:var(--block-gap); }
        .results-live > .banner, .results-flow > .banner { margin-top:0; }
        .results-flow > .primary-figure { margin-bottom:0; }
        /* The dashboard's chart column. `.dash-top` is a two-column grid, so N chart cards
           dropped straight into it are N+1 grid items and the second one wraps into the
           narrow KPI column. One wrapper keeps the grid at two items for any number of
           charts, and stacks them on the same rhythm as every other block. */
        .dash-charts { display:flex; flex-direction:column; gap:var(--block-gap); min-width:0; }

        /* ---- Workspace: inputs left, live results right ---- */
        /* Two columns side by side, so this 22px is a COLUMN gap and not a rhythm the eye
           reads down the page. It stays a literal for exactly as long as it stays
           horizontal — see the media query below, which is where it stops being. */
        .workspace { display:grid; grid-template-columns:minmax(0,1fr) 358px; gap:22px; align-items:start; }
        .inputs-card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 24px 24px; }
        .panel-kicker { text-transform:uppercase; letter-spacing:.08em; font-size:.72rem; font-weight:700; color:var(--muted); margin:0 0 14px; }
        .fields { display:grid; grid-template-columns:repeat(auto-fit, minmax(214px,1fr)); gap:16px 22px; }

        .results-panel { position:sticky; top:20px; background:var(--surface); border:1px solid var(--line); border-top:3px solid var(--accent);
                         border-radius:var(--radius); box-shadow:var(--shadow-md); padding:18px 20px 20px; }
        .results-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
        .results-kicker { text-transform:uppercase; letter-spacing:.08em; font-size:.72rem; font-weight:700; color:var(--muted); }
        .primary-figure { padding:8px 0 14px; border-bottom:1px solid var(--line); margin-bottom:12px; }
        .primary-value { font-size:2.4rem; font-weight:800; line-height:1.04; color:var(--accent); letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .primary-unit { font-size:1rem; font-weight:600; color:var(--muted); }
        .primary-name { margin-top:5px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }

        .breakdown { margin:0; display:grid; grid-template-columns:1fr auto; gap:10px 14px; }
        .breakdown dt { color:var(--muted); font-size:.85rem; min-width:0; }
        .breakdown dd { margin:0; text-align:right; font-weight:600; font-size:.9rem; font-variant-numeric:tabular-nums; white-space:nowrap; }
        .dd-unit { color:var(--muted); font-weight:500; font-size:.78rem; }
        .results-empty { color:var(--muted); font-size:.9rem; padding:16px 0; }
        .results-panel .banner { margin-top:0; }
        @media (max-width:900px) { .workspace { grid-template-columns:1fr; gap:var(--block-gap); } .results-panel { position:static; } }

        /* ---- Segmented choices ---- */
        .segmented-choice { display:block; margin:0; padding:0; min-width:0; border:0; }
        .choice-legend { margin:0 0 5px; padding:0; font-size:.82rem; font-weight:600; color:color-mix(in srgb, var(--ink) 88%, var(--muted)); }
        .choice-legend .help-btn { margin-left:6px; }
        .segmented { display:flex; flex-wrap:wrap; gap:6px; margin:0; padding:0; min-width:0; border:0; }
        .seg { border:1px solid var(--line-strong); background:var(--surface); color:var(--ink); font:inherit; font-size:.85rem;
               padding:7px 11px; border-radius:9px; cursor:pointer; text-align:left;
               transition:border-color .12s, background .12s, color .12s; }
        .seg:hover { border-color:var(--accent); }
        .seg:focus-within { outline:2px solid var(--accent); outline-offset:2px; }
        .seg.active { background:color-mix(in srgb, var(--accent) 12%, white); border-color:var(--accent); color:var(--accent); font-weight:600; }

        /* ---- Show inputs: Required | All (Ugur, 2026-11-02; CONTROL_CATALOG.md "Not input controls") ----
           Page chrome, not an input: the first row of the inputs, the full width of the fields grid,
           in the option-buttons look. The note under it is a polite live region that takes no space
           until "Required" hides inputs that differ from their defaults. */
        .input-view { grid-column:1 / -1; min-width:0; }
        .input-view-row { display:flex; flex-wrap:wrap; align-items:center; gap:6px 12px; }
        .input-view-label { font-size:.82rem; font-weight:600; color:color-mix(in srgb, var(--ink) 88%, var(--muted)); }
        .input-view .segmented { flex-wrap:nowrap; }
        .input-view .seg { padding:5px 12px; }
        .input-view-note { margin:6px 0 0; color:var(--muted); font-size:.82rem; }
        .input-view-note:not(.has-note) { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
                                         overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
        .input-view-show-all { font:inherit; color:var(--accent); background:none; border:0; padding:0;
                               text-decoration:underline; text-underline-offset:2px; cursor:pointer; }
        .input-view-show-all:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:2px; }
        .bd-surface .input-view, .ds-data .input-view { margin-bottom:2px; }
        .fx-ribbon .input-view { padding:12px 16px; border-bottom:1px solid var(--line); }

        /* ---- Dashboard variant ---- */
        /* One column, so this seam IS the page's vertical rhythm (#378): the inputs card
           sits ABOVE the results, not beside them. It ran at 22px against an 18px results
           region below it. */
        .dashboard, .analytics, .formula, .decision, .builder, .dataset, .flow, .classifier, .assessment { display:flex; flex-direction:column; gap:var(--block-gap); }
        .fields-dense { grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); }
        .dash-results, .an-results, .fx-results, .dc-results, .bd-results, .ds-results, .fl-results, .cl-results, .as-results, .bl-results { display:flex; flex-direction:column; gap:var(--block-gap); }
        .dash-top { display:grid; grid-template-columns:minmax(200px,258px) 1fr; gap:var(--block-gap); align-items:stretch; }
        .dash-kpi { position:relative; background:color-mix(in srgb, var(--accent) 8%, white); border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                    border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 20px; display:flex; flex-direction:column; justify-content:center; }
        .dash-kpi .primary-value { font-size:2.5rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .dash-kpi .primary-name { margin-top:6px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        /* A contract may declare several primary_result figures (#92); every one gets the
           KPI treatment, separated by a rule. Sibling combinator, so it cannot match on a
           single-primary calculator — those render exactly as before. */
        .dash-kpi .kpi-figure + .kpi-figure { margin-top:16px; padding-top:14px; border-top:1px solid color-mix(in srgb, var(--accent) 22%, white); }
        .chart-card { background:var(--surface); border:1px solid color-mix(in srgb, var(--line) 76%, var(--accent)); border-radius:var(--radius); box-shadow:0 16px 38px -30px rgba(20,30,55,.55); padding:20px 22px; min-width:0; overflow:hidden; }
        .chart-title { font-size:.74rem; text-transform:uppercase; letter-spacing:.11em; color:var(--accent-strong); font-weight:750; margin-bottom:10px; }
        /* THE CHART HELP DISCLOSURE IS ANCHORED TO THE HEADING, NOT TO ITS ICON.
           Two defects, both desktop-only, both measured in Chromium at 1024px on
           `rule-of-72-doubling-time-calculator` before this rule existed.

           1. THE PANEL WAS CLIPPED AWAY. The base `.out-info-body` rule opens rightward at
              a fixed `left:-8px; width:min(280px,72vw)` and never flips or clamps. This
              heading was a flex ROW with the title first and the icon last, so the icon
              sits near the card's right edge BY CONSTRUCTION, and `.chart-card` above
              hard-clips with `overflow:hidden`. Four charts, panel 280px in every case:
              visible 82px / 45px / 45px / 166px — 71% / 84% / 84% / 41% of the definition
              lost. THE CHART CARD IS NOT THE ONLY CONTAINER THAT CLIPS, AND THE CHART
              HEADING IS NOT THE ONLY SURFACE. This comment first said an output field
              loses nothing "because `.fx-stage` does not clip". That was wrong and it was
              the stated reason for scoping the rules below to `.chart-heading`, so it is
              worth the space: `.fx-stage` declares `overflow:hidden` too, on the third
              line of its own multi-line block further down this sheet — which is how a
              one-line grep for it came back empty. The 0% behind the original claim came
              from one viewport width on one page that renders no `.fx-stage` at all; what
              it showed is that an icon far from its container's clip edge is not clipped,
              not that the container does not clip. That is the same defect class on a
              different surface and it is issue #935, and IT IS FIXED HERE — not by these
              rules but by `placeInfoPanel` in island.js, which keeps ANY opened
              `.out-info-body` inside whatever clips it. Read that function's own comment
              for the mechanism, the three classes of clipper it has to handle and why
              `position:fixed` was rejected. The rules below stay scoped to
              `.chart-heading` because they are the correct CSS RESTING STATE for a chart
              panel, which is exactly what lets the positioner be an enhancement rather
              than load-bearing: with the script absent the panel still opens clamped to
              its heading, which a `position:fixed` panel could not — with auto offsets it
              would resolve against the VIEWPORT at its static position, measured 1381px
              to 3022px down at 1280x900, off screen and unscrollable.
           2. THE ICON WAS STRANDED. `.chart-title` was a flex item filling the row even
              when its last line ended short, so the icon stayed pinned to the far edge.
              Gap from the end of the title text to the icon: 7px on a one-line title,
              321px and 351px on the two-line ones.

           WHY THE HEADING AND NOT THE ICON. Right-aligning the panel to the icon
           (`left:auto; right:-8px`, the `.an-readings .out-info-body` variant) takes the clipping
           to 0% but leaves the icon stranded at 321/351px. Making the icon inline alone
           fixes the gap to 5px but RE-BREAKS the clipping to 30% and 65%, because a panel
           right-aligned to an icon that now sits mid-line runs off the LEFT edge instead.
           Anchoring to the icon is a guess about which edge you are near and it is wrong
           on one side whichever side you pick. Anchoring to the heading and clamping to
           its width cannot be wrong on either: `width:min(280px,100%)` is 100% OF THE
           HEADING, which is the card's content box, so the panel is contained by
           construction at every width — HORIZONTALLY. Name the axis, because nothing in
           this stylesheet bounds the panel's HEIGHT. Above 520px it has `max-height:none`
           and no scroller (only the touch sheet sets `max-height:42vh; overflow:auto`), so
           on the CSS alone a definition taller than the card is cut off at the bottom by
           the same `overflow:hidden`, with no scrollbar and no other signal — and a
           NARROWER card makes that worse, not better, because a narrower panel is a taller
           one. `placeInfoPanel` in island.js is what bounds it now: the chart card is an
           `overflow:hidden` container, so a panel taller than the room left below it is
           clamped to that room and given `overflow-y:auto`. That is a scroller, not more
           room, so it ends the SILENT loss rather than making length free.

           NO CHARACTER BUDGET IS STATED HERE, AND ONE MUST NOT BE ADDED WITHOUT
           MEASURING. Two have been published in this comment and both were wrong — the
           first measured through the stylesheet #934 breaks (the Playwright gates capture
           `<style>` non-greedily from the first literal occurrence, which is inside the
           Razor comment at the top of THIS file, so `:root` never becomes a rule,
           `--font-ui` is empty and this panel's `border:1px solid var(--line)` computes to
           0px: the text laid out in Times New Roman in a border-less box, and a regex over
           the captured text does not catch it, only a computed value does), and the second
           clipped below its own figure when someone else measured it against the real
           corpus. Both are withdrawn and deliberately NOT replaced: a missing number is
           honest, a wrong one is not, and nothing in the catalog is near any limit — 185
           chart presentations, 32 described, the longest 265 characters. The runtime
           positioner in island.js now clamps a too-tall panel to its container and gives
           it `overflow-y:auto`, so an over-long description scrolls instead of vanishing.
           If a real limit is ever needed, drive the running site in a browser and prove
           the stylesheet landed BY COMPUTED VALUE — `--font-ui` non-empty, this panel's
           border-width 1px, a non-serif font-family — before reading any figure.

           MEASURED AFTER, same page, same four charts: clipping 71/84/84/41% -> 0/0/0/0%,
           icon gap 7/321/351px -> 5px on all four (the 5px is `.out-info`'s own
           `margin-left`, which is why the flex `gap:2px` is gone rather than left inert).
           Panel box 453 -> 733 inside a card spanning 430 -> 956.

           `.chart-card { overflow:hidden }` is deliberately NOT relaxed; it is load-bearing
           for the chart SVG and neither this rule nor the positioner needs it relaxed.
           Scoped to `.chart-heading` because a chart heading is the one surface where the
           correct resting anchor is knowable from CSS — an output field's is not, which is
           why #935 is solved in script — so the base rule and
           `.an-readings` keep their own anchoring untouched, and left at
           specificity (0,2,0) so the touch-width sheet in `@media (max-width:520px)` —
           `.out-info[open] > .out-info-body`, (0,3,0), and later in the file — still wins
           there, where a viewport-bound sheet is already the right answer for the same
           reason. Raising these selectors above (0,2,0) would silently take that sheet
           over on phones. `CalculatorPageCssTests` pins the two cheap ways that happens —
           an added `[open]`, an added `>` — plus the source order; it cannot pin
           specificity itself, so a (0,4,0) chart-heading override carrying neither form
           would pass the suite and still beat the sheet. Checked rather than
           reasoned about: at 375px, against this stylesheet before and after, both chart
           panels and an output-field panel resolve identically to the sheet —
           position:fixed, left/right 12px, bottom 16px, 351px wide, max-height 42vh. */
        .chart-heading { display:block; position:relative; margin-bottom:10px; }
        .chart-heading .chart-title { display:inline; margin-bottom:0; }
        .chart-heading .out-info { display:inline; position:static; }
        .chart-heading .out-info-body { left:0; right:auto; top:calc(100% + 6px); width:min(280px, 100%); }
        .conditional-table-card { display:flex; flex-direction:column; gap:10px; grid-column:1 / -1; }
        .conditional-table-card .chart-title { margin-bottom:0; }
        .conditional-table-description { margin:0; color:var(--muted); font-size:.86rem; max-width:78ch; }
        .conditional-axis-title { display:block; font-weight:650; color:var(--ink); }
        /* The complete matrix fits its card. There is deliberately no overflow/scroll
           container: compact fixed columns preserve the whole comparison at once, which
           is the point of a two-way table. */
        .conditional-table-frame { width:100%; min-width:0; }
        .conditional-table { width:100%; table-layout:fixed; border-collapse:separate; border-spacing:0;
                             color:var(--conditional-cell-ink); font-size:clamp(.36rem,1.05vw,.76rem);
                             font-variant-numeric:tabular-nums; }
        .conditional-table .conditional-row-axis-column { width:32px; }
        .conditional-table .conditional-row-value-column { width:clamp(38px,5vw,58px); }
        .conditional-table th, .conditional-table td { min-width:0; padding:clamp(2px,.42vw,7px) clamp(1px,.36vw,6px);
                                                        border-right:1px solid color-mix(in srgb, var(--line) 78%, transparent);
                                                        border-bottom:1px solid color-mix(in srgb, var(--line) 78%, transparent); text-align:right; white-space:nowrap; }
        .conditional-table thead th { color:var(--muted); background:color-mix(in srgb, var(--surface) 91%, var(--line)); font-weight:650; }
        .conditional-table .conditional-axis-spacer { padding:0; border:0; background:transparent; }
        .conditional-table .conditional-column-axis { text-align:center; white-space:normal; padding-block:3px 6px; }
        .conditional-table tbody th { color:var(--muted); background:var(--surface); font-weight:650; }
        .conditional-table .conditional-row-axis { padding:4px 2px; text-align:center; vertical-align:middle; white-space:normal; }
        .conditional-table .conditional-row-axis .conditional-axis-title { margin:auto; writing-mode:vertical-rl; transform:rotate(180deg); }
        .conditional-table .conditional-row-value { text-align:right; }
        .conditional-table .conditional-cell-compact { display:none; }
        /* Whole-body switch to the compact form when any full value is wider than its column;
           island.js measures and sets the class. Only `display` is set, and to the values the
           520px rules below already use, so the narrow-screen rendering and its 320px no-scroll
           gate are unchanged. */
        .conditional-table.conditional-table-compact .conditional-cell-full { display:none; }
        .conditional-table.conditional-table-compact .conditional-cell-compact { display:inline-block; }
        /* Where even the compact form is slightly wider than its column, island.js sets exactly
           the tracking that closes the shortfall (measured at a 1024px window: 533px table,
           10.75px type, `−106k` 1.2px too wide), never tighter than the narrow-screen -.16em.
           Wider than 520px only — the exact complement of the rule below — so the narrow-screen
           tracking and its 320px gate are untouched. */
        @media not all and (max-width:520px) {
            .conditional-table.conditional-table-compact .conditional-cell-compact {
                letter-spacing:var(--conditional-compact-tracking, normal);
            }
        }
        /* The reader's own result. Inset, so it never changes the cell's box: it cannot move a
           column, disturb the fit measurement, or be painted over by a neighbour. Ink rather
           than fill, because the fill already encodes the value; no bold either, because bold
           text is wider than the typography the fit measurement reads. */
        .conditional-table td.conditional-anchor-cell { box-shadow:inset 0 0 0 2px var(--conditional-cell-ink); }
        .conditional-table tr:first-child th:first-child { border-top-left-radius:10px; }
        .conditional-table tr:first-child th:last-child { border-top-right-radius:10px; }
        @media (max-width:520px) {
            .conditional-table-card { padding-inline:6px; }
            .conditional-table { font-size:.5rem; letter-spacing:-.015em; }
            /* Keep the real rowspan cell in the table. Removing it from layout made the
               first body row use a different column geometry from every row below it and
               discarded the source table's vertical axis heading. Twenty-two pixels is
               still a narrow gutter, but gives the full vertical question a readable,
               independent column instead of reducing it to a line beside the percentages. */
            .conditional-table .conditional-row-axis-column { width:22px; }
            .conditional-table .conditional-row-value-column { width:29px; }
            .conditional-table th, .conditional-table td { padding:3px 0; text-align:center; }
            .conditional-table .conditional-column-values th:not(.conditional-axis-spacer) {
                height:50px; writing-mode:vertical-rl; transform:rotate(180deg); text-align:left;
            }
            .conditional-table .conditional-cell-full { display:none; }
            /* Five-character compact values (for example −172k) must fit the
               narrowest of eleven data columns at the real 320px page width. Keep
               the readable 8px type size and tighten only its tracking; shrinking
               the type itself made the matrix illegible, while allowing glyphs to
               escape their cells clipped the final column inside the card. */
            .conditional-table .conditional-cell-compact {
                display:inline-block; width:100%; line-height:1;
                white-space:normal; overflow-wrap:anywhere; letter-spacing:-.16em;
            }
            .conditional-table .conditional-row-axis { padding:2px 0; }
            .conditional-table .conditional-row-axis .conditional-axis-title {
                font-size:.5625rem; line-height:1; letter-spacing:0; white-space:nowrap;
            }
        }
        .conditional-table-derived { display:flex; flex-direction:column; gap:7px; }
        .conditional-table-derived dl { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:8px; margin:0; }
        .conditional-table-metric { padding:9px 10px; border:1px solid var(--line); border-radius:var(--radius-sm); background:color-mix(in srgb, var(--surface) 94%, var(--accent)); }
        .conditional-table-metric dt { color:var(--muted); font-size:.7rem; font-weight:650; text-transform:uppercase; letter-spacing:.035em; }
        .conditional-table-metric dd { margin:3px 0 0; color:var(--ink); font-size:1rem; font-weight:750; font-variant-numeric:tabular-nums; }
        .conditional-table-warning { padding:12px 14px; border:1px solid color-mix(in srgb, var(--required-ink) 36%, var(--line));
                                     border-radius:var(--radius-sm); color:var(--required-ink); background:color-mix(in srgb, var(--required-ink) 6%, var(--surface)); }
        @media (prefers-color-scheme:dark) {
            :root[data-theme="dark"] {
                --conditional-worse:#78373d; --conditional-middle:#71601f;
                --conditional-better:#276442; --conditional-cell-ink:#f7faf8;
            }
        }
        .chart-plot { min-width:0; }
        .chart-svg { width:100%; height:auto; display:block; border-radius:16px; background:linear-gradient(180deg, #f7f8fc 0%, #fbfcfe 58%, #fff 100%); }
        .chart-grid { stroke:#dfe4ed; stroke-width:1; opacity:.82; }
        .chart-axis { font-size:10px; fill:#718096; font-variant-numeric:tabular-nums; }
        /* The axis titles (#361). A shade larger and heavier than the ticks, because they
           are the label and the ticks are the scale — the reader should be able to tell
           which is which without reading either. Same muted colour: this is a caption on
           the chart, not a second title competing with `.chart-title` above it.
           The 6.1px-per-glyph budget the renderer truncates against is calibrated to this
           font-size; changing it here without changing it there lets a long title run past
           the plot. */
        .chart-axis-title { font-size:11px; font-weight:600; fill:var(--muted); letter-spacing:.01em; }
        /* ---- Chart value readouts (Ugur, 2026-08-24) ----
           Both states in one rule, which is the point: `:hover` is the mouse and
           `:focus-within` is the keyboard, and a value reachable by one and not the other
           is the accessibility regression this was asked to avoid. No JS and no state —
           the plate is always drawn and only its opacity moves, so the renderer stays the
           pure function it is. */
        .chart-readout { opacity:0; pointer-events:none; transition:opacity .1s ease;
                         font-size:11px; font-weight:700; fill:var(--ink);
                         /* The halo that replaces a background plate: the stroke is painted
                            UNDER the glyphs, so the label stays legible over a gridline or a
                            bar without adding a node to the chart. */
                         paint-order:stroke; stroke:var(--surface); stroke-width:3.5px;
                         stroke-linejoin:round; }
        .chart-mark-g:hover .chart-readout,
        .chart-mark-g:focus-within .chart-readout { opacity:1; }
        /* The line's dot is hidden until its point is the one being read, so a hovered
           line does not sprout twelve permanent markers. Bars need no equivalent: the bar
           IS the mark. */
        .chart-dot { opacity:0; transition:opacity .1s ease; }
        .chart-mark-g:hover .chart-dot,
        .chart-mark-g:focus-within .chart-dot { opacity:1; }
        /* A transparent hit target still has to take the pointer. */
        .chart-mark { cursor:default; }
        .chart-mark:focus { outline:none; }
        .chart-mark:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
        .chart-mark-g:hover .chart-mark[fill]:not([fill="transparent"]) { filter:brightness(1.08); }
        .chart-area { opacity:.12; }
        .chart-bar { opacity:.96; filter:drop-shadow(0 3px 5px rgba(55,65,110,.13)); transition:filter .15s ease, opacity .15s ease; }
        .chart-mark-g:hover .chart-bar, .chart-mark-g:focus-within .chart-bar { opacity:1; filter:drop-shadow(0 5px 8px rgba(55,65,110,.2)) brightness(1.03); }
        .chart-reference { opacity:.92; }
        .chart-peak-reference { opacity:.9; pointer-events:none; }
        .chart-guide-label, .chart-series-end-label, .chart-value-label { fill:var(--ink); font-size:9px; font-weight:750; paint-order:stroke; stroke:rgba(255,255,255,.95); stroke-width:4px; stroke-linejoin:round; pointer-events:none; }
        .chart-peak-label { fill:#087a73; }
        .chart-series-end-label { fill:#506078; letter-spacing:.01em; }
        .chart-value-label { fill:#5d6b80; font-size:8.5px; transition:opacity .1s ease; }
        /* A persistent compact number and the full hover/focus sentence describe the
           same point. Hide the compact number while the sentence is active so the two
           labels cannot overprint each other. */
        .chart-mark-g:hover .chart-value-label, .chart-mark-g:focus-within .chart-value-label { opacity:0; }
        .chart-point { filter:drop-shadow(0 3px 5px rgba(16,19,28,.2)); }
        .chart-legend { display:flex; gap:16px; flex-wrap:wrap; margin:0 0 7px; }
        .legend-item { display:inline-flex; align-items:center; gap:6px; font-size:.8rem; color:var(--muted); }
        .legend-swatch { width:14px; height:5px; border:0; border-radius:2px; display:inline-block; }
        .legend-line, .legend-reference { height:0; border-top:2px solid currentColor; background:transparent!important; border-radius:0; }
        .legend-line.dashed, .legend-reference { border-top-style:dashed; }
        .legend-point { width:8px; height:8px; border-radius:50%; border:2px solid currentColor; background:white!important; }
        /* 3R / map S9: every drawn chart carries its data as a table, closed by default, built
           from the same payload and listing every value, including any the plot could not
           draw. The scroll box keeps a wide table inside the card at 320px. */
        .chart-data { margin-top:.75em; font-size:.82rem; }
        .chart-data > summary { cursor:pointer; color:var(--accent-strong); font-weight:650; }
        .chart-data-scroll { overflow-x:auto; margin-top:.5em; }
        .chart-data-table { border-collapse:collapse; width:100%; font-variant-numeric:tabular-nums; }
        .chart-data-table caption { text-align:left; color:var(--muted); padding-bottom:.35em; }
        .chart-data-table th, .chart-data-table td { padding:.3em .6em; border-bottom:1px solid var(--line); text-align:left; vertical-align:top; }
        .chart-data-table td { text-align:right; }
        .chart-note { margin:.6em 0 0; font-size:.8rem; color:var(--muted); }
        .chart-donut-layout { display:grid; grid-template-columns:minmax(180px,240px) minmax(180px,1fr); gap:20px; align-items:center; }
        .chart-donut { width:100%; max-width:240px; margin:auto; overflow:visible; }
        .donut-track { stroke:#e7eaf0; }
        .donut-slice { transition:stroke-width .15s ease, filter .15s ease; }
        .donut-slice:hover, .donut-slice:focus { stroke-width:34; filter:brightness(1.04) drop-shadow(0 5px 6px rgba(55,65,110,.18)); }
        .donut-total { fill:var(--ink); font-family:var(--font-display); font-size:34px; font-weight:700; }
        .donut-caption { fill:var(--muted); font-size:12px; }
        .donut-legend { display:grid; gap:10px; }
        .donut-legend-item { display:grid; grid-template-columns:10px minmax(0,1fr); gap:9px; align-items:center; font-size:.8rem; }
        .donut-swatch { width:10px; height:10px; border-radius:3px; }
        .donut-label { color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .dash-metrics { display:grid; grid-template-columns:repeat(auto-fill, minmax(168px,1fr)); gap:var(--block-gap); }
        .stat { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); padding:12px 14px; box-shadow:var(--shadow-sm); }
        .stat-name { font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:600; }
        .stat-value { font-size:1.15rem; font-weight:700; margin-top:4px; font-variant-numeric:tabular-nums; }
        .grid-out { border-collapse:collapse; font-size:.8rem; margin-top:4px; }
        .grid-out th, .grid-out td { border:1px solid var(--line); padding:4px 9px; text-align:right; font-variant-numeric:tabular-nums; }
        .grid-out th { color:var(--muted); font-weight:600; }
        .grid-out th.grid-align-left, .grid-out td.grid-align-left { text-align:left; font-variant-numeric:normal; }
        .stat:has(.grid-out), .card:has(.grid-out) { grid-column:1 / -1; }
        /* Wide supporting tables keep every column readable in a LOCAL scroller.
           The grid item may shrink below its table's intrinsic width; help stays outside. */
        .stat:has(.grid-out-scroll) { min-width:0; }
        .grid-out-scroll { min-width:0; max-width:100%; overflow-x:auto; }

        .out-info { position:relative; display:inline-block; margin-left:5px; vertical-align:middle; text-transform:none; letter-spacing:normal; }
        .out-info > summary { list-style:none; }
        .out-info > summary::-webkit-details-marker { display:none; }
        .out-info-button { display:inline; color:var(--muted); font-size:.9rem; cursor:pointer; }
        .out-info-button:hover, .out-info-button:focus-visible { color:var(--accent); outline:none; }
        /* Chromium still includes an absolutely-positioned descendant of a closed
           <details> in page-width calculations. Hide it explicitly so a row of output
           definitions cannot create a horizontal scrollbar while every disclosure is
           closed. The open mobile treatment below uses a viewport-bound sheet. */
        .out-info:not([open]) > .out-info-body { display:none; }
        .out-info-body { position:absolute; z-index:50; top:23px; left:-8px; width:min(280px,72vw); padding:10px 12px;
                         border:1px solid var(--line); border-radius:11px; background:var(--surface); box-shadow:0 12px 34px -10px rgba(16,19,28,.4);
                         color:var(--muted); font-size:.78rem; font-weight:400; line-height:1.45; text-align:left; white-space:normal; }

        /* ---- Analytics variant (the diagnostic container) ----
           Deliberately thin, and thin is the finding: `.analytics` and `.an-results` are
           GROUPED ONTO the dashboard's own rules above rather than given their own copies
           of the same three declarations, and the tiles inside the disclosure are the
           dashboard's `.dash-metrics`/`.stat` untouched. What is genuinely new here is the
           KPI strip's axis, the chart-beside-rail split, and the rail itself. */
        /* The KPI strip reads ACROSS, which is the one thing that makes this not the
           dashboard's KPI column: `auto-fit` so one primary fills the row and five share
           it, and `minmax(0,…)` so a long figure shrinks instead of forcing a scrollbar.
           24 of the 72 delivered contracts declare a single primary_result. */
        .an-kpis { position:relative; display:grid; grid-template-columns:repeat(auto-fit, minmax(min(210px,100%),1fr)); gap:var(--block-gap);
                   background:color-mix(in srgb, var(--accent) 8%, white); border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                   border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 20px; }
        .an-kpis .primary-value { font-size:2.1rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .an-kpis .primary-name { margin-top:6px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        /* No `+` separator rule here, unlike `.dash-kpi`: these figures sit side by side in
           their own grid cells, so the rule that divides a COLUMN of them would be drawn
           across the reading direction. */

        /* The dominant visual and the rail. The chart takes the flexible column so the
           plot keeps its width; the rail is fixed, because prose wider than about 40em
           stops being readable. */
        .an-diagnosis { display:grid; grid-template-columns:minmax(0,1fr) 300px; gap:var(--block-gap); align-items:start; }
        /* 12 of the 72 delivered contracts declare no chart, and a contract could declare
           no status and no described primary. Whichever survives takes the whole width —
           decided here rather than with a second className in the island, so there is one
           place that knows what the collapsed form looks like. */
        .an-diagnosis > *:only-child { grid-column:1 / -1; }
        .an-charts { display:flex; flex-direction:column; gap:var(--block-gap); min-width:0; }
        .an-rail { display:flex; flex-direction:column; gap:var(--block-gap); min-width:0; }
        .an-readings { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:14px 16px; }
        /* Consecutive readings are separated by a rule rather than by a gap, the same way
           `.dash-kpi .kpi-figure + .kpi-figure` separates stacked KPIs above. Sibling
           combinator, so it cannot match when a contract yields a single reading — which
           is the common case, since a primary that draws a ring owns its info disclosure
           there and never reaches this list. */
        .an-reading + .an-reading { margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
        .an-reading-name { display:flex; align-items:flex-start; gap:5px; font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:600; }
        .an-readings .out-info-body { left:auto; right:-8px; text-transform:none; letter-spacing:normal; font-weight:400; }

        /* Supporting metrics, demoted behind a disclosure — the one place this container
           hides something the dashboard shows. Closed by default: the container's whole
           claim is that the KPI strip and the chart are the answer. */
        /* The disclosure rules that were here are now GROUPED with dataset's, further
           down: the same block, three container names, rather than a second copy free
           to drift from the first. */
        /* ---- Formula container (one transformation, one answer) ----
           Thin on purpose, and thin is the finding again: `.formula` is GROUPED ONTO the
           dashboard's column rule above rather than given a copy, and every block inside is
           a primitive that already had a rule. What is new is a MEASURE and a type scale. */
        /* A three-input transformation has no business spanning 1200px. The answer and the
           fields that produce it belong within one readable measure; every other container
           takes the full width because every other container has more than one thing in
           it. */
        .formula { max-width:760px; }
        .fx-answers { background:color-mix(in srgb, var(--accent) 8%, white);
                      border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                      border-radius:var(--radius); box-shadow:var(--shadow-md); padding:26px 24px; text-align:center; }
        /* The largest type scale on the site: on this container the answer IS the page. */
        .fx-answer .primary-value { font-size:3rem; line-height:1.02; }
        /* The label LEADS on this container (nameFirst), so its margin moves above it. The
           answer carries a COMPUTED label that changes with the direction selector, and a
           number whose meaning is chosen by a dropdown must say what it is first. */
        .fx-answer .primary-name { margin:0 0 7px; }
        /* Prose centred under a centred answer needs a measure of its own, or it runs the
           full 760px and stops being readable at the exact moment it matters. */
        .fx-answer + .fx-answer { margin-top:18px; padding-top:16px; border-top:1px solid color-mix(in srgb, var(--accent) 22%, white); }

        /* ---- Decision container (a recommendation, and the evidence for it) ----
           The one rule here that is not an arrangement: PROSE GETS A PROSE TREATMENT. */
        .dc-recommendation { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
                             box-shadow:var(--shadow-sm); padding:20px 22px;
                             display:flex; flex-direction:column; gap:var(--block-gap); }
        /* Deliberately the OPPOSITE of `.primary-value`, declaration for declaration. That
           rule sets tabular-nums and -.02em tracking at 2.4rem, which is a treatment tuned
           for digits - and Voltage_Drop's primary is declared `text`, so today a conductor
           size is set in a numeric face and tracked in like a headline figure. A
           recommendation is a phrase: it wraps, it has no columns to align, and it needs a
           line-height that can carry a second line. */
        .dc-verdict .primary-value { font-size:1.9rem; font-weight:700; line-height:1.28;
                                     letter-spacing:normal; font-variant-numeric:normal; }
        .dc-verdict .primary-name { margin:0 0 6px; }
        .dc-verdict + .dc-verdict { margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
        /* The `text` details: verdicts, not measurements. No gauge can be drawn for one and
           no unit belongs on one, so the tile is quieter than a `.stat` and sits beside the
           recommendation rather than down in the evidence. */
        .dc-checks { display:grid; grid-template-columns:repeat(auto-fill, minmax(210px,1fr)); gap:12px; }
        .dc-check { background:color-mix(in srgb, var(--accent) 5%, white);
                    border:1px solid var(--line); border-radius:var(--radius-sm); padding:10px 13px; }
        .dc-check-name { font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:600; }
        .dc-check-value { margin-top:3px; font-size:1rem; font-weight:700; line-height:1.3; }

        /* A KICKER BOUND TO THE BLOCK IT LABELS, on both containers. Without the wrapper
           the label and the strip are two children of `.results-flow` and its gap
           separates them by the same amount as two unrelated blocks - a heading floating
           between the thing above it and the thing it names. The rule is not just the
           binding: the line is where the ANSWER stops and the supporting material starts,
           which on these two containers is the only hierarchy there is. */
        .fx-factors, .dc-evidence { border-top:1px solid var(--line); padding-top:var(--block-gap); }
        /* ---- Builder and Dataset containers ----
           Both put a TABLE at the centre of the form and a few scalar settings beside it,
           so both take the same two-column form and differ in where the answer sits.
           Grouped onto the dashboard's column rules above, as `.formula` and `.decision`
           are; what is new here is the form split and the emphasis. */
        /* The build surface takes the room; the settings take what a handful of number
           boxes and dropdowns actually need. Below 900px they stack, because a 26-row
           table in half a phone screen is worse than either. */
        .bd-form, .ds-form { display:grid; grid-template-columns:minmax(0,1fr) 300px; gap:var(--block-gap); align-items:start; }
        @media (max-width:900px) { .bd-form, .ds-form { grid-template-columns:1fr; } }
        /* The grid input is the only field in its card, so the field grid collapses to one
           column and the table gets the full width of it rather than a 190px track. */
        .bd-surface .fields, .ds-data .fields { grid-template-columns:1fr; }
        /* THE SETTINGS STAY WITH YOU. These two containers are the only ones whose form
           can be taller than the viewport - a 26-row subgroup table is - and a subgroup
           size that scrolls away while you are typing into the table it governs is the
           whole reason this card is beside the surface rather than above it. Same
           treatment and same breakpoint as `.results-panel` on `panel`, which solves
           the identical problem from the results side. */
        .bd-settings, .ds-settings { position:sticky; top:16px; }
        @media (max-width:900px) { .bd-settings, .ds-settings { position:static; } }
        /* ---- Flow and Classifier containers ----
           Both draw an ORDERED LIST that the ui-definition authored, so both build on one
           numbered-step treatment and diverge in what a step contains: a flow's step holds
           metric tiles, a classifier's holds one sentence. */
        .fl-stages, .cl-steps { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--block-gap); }
        /* The number is the ordering made visible, and it is the only thing either
           container adds to what the author wrote. Same disc on both, so a reader moving
           between them reads the same signal. */
        .fl-stage-index, .cl-step-index { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
                                          width:22px; height:22px; border-radius:50%; font-size:.75rem; font-weight:700;
                                          background:color-mix(in srgb, var(--accent) 14%, white); color:var(--accent); }

        /* FLOW: a stage is a titled block of tiles; the System Flow section below draws
           each one as a card and joins them with an arrow. */
        .fl-stage-head { display:flex; align-items:center; gap:9px; margin-bottom:10px; }
        .fl-stage-title { font-size:.8rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700; color:var(--ink); }
        .fl-totals { position:relative; display:grid; grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); gap:var(--block-gap);
                     background:color-mix(in srgb, var(--accent) 8%, white);
                     border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                     border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 20px; }
        .fl-totals .primary-value { font-size:1.9rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .fl-totals .primary-name { margin-top:6px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }

        /* CLASSIFIER: the answer is a SYMBOL, so it takes the prose treatment `.dc-verdict`
           takes and for the same reason - `Uscs_Group_Symbol` is declared `text`, and
           tabular figures with -.02em tracking are for columns of numbers. Larger than the
           recommendation because a group symbol is two or three characters and this
           container has nothing else competing for the top of the page. */
        .cl-verdict { background:var(--surface); border:1px solid var(--line);
                      border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:20px 22px; }
        .cl-symbol .primary-value { font-size:2.6rem; font-weight:800; line-height:1.1; color:var(--accent);
                                    letter-spacing:normal; font-variant-numeric:normal; }
        .cl-symbol .primary-name { margin:0 0 6px; }
        .cl-symbol + .cl-symbol { margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }

        /* THE TRACE. One sentence per step, as the workbook wrote it. Set as PROSE at a
           readable measure - it is the one region on any container whose content is meant
           to be read rather than scanned - and never truncated, because a step that has
           been shortened is a step whose rule the reader can no longer check. */
        .cl-trace { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:16px 18px; }
        .cl-steps { gap:12px; }
        .cl-step { display:flex; align-items:flex-start; gap:11px; }
        .cl-step-text { font-size:.9rem; line-height:1.5; color:var(--ink); max-width:70ch; }
        /* The steps are a CHAIN, so they are joined rather than merely listed. */
        .cl-step + .cl-step { border-top:1px solid var(--line); padding-top:12px; }

        /* The demoted remainder on both, joining the disclosure the other containers share. */
        .fl-supporting, .cl-supporting { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:12px 16px; }
        .fl-supporting > summary, .cl-supporting > summary { cursor:pointer; font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700; color:var(--muted); }
        .fl-supporting > summary:focus-visible, .cl-supporting > summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }
        .fl-supporting[open] > summary, .cl-supporting[open] > summary { margin-bottom:14px; }
        /* ---- Assessment container (work through it, then read where you stand) ----
           The questionnaire is one long grid, so the card that holds it gets a header the
           other inputs cards do not have: a completion count and category navigation. */
        .as-quiz-head { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:14px; }
        /* The questionnaire is ONE grid input, so the field grid collapses to a single
           track and the 20-row table gets the whole width of the card. Same rule the build
           surface needs, for the same reason: an auto-fit track sized for a number box is
           the wrong container for a table. */
        .as-quiz .fields { grid-template-columns:1fr; }
        /* `.panel-kicker` carries a 14px bottom margin meant for a block above a field grid;
           inside a flex row it becomes a shove to the left of centre. */
        .as-quiz-kicker { margin:0; }
        .as-progress { font-size:.8rem; color:var(--muted); font-variant-numeric:tabular-nums; }
        /* The score is the page's headline and takes the accented treatment. Where the
           contract declares display bounds - ESG_Scorecard does - the headline slot draws
           the RADIAL ring instead, and this rule then styles nothing, which is correct:
           §4.4 owns that decision and this container does not second-guess it. */
        .as-score { background:color-mix(in srgb, var(--accent) 8%, white);
                    border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                    border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 24px; }
        .as-figure .primary-value { font-size:2.6rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .as-figure .primary-name { margin-top:6px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        .as-findings { border-top:1px solid var(--line); padding-top:var(--block-gap); }

        /* ---- Scenario container (assumptions left, outcome right) ----
           `panel`'s workspace with two deliberate differences: the time series is INSIDE
           the sticky outcome column, and the sixteen financial details are behind a
           disclosure rather than listed in a <dl> between you and the curve. */
        .scenario { display:grid; grid-template-columns:minmax(0,1fr) 420px; gap:22px; align-items:start; }
        /* Wider than `.results-panel`'s 358px because this column holds a CHART, and a
           cumulative cash-flow curve at 358px is a sparkline. */
        .sc-panel { position:sticky; top:16px; background:var(--surface); border:1px solid var(--line);
                    border-radius:var(--radius); box-shadow:var(--shadow-md); padding:20px 22px; }
        @media (max-width:1000px) { .scenario { grid-template-columns:minmax(0,1fr); gap:var(--block-gap); }
                                     .sc-panel { position:static; } }
        .sc-outcome .primary-value { font-size:2.4rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .sc-outcome .primary-name { margin-top:5px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        .sc-series { display:flex; flex-direction:column; gap:var(--block-gap); }
        /* Sixteen assumptions down one column is a ladder nobody reads to the bottom of.
           This is the only container where the INPUT side is the tall one, so its fields
           flow into as many tracks as the column will carry. */
        .sc-assumptions .fields { grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); }

        /* Both containers join the disclosure the other three share. */
        .as-supporting, .sc-supporting { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:12px 16px; }
        .as-supporting > summary, .sc-supporting > summary { cursor:pointer; font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700; color:var(--muted); }
        .as-supporting > summary:focus-visible, .sc-supporting > summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }
        .as-supporting[open] > summary, .sc-supporting[open] > summary { margin-bottom:14px; }



        /* BUILDER: the answer is a set of properties OF the thing you assembled, so they
           read across as a row of figures rather than down as a column of cards. Same
           accented treatment as the dashboard's KPI card, because it is the same claim. */
        .bd-properties { position:relative; display:grid; grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); gap:var(--block-gap);
                         background:color-mix(in srgb, var(--accent) 8%, white);
                         border:1px solid color-mix(in srgb, var(--accent) 26%, white);
                         border-radius:var(--radius); box-shadow:var(--shadow-md); padding:22px 20px; }
        .bd-properties .primary-value { font-size:1.9rem; font-weight:800; color:var(--accent); line-height:1.04; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        .bd-properties .primary-name { margin-top:6px; font-size:.75rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }

        /* DATASET: the chart IS the answer, so it comes first and gets the room. The
           figures below it read as a caption on the picture rather than as the headline -
           which is the one thing that distinguishes this container from `builder`, both of
           them having the same form. */
        .ds-charts { display:flex; flex-direction:column; gap:var(--block-gap); }
        .ds-figures { display:grid; grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); gap:var(--block-gap);
                      background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
                      box-shadow:var(--shadow-sm); padding:16px 18px; }
        /* Deliberately QUIETER than the builder's properties row and than `.dash-kpi`: on
           this container the number is not the headline, the chart above it is. */
        .ds-figures .primary-value { font-size:1.5rem; font-weight:700; color:var(--ink); line-height:1.1; font-variant-numeric:tabular-nums; }
        .ds-figures .primary-name { margin-top:5px; font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }

        /* The demoted details, on both containers that demote. The same disclosure
           `analytics` uses - one rule, three names, rather than three copies of a
           border-and-padding block that would then be free to drift apart. */
        .an-supporting, .ds-supporting, .bl-supporting { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:12px 16px; }
        .an-supporting > summary, .ds-supporting > summary, .bl-supporting > summary { cursor:pointer; font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700; color:var(--muted); }
        .an-supporting > summary:focus-visible, .ds-supporting > summary:focus-visible, .bl-supporting > summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }
        .an-supporting[open] > summary, .ds-supporting[open] > summary, .bl-supporting[open] > summary { margin-bottom:14px; }



        /* Repeating-table input. Optional Add/Remove controls only toggle authored rows;
           the fixed named-range extent still travels in every request. */
        .grid-in-wrap { overflow-x:auto; }
        .grid-in { border-collapse:separate; border-spacing:0 5px; font-size:.85rem; width:100%; }
        .grid-in th, .grid-in td { border:0; border-top:1px solid var(--line); border-bottom:1px solid var(--line); padding:0; background:var(--surface); }
        .grid-in tr > :first-child { border-left:1px solid var(--line); border-radius:9px 0 0 9px; }
        .grid-in tr > :last-child { border-right:1px solid var(--line); border-radius:0 9px 9px 0; }
        .grid-in thead th { border:0; background:transparent; }
        .grid-in th { padding:5px 9px; color:var(--muted); font-weight:700; text-align:left; font-size:.72rem; text-transform:uppercase; letter-spacing:.035em; }
        .grid-column-label, .grid-column-unit { display:block; }
        .grid-column-unit { margin-top:3px; color:color-mix(in srgb,var(--muted) 88%,var(--ink));
                            font-size:.64rem; font-weight:600; line-height:1.2; letter-spacing:.01em; text-transform:none; }
        .grid-in th.readonly { color:var(--muted); opacity:.72; }
        .grid-in td.readonly { padding:7px 9px; color:var(--muted); background:var(--surface-2, #f7f8fa); text-align:right; font-variant-numeric:tabular-nums; }
        .grid-in tbody tr:hover td { border-color:color-mix(in srgb,var(--accent) 35%,var(--line)); }
        .grid-in input { width:100%; border:0; background:transparent; padding:8px 9px; font:inherit; font-size:.85rem;
            text-align:right; font-variant-numeric:tabular-nums; }
        .grid-in td.grid-align-left, .grid-in input.grid-align-left, .grid-in td.grid-align-left > input { text-align:left; font-variant-numeric:normal; }
        .grid-in input:focus { outline:2px solid var(--accent); outline-offset:-2px; }
        /* Mouse activation must not inherit the full-cell text-input outline. Keep a
           compact keyboard-only indicator on the checkbox itself instead. */
        .grid-in input[type=checkbox]:focus { outline:0; box-shadow:none; }
        .grid-in input[type=checkbox]:focus-visible { outline:2px solid color-mix(in srgb,var(--accent) 55%,transparent); outline-offset:2px; border-radius:3px; }
        .grid-in-note { font-size:.82rem; color:var(--muted); margin:2px 0 0; }
        /* The withheld-table note (#345). Louder than the muted note beside it because it
           is telling a visitor their input is not reaching the answer yet — that is a
           correction to what the result cards appear to be saying, not an aside. */
        .grid-in-withheld { color:var(--ink); border-left:3px solid var(--accent); padding:4px 0 4px 9px; margin-top:8px; }
        /* Per-column controls (schema 0.17 grid.columns, issue #207). The readonly cell
           above is right-aligned and tabular because it used to hold a row counter; a
           LOCKED column holds the row's label - a question - so it reads left and wraps. */
        .grid-in td.locked { text-align:left; white-space:normal; font-variant-numeric:normal; min-width:14ch; }
        .grid-in select { width:100%; border:0; background:transparent; padding:6px 9px; font:inherit; font-size:.85rem; text-align:left; }
        .grid-in select:focus { outline:2px solid var(--accent); outline-offset:-2px; }
        .grid-in input[type=checkbox] { width:auto; margin:6px 9px; text-align:left; }
        .grid-in input[type=date] { text-align:left; font-variant-numeric:normal; }
        .grid-cell-slider { display:flex; align-items:center; gap:8px; padding-right:9px; }
        .grid-cell-slider input[type=range] { padding:6px 0 6px 9px; }
        .grid-cell-slider output { font-size:.8rem; color:var(--muted); font-variant-numeric:tabular-nums; min-width:4ch; text-align:right; }
        .grid-actions { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:10px; }
        .grid-action-meta { display:grid; gap:3px; min-width:0; }
        .grid-row-count { color:var(--muted); font-size:.78rem; }
        .grid-draft-status { color:#946200; font-size:.73rem; line-height:1.3; }
        .grid-action-cell { width:70px; text-align:center; padding:0 7px!important; }
        .dataset .grid-in th:first-child, .dataset .grid-in td:first-child { width:76px; min-width:76px; max-width:76px; }
        .dataset .grid-in th:first-child { font-size:.66rem; line-height:1.15; white-space:normal; }
        /* A two-column dataset is an editing pair, not a row-label table. Give both
           controls the same usable surface instead of applying the compact 76px label
           column used by wider statistical grids. This keeps SVI's elapsed-time slider
           operable without changing the six-column control-chart treatment. */
        .dataset .grid-in:has(> thead > tr > th:first-child:nth-last-child(2)) { table-layout:fixed; }
        .dataset .grid-in:has(> thead > tr > th:first-child:nth-last-child(2)) th { width:50%; min-width:0; max-width:none; }
        .dataset .grid-in:has(> thead > tr > th:first-child:nth-last-child(2)) td { width:50%; min-width:0; max-width:none; }
        /* A table needs the full width of the field grid. */
        .field:has(.grid-in-wrap) { grid-column:1 / -1; }
        .breakdown dd:has(.grid-out) { white-space:normal; }
        /* One column below 760px. The container's mobile rule is that the primary CHART
           comes before the insight rail, and it does already: source order is chart then
           rail, and a collapsed grid follows source order. Nothing reorders here, which is
           why there is no `order` declaration to get wrong. */
        @media (max-width:760px) { .dash-top { grid-template-columns:1fr; } .an-diagnosis { grid-template-columns:1fr; } }

        /* ---- Powered by ---- */
        .powered-by { margin-top:22px; text-align:center; font-size:.8rem; color:var(--muted); }
        .powered-by a { color:var(--muted); text-decoration:none; }
        .powered-by a:hover { color:var(--accent); }
        .powered-by strong { color:color-mix(in srgb, var(--ink) 80%, var(--muted)); font-weight:700; }

        /* ---- Form card ---- */
        .form { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
                box-shadow:var(--shadow-md); padding:24px 24px 26px; display:grid; grid-template-columns:1fr 1fr; gap:18px 26px; }
        .form-head { grid-column:1 / -1; display:flex; align-items:baseline; gap:10px; margin:-2px 0 4px;
                     padding-bottom:14px; border-bottom:1px solid var(--line); }
        .calc-indicator { color:var(--accent); font-size:.8rem; font-weight:600; }
        .muted { color:var(--muted); font-size:.85rem; }

        .field { display:flex; flex-direction:column; gap:5px; position:relative; }
        .field label { font-size:.82rem; font-weight:600; display:flex; align-items:center; gap:6px; color:color-mix(in srgb, var(--ink) 88%, var(--muted)); }
        .help-btn { border:0; background:none; color:var(--muted); cursor:pointer; font-size:.9rem; padding:0; line-height:1; transition:color .15s; }
        .help-btn:hover { color:var(--accent); }

        .ctl { display:flex; align-items:center; gap:8px; }
        .ctl.grid-in-wrap { display:block; }
        .ctl select, .affix { border:1px solid var(--line-strong); border-radius:10px; background:var(--surface);
                              transition:border-color .15s, box-shadow .15s; }
        .ctl select { padding:9px 32px 9px 11px; font:inherit; font-size:.95rem; color:var(--ink); flex:1 1 auto; min-width:0; width:100%;
                      appearance:none; -webkit-appearance:none; cursor:pointer; text-align:left;
                      background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23606a7b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
                      background-repeat:no-repeat; background-position:right 10px center; }
        .affix { display:flex; align-items:center; gap:6px; padding:0 11px; flex:1 1 auto; min-width:0; }
        .affix input { border:0; outline:none; background:transparent; padding:9px 0; font:inherit; font-size:.95rem; color:inherit;
                       flex:1 1 auto; min-width:0; width:100%; font-variant-numeric:tabular-nums; }
        .affix .prefix, .affix .suffix { color:var(--muted); font-size:.8rem; font-weight:500; white-space:nowrap; flex:0 0 auto; }
        .affix:focus-within, .ctl select:focus { border-color:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); outline:none; }
        /* The searchable dropdown (choice-list and cell-choice with more than 60 declared options;
           components/fields/searchable-choice.js). The box looks like the select it replaces; its
           popup is fixed to the viewport (placed by the island), so no table wrapper clips it. */
        .choice-search { position:relative; flex:1 1 auto; min-width:0; width:100%; }
        .ctl .choice-search-input { box-sizing:border-box; width:100%; min-width:0; padding:9px 34px 9px 11px; font:inherit; font-size:.95rem;
                                    color:var(--ink); background:var(--surface); border:1px solid var(--line-strong); border-radius:10px;
                                    text-overflow:ellipsis; transition:border-color .15s, box-shadow .15s; }
        .ctl .choice-search-input:focus { border-color:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); outline:none; }
        .ctl .choice-search-input::placeholder { color:var(--muted); opacity:1; }
        .choice-search-cell .choice-search-input { box-sizing:border-box; width:100%; min-width:9ch; padding-right:28px; text-overflow:ellipsis; text-align:left; }
        .choice-search-chevron { position:absolute; right:10px; top:50%; width:16px; height:16px; margin-top:-8px; pointer-events:none;
                                 background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23606a7b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") no-repeat center; }
        .choice-search-cell .choice-search-chevron { right:6px; }
        .choice-search.is-open .choice-search-chevron { transform:rotate(180deg); }
        .choice-search-popup { position:fixed; z-index:1000; top:0; left:0; display:flex; flex-direction:column; max-height:320px;
                               background:var(--surface); border:1px solid var(--line-strong); border-radius:10px;
                               box-shadow:0 12px 28px rgba(16,19,28,.18); overflow:hidden; }
        .choice-search-popup[hidden] { display:none; }
        .choice-search-list { list-style:none; margin:0; padding:4px; overflow-y:auto; overscroll-behavior:contain; flex:1 1 auto; min-height:0; }
        .choice-search-option { padding:8px 10px; border-radius:7px; cursor:pointer; font-size:.9rem; line-height:1.3; color:var(--ink);
                                overflow-wrap:anywhere; text-align:left; }
        .choice-search-option:hover { background:color-mix(in srgb, var(--accent) 8%, transparent); }
        .choice-search-option.is-active { background:color-mix(in srgb, var(--accent) 16%, transparent); }
        .choice-search-option.is-selected { font-weight:600; }
        .choice-search-empty { margin:0; padding:10px 12px; font-size:.88rem; color:var(--muted); }
        @media (max-width: 600px) { .choice-search-option { padding:10px 10px; } }
        .field:has(> .slider-control) { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:5px 10px; }
        .field:has(> .slider-control) > label { grid-column:1; grid-row:1; align-self:center; }
        .slider-control { display:contents; }
        .slider-value-row { grid-column:2; grid-row:1; align-self:center; }
        /* #1333: a slider with an authored track shows a compact value box in place of the readout. */
        .slider-with-entry .slider-value-row .ctl { width:9.5rem; max-width:100%; }
        .slider-value { display:inline-flex; align-items:baseline; min-width:66px; justify-content:center; padding:5px 9px;
                        border:1px solid color-mix(in srgb,var(--accent) 30%,var(--line)); border-radius:9px;
                        background:var(--template-soft); color:var(--accent-strong); font-size:.87rem; font-weight:700; font-variant-numeric:tabular-nums; }
        .slider-value-unset { color:var(--muted); font-style:italic; font-weight:600; }
        .slider-unit { margin-left:4px; color:var(--muted); font-size:.7rem; font-weight:600; }
        .slider-control input[type=range] { grid-column:1 / -1; grid-row:2; width:100%; min-width:0; height:22px; accent-color:var(--accent); cursor:pointer; }
        .slider-scale { grid-column:1 / -1; grid-row:3; display:flex; justify-content:space-between; color:var(--muted); font-size:.67rem; font-variant-numeric:tabular-nums; }
        .unit { color:var(--muted); font-size:.8rem; white-space:nowrap; flex:0 0 auto; }

        /* ---- Info popup ---- */
        .info { position:absolute; top:1.8rem; left:0; z-index:40; width:max-content; max-width:300px;
                font-size:.82rem; color:#39414f; background:var(--surface); border:1px solid var(--line);
                border-radius:12px; padding:11px 32px 11px 13px; line-height:1.5; box-shadow:0 14px 40px -12px rgba(16,19,28,.4); }
        .info-close { position:absolute; top:5px; right:9px; border:0; background:none; color:var(--muted); cursor:pointer; font-size:1.15rem; line-height:1; }

        /* ---- Banners ---- */
        .banner { margin:18px 0 0; padding:13px 15px; border-radius:12px; font-size:.92rem; box-shadow:var(--shadow-sm); }
        .banner.not-valid { background:#fdecec; color:#8a1c1c; border:1px solid #f3b4b4; }
        .banner.check { background:#fff7e6; color:#8a5a00; border:1px solid #f2d48a; }
        .banner.errors { background:#fdecec; color:#8a1c1c; border:1px solid #f3b4b4; }
        .banner.errors ul { margin:6px 0 0; padding-left:18px; }
        /* The ordinary status: the workbook wrote a sentence and it was being DROPPED
           unless it began NOT VALID or CHECK. Deliberately the quietest of the four - it
           carries no warning, so it must not look like one sitting beside a result that is
           fine. Surface and line, not a colour that means something. */
        .banner.status { background:var(--surface); color:var(--ink); border:1px solid var(--line); }
        /* A workbook status is model health, not the calculator's result. This compact
           treatment labels that distinction explicitly while retaining the three existing
           status classes and their accessible live-region roles. */
        .banner.model-check { padding:0; overflow:hidden; box-shadow:none; }
        .model-check-visible { display:grid; grid-template-columns:36px minmax(0,1fr); gap:12px; align-items:start; padding:13px 15px; }
        /* No coloured stripe down the left edge (Ugur, 2026-09-28, with every card
           fingernail): the state is carried by the tint, the border colour, the icon and
           the title, all of which it already had. */
        .banner.model-check.status.model-check-ok { background:#f3fbf6; color:#1e5133; border:1px solid #c9e7d5; }
        .banner.model-check.status.model-check-neutral { background:var(--surface); color:var(--ink); border:1px solid var(--line); }
        /* The TEXT darkens from .banner.check's #8a5a00 because
           the label and context below are drawn at 78% and 76% of currentColor. At
           #8a5a00 that muting lands at 3.54:1 and 3.42:1, under the 4.5:1 AA needs for
           text this size; at #664200 it lands at 4.74:1 and 4.54:1. The other three
           states already pass at those opacities, so only this one moves. Scoped to
           .model-check so plain .banner.check, which has no muted children, is left
           alone. */
        .banner.model-check.check { color:#664200; }
        .model-check-icon { display:grid; place-items:center; width:34px; height:34px; border-radius:50%; background:rgba(255,255,255,.62); font-size:1.12rem; font-weight:700; line-height:1; }
        .model-check-copy { min-width:0; }
        .model-check-label { display:block; margin-bottom:1px; color:currentColor; opacity:.78; font-size:.67rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; }
        .model-check-title { display:block; font-size:.96rem; line-height:1.35; }
        .model-check-detail, .model-check-context { margin:3px 0 0; line-height:1.45; }
        .model-check-context { opacity:.76; font-size:.78rem; }
        .model-check-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 (max-width:520px) {
            .model-check-visible { grid-template-columns:32px minmax(0,1fr); gap:9px; padding:12px; }
            .model-check-icon { width:30px; height:30px; }
        }

        /* ---- Results ---- */
        /* THE HERO-TO-DETAIL GAP (#378). `.card.primary` spans the full row and the detail
           cards sit under it, so this grid gap is the seam directly beneath the number the
           page exists to show — and it ran at 14px inside a region running at 18.
           `margin-top:20px` came off with it: on THIS page `.cards` only ever renders as a
           child of `.results-live` (island.js builds it in one place and puts it in one
           place), where the flow zeroed the margin anyway, so it was a third number that
           could only ever be cancelled. The `.cards` on Home/Category/Search is site.css's
           rule and a different element; this stylesheet does not reach it. */
        .cards { display:grid; grid-template-columns:repeat(auto-fill, minmax(184px,1fr)); gap:var(--block-gap); }
        .card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); padding:14px 16px; box-shadow:var(--shadow-sm); }
        .card-name { font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        .card-value { font-size:1.35rem; font-weight:700; margin-top:5px; font-variant-numeric:tabular-nums; letter-spacing:-.01em; }
        .card-unit { font-size:.78rem; font-weight:600; color:var(--muted); }
        /* The headline is told apart from a detail by its tint, its accent border and its
           accent-coloured 2.05rem value. It used to carry a 4px accent bar down its left
           edge as well, drawn by `::before` in a widened left padding; that bar was removed
           with every other card fingernail (Ugur, 2026-09-28), and the padding it needed
           went with it, so the headline keeps `.card`'s own even padding. */
        .card.primary { grid-column:1 / -1;
                        background:color-mix(in srgb, var(--accent) 7%, white);
                        border:1px solid color-mix(in srgb, var(--accent) 26%, white); box-shadow:var(--shadow-md); }
        .card.primary .card-value { font-size:2.05rem; color:var(--accent); margin-top:3px; }

        /* ---- #409: TWO OR MORE HEADLINE FIGURES FLOW; THEY DO NOT STACK ----
           `grid-column:1 / -1` above is RIGHT for one primary — a single headline IS the
           row — and wrong for the 48 calculators of 72 that declare two or more. On
           pump-fan-affinity-law-scaling three of them each took a 1000px row with the
           number at its left, while five DETAIL cards sat side by side in the same width;
           one calculator stacks eight. Only the layout moves here: the tint, the border
           and the 2.05rem accent value are untouched, because that treatment is what
           makes a headline legible AS a headline once they sit in a row.

           SPAN 2, AND 2 IS NOT A TASTE. `.cards` is `main`'s content width — border-box
           1040px less 40px of padding, so 1000px at most — and auto-fill over
           minmax(184px,1fr) at an 18px gap therefore tops out at FIVE tracks. That leaves
           three candidates and two of them are not layouts: span 1 is a detail's 184px and
           would wrap a 2.05rem figure, span 3 is 592px and puts one per row, which is the
           defect with extra steps. span 2 is the only span that ever puts two headline
           figures on one row, so the maximum per row is 2 by construction rather than by
           preference — and it is also the wrap point, because five across would be as
           unreadable as five stacked.

           IT CLEARS THE GAUGE. A primary card holding `.gauge-radial` draws a 150px dial
           inside `.card`'s 16px of padding either side, so that card needs 182px of card
           before the ring is squeezed. span 2 is 386px at its narrowest (four 184px
           tracks) — 354px of content box — so the ring is never the thing that is short
           of room. PrimaryCardFlowCssTests recomputes that from the dial and the padding.

           THE BANDS DO NOT MIX. Auto-placement would drop the first DETAIL into whatever
           track is left over at the end of a headline row, where the grid row stretches it
           to the headline's height. `grid-column-start:1` on the first non-primary card
           starts the detail grid on a fresh row instead. "First non-primary" is readable
           only because island.js emits `primaries.concat(details)` in one place — the same
           DOM order `.card.primary + .card.primary` counts them by — and
           island-primary-flow.test.js is what holds that order there.

           840px IS DERIVED, NOT CHOSEN. Below four tracks span 2 stops meaning two per
           row: at three tracks it is one per row with a third of the row empty, which is
           worse than the full-width card it replaces, and at one track it asks for an
           implicit column the template does not have. Four tracks need 4*184 + 3*18 =
           790px of `.cards`, which is 830px of viewport through `main`'s cap and padding;
           840 leaves 10px so a sub-pixel width at the exact track boundary cannot land the
           rule on a three-track grid. That is a number that is only correct because of
           numbers in two other rules, so the test recomputes it from those rules instead
           of restating it. Under 840px every primary keeps the full row it has today.

           WHAT IS LEFT OVER, SAID PLAINLY. At five tracks (viewport >= 1040px) two spans
           of 2 leave one 184px track unused at the right of each headline row: 5 is not
           divisible by 2 and a grid item cannot span a fraction of a track. Closing it
           costs either a different floor for the DETAIL cards — a relayout of all 72 — or
           flex, which cannot reproduce auto-fill's trailing rows (flex-grow stretches a
           short last row; no grow leaves up to 70px of slack at 860px). 18.5% of one row
           was the cheapest of the three. Nothing in the harness paints, so every number in
           this comment is arithmetic on the element tree and none of it is a claim about
           how the page looks. A browser without `:has()` drops both rules and gets today's
           full-width stack, which is the correct fallback rather than a broken one. */
        @media (min-width:840px) {
            /* `:not(:has(.grid-out))` because `.card:has(.grid-out)` gives a table output
               the full row, and this selector is specific enough to take it away again. No
               primary_result declares a grid today — all five that do are `detail` — which
               is exactly when a trap is cheap to close. */
            .cards:has(> .card.primary + .card.primary) > .card.primary:not(:has(.grid-out)) { grid-column:span 2; }
            .cards:has(> .card.primary + .card.primary) > .card.primary + .card:not(.primary) { grid-column-start:1; }
        }

        /* ---- 0.17 gauge: computed.display_min / display_max (spec §4.4) ----
           A GAUGE THAT CANNOT BE DRAWN CORRECTLY IS NOT DRAWN. Declared bounds are the
           sole trigger; a value outside them shows the number and withholds the gauge,
           and that case is NOT an error — no warning, no styling change, no annotation.
           There is deliberately no .gauge-over / .gauge-under rule here any more: the
           absence of the gauge is the entire difference, so there is nothing to paint.
           (Clamping would draw 100.001 as 100 and contradict the number beside it;
           stretching would redefine the axis and make two results incomparable.)

           TWO FORMS, CHOSEN BY THE SLOT — a platform-authored decision, same precedent as
           render_unit / layout / character (CLAUDE.md §3), never a contract field. */
        .gauge { margin-top:9px; }

        /* LINEAR — the compact slots. The reference composition (small-caps label, value,
           bar beneath) is assembled from the slot's own parts: .card-name / .stat-name /
           <dt> is the label, .card-value / .stat-value / <dd> is the value, and the gauge
           contributes the track. */
        .gauge-track { position:relative; height:7px; border-radius:4px; background:var(--line);
                       overflow:visible; }
        .gauge-fill { height:100%; border-radius:4px; background:var(--accent); transition:width .18s ease; }
        .gauge-tick { position:absolute; top:-2px; width:2px; height:11px; border-radius:1px;
                      background:var(--line-strong); transform:translateX(-1px); }
        /* The breakdown's value column is `auto`, so it is only as wide as the number in
           it — which leaves a bar a few dozen pixels long, too short to read a position
           off. The floor widens the column instead, and only on the rows that draw one. */
        .breakdown dd .gauge { min-width:132px; }

        /* RADIAL — the headline slot. A full ring: unfilled portion light grey, filled arc
           in the accent colour, the value large and centred, name above, description
           below. The ring is the figure, so it stands in place of the slot's own
           name/value pair rather than sitting under it. */
        .gauge-radial { margin-top:0; text-align:center; }
        .gauge-name { font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:600; }
        .gauge-dial { position:relative; width:150px; height:150px; max-width:100%; margin:10px auto 0; }
        .gauge-ring { display:block; width:100%; height:100%; overflow:visible; }
        .gauge-ring-track { fill:none; stroke:var(--line); }
        /* butt, not round. The arc is a DASH, and SVG draws a zero-length dash as a dot
           under a round cap — so a value sitting exactly on display_min would paint a
           mark at twelve o'clock where nothing is filled. A page built around not drawing
           a picture that misstates its number does not get to make that trade for a
           softer edge. */
        .gauge-ring-arc { fill:none; stroke:var(--accent); stroke-linecap:butt;
                          transition:stroke-dashoffset .18s ease; }
        .gauge-value { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
                       gap:3px; flex-wrap:wrap; padding:0 16px; line-height:1.05;
                       font-size:1.75rem; font-weight:800; color:var(--accent);
                       letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
        /* Bands are room left for a case the fleet does not have yet: every `display`
           declares `thresholds: []`, so nothing below matches today. */
        .gauge-band { margin-top:5px; font-size:.72rem; font-weight:600; color:var(--muted); }
        .gauge-critical .gauge-fill { background:#b91c1c; }
        .gauge-critical .gauge-ring-arc { stroke:#b91c1c; }
        .gauge-critical .gauge-band, .gauge-critical .gauge-value { color:#b91c1c; }
        .gauge-warning .gauge-fill { background:#b45309; }
        .gauge-warning .gauge-ring-arc { stroke:#b45309; }
        .gauge-warning .gauge-band, .gauge-warning .gauge-value { color:#b45309; }

        .disclaimer { margin-top:30px; font-size:.8rem; color:var(--muted); line-height:1.55; }
        .beta-note { margin:8px 0 0; font-size:.8rem; color:var(--muted); line-height:1.55; }
        .beta-note a { color:var(--accent-strong); font-weight:650; text-decoration:underline; }

        /* Editorial restyle (Ugur, 2026-10-20): Methodology, References and every other prose
           section sit directly on the page in full, under the homepage's ruled head (a 2px ink
           rule and a Fraunces title), with no bordered box. */
        .content-section { margin:32px 0 0; padding:18px 0 0; border-top:2px solid var(--ink); }
        .content-section h2 { font-family:var(--font-display); font-weight:600; font-size:clamp(1.6rem,3.2vw,2.2rem); line-height:1.1; letter-spacing:-.02em; margin:0 0 .9rem; }
        /* Collapsible sections (Ugur, 2026-10-28): each section is a native <details>, closed by
           default. The summary is the ruled head itself: the real h2, a one-line generated teaser
           and a Show/Hide pill. The whole summary is the control (at least 44px tall), so the pill
           is decoration and hidden from assistive technology, which reads the native expanded
           state instead. Nothing here hides the body: only the browser's closed <details> does. */
        .content-summary { display:grid; grid-template-columns:minmax(0,1fr) auto; column-gap:24px; row-gap:6px;
                           align-items:start; min-height:44px; list-style:none; cursor:pointer; border-radius:4px; }
        .content-summary::-webkit-details-marker { display:none; }
        .content-summary::marker { content:""; }
        .content-summary > h2 { grid-column:1; grid-row:1; margin:0; min-width:0; }
        .content-teaser { grid-column:1; grid-row:2; display:block; min-width:0; color:var(--muted); font-size:.94rem;
                          line-height:1.5; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .content-toggle { grid-column:2; grid-row:1 / span 2; display:inline-flex; align-items:center; gap:6px;
                          margin-top:4px; padding:8px 14px; min-height:36px; box-sizing:border-box;
                          border:1px solid var(--line-strong); border-radius:11px; background:var(--surface);
                          color:#4a5263; font-size:.875rem; font-weight:600; line-height:1; white-space:nowrap;
                          transition:border-color .12s, color .12s; }
        .content-toggle-chevron { transition:transform .15s; }
        .content-summary:hover .content-toggle { border-color:var(--accent); color:var(--accent-strong); }
        .content-summary:focus-visible { outline:2px solid var(--accent); outline-offset:4px; }
        .content-section[open] .content-teaser,
        .content-section[open] .content-toggle-show,
        .content-section:not([open]) .content-toggle-hide { display:none; }
        .content-section[open] .content-toggle-chevron { transform:rotate(180deg); }
        .content-summary + * { margin-top:16px; }
        @media (prefers-reduced-motion:reduce) { .content-toggle, .content-toggle-chevron { transition:none; } }
        @media print {
            .content-teaser, .content-toggle { display:none !important; }
            .content-summary { display:block; }
        }
        .prose { color:var(--ink); line-height:1.65; font-size:1rem; }
        .prose p { margin:.5rem 0; max-width:72ch; } .prose ul, .prose ol { margin:.5rem 0; padding-left:1.3rem; max-width:72ch; }
        .prose a { color:var(--accent-strong); text-decoration:underline; overflow-wrap:anywhere; word-break:break-word; }
        .prose code { font-family:ui-monospace, monospace; font-size:.85em; background:var(--bg); border:1px solid var(--line); border-radius:5px; padding:1px 5px; }
        .prose h3 { font-family:var(--font-display); font-weight:600; font-size:1.25rem; letter-spacing:-.01em; margin:1.4rem 0 .35rem; }
        /* The homepage's editorial table (.hp-table): uppercase muted headers over a 1px ink
           rule, then hairline rows; no box, no zebra. */
        .prose table { width:100%; margin:1rem 0 1.25rem; border:0; border-spacing:0; table-layout:fixed; font-size:.9rem; line-height:1.45; }
        .prose th, .prose td { padding:11px 13px 11px 0; text-align:left; vertical-align:top; overflow-wrap:anywhere; border-bottom:1px solid var(--line); }
        .prose th { color:var(--muted); font-size:.75rem; text-transform:uppercase; letter-spacing:.08em; font-weight:700; border-bottom:1px solid var(--ink); }
        .prose td code { white-space:normal; }
        .faq-item { border-top:1px solid var(--line); padding:10px 0; }
        .faq-item:first-of-type { border-top:0; }
        .faq-item summary { cursor:pointer; font-weight:600; font-size:.95rem; }
        .faq-item .prose { margin-top:.5rem; }

        /* ---- G25: related calculators ---- */
        /* Editorial restyle (Ugur, 2026-10-20): the section takes the homepage's ruled head and
           the cards the listing pages' editorial card (site.css a.card): no icon tile, no
           accent, a teal subcategory label, the serif name, the full summary. */
        .related { margin:56px 0 18px; border-top:2px solid var(--ink); padding-top:18px; }
        .related h2 { font-family:var(--font-display); font-weight:600; font-size:clamp(1.6rem,3.2vw,2.2rem); line-height:1.1; letter-spacing:-.02em; margin:0 0 24px; }
        .related-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:20px; }
        .related-card { display:flex; flex-direction:column; min-width:0; color:inherit; text-decoration:none;
            background:var(--surface); border:1px solid var(--line);
            border-radius:var(--radius); padding:22px 22px 20px; box-shadow:var(--shadow-sm); transition:box-shadow .15s, border-color .15s; }
        .related-card:hover { box-shadow:var(--shadow-md); border-color:var(--line-strong); }
        .related-card:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
        .rc-kicker { margin:0 0 10px; font-size:.75rem; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#1B6B6B; }
        .rc-name { font-family:var(--font-display); font-weight:600; font-size:1.3rem; line-height:1.2; letter-spacing:-.012em; color:var(--ink); }
        .rc-sum { margin-top:10px; font-size:.88rem; line-height:1.5; color:#3f4757;
            display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
        .rc-go { margin-top:auto; padding-top:18px; font-weight:600; font-size:.92rem; color:var(--accent); }
        .related-card:hover .rc-go { color:var(--accent-strong); text-decoration:underline; }
        @media (max-width:1020px) { .related-grid { grid-template-columns:repeat(2, minmax(0,1fr)); } }
        @media (max-width:640px) { .related-grid { grid-template-columns:1fr; } }

        /* ---- G13 tier-2: input sections & tabs (full-width items in the field grid) ---- */
        .field-group { grid-column:1 / -1; min-width:0; }
        .field-group-title { font-size:.72rem; line-height:1.25; text-transform:uppercase; letter-spacing:.07em;
            font-weight:700; color:var(--muted); margin:14px 0 12px; padding-top:14px; border-top:1px solid var(--line); }
        .field-group:first-child .field-group-title { border-top:0; padding-top:0; margin-top:0; margin-bottom:12px; }
        /* The first titled group's header line (compact head, 2026-10-28): its title, then the
           "* Required" legend, then, when the page has no worked examples, a small Reset at the
           line's end; with examples the legend itself is right-aligned. */
        .field-group-head { display:flex; flex-wrap:wrap; align-items:center; gap:6px 14px; margin:0 0 12px; }
        .field-group > .field-group-head > .field-group-title { margin:0; padding:0; border-top:0; }
        .field-group-head > .required-legend { flex:none; }
        .field-group-head > .required-legend:last-child, .field-group-head > .reset-btn { margin-left:auto; }
        .field-group-head > .reset-btn { gap:5px; padding:5px 12px; min-height:32px; font-size:.8rem; }
        .field-group-head > .reset-btn .reset-icon { width:14px; height:14px; }
        .field-group-fields { display:grid; gap:16px 22px; min-width:0; }
        .template-workspace .field-group-fields .field > label { font-size:.635rem; letter-spacing:.005em;
                                                                  line-height:1.25; align-items:flex-start; flex-wrap:wrap;
                                                                  white-space:normal; overflow-wrap:anywhere; gap:3px 4px; }
        .field-group-cols-1 .field-group-fields { grid-template-columns:minmax(0,1fr); }
        .field-group-cols-2 .field-group-fields { grid-template-columns:repeat(2,minmax(0,1fr)); }
        .field-group-cols-3 .field-group-fields { grid-template-columns:repeat(3,minmax(0,1fr)); }
        .field-group-cols-4 .field-group-fields { grid-template-columns:repeat(4,minmax(0,1fr)); }
        .field-group-cols-5 .field-group-fields { grid-template-columns:repeat(5,minmax(0,1fr)); }
        .tabbar { grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:6px; margin-bottom:4px; }
        .tab-btn { padding:6px 14px; border:1px solid var(--line-strong); background:#fff; border-radius:999px;
            font:inherit; font-size:.82rem; font-weight:600; color:var(--muted); cursor:pointer; }
        /* Same --hero-ink as .fb-send: the contrast gate only guarantees 3.0:1 against
           white, which is the threshold for non-text UI, not for a text label. */
        .tab-btn.active { background:var(--accent); color:var(--hero-ink); border-color:var(--accent); }

        /* ---- Calculator template parity milestone ---------------------------------
           The ten semantic containers now take the visual hierarchy of the reviewed
           reference system: calculator first, restrained editorial shell, and a distinct
           workspace composition per task. These rules are scoped to `.template-workspace`
           so the three legacy containers remain a stable fallback while the fleet is
           migrated. No value, formula or API behaviour is represented here. */
        /* Editorial restyle (Ugur, 2026-10-20): the home hero's teal kicker (the subcategory),
           a large Fraunces H1 and a lede, on the white page. */
        .hero { color:var(--ink); background:var(--surface); padding:28px 20px 32px; overflow:visible; }
        .hero-inner { max-width:1180px; align-items:flex-start; gap:18px; }
        .hero-copy { min-width:0; }
        .hero-eyebrow { color:#1B6B6B; opacity:1; font-size:.8rem; font-weight:600; letter-spacing:.14em; }
        .hero h1 { font-size:clamp(2.2rem,4.6vw,3.6rem); max-width:24ch; line-height:1.02; letter-spacing:-.025em; margin:.6rem 0 .75rem; }
        .hero-tagline { color:#3f4757; opacity:1; max-width:62ch; font-size:1.12rem; line-height:1.6; }
        /* The Last-updated pill holds its text and nothing else: the accent dot that led it
           was removed (Ugur, 2026-09-28), and the gap that spaced the dot from the text went
           with it. */
        .hero-freshness { display:inline-flex; align-items:center; margin-top:13px; padding:5px 10px;
                          border:1px solid color-mix(in srgb,var(--accent) 18%,var(--line)); border-radius:999px;
                          background:color-mix(in srgb,var(--accent) 5%,white); color:var(--muted);
                          font-size:.76rem; font-weight:600; line-height:1.3; }
        .hero-freshness time { color:var(--ink); font-variant-numeric:tabular-nums; }

        /* ---- The tutorial video (Ugur, 2026-10-12) ----
           Drawn only for a calculator whose character.json declares a video, so these rules
           style nothing on any other page. From 900px: a card in the hero's empty right side,
           centred against the title block (a 16:9 poster, a round play button, the duration,
           and one line under it). Below 900px the card goes and one full-width button under
           the description takes its place. Without scripts neither is drawn: the dialog is
           opened by the page script, and a button that does nothing is worse than none. */
        .hero-video-card { flex:0 0 clamp(250px, 28vw, 340px); align-self:center; margin-left:auto;
                           display:flex; flex-direction:column; gap:10px; padding:0; border:0; border-radius:14px;
                           background:none; color:var(--ink); font:inherit; text-align:center; cursor:pointer; }
        /* Centred against each other whichever is taller: the card against the title block, or
           the title block against a card taller than it. The icon keeps its place at the top. */
        .hero-inner:has(> .hero-video-card) > .hero-copy { align-self:center; }
        .hero-video-frame { position:relative; display:block; aspect-ratio:16 / 9; overflow:hidden; border-radius:14px;
                            border:1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
                            background:color-mix(in srgb, var(--accent) 14%, #10131c); box-shadow:var(--shadow-md); }
        .hero-video-poster { display:block; width:100%; height:100%; object-fit:cover; }
        .hero-video-play { position:absolute; left:50%; top:50%; width:60px; height:60px; margin:-30px 0 0 -30px;
                           border-radius:50%; background:rgba(16,19,28,.74); box-shadow:0 8px 20px -8px rgba(0,0,0,.55);
                           transition:transform .15s ease, background-color .15s ease; }
        .hero-video-play::before { content:""; position:absolute; left:23px; top:18px; border-style:solid;
                                   border-width:12px 0 12px 19px; border-color:transparent transparent transparent #fff; }
        .hero-video-duration { position:absolute; right:8px; bottom:8px; padding:2px 7px; border-radius:6px;
                               background:rgba(16,19,28,.82); color:#fff; font-size:.75rem; font-weight:700;
                               line-height:1.4; font-variant-numeric:tabular-nums; }
        .hero-video-caption { color:var(--accent-strong); font-size:.92rem; font-weight:700; }
        .hero-video-card:hover .hero-video-play { transform:scale(1.06); background:var(--accent-strong); }
        .hero-video-card:focus-visible, .hero-video-pill:focus-visible {
            outline:3px solid color-mix(in srgb, var(--accent) 55%, white); outline-offset:4px; }
        .hero-video-pill { display:none; }
        .hero-video-pill-icon { position:relative; flex:0 0 auto; width:32px; height:32px; border-radius:50%;
                                background:var(--accent-strong); }
        .hero-video-pill-icon::before { content:""; position:absolute; left:12px; top:9px; border-style:solid;
                                        border-width:7px 0 7px 11px; border-color:transparent transparent transparent var(--hero-ink); }
        .hero-video-pill-label { flex:1 1 auto; min-width:0; text-align:left; }
        .hero-video-pill-duration { flex:0 0 auto; color:var(--muted); font-size:.85rem; font-weight:600;
                                    font-variant-numeric:tabular-nums; }
        @media (max-width:899.98px) {
            .hero-video-card { display:none; }
            .hero-video-pill { display:flex; align-items:center; gap:12px; box-sizing:border-box; width:100%;
                               max-width:520px; min-height:48px; margin-top:16px; padding:8px 18px 8px 8px;
                               border:1px solid color-mix(in srgb, var(--accent) 26%, var(--line)); border-radius:999px;
                               background:var(--surface); color:var(--ink); font:inherit; font-weight:650;
                               box-shadow:var(--shadow-sm); cursor:pointer; }
            .hero-video-pill:hover { border-color:var(--accent); }
        }
        @media (max-width:640px) { .hero-video-pill { max-width:none; } }
        @media (scripting: none) { .hero-video-card, .hero-video-pill { display:none !important; } }
        @media (prefers-reduced-motion: reduce) {
            .hero-video-play { transition:none; }
            .hero-video-card:hover .hero-video-play { transform:none; }
        }
        /* The dialog: a native dialog element opened with showModal(): the page behind is inert (the
           focus trap), Esc closes it, and the backdrop is the dialog's own ::backdrop, so a
           click there reaches the dialog element itself. The page under it is held still
           (html.tutorial-video-open) with its scrollbar gutter kept, so nothing behind it moves
           or resizes; the drawing and every input stay exactly as they were. */
        html.tutorial-video-open { overflow:hidden; scrollbar-gutter:stable; }
        .tutorial-dialog { box-sizing:border-box; width:min(960px, calc(100% - 32px)); max-width:none;
                           max-height:calc(100% - 32px); padding:0; border:0; border-radius:16px; overflow:auto;
                           background:var(--surface); color:var(--ink); box-shadow:0 30px 80px -20px rgba(0,0,0,.6); }
        .tutorial-dialog::backdrop { background:rgba(10,12,18,.74); }
        .tutorial-dialog-body { padding:14px 16px 16px; }
        .tutorial-dialog-head { display:flex; align-items:center; gap:12px; margin-bottom:12px; }
        .tutorial-dialog-title { flex:1 1 auto; min-width:0; margin:0; font-family:var(--font-ui); font-size:1.05rem;
                                 font-weight:700; line-height:1.3; }
        .tutorial-dialog-close { flex:0 0 auto; display:grid; place-items:center; width:44px; height:44px; padding:0;
                                 border:1px solid var(--line-strong); border-radius:50%; background:var(--surface);
                                 color:var(--ink); font:inherit; font-size:1.6rem; line-height:1; cursor:pointer; }
        .tutorial-dialog-close:hover { border-color:var(--accent); color:var(--accent-strong); }
        .tutorial-dialog-close:focus-visible { outline:3px solid color-mix(in srgb, var(--accent) 55%, white); outline-offset:2px; }
        .tutorial-dialog-player { position:relative; aspect-ratio:16 / 9; overflow:hidden; border-radius:10px; background:#000; }
        .tutorial-dialog-player iframe { position:absolute; inset:0; width:100%; height:100%; border:0; }
        .tutorial-dialog-note { margin:10px 0 0; color:var(--muted); font-size:.82rem; }
        /* A phone: the player takes the whole width, inside the safe-area insets, and plays
           inline (playsinline in its address). */
        @media (max-width:640px) {
            .tutorial-dialog { width:100%; max-height:100%; border-radius:0; }
            .tutorial-dialog-body { padding:max(10px, env(safe-area-inset-top)) env(safe-area-inset-right)
                                            max(12px, env(safe-area-inset-bottom)) env(safe-area-inset-left); }
            .tutorial-dialog-head, .tutorial-dialog-note { padding-inline:12px; }
            .tutorial-dialog-player { border-radius:0; }
        }
        main { max-width:1180px; margin:0 auto 72px; }
        .calculator-stage { margin:6px 0 34px; }

        /* L11: the HTML contract summary is the usable no-script interface and the
           first-paint placeholder the React island replaces. Reservation is applied
           only where the browser says scripting is enabled, so a no-JS reader never
           receives a large empty tail beneath the shorter summary. Values are scoped
           by the authored layout family and can be tuned without calculator-specific
           names or business rules. */
        /* ---- The dynamic diagram region (migration guide §12) ----
           NOT the delivered workbook figure, which stays removed. This region has a
           vocabulary of its own so the two can never be confused, and so the test that
           keeps the removed one out keeps passing unedited.

           The figure carries an inline aspect-ratio from the module's viewBox, so the
           box is the right height before the drawing exists and nothing below it moves
           between first paint and hydration. Height is therefore never set here. */
        /* SPAN THE ROW IN A GRID CONTAINER. `fx-stage` is a two-column grid, so the figure
           landed in column two of row one and pushed the relationship panel into row two
           beside empty space — it was rearranging the layout, not just fitting badly in it.
           Any grid container the figure can land in needs this. */
        .fx-stage > .calc-figure, .an-diagnosis > .calc-figure, .as-journey > .calc-figure {
            grid-column: 1 / -1;
        }
        /* SPACING, and only here. `an-diagnosis` and `as-journey` are plain grids that
           already space their rows with `gap:var(--block-gap)`, and every results column is
           a flex stack that does the same — a margin on the figure itself would double the
           seam in all of them. `fx-stage` is the exception: it is a bordered card with
           `overflow:hidden` and NO row gap, so a figure spanning its bottom row sat flush
           against the panel above and against the card's own edges. Inset it on every side
           so it reads as a block inside that card rather than a second border drawn along
           the first. */
        .fx-stage > .calc-figure { margin:var(--block-gap); }
        /* IN `dash-top`'s CHART COLUMN. A chartless contract leaves that column empty and
           the figure now takes it (see renderDashboard). `align-items:stretch` on the row
           would stretch the figure to the KPI card's height and fight the aspect-ratio it
           carries, so it starts at the top and keeps its own proportions; the row's `gap`
           already provides the seam, so no margin here. */
        .dash-top > .calc-figure { align-self:start; }
        /* WIDTH: the figure takes the column it is given. It was capped at 860px and
           centred, from when it sat in a full-width results column and looked oversized.
           But it spans a row of its own with nothing beside it, so the cap bought nothing
           except a gutter either side and a drawing full of dimension strings rendered
           smaller than the space allowed. How wide the column is is the layout's decision,
           which is the right place for it; the figure does not second-guess it. */
        .calc-figure { margin:0; background:var(--surface); border:1px solid var(--line);
                       border-radius:var(--radius); box-shadow:var(--shadow-sm);
                       padding:16px; min-width:0; }
        /* The drawing may exceed the column at phone widths. It scrolls in HERE, so the
           page body never scrolls sideways. */
        .calc-figure-stage { overflow-x:auto; overflow-y:hidden; }
        .calc-figure-svg { display:block; width:100%; height:auto; }
        /* Module marks. Neutrals inherit the page theme; the two semantic colours stay
           identical to the approved drawing artifacts on every calculator. Driving or
           demand is burnt orange, and resisting or capacity is deep teal. */
        /* ---- the approved drawings' own visual system ----
           Material colours are the SUBJECT's and have no equivalent on the page, so they
           are declared here as tokens rather than borrowed from one that means something
           else. The semantic pair comes from the approved house drawing palette rather
           than the calculator category accent; changing it per page changes the meaning
           of every load and reaction. Class names are the artifacts' names, scoped to the
           figure. */
        .calc-figure {
            --soil:#D9CBA8; --soil-hatch:#A3906A; --soil-edge:#8E7C56;
            --found:#C9CFCD; --found-hatch:#8C9794;
            --concrete:#C3CAC8; --concrete-edge:#77837F;
            --clay:#B9A98C; --clay-hatch:#8E7C56; --clay-edge:#7C6C4C;
            --sand:#D8CDA8; --sand-dot:#A79A72;
            --steel:#AEB7B4; --steel-edge:#6E7A77;
            --insul:#DCD6C4; --insul-edge:#A79E88; --insul-hatch:#B9B09A;
            --rock:#9AA2A0; --rock-edge:#6E7A77;
            --liquid:#7FB2C4;
            /* Semantic pair from CALCULATOR_DIAGRAM_STANDARD.md and the approved previews. */
            --drive:#A94A15; --resist:#0F5F68; --bad:#9A2A21;
            /* The page owns --ink, --muted, --line and --surface, so those are inherited.
               It does NOT own these three: --halo is the label backing, --paper the colour
               a label sits ON, --faint the quietest annotation. An undefined var() makes
               the whole declaration invalid, and an invalid `fill` falls back to BLACK —
               which is exactly what turned every label halo into a black box. Verify a
               token is the page's by looking at the page WITHOUT this block: a grep that
               matches the block being deleted proves nothing. */
            --figure-mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Consolas,monospace;
            --halo:rgba(255,255,255,.88);
            --paper:var(--surface,#fff);
            --faint:color-mix(in srgb, var(--muted,#606a7b) 72%, var(--surface,#fff));
            /* --ink, --muted, --line and --surface are the PAGE's and are inherited. An
               earlier version restated them here; the restatements shadowed the real ones,
               and two referenced themselves — a cycle makes a custom property permanently
               invalid, which silently unpainted every muted line and label in every ported
               drawing. Do not redeclare a token the page already owns, and do not delete
               one it does not. */
        }
        /* The calculator page currently has one supported, light presentation. Device
           colour preference must not switch only the drawing to a dark material palette:
           that produces dark fills and label halos inside an otherwise light card. If a
           page-wide dark theme is introduced later, its palette belongs with that theme,
           not in a diagram-only media query. */
        .calc-figure text { font-family:var(--figure-mono);
                            fill:var(--ink); font-variant-numeric:tabular-nums; }
        .calc-figure .L1 { font-size:17px; font-weight:500; }
        .calc-figure .L2 { font-size:15px; fill:var(--muted); }
        .calc-figure .tag { font-size:14px; fill:var(--faint); letter-spacing:.1em; }
        .calc-figure .L1.drive, .calc-figure .L2.drive { fill:var(--drive); }
        .calc-figure .L1.resist, .calc-figure .L2.resist { fill:var(--resist); }
        .calc-figure .L1.bad, .calc-figure .L2.bad { fill:var(--bad); }
        .calc-figure .halo { fill:var(--halo); stroke:none; }
        .calc-figure .hatch-soil { stroke:var(--soil-hatch); stroke-width:1; }
        .calc-figure .hatch-found { stroke:var(--found-hatch); stroke-width:1; }
        .calc-figure .soil-body { fill:var(--soil); stroke:var(--soil-edge); stroke-width:1.25; }
        .calc-figure .found-body { fill:var(--found); stroke:none; }
        .calc-figure .wall-body { fill:var(--concrete); stroke:var(--concrete-edge); stroke-width:2; }
        .calc-figure .baseline { stroke:var(--ink); stroke-width:2.5; fill:none; }
        .calc-figure .dimline { stroke:var(--muted); stroke-width:1.1; fill:none; }
        .calc-figure .extline { stroke:var(--line); stroke-width:1.1; fill:none; }
        .calc-figure .leader { stroke:var(--faint); stroke-width:1.1; fill:none; opacity:.9; }
        .calc-figure .leader.drive { stroke:var(--drive); opacity:.7; }
        .calc-figure .leader.resist { stroke:var(--resist); opacity:.7; }
        .calc-figure .leader.bad { stroke:var(--bad); opacity:.8; }
        .calc-figure .leader-dot { fill:var(--faint); }
        .calc-figure .leader-dot.drive { fill:var(--drive); }
        .calc-figure .leader-dot.resist { fill:var(--resist); }
        .calc-figure .leader-dot.bad { fill:var(--bad); }
        .calc-figure .wedge { stroke:var(--ink); stroke-width:1.4; stroke-dasharray:8 5; fill:none; opacity:.6; }
        .calc-figure .p-soil { fill:var(--drive); opacity:.30; stroke:var(--drive); stroke-width:1.3; }
        .calc-figure .p-surch { fill:var(--drive); opacity:.14; stroke:var(--drive); stroke-width:1.2; stroke-dasharray:5 3; }
        .calc-figure .p-arrow { stroke:var(--drive); stroke-width:1.2; fill:none; opacity:.85; }
        .calc-figure .drive-vec { stroke:var(--drive); stroke-width:3; fill:none; }
        .calc-figure .resist-vec { stroke:var(--resist); stroke-width:3; fill:none; }
        .calc-figure .react { fill:var(--resist); opacity:.24; stroke:var(--resist); stroke-width:1.3; }
        .calc-figure .react-neg { fill:var(--bad); opacity:.22; stroke:var(--bad); stroke-width:1.3; }
        .calc-figure .pivot { fill:var(--paper); stroke:var(--ink); stroke-width:2; }
        /* Effective Stress: three soil bands, saturated zone, footing and 2:1 spread,
           the query marker and the stacked decomposition bars. */
        .calc-figure { --soil1:#E2D7BC; --soil2:#D3C29D; --soil3:#C2AE86;
                       --water:#2F6F8F; --water-fill:rgba(47,111,143,.16); }
        .calc-figure .band1 { fill:var(--soil1); }
        .calc-figure .band2 { fill:var(--soil2); }
        .calc-figure .band3 { fill:var(--soil3); }
        .calc-figure .band-edge { fill:none; stroke:var(--soil-edge); stroke-width:1.2; }
        .calc-figure .sat { fill:var(--water-fill); stroke:none; }
        .calc-figure .wt-line { stroke:var(--water); stroke-width:2; fill:none; }
        .calc-figure .wt-mark { fill:var(--water); stroke:none; }
        .calc-figure .ground { stroke:var(--ink); stroke-width:2.5; fill:none; }
        .calc-figure .foot { fill:var(--concrete); stroke:var(--concrete-edge); stroke-width:2; }
        .calc-figure .spread { stroke:var(--drive); stroke-width:1.4; stroke-dasharray:8 5; fill:none; opacity:.75; }
        .calc-figure .spread-fill { fill:var(--drive); opacity:.10; stroke:none; }
        .calc-figure .query { stroke:var(--ink); stroke-width:1.8; stroke-dasharray:2 4; fill:none; }
        .calc-figure .query-dot { fill:var(--ink); }
        .calc-figure .axis { stroke:var(--muted); stroke-width:1.1; fill:none; }
        .calc-figure .tick { stroke:var(--muted); stroke-width:1.1; }
        .calc-figure .bar-eff { fill:var(--resist); opacity:.78; }
        .calc-figure .bar-u { fill:var(--water); opacity:.62; }
        .calc-figure .bar-d { fill:var(--drive); opacity:.80; }
        .calc-figure .bar-edge { fill:none; stroke:var(--ink); stroke-width:1; opacity:.35; }
        .calc-figure .bar-txt { font-size:14px; fill:var(--paper); font-weight:600; }
        .calc-figure .below { fill:none; stroke:var(--bad); stroke-width:1.8; stroke-dasharray:4 4; }
        .calc-figure .L1.water, .calc-figure .L2.water { fill:var(--water); }
        .calc-figure .leader.water { stroke:var(--water); opacity:.7; }
        .calc-figure .leader-dot.water { fill:var(--water); }
        /* Bearing Capacity: the active wedge under the base, the surcharge the Nq term
           uses, the required width ghosted over the entered one, the three bearing terms
           stacked to the ultimate pressure, and the demand against the allowable. */
        .calc-figure { --ok:#2F6B4F; }
        .calc-figure .req { fill:none; stroke:var(--ok); stroke-width:2.2; stroke-dasharray:9 6; }
        .calc-figure .req.grow { stroke:var(--bad); }
        /* The bearing wedge is a FILLED polygon. The gravity wall's .wedge is a <line>
           and keeps the rule it already has: qualifying by element is what lets one class
           name mean the right thing in both drawings without either being rewritten. */
        .calc-figure polygon.wedge { fill:var(--drive); opacity:.16; stroke:var(--drive);
                                     stroke-width:1.5; stroke-dasharray:7 4; }
        .calc-figure .wedge-open { fill:none; stroke:var(--drive); stroke-width:1.5; stroke-dasharray:7 4; opacity:.75; }
        .calc-figure .surch { stroke:var(--resist); stroke-width:1.6; fill:none; opacity:.85; }
        .calc-figure .founding { stroke:var(--resist); stroke-width:1.4; stroke-dasharray:5 5; fill:none; opacity:.7; }
        .calc-figure .t1 { fill:var(--resist); opacity:.85; }
        .calc-figure .t2 { fill:var(--resist); opacity:.55; }
        .calc-figure .t3 { fill:var(--resist); opacity:.30; }
        .calc-figure .dem-track { fill:none; stroke:var(--resist); stroke-width:1.6; }
        .calc-figure .dem-fill { fill:var(--drive); opacity:.82; }
        .calc-figure .dem-fill.over { fill:var(--bad); opacity:.72; }
        .calc-figure .dem-limit { stroke:var(--resist); stroke-width:2.4; stroke-dasharray:7 5; }
        /* the ok tone: the page has no good/success token of its own, and the required
           width is drawn in it when the footing entered is already wide enough */
        .calc-figure .L1.ok, .calc-figure .L2.ok { fill:var(--ok); }
        .calc-figure .leader.ok { stroke:var(--ok); opacity:.8; }
        .calc-figure .leader-dot.ok { fill:var(--ok); }
        /* Insulated Pipe: the pipe in section — bore, wall and insulation with their own
           material fills and the insulation hatch — the radial temperature probe, the
           signed heat-flow arrows, and the two stacked splits (the drop across the four
           series resistances, and the exterior between convection and radiation). */
        .calc-figure { --bore:#E7ECEA; --bore-edge:#8E9A96; }
        .calc-figure .bore { fill:var(--bore); stroke:var(--bore-edge); stroke-width:1.4; }
        .calc-figure .steel { fill:var(--steel); stroke:var(--steel-edge); stroke-width:1.8; }
        .calc-figure .insul { fill:var(--insul); stroke:var(--insul-edge); stroke-width:1.8; }
        .calc-figure .hatch-i { stroke:var(--insul-hatch); stroke-width:1; }
        .calc-figure .probe { stroke:var(--ink); stroke-width:1.6; fill:none; }
        .calc-figure .probe-tick { stroke:var(--ink); stroke-width:2.2; }
        .calc-figure .qvec { stroke:var(--drive); stroke-width:2.6; fill:none; }
        /* One series, four opacities: the reader reads the WIDTHS, and four separate hues
           would read as four unrelated quantities instead of one drop being divided. */
        .calc-figure .seg-film { fill:var(--resist); opacity:.85; }
        .calc-figure .seg-pipe { fill:var(--resist); opacity:.55; }
        .calc-figure .seg-ins { fill:var(--drive); opacity:.62; }
        .calc-figure .seg-out { fill:var(--resist); opacity:.32; }
        .calc-figure .seg-conv { fill:var(--drive); opacity:.75; }
        .calc-figure .seg-rad { fill:var(--drive); opacity:.38; }
        /* Consolidation Settlement: the clay layer and its hatch, the draining sand and
           impermeable rock boundaries, the original and settled surfaces, the uniform
           load, the escaping water, and the stress-history bar with its three segments
           and the preconsolidation mark. Colours are the shared material tokens. */
        .calc-figure .clay { fill:var(--clay); stroke:var(--clay-edge); stroke-width:1.8; }
        .calc-figure .hatch-c { stroke:var(--clay-hatch); stroke-width:1; }
        .calc-figure .sand { fill:var(--sand); stroke:var(--sand-dot); stroke-width:1.2; }
        .calc-figure .sand-dot { fill:var(--sand-dot); }
        .calc-figure .rock { fill:var(--rock); stroke:var(--rock-edge); stroke-width:1.8; }
        .calc-figure .rock-h { stroke:var(--rock-edge); stroke-width:1.3; }
        .calc-figure .surf { stroke:var(--ink); stroke-width:2.6; fill:none; }
        .calc-figure .before { stroke:var(--faint); stroke-width:1.6; stroke-dasharray:7 5; fill:none; }
        .calc-figure .ultimate { stroke:var(--drive); stroke-width:1.8; stroke-dasharray:9 5; fill:none; }
        .calc-figure .load { stroke:var(--drive); stroke-width:2.4; fill:none; }
        .calc-figure .wvec { stroke:var(--water); stroke-width:2.4; fill:none; }
        .calc-figure .dimline.hdr { stroke:var(--water); stroke-width:1.6; }
        .calc-figure .seg-s0 { fill:var(--resist); opacity:.80; }
        /* One increment, one fill. `seg-re` and `seg-cc` painted the recompression and
           virgin parts at two opacities, which drew the stress-history branch — the
           model decision about which compression index applies — in colour. */
        .calc-figure .seg-ds { fill:var(--drive); opacity:.62; }
        .calc-figure .pc-line { stroke:var(--ink); stroke-width:2.4; stroke-dasharray:7 4; }
        /* Beam Deflection: the beam and its supports, the loading, the elastic curve
           and the two allowable limits it is judged against. --support is declared
           here because the page has no support-material token; --steel and --ok are
           the drawing's remaining needs and --steel already exists above. */
        .calc-figure { --support:#C3CAC8; --support-edge:#77837F; --ok:#2F6B4F; }
        .calc-figure .L1.ok, .calc-figure .L2.ok { fill:var(--ok); }
        .calc-figure .beam { fill:var(--steel); stroke:var(--steel-edge); stroke-width:1.8; }
        .calc-figure .sup { fill:var(--support); stroke:var(--support-edge); stroke-width:1.8; }
        .calc-figure .wall { stroke:var(--support-edge); stroke-width:2.4; fill:none; }
        .calc-figure .wall-h { stroke:var(--support-edge); stroke-width:1.2; fill:none; }
        .calc-figure .axis0 { stroke:var(--faint); stroke-width:1.2; stroke-dasharray:6 5; fill:none; }
        .calc-figure .curve { stroke:var(--drive); stroke-width:3; fill:none; stroke-linecap:round; }
        .calc-figure .limit { stroke:var(--resist); stroke-width:1.5; stroke-dasharray:9 6; fill:none; opacity:.9; }
        .calc-figure .limit.over { stroke:var(--bad); }
        .calc-figure .load-vec { stroke:var(--drive); stroke-width:2.4; fill:none; }
        .calc-figure .load-vec.thick { stroke-width:3.2; }
        .calc-figure .load-top { stroke:var(--drive); stroke-width:2; fill:none; }
        .calc-figure .react-vec { stroke:var(--resist); stroke-width:3; fill:none; }
        .calc-figure .dmark { stroke:var(--drive); stroke-width:1.4; fill:none; }
        /* Shaft in torsion: the shaft body and its bore, the built-in end, the surface
           fibres against the one they started as, the applied torque, and the two bars
           measuring the bore against a solid shaft. --steel/--steel-edge already exist. */
        .calc-figure { --bore:var(--paper); --fix:#9AA2A0; --fix-edge:#5E6A67; }
        .calc-figure .shaft { fill:var(--steel); stroke:var(--steel-edge); stroke-width:2; }
        .calc-figure .bore { fill:var(--bore); stroke:var(--steel-edge); stroke-width:1.6; }
        .calc-figure .endcap { fill:var(--steel); stroke:var(--steel-edge); stroke-width:1.8; }
        .calc-figure .fixw { fill:var(--fix); stroke:var(--fix-edge); stroke-width:1.8; }
        .calc-figure .fixh { stroke:var(--fix-edge); stroke-width:1.3; }
        .calc-figure .fibre { stroke:var(--drive); stroke-width:1.9; fill:none; opacity:.85; }
        .calc-figure .fibre0 { stroke:var(--faint); stroke-width:1.6; fill:none; stroke-dasharray:7 5; }
        .calc-figure .twistarc { stroke:var(--drive); stroke-width:2.6; fill:none; }
        .calc-figure .torque { stroke:var(--drive); stroke-width:3; fill:none; }
        .calc-figure .radial { stroke:var(--ink); stroke-width:1.8; fill:none; }
        .calc-figure .radial0 { stroke:var(--faint); stroke-width:1.6; stroke-dasharray:6 4; fill:none; }
        .calc-figure .bar-keep { fill:var(--resist); opacity:.78; }
        .calc-figure .bar-gone { fill:var(--drive); opacity:.34; }
        .calc-figure .invalid { fill:none; stroke:var(--bad); stroke-width:2; stroke-dasharray:10 6; }
        /* Gear Train: two gear bodies with drawn teeth, the pitch circles they mesh on,
           hubs and the line of centres, the rotation arcs, and the force triangle at the
           pitch point with its line of action. --steel-2 is the second body's fill; the
           page had --steel and --steel-edge already and needed the third. */
        .calc-figure { --steel-2:#D2D8D6; }
        .calc-figure .body1 { fill:var(--steel); stroke:var(--steel-edge); stroke-width:2; }
        .calc-figure .body2 { fill:var(--steel-2); stroke:var(--steel-edge); stroke-width:2; }
        .calc-figure .pitch { fill:none; stroke:var(--ink); stroke-width:1.8; stroke-dasharray:10 5 2 5; }
        .calc-figure .centres { stroke:var(--muted); stroke-width:1.2; stroke-dasharray:7 5; fill:none; }
        .calc-figure .hub { fill:var(--paper); stroke:var(--steel-edge); stroke-width:1.6; }
        .calc-figure .pp { fill:var(--drive); stroke:var(--paper); stroke-width:1.5; }
        .calc-figure .loa { stroke:var(--drive); stroke-width:1.5; stroke-dasharray:6 4; fill:none; opacity:.8; }
        .calc-figure .vec-t { stroke:var(--drive); stroke-width:3; fill:none; }
        .calc-figure .vec-r { stroke:var(--resist); stroke-width:3; fill:none; }
        .calc-figure .vec-n { stroke:var(--ink); stroke-width:2.6; fill:none; }
        .calc-figure .rot { stroke:var(--resist); stroke-width:2.4; fill:none; }
        .calc-figure .invalid { fill:none; stroke:var(--bad); stroke-width:2; stroke-dasharray:10 6; }
        .calc-figure .pw-in { fill:var(--resist); opacity:.85; }
        .calc-figure .pw-loss { fill:var(--drive); opacity:.45; }
        /* Pump Head and Power: the source vessel and its surface, the schematic pipe run
           and pump, the datum, the head stack split into static lift and the two
           frictions, NPSH available against required, and the power cascade. */
        .calc-figure { --ok:#2F6B4F; --warn:#8A5107; }
        .calc-figure .L1.ok, .calc-figure .L2.ok { fill:var(--ok); }
        .calc-figure .vessel { fill:var(--steel); stroke:var(--steel-edge); stroke-width:1.8; }
        .calc-figure .wsurf { stroke:var(--water); stroke-width:2.2; fill:none; }
        .calc-figure .wfill { fill:var(--water-fill); stroke:none; }
        .calc-figure .pipe { stroke:var(--steel-edge); fill:none; stroke-linecap:round; stroke-linejoin:round; }
        .calc-figure .pump { fill:var(--steel); stroke:var(--steel-edge); stroke-width:2; }
        .calc-figure .datum { stroke:var(--faint); stroke-width:1.2; stroke-dasharray:6 5; fill:none; }
        .calc-figure .lvl { stroke:var(--line); stroke-width:1.1; stroke-dasharray:4 5; fill:none; }
        .calc-figure .seg-static { fill:var(--resist); opacity:.80; }
        .calc-figure .seg-sf { fill:var(--drive); opacity:.75; }
        .calc-figure .seg-df { fill:var(--drive); opacity:.45; }
        .calc-figure .seg-neg { fill:var(--resist); opacity:.32; }
        .calc-figure .npsh-av { fill:var(--resist); opacity:.55; }
        .calc-figure .npsh-req { stroke:var(--drive); stroke-width:2.6; stroke-dasharray:8 5; }
        .calc-figure .npsh-thr { stroke:var(--warn); stroke-width:1.6; stroke-dasharray:3 4; }
        .calc-figure .pw-h { fill:var(--resist); opacity:.85; }
        .calc-figure .pw-p { fill:var(--drive); opacity:.55; }
        .calc-figure .pw-m { fill:var(--drive); opacity:.30; }
        /* Pipe Run Support Layout: the run anchored at one end and free at the other,
           the supports at the recommended spacing, the candidate spans as bars on one
           scale, and the restrained thermal stress against its allowable. `sup` and the
           `ok` tone are already declared above and are deliberately not repeated; `pipe`
           is qualified to the rect so Pump Head's polyline keeps its own rule. */
        .calc-figure { --anchor:#9AA2A0; --anchor-edge:#5E6A67; }
        .calc-figure rect.pipe { fill:var(--steel); stroke:var(--steel-edge); stroke-width:1.8; }
        .calc-figure .anchor { fill:var(--anchor); stroke:var(--anchor-edge); stroke-width:1.8; }
        .calc-figure .anchor-h { stroke:var(--anchor-edge); stroke-width:1.3; }
        .calc-figure .grow { fill:none; stroke:var(--drive); stroke-width:2; stroke-dasharray:7 4; }
        .calc-figure .bar-run { fill:var(--resist); opacity:.20; stroke:var(--resist); stroke-width:1.3; }
        .calc-figure .bar-gov { fill:var(--drive); opacity:.78; }
        .calc-figure .bar-other { fill:var(--resist); opacity:.42; }
        .calc-figure .bar-loop { fill:var(--drive); opacity:.40; }
        .calc-figure .stress-track { fill:none; stroke:var(--resist); stroke-width:1.6; }
        .calc-figure .stress-fill { fill:var(--drive); opacity:.80; }
        .calc-figure .stress-fill.over { fill:var(--bad); opacity:.72; }
        .calc-figure .stress-limit { stroke:var(--resist); stroke-width:2.4; stroke-dasharray:7 5; }
        /* Tank Volume: the vessel shell and its void, the liquid and its surface, the
           saddles it sits on, the head seams, the entered dip when it is above the tank,
           and the paired depth/capacity bars with the tie between them. */
        .calc-figure { --void:#F2F5F4; --shell-edge:#6E7A77;
                       --saddle:#9AA2A0; --saddle-edge:#5E6A67;
                       --liquid-fill:color-mix(in srgb, var(--liquid) 55%, transparent); }
        .calc-figure .shell { fill:var(--void); stroke:var(--shell-edge); stroke-width:2.2; }
        .calc-figure .liq { fill:var(--liquid-fill); stroke:none; }
        .calc-figure .liq-line { stroke:var(--liquid); stroke-width:2.4; fill:none; }
        .calc-figure .saddle { fill:var(--saddle); stroke:var(--saddle-edge); stroke-width:1.5; }
        .calc-figure .seam { stroke:var(--shell-edge); stroke-width:1.2; stroke-dasharray:6 4; fill:none; }
        .calc-figure .rim { fill:none; stroke:var(--shell-edge); stroke-width:1.4; }
        .calc-figure .over { stroke:var(--bad); stroke-width:2.2; stroke-dasharray:8 5; fill:none; }
        .calc-figure .bar-track { fill:none; stroke:var(--line); stroke-width:1.4; }
        .calc-figure .bar-depth { fill:var(--resist); opacity:.55; }
        .calc-figure .bar-fill { fill:var(--liquid); opacity:.80; }
        .calc-figure .bar-ull { fill:var(--resist); opacity:.20; }
        .calc-figure .tie { stroke:var(--faint); stroke-width:1.2; stroke-dasharray:4 4; fill:none; }
        .calc-figure .L1.liquid, .calc-figure .L2.liquid { fill:var(--liquid); }
        .calc-figure .leader.liquid { stroke:var(--liquid); opacity:.75; }
        .calc-figure .leader-dot.liquid { fill:var(--liquid); }
        /* Belt & Pulley Drive: the belt on the tangents its geometry gives, the wrap
           lit on the smaller pulley, the arc that measures that wrap, the effective-pull
           vector at the driver pitch line, the ghosted pulleys shown when the centres are
           too close for any external tangent, and the delivered half of the power bar.
           The bodies, pitch circles, hubs, line of centres, rotation arcs and the lost
           half of that bar are shared with Gear Train and already on the page. */
        .calc-figure { --belt:#4A4A48; --belt-lit:#A94A15; }
        .calc-figure .belt { fill:none; stroke:var(--belt); stroke-width:7; stroke-linejoin:round; stroke-linecap:round; }
        .calc-figure .belt-lit { fill:none; stroke:var(--belt-lit); stroke-width:9; stroke-linecap:round; }
        .calc-figure .wrap-arc { fill:none; stroke:var(--drive); stroke-width:1.6; }
        .calc-figure .vec-f { stroke:var(--drive); stroke-width:3.2; fill:none; }
        .calc-figure .ghost { fill:none; stroke:var(--bad); stroke-width:1.8; stroke-dasharray:7 5; opacity:.85; }
        .calc-figure .pw-out { fill:var(--resist); opacity:.85; }
        /* Duct Sizing: the section with the exact requirement ghosted inside the stock
           size, the equivalent-round ring, the run, and the falling static-pressure
           gradient with the target slope beside it. --air and --ghost are the drawing's
           own tokens and this page has none that mean the same thing; --warn it already
           has, from Pump Head, at the same two values. */
        .calc-figure { --air:#DCE6E8; --ghost:#8E9A96; }
        .calc-figure .L1.warn, .calc-figure .L2.warn { fill:var(--warn); }
        .calc-figure .L1.ghost, .calc-figure .L2.ghost { fill:var(--ghost); }
        .calc-figure .ptitle { font-size:12px; fill:var(--faint); letter-spacing:.13em; }
        .calc-figure .air { fill:var(--air); stroke:none; }
        /* the wall, on its modifiers only — see the head of _css_duct.js */
        .calc-figure .wall.given { stroke:var(--ink); stroke-width:2.6; }
        .calc-figure .wall.found { stroke:var(--resist); stroke-width:2.6; }
        .calc-figure .ghostline { fill:none; stroke:var(--ghost); stroke-width:1.9; stroke-dasharray:8 5; }
        .calc-figure .eqline { fill:none; stroke:var(--resist); stroke-width:1.7; stroke-dasharray:3 4; opacity:.85; }
        .calc-figure .grad { fill:none; stroke:var(--drive); stroke-width:2.8; stroke-linecap:round; }
        .calc-figure .gradt { fill:none; stroke:var(--muted); stroke-width:1.8; stroke-dasharray:9 6; }
        .calc-figure .gradbase { stroke:var(--line); stroke-width:1.2; }
        .calc-figure .gradfill { fill:var(--drive); opacity:.10; }
        .calc-figure .flow { stroke:var(--drive); stroke-width:2.4; fill:none; }
        .calc-figure .xsym { stroke:var(--drive); stroke-width:1.8; fill:none; }
        .calc-figure .scale { stroke:var(--ink); stroke-width:1.8; }
        .calc-figure .scale-t { font-size:12.5px; fill:var(--muted); }
        .calc-figure .leader.ghost { stroke:var(--ghost); opacity:.7; }
        .calc-figure .leader-dot.ghost { fill:var(--ghost); }
        .calc-figure .arrowhead { fill:var(--drive); }
        /* Stair Calculator: the approved stair preview's drawing CSS, brought across with its
           class names and SCOPED to its own figure. Several of its names (limit, datum,
           hatch-c, ghost, bar-edge) are already taken above by other drawings with other
           values, so unscoped rules would repaint those drawings, and theirs this one; the
           figure's data-diagram attribute keeps each set to its own drawing. `opacity:1` on
           .limit is the preview's value restated, because Beam's .limit sets .9. The text
           face and ink come from the page (.calc-figure text above); timber and the concrete
           hatch are the subject's materials, declared here in the light palette only. */
        .calc-figure[data-diagram="stair-flight-with-returned-run"] {
            --concrete:#C3CAC8; --concrete-edge:#77837F; --concrete-hatch:#98A29F;
            --timber:#D9C39A; --timber-edge:#8C7248; --timber-hatch:#B79C6B;
            --ok:#2F6B4F; --warn:#8A5107; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1 { font-size:17px; font-weight:500; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2 { font-size:15px; fill:var(--muted); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1.drive, .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2.drive { fill:var(--drive); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1.resist, .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2.resist { fill:var(--resist); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1.bad, .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2.bad { fill:var(--bad); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1.ok, .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2.ok { fill:var(--ok); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .L1.warn, .calc-figure[data-diagram="stair-flight-with-returned-run"] .L2.warn { fill:var(--warn); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .halo { fill:var(--halo); stroke:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .slab { fill:var(--concrete); stroke:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .hatch-c { stroke:var(--concrete-hatch); stroke-width:1; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .hatch-t { stroke:var(--timber-hatch); stroke-width:1; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .floorline { stroke:var(--ink); stroke-width:2.5; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .slab-edge { stroke:var(--concrete-edge); stroke-width:1.4; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .timber { fill:var(--timber); stroke:var(--timber-edge); stroke-width:1.4; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .stringer { fill:var(--timber); stroke:var(--timber-edge); stroke-width:1.6; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .cut { stroke:var(--timber-edge); stroke-width:1.4; stroke-dasharray:4 3; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .datum { stroke:var(--muted); stroke-width:1.1; stroke-dasharray:6 5; fill:none; opacity:.8; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .nosing { stroke:var(--muted); stroke-width:1.2; stroke-dasharray:3 4; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .head-line { stroke:var(--drive); stroke-width:2.2; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .head-line.bad { stroke:var(--bad); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .limit { stroke:var(--resist); stroke-width:2; stroke-dasharray:7 5; fill:none; opacity:1; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .limit.bad { stroke:var(--bad); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .avail { fill:var(--resist); fill-opacity:.10; stroke:var(--resist); stroke-width:2; stroke-dasharray:9 6; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .ghost { fill:none; stroke:var(--drive); stroke-width:1.8; stroke-dasharray:6 5; opacity:.9; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .dimline { stroke:var(--muted); stroke-width:1.1; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .dimline.drive { stroke:var(--drive); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .dimline.resist { stroke:var(--resist); stroke-dasharray:7 5; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .extline { stroke:var(--line); stroke-width:1.1; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader { stroke:var(--faint); stroke-width:1.1; fill:none; opacity:.9; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader.drive { stroke:var(--drive); opacity:.7; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader.resist { stroke:var(--resist); opacity:.7; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader.ok { stroke:var(--ok); opacity:.8; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader.bad { stroke:var(--bad); opacity:.8; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader.warn { stroke:var(--warn); opacity:.8; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot { fill:var(--faint); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot.drive { fill:var(--drive); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot.resist { fill:var(--resist); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot.ok { fill:var(--ok); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot.bad { fill:var(--bad); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .leader-dot.warn { fill:var(--warn); }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .seg-r1 { fill:var(--drive); opacity:.85; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .seg-r2 { fill:var(--drive); opacity:.62; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .seg-t { fill:var(--drive); opacity:.36; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .bar-edge { fill:none; stroke:var(--ink); stroke-width:1; opacity:.3; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .range { fill:var(--resist); fill-opacity:.14; stroke:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .range.bad { fill:var(--bad); fill-opacity:.12; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .bar-txt { font-size:14px; fill:var(--paper) !important; font-weight:600; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .panel-rule { stroke:var(--line); stroke-width:1; fill:none; }
        .calc-figure[data-diagram="stair-flight-with-returned-run"] .stringer-sec { fill:var(--timber); stroke:var(--timber-edge); stroke-width:1.2; }
        .cf-text { font-family:var(--figure-mono);
                   font-size:20px; fill:var(--ink); font-variant-numeric:tabular-nums; }
        .cf-lead { font-size:24px; font-weight:500; }
        .cf-eyebrow { font-size:17px; letter-spacing:.13em; }
        .cf-muted { fill:var(--muted); }
        .cf-drive { fill:var(--drive); }
        .cf-found { fill:var(--accent-strong); }
        .cf-given { fill:var(--ink); }
        .cf-ghost-text { fill:var(--muted); }
        .cf-fill { fill:var(--surface-sunken,rgba(127,127,127,.10)); stroke:none; }
        .cf-wall { fill:none; stroke-width:2.4; }
        .cf-wall.cf-found { stroke:var(--accent-strong); }
        .cf-wall.cf-given { stroke:var(--ink); }
        .cf-ghost { fill:none; stroke:var(--muted); stroke-width:1.8; stroke-dasharray:8 5; }
        .cf-equiv { fill:none; stroke:var(--accent-strong); stroke-width:1.6; stroke-dasharray:3 4; opacity:.85; }
        .cf-flow { fill:none; stroke:var(--drive); stroke-width:1.8; }
        .cf-vector { fill:none; stroke:var(--drive); stroke-width:2.4; }
        .cf-arrowhead { fill:var(--drive); }
        .cf-grad { fill:none; stroke:var(--drive); stroke-width:2.6; stroke-linecap:round; }
        .cf-gradfill { fill:var(--drive); opacity:.10; stroke:none; }
        .cf-target { fill:none; stroke:var(--muted); stroke-width:1.7; stroke-dasharray:9 6; }
        .cf-axis { stroke:var(--line); stroke-width:1.2; }
        .cf-scale { stroke:var(--ink); stroke-width:1.7; }
        .cf-leader { fill:none; stroke:var(--muted); stroke-width:1.1; opacity:.85; }
        .cf-leader-dot { fill:var(--muted); }
        /* Material tones. Three is enough to separate adjacent layers; a fourth would be
           a colour the reader has to decode rather than one they can tell apart. */
        .cf-layer-0 { fill:rgba(140,120,80,.20); }
        .cf-layer-1 { fill:rgba(140,120,80,.32); }
        .cf-layer-2 { fill:rgba(140,120,80,.44); }
        .cf-water { stroke:var(--accent-strong); stroke-width:2; fill:none; }
        .cf-water-t { fill:var(--accent-strong); }
        .cf-drive-fill { fill:var(--drive); }
        .cf-liquid { fill:var(--accent-strong); opacity:.22; }
        .cf-bore { fill:var(--surface); stroke:none; }
        /* Equal weight with the chart on `balanced`, which is what that container is for. */
        .bl-charts .calc-figure { box-shadow:none; border:0; padding:0; }
        @media (prefers-reduced-motion: reduce) {
            .calc-figure * { animation:none !important; transition:none !important; }
        }

        #island { min-width:0; }
        @media (scripting: enabled) {
            #island { min-height:520px; }
            #island[data-calc-layout="analytics"],
            #island[data-calc-layout="dashboard"],
            #island[data-calc-layout="flow"],
            #island[data-calc-layout="dataset"] { min-height:620px; }
            #island[data-calc-layout="builder"],
            #island[data-calc-layout="scenario"] { min-height:680px; }
            /* The drawing (toolbar, a stage of up to 520px, its info panel), the Calculate
               bar and the results panel, so the content below does not jump on mount. */
            #island[data-calc-layout="canvas"] { min-height:780px; }
        }
        .calculator-fallback { box-sizing:border-box; width:100%; padding:24px;
                               border:1px solid var(--line); border-radius:18px;
                               background:var(--surface); box-shadow:var(--shadow-sm); }
        .calculator-fallback-head { margin-bottom:22px; }
        .calculator-fallback-kicker { margin:0 0 5px; color:var(--accent-strong); font-size:.7rem;
                                      font-weight:750; letter-spacing:.1em; text-transform:uppercase; }
        .calculator-fallback-head h2 { margin:0; font-size:1.35rem; }
        .calculator-fallback-head > p:last-child { margin:.45rem 0 0; color:var(--muted); }
        .calculator-fallback-columns { display:grid; grid-template-columns:minmax(0,1.25fr) minmax(260px,.75fr);
                                       gap:24px; align-items:start; }
        .calculator-fallback-columns h3 { margin:0 0 10px; color:var(--muted); font-size:.78rem;
                                          letter-spacing:.075em; text-transform:uppercase; }
        .calculator-fallback-list { display:grid; gap:8px; margin:0; }
        .calculator-fallback-field { padding:12px 13px; border:1px solid var(--line);
                                     border-radius:11px; background:#f8f9fc; }
        .calculator-fallback-field dt { display:flex; gap:8px; align-items:center; justify-content:space-between;
                                        color:var(--ink); font-weight:700; }
        .calculator-fallback-field dd { display:flex; flex-wrap:wrap; gap:6px 15px; margin:7px 0 0;
                                        color:var(--muted); font-size:.82rem; }
        .calculator-fallback-fact { display:inline-flex; gap:5px; }
        .calculator-fallback-description { flex-basis:100%; color:var(--muted); line-height:1.45; }
        .calculator-fallback-description summary { width:max-content; cursor:pointer; color:var(--accent-strong);
                                                   font-weight:650; }
        .calculator-fallback-description p { margin:7px 0 2px; }
        .calculator-fallback-fact strong { color:var(--ink); font-size:.72rem; letter-spacing:.035em;
                                           text-transform:uppercase; }
        .calculator-fallback-conditional { flex:0 0 auto; padding:3px 7px; border-radius:999px;
                                           background:color-mix(in srgb,var(--accent) 10%,white);
                                           color:var(--accent-strong); font-size:.68rem; font-weight:700; }
        .calculator-fallback-grid-default, .calculator-fallback-grid-wrap { flex-basis:100%; }
        .calculator-fallback-grid-wrap { max-width:100%; overflow-x:auto; }
        .calculator-fallback-grid { width:100%; margin-top:4px; border-collapse:collapse; font-size:.78rem; }
        .calculator-fallback-grid th, .calculator-fallback-grid td { padding:7px 8px; border-top:1px solid var(--line);
                                                                    text-align:left; vertical-align:top; }
        .calculator-fallback-grid thead th { color:var(--muted); font-size:.68rem; letter-spacing:.04em;
                                             text-transform:uppercase; }
        .calculator-fallback-grid tbody th { color:var(--ink); }
        .calculator-fallback-noscript { margin:18px 0 0; padding:12px 14px; border-radius:10px;
                                        background:color-mix(in srgb,var(--accent) 7%,white); color:var(--ink); }
        @media (max-width:720px) {
            .calculator-fallback { padding:18px; }
            .calculator-fallback-columns { grid-template-columns:minmax(0,1fr); gap:20px; }
        }

        .template-workspace { --template-soft:color-mix(in srgb, var(--accent) 8%, white);
                              --template-line:color-mix(in srgb, var(--accent) 22%, white);
                              display:flex; flex-direction:column; gap:20px; min-width:0; }
        .template-workspace .calc-head { margin:0; }
        .template-kicker { margin:0; color:var(--accent-strong); font-size:.7rem; line-height:1.2;
                           font-weight:750; letter-spacing:.11em; text-transform:uppercase; }
        .template-workspace .inputs-card { box-sizing:border-box; width:100%; min-width:0; padding:20px; border-radius:18px; box-shadow:var(--shadow-sm); }
        .template-workspace .panel-kicker { color:var(--accent-strong); letter-spacing:.1em; }
        .template-workspace .field, .template-workspace .ctl { min-width:0; }
        .template-workspace .field > label { min-width:0; flex-wrap:wrap; overflow-wrap:anywhere;
                                              font-size:.73rem; text-transform:uppercase; letter-spacing:.045em; }
        .template-workspace input:not([type="range"]), .template-workspace select {
            min-height:42px; border-radius:10px; background:#f8f9fc; }
        .template-workspace .segmented { background:#f5f6fa; border-radius:11px; padding:3px; gap:3px; }
        .template-workspace .seg { border-radius:8px; border-color:transparent; background:transparent; }
        .template-workspace .seg.active { background:var(--surface); color:var(--accent-strong);
                                          border-color:var(--template-line); box-shadow:var(--shadow-sm); }
        .template-workspace .chart-card { border-radius:18px; box-shadow:none; }
        .template-workspace .stat { box-shadow:none; border-radius:12px; background:#f8f9fc; }
        .template-workspace .results-empty { min-height:160px; display:grid; place-items:center;
                                             border:1px dashed var(--line-strong); border-radius:16px;
                                             background:rgba(255,255,255,.55); }

        /* Formula Focus — ribbon, dominant answer, relationship/evidence surface. */
        .template-workspace.formula { max-width:none; }
        .fx-ribbon { padding:0; overflow:hidden; }
        .fx-ribbon .fields { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:0; }
        .fx-ribbon .field-group-fields { gap:0; }
        .fx-ribbon .field { padding:15px 16px; border-right:1px solid var(--line); border-bottom:1px solid var(--line); }
        .fx-ribbon .field:last-child { border-right:0; }
        /* The ribbon card keeps the workspace card's padding (`.template-workspace .inputs-card`
           outranks `.fx-ribbon`), and each field adds its own 16px inset inside it, so the Run bar
           takes that same inset to line its button up with the fields' text. Its rule is pulled up
           1px onto the last row's cell borders, so one full-width line separates it from the fields
           even when the last row is only part filled. */
        .fx-ribbon > .run-bar { margin:-1px 0 0; padding:15px 16px; }
        .fx-stage { display:grid; grid-template-columns:minmax(300px,.78fr) minmax(0,1.22fr); min-height:390px;
                    background:var(--surface); border:1px solid var(--line); border-radius:20px;
                    overflow:hidden; box-shadow:var(--shadow-md); }
        .fx-answer-stage { display:flex; flex-direction:column; justify-content:center; padding:42px 38px;
                           color:white; background:linear-gradient(145deg,var(--accent-strong),var(--accent)); }
        .fx-answer-stage .primary-name, .fx-answer-stage .primary-unit { color:rgba(255,255,255,.82); }
        .fx-answer-stage .primary-value { color:#fff; font-family:var(--font-display); font-size:clamp(3.25rem,7vw,5.3rem); }
        .fx-answer-stage .gauge-name, .fx-answer-stage .gauge-value, .fx-answer-stage .gauge-band, .fx-answer-stage .out-info-button { color:rgba(255,255,255,.94); }
        .fx-answer-stage .gauge-track { background:rgba(255,255,255,.25); }
        .fx-answer-stage .gauge-fill { background:#7dd3fc; }
        .fx-answer-stage .gauge-ring-track { stroke:rgba(255,255,255,.25); }
        .fx-answer-stage .gauge-ring-arc { stroke:#7dd3fc; }
        .fx-answer-stage .gauge-critical .gauge-fill { background:#fecaca; }
        .fx-answer-stage .gauge-critical .gauge-ring-arc { stroke:#fecaca; }
        .fx-answer-stage .gauge-warning .gauge-fill { background:#fde68a; }
        .fx-answer-stage .gauge-warning .gauge-ring-arc { stroke:#fde68a; }
        .fx-answer-stage .gauge-critical .gauge-band, .fx-answer-stage .gauge-critical .gauge-value,
        .fx-answer-stage .gauge-warning .gauge-band, .fx-answer-stage .gauge-warning .gauge-value { color:#fff; }
        .fx-relationship-panel { display:flex; flex-direction:column; justify-content:center; gap:20px; padding:32px; min-width:0; }
        .fx-relationship-panel .dash-metrics { grid-template-columns:repeat(auto-fit,minmax(145px,1fr)); }
        .fx-relationship-line { display:flex; align-items:center; gap:12px; color:var(--muted); }
        .fx-relationship-line::before, .fx-relationship-line::after { content:""; height:1px; flex:1; background:var(--template-line); }
        .fx-relationship-line span { color:var(--accent-strong); font-weight:700; font-size:.78rem; text-transform:uppercase; letter-spacing:.08em; }

        /* Decision Canvas — stable input rail and a result-led decision surface. */
        .template-workspace.decision { display:grid; grid-template-columns:320px minmax(0,1fr); align-items:start; }
        .decision .dc-inputs { position:sticky; top:16px; }
        .decision .dc-inputs .fields { grid-template-columns:1fr; }
        .dc-canvas { min-width:0; background:var(--surface); border:1px solid var(--line); border-radius:20px;
                     box-shadow:var(--shadow-md); padding:24px; }
        .dc-recommendation { border:0; border-radius:16px; background:linear-gradient(145deg,var(--template-soft),white); box-shadow:none; }
        .dc-recommendation .primary-value { font-family:var(--font-display); font-size:clamp(2.2rem,5vw,3.8rem); color:var(--accent-strong); }
        .dc-primary-metrics .dash-metrics { grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
        /* A decision page may carry more than one chart. Keep those chart cards on the
           same vertical rhythm as every other result stack; without a layout container
           the cards are ordinary block siblings and their borders touch. The parent
           `.results-flow` already owns the seam above this block, so a margin here would
           double that one seam while leaving the chart-to-chart seam at zero. */
        .dc-visual { display:flex; flex-direction:column; gap:var(--block-gap); }
        .dc-evidence .dash-metrics { grid-template-columns:repeat(3,minmax(0,1fr)); }

        /* Diagnostic Analytics — input strip, KPI band and chart-led interpretation. */
        .analytics > .inputs-card .fields { grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); }
        .analytics > .inputs-card { padding:16px 18px; }
        .an-kpis { background:var(--surface); border-color:var(--line); box-shadow:var(--shadow-sm); }
        .an-kpis .primary-value { font-family:var(--font-display); font-size:2.65rem; }
        .an-diagnosis { grid-template-columns:minmax(0,1.7fr) minmax(260px,.7fr); }
        .an-readings { border-radius:16px; }

        /* Builder — editable surface beside a persistent property/visual summary. */
        .template-workspace.builder { display:grid; grid-template-columns:minmax(0,1.45fr) minmax(320px,.72fr); align-items:start; }
        .builder .bd-form { display:block; }
        .builder .bd-settings { position:static; margin-top:16px; }
        .builder .bd-results { position:sticky; top:16px; min-width:0; }
        .bd-properties { background:linear-gradient(145deg,var(--accent-strong),var(--accent)); color:white;
                         border-radius:18px; padding:22px; }
        .bd-properties .primary-name, .bd-properties .primary-unit { color:rgba(255,255,255,.78); }
        .bd-properties .primary-value { color:#fff; font-family:var(--font-display); font-size:2.35rem; }
        /* The builder properties surface is saturated, so the gauge cannot inherit the
           light-card palette. That made the name, value, ring and information icon almost
           disappear on System Reliability Block. Keep every part of this composite figure
           on one explicit high-contrast palette, including its warning/critical variants. */
        .bd-properties .gauge-name, .bd-properties .gauge-value, .bd-properties .gauge-band, .bd-properties .out-info-button { color:rgba(255,255,255,.94); }
        .bd-properties .gauge-ring-track { stroke:rgba(255,255,255,.25); }
        .bd-properties .gauge-ring-arc { stroke:#7dd3fc; }
        .bd-properties .gauge-critical .gauge-ring-arc { stroke:#fecaca; }
        .bd-properties .gauge-warning .gauge-ring-arc { stroke:#fde68a; }
        .bd-properties .gauge-critical .gauge-band, .bd-properties .gauge-critical .gauge-value,
        .bd-properties .gauge-warning .gauge-band, .bd-properties .gauge-warning .gauge-value { color:#fff; }
        .builder .chart-card { margin-top:0; }

        /* Dataset Lab — statistical strip over chart and data editor. */
        .dataset .ds-figures { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; }
        .dataset .ds-figures .primary-value { font-family:var(--font-display); font-size:2.1rem; }
        .ds-workspace { display:grid; grid-template-columns:minmax(0,1.35fr) minmax(340px,.65fr); gap:20px; align-items:start; }
        .dataset .ds-form { display:flex; flex-direction:column; gap:16px; }
        .dataset .ds-data, .dataset .ds-settings { box-sizing:border-box; width:100%; min-width:0; }
        .dataset .ds-data .fields, .dataset .ds-data .field { min-width:0; }
        .dataset .ds-settings { position:static; }
        .dataset .ds-results { min-width:0; }
        .dataset .grid-in-wrap { box-sizing:border-box; width:100%; max-width:100%; max-height:560px; overflow:auto; }

        /* Grid-bearing application templates need an editing stage, not an input rail.
           The template still owns the result hierarchy (decision, builder, dataset, or
           scenario), but its editable table spans the workspace above that hierarchy.
           This prevents a semantically correct template choice from forcing a desktop
           horizontal scrollbar merely because the ordinary variant uses two columns. */
        .template-workspace.decision:has(.grid-in-wrap), .template-workspace.builder:has(.grid-in-wrap), .template-workspace.scenario:has(.grid-in-wrap), .ds-workspace:has(.grid-in-wrap) { grid-template-columns:minmax(0,1fr); }
        .decision:has(.grid-in-wrap) .dc-inputs, .builder:has(.grid-in-wrap) .bd-results, .scenario:has(.grid-in-wrap) .sc-assumptions { position:static; }

        /* Balanced Workspace — equal editing and visual-analysis columns. This is the
           tenth application-type template. It is for a compact editable grid whose chart
           must remain large enough to interpret while values are changed. */
        .template-workspace.balanced { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
                                       gap:20px; align-items:start; }
        .balanced .bl-inputs, .balanced .bl-results { min-width:0; }
        .balanced .bl-inputs > .inputs-card { padding:20px; }
        .balanced .bl-inputs .fields { grid-template-columns:1fr; }
        .balanced .field-group-fields { grid-template-columns:1fr; }
        .template-workspace .grid-in-wrap { box-sizing:border-box; width:100%; max-width:100%; overflow-x:auto; }
        .template-workspace .grid-in { min-width:0; table-layout:fixed; }
        /* Control semantics, not a particular template or column position, own the track
           width. A catalog reassignment must not make the same grid unusable merely by
           changing its ancestor from balanced to analytics or scenario. Text columns
           absorb the remaining surface; ordinary numeric values stay compact. Slider
           tables keep a scrollable editing surface because precise dragging needs room. */
        .template-workspace .grid-in .grid-col-number { width:7.5rem; }
        .template-workspace .grid-in .grid-col-dropdown { width:9.5rem; }
        .template-workspace .grid-in .grid-col-date { width:9rem; }
        .template-workspace .grid-in .grid-col-checkbox { width:5rem; }
        .template-workspace .grid-in:has(.grid-col-slider) { min-width:660px; }
        .template-workspace .grid-in .grid-col-slider { width:140px; }
        .template-workspace .grid-in:has(.grid-col-slider) th:first-child,
        .template-workspace .grid-in:has(.grid-col-slider) td:first-child { width:92px; }
        .template-workspace .grid-cell-slider { display:grid; grid-template-columns:minmax(76px,1fr) minmax(4ch,auto);
                                                gap:6px; padding-right:7px; }
        .template-workspace .grid-in input[type="range"] { width:100%; min-width:76px; }
        /* cell-slider WITH ITS #1368 VALUE BOX NEVER CLIPS OR SPILLS (staging Capital Gains, 2026-10-26: the track
           and box shared one 92px/140px cell, the box read "$ 10,0" and both ran into the next column). The box
           sits on its own line above the track, each the cell's full width, and a table that has such a cell
           gives its slider columns 13rem, room for a grouped figure up to $1,000,000,000,000 with its "$" (the
           first column too: 92px is for a row label). Cells without a box, the pop-up cells and a compact
           grid-fit-container table are unchanged: every rule here is scoped to a table that is not
           grid-fit-container (REVIEW LC on #1454), so a fit table keeps its own track-then-box layout below.
           Presentation only: the same control, value and payload. */
        .grid-in:not(.grid-fit-container) .grid-cell-slider.grid-cell-slider-with-entry { flex-direction:column; align-items:stretch;
                                                                 gap:4px; padding:4px 7px 4px 9px; }
        .template-workspace .grid-in:not(.grid-fit-container) .grid-cell-slider.grid-cell-slider-with-entry { grid-template-columns:minmax(0,1fr); }
        .grid-in:not(.grid-fit-container) .grid-cell-slider-with-entry .grid-slider-entry { order:-1; flex:0 0 auto; width:auto; min-width:0; }
        .grid-in:not(.grid-fit-container) .grid-cell-slider-with-entry input[type="range"] { min-width:0; padding:0; }
        .template-workspace .grid-in:not(.grid-fit-container):has(.grid-cell-slider-with-entry) th.grid-col-slider,
        .template-workspace .grid-in:not(.grid-fit-container):has(.grid-cell-slider-with-entry) td.grid-col-slider { width:13rem; }
        /* A compact authored schedule can opt out of the general 660px slider canvas.
           This is intentionally explicit: ordinary wide grids still scroll, while a
           reviewed compact schedule uses the whole card. Control semantics keep labels
           and dropdowns narrower than the slider tracks that need editing precision. */
        .template-workspace .grid-in.grid-fit-container { width:100%; min-width:0; table-layout:fixed; }
        .template-workspace .grid-in.grid-fit-container th,
        .template-workspace .grid-in.grid-fit-container td { width:auto; min-width:0; }
        .template-workspace .grid-in.grid-fit-container .grid-col-text { width:18%; }
        .template-workspace .grid-in.grid-fit-container .grid-col-dropdown { width:24%; }
        .template-workspace .grid-in.grid-fit-container .grid-col-number { width:18%; }
        .template-workspace .grid-in.grid-fit-container .grid-col-slider { width:29%; }
        .template-workspace .grid-in.grid-fit-container .grid-col-date { width:20%; }
        .template-workspace .grid-in.grid-fit-container .grid-col-checkbox { width:12%; }
        .template-workspace .grid-in.grid-fit-container .grid-cell-slider {
            grid-template-columns:minmax(0,1fr) minmax(4ch,auto);
        }
        .template-workspace .grid-in.grid-fit-container input[type="range"] { min-width:0; }
        /* AUTHORED COLUMN WIDTHS (#1345). A grids entry's column_widths draws a <colgroup>
           whose data columns carry --grid-col-width (island.js gridColumnWidths). Above the
           touch breakpoint the table lays out automatically, so those percentages divide the
           row while the Remove column, which takes no share, shrinks to its button. Every
           cell width from the control-class and template rules above yields to the colgroup;
           !important is scoped to the opt-in class for that reason. At 520px and below none
           of this applies and the table keeps its ordinary layout, which the phone sheet
           already handles: fixed percentages there would clip a description. */
        @media not all and (max-width:520px) {
            .grid-in.grid-authored-widths { width:100%; min-width:0 !important; table-layout:auto !important; }
            .grid-in.grid-authored-widths > colgroup > col.grid-col-authored { width:var(--grid-col-width); }
            .grid-in.grid-authored-widths > thead > tr > th, .grid-in.grid-authored-widths > tbody > tr > td { width:auto !important; min-width:0 !important; max-width:none !important; }
            .grid-in.grid-authored-widths .grid-action-heading, .grid-in.grid-authored-widths .grid-action-cell { white-space:nowrap; }
        }
        /* 3R PASTE GRIDS (schema 0.20 records grids, components/grids/records.js). A toolbar,
           the paste panel with its mapping, choices, problem list and preview, then a paged
           table. At 40em and below each row becomes a card of label: value pairs (the cell's
           data-label), and the row number (or, on a card, a tap outside its controls) opens the
           row detail form, so nothing needs sideways scrolling. Row actions are icon buttons. */
        .records-toolbar, .records-panel-actions, .records-pager { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin:0 0 10px; }
        .records-toolbar .grid-row-count { margin-left:auto; }
        .records-notice { margin:0 0 10px; }
        .records-paste-panel, .records-detail { border:1px solid var(--line); border-radius:12px; padding:14px; margin:0 0 12px; background:var(--surface); display:flex; flex-direction:column; gap:10px; }
        .records-paste-text { width:100%; min-height:7em; font-family:inherit; }
        .records-mapping { border:0; padding:0; margin:0; display:grid; gap:6px; }
        .records-map-row, .records-choice, .records-mode, .records-detail-field { display:flex; flex-wrap:wrap; gap:6px 10px; align-items:center; }
        .records-errors { border:1px solid var(--accent); border-radius:10px; padding:8px 12px; }
        .records-errors ul { margin:4px 0 0; padding-left:18px; }
        .records-error-link { background:none; border:0; padding:0; text-align:left; text-decoration:underline; color:inherit; cursor:pointer; font:inherit; }
        .records-cell-invalid, .records-row-invalid { outline:2px solid var(--accent); outline-offset:-2px; }
        .records-actions { white-space:nowrap; }
        .records-actions button { margin-right:4px; }
        .records-icon-btn { display:inline-flex; align-items:center; justify-content:center; width:2em; height:2em; padding:0; border:0; border-radius:6px; background:transparent; color:var(--muted); cursor:pointer; vertical-align:middle; }
        .records-icon-btn:hover { color:var(--accent); background:color-mix(in srgb,var(--accent) 7%,transparent); }
        .records-icon-btn.grid-remove:hover { color:#b42318; }
        .records-icon-btn:focus-visible, .records-row-open:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
        .records-rownum { width:3ch; text-align:right; }
        .records-row-open { min-width:2em; min-height:2em; padding:0 4px; border:1px solid transparent; border-radius:6px; background:transparent; color:inherit; font:inherit; cursor:pointer; text-decoration:underline dotted; text-underline-offset:3px; }
        .records-row-open:hover { border-color:var(--line); color:var(--accent); }
        .records-preview { overflow-x:auto; }
        @media (max-width:40em) {
            .records-table, .records-table tbody, .records-table tr, .records-table td, .records-table th[scope="row"] { display:block; width:auto; }
            .records-table thead { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
            .records-table tr { border:1px solid var(--line); border-radius:10px; margin:0 0 10px; padding:8px; }
            .records-table td[data-label]::before { content:attr(data-label); display:block; font-size:.85em; opacity:.8; }
            .records-detail-field { flex-direction:column; align-items:stretch; }
            .records-icon-btn, .records-row-open { min-width:2.75rem; min-height:2.75rem; }
        }
        .bl-charts { display:flex; flex-direction:column; gap:var(--block-gap); }
        .balanced .bl-charts .chart-card { min-height:360px; }
        .bl-figures { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; }
        .bl-figures .primary-value { font-family:var(--font-display); font-size:2.1rem; }

        /* System Flow — controls above a horizontal, scroll-safe process path. */
        .flow .fl-inputs .fields { grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); }
        .fl-stage-scroll { overflow-x:auto; padding:4px 2px 12px; }
        .fl-stages { flex-direction:row; min-width:max-content; gap:34px; align-items:stretch; }
        .fl-stage { position:relative; width:250px; border:1px solid var(--line); border-radius:16px;
                    padding:18px; background:var(--surface); box-shadow:var(--shadow-sm); }
        .fl-stage::after { content:"\2192"; position:absolute; right:-27px; top:50%; color:var(--accent); font-size:1.25rem; }
        .fl-stage:last-child::after { display:none; }
        .fl-stage .dash-metrics { grid-template-columns:1fr; gap:10px; }
        .fl-totals { box-shadow:var(--shadow-sm); background:var(--surface); border-color:var(--line); }

        /* Classification Navigator — measurement rail, classification badge and trace. */
        .template-workspace.classifier { display:grid; grid-template-columns:340px minmax(0,1fr); align-items:start; }
        .classifier .cl-inputs { position:sticky; top:16px; }
        .classifier .cl-inputs .fields { grid-template-columns:1fr; }
        /* A true side rail must stay one column. Grid-bearing decision and scenario
           calculators are promoted to a full-width editing stage above, so keeping this
           rail override there wastes the entire canvas and turns every scalar into a
           page-wide control. Let those promoted forms retain their authored section
           columns; only the still-narrow rail variants collapse to one track. */
        .classifier .field-group-fields, .decision:not(:has(.grid-in-wrap)) .field-group-fields, .scenario:not(:has(.grid-in-wrap)) .field-group-fields {
            grid-template-columns:1fr;
        }
        .cl-results { min-width:0; }
        .cl-verdict { padding:26px; text-align:center; background:linear-gradient(145deg,var(--template-soft),white); }
        .cl-symbol .primary-value { font-family:var(--font-display); font-size:4.5rem; }
        .cl-trace { padding:20px; border-radius:18px; }

        /* Assessment Journey — progress navigator, focused question, live result rail. */
        .template-workspace.assessment { display:flex; flex-direction:column; }
        /* The sticky rail belongs to this bounded row, not to the evidence below it. Its
           containing block therefore ends before the chart/table and cannot cover them
           when the reader scrolls through the supporting analysis. */
        .as-journey { display:grid; grid-template-columns:210px minmax(0,1fr) 250px;
                      gap:var(--block-gap); align-items:start; min-width:0; }
        .as-nav, .as-question, .as-results { background:var(--surface); border:1px solid var(--line); border-radius:18px;
                                            box-shadow:var(--shadow-sm); min-width:0; }
        .as-nav { padding:18px 14px; max-height:680px; overflow-y:auto; overflow-x:hidden; }
        .as-nav-head { display:flex; align-items:flex-end; justify-content:space-between; gap:10px; margin-bottom:10px; }
        .as-nav-head strong { font-family:var(--font-display); font-size:1.35rem; }
        .as-progress-track { height:6px; border-radius:999px; background:var(--line); overflow:hidden; margin-bottom:14px; }
        .as-progress-track span { display:block; height:100%; background:var(--accent); }
        .as-category-list { display:grid; gap:8px; }
        .as-category-link { box-sizing:border-box; width:100%; display:grid; gap:8px; text-align:left; padding:11px 10px; border:1px solid var(--line);
                            border-radius:11px; background:var(--surface); color:var(--ink); font:inherit; cursor:pointer; }
        .as-category-link.active { border-color:var(--template-line); background:var(--template-soft); box-shadow:0 0 0 1px var(--template-line); }
        .as-category-copy { display:grid; gap:2px; }
        .as-category-copy strong { font-size:.8rem; }
        .as-category-copy small { color:var(--muted); font-size:.63rem; line-height:1.25; white-space:normal; }
        .as-category-meter { height:4px; overflow:hidden; border-radius:999px; background:var(--line); }
        .as-category-meter span { display:block; height:100%; border-radius:inherit; background:var(--accent); }
        .as-question { padding:26px; }
        .as-question-meta { display:flex; justify-content:space-between; gap:12px; color:var(--muted);
                            font-size:.7rem; font-weight:700; letter-spacing:.045em; text-transform:uppercase; }
        .as-question h2 { font-family:var(--font-display); font-size:clamp(1.9rem,4vw,3rem); line-height:1.06; margin:18px 0 10px; }
        .as-question-controls { display:flex; flex-direction:column; gap:18px; margin-top:22px; }
        .as-question-control { min-width:0; }
        .as-weight-control { width:120px; max-width:100%; }
        .as-weight-control > input { box-sizing:border-box; width:120px; max-width:100%; min-height:42px; padding:9px 11px;
                                    border:1px solid var(--line-strong); border-radius:10px; background:#f8f9fc;
                                    color:var(--ink); font:inherit; font-variant-numeric:tabular-nums;
                                    transition:border-color .15s, box-shadow .15s; }
        .as-weight-control > input:focus, .as-weight-control > input:focus-visible { border-color:var(--accent);
                                    box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 16%,transparent); outline:none; }
        .as-control-label { display:block; font-size:.72rem; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px; }
        .as-options { display:grid; grid-template-columns:repeat(auto-fit,minmax(78px,1fr)); gap:7px; }
        .as-option { min-height:76px; padding:9px 6px; border:1px solid var(--line); border-radius:11px; background:#fff;
                     color:var(--ink); font:inherit; cursor:pointer; }
        .as-option strong { display:block; font-family:var(--font-display); font-size:1.2rem; }
        .as-option span { display:block; margin-top:4px; color:var(--muted); font-size:.68rem; line-height:1.2; }
        .as-option.active { color:var(--accent-strong); border-color:var(--accent); background:var(--template-soft); box-shadow:0 0 0 1px var(--accent); }
        .as-question-actions { display:flex; justify-content:space-between; gap:12px; margin-top:24px; padding-top:18px; border-top:1px solid var(--line); }
        /* On an on-demand assessment page the Run bar follows Previous / Next, whose row already
           carries a rule above it; the bar keeps its spacing and drops its own rule. */
        .as-question > .run-bar { border-top:0; padding-top:0; }
        .as-question-actions button { border:1px solid var(--line-strong); border-radius:10px; padding:9px 13px; background:#fff; font:inherit; font-weight:650; cursor:pointer; }
        .as-question-actions .primary { color:white; border-color:var(--accent); background:var(--accent); }
        .as-results { padding:20px; position:sticky; top:16px; }
        .as-score .gauge-dial { width:170px; height:170px; }
        .as-findings .dc-checks { grid-template-columns:1fr; }
        /* Charts and tabular evidence need the width of the JOURNEY, not its 250px live
           summary rail. This outer sibling receives the whole assessment width. */
        .as-evidence { min-width:0; display:flex; flex-direction:column; gap:var(--block-gap); }
        .as-evidence > .panel-kicker { margin:4px 2px -4px; }
        .as-evidence > .chart-card { min-height:360px; }
        .as-evidence .as-supporting { box-sizing:border-box; width:100%; }
        /* Wide assessment grids fit naturally on desktop and scroll inside their own
           evidence tile on a phone; they can no longer widen the page or escape a card. */
        .as-evidence .stat:has(.grid-out) { overflow-x:auto; }
        .as-evidence .grid-out { width:100%; min-width:480px; }

        /* Scenario Explorer — assumption rail and time-based outcome story. */
        .template-workspace.scenario { display:grid; grid-template-columns:340px minmax(0,1fr); gap:20px; }
        .scenario .sc-assumptions { position:sticky; top:16px; }
        .scenario .sc-assumptions .fields { grid-template-columns:1fr; }
        .sc-panel { border-radius:20px; padding:22px; box-shadow:var(--shadow-md); }
        .sc-outcome { padding:20px; border-radius:16px; background:linear-gradient(145deg,var(--template-soft),white); }
        .sc-outcome .primary-value { font-family:var(--font-display); font-size:clamp(2.8rem,6vw,4.8rem); color:var(--accent-strong); }
        .sc-series .chart-card { min-height:360px; }

        @media (max-width:1000px) {
            .template-workspace.decision, .template-workspace.classifier, .template-workspace.scenario,
            .template-workspace.builder, .template-workspace.balanced { grid-template-columns:minmax(0,1fr); }
            /* Four- and five-track desktop sections become unreadably narrow as soon as
               a two-pane template collapses to one pane. Give their labels a real tablet
               track before the fleet-wide two-column phone rule takes over at 760px. */
            .field-group-cols-4 .field-group-fields,
            .field-group-cols-5 .field-group-fields { grid-template-columns:repeat(3,minmax(0,1fr)); }
            .decision .dc-inputs, .classifier .cl-inputs, .scenario .sc-assumptions,
            .builder .bd-results, .as-results { position:static; }
            .as-journey { grid-template-columns:180px minmax(0,1fr); }
            .assessment .as-results { grid-column:1 / -1; }
            .ds-workspace { grid-template-columns:1fr; }
        }
        @media (max-width:760px) {
            .hero { padding-top:20px; }
            .crumbs { padding:16px 12px 0; }
            .hero h1 { max-width:none; }
            .field-group-fields { grid-template-columns:repeat(2,minmax(0,1fr)) !important; }
            .fx-stage, .template-workspace.scenario, .as-journey { grid-template-columns:minmax(0,1fr); }
            .fx-answer-stage { min-height:310px; padding:30px 24px; }
            .fx-relationship-panel { padding:24px 20px; }
            .dc-evidence .dash-metrics { grid-template-columns:1fr 1fr; }
            .an-diagnosis { grid-template-columns:1fr; }
            .as-nav { max-height:none; display:flex; gap:8px; overflow-x:auto; overflow-y:hidden; padding:12px; }
            .as-nav-head, .as-progress-track { flex:0 0 150px; }
            .as-category-list { display:flex; gap:8px; }
            .as-category-link { flex:0 0 190px; }
            .assessment .as-results { grid-column:auto; }
        }
        @media (max-width:520px) {
            main { padding-left:12px; padding-right:12px; }
            .hero { padding-left:12px; padding-right:12px; }
            .field-group-fields { grid-template-columns:minmax(0,1fr) !important; }
            /* Desktop grouped labels stay compact enough for four- and five-column
               authored grids. On a phone, long engineering labels need to wrap with
               their help icon inside the card instead of widening the whole page. */
            .template-workspace .field-group-fields .field > label {
                flex-wrap:wrap; white-space:normal; overflow-wrap:anywhere;
            }
            .template-workspace .inputs-card, .dc-canvas, .as-question, .as-results { padding:16px; }
            .dc-evidence .dash-metrics { grid-template-columns:1fr; }
            .as-options { grid-template-columns:repeat(3,1fr); }
            /* Output definitions become a viewport-bound sheet on touch widths. Anchoring
               an absolute 280px card to an icon near either edge makes the page wider or
               places half the definition off-screen. */
            .out-info[open] > .out-info-body { position:fixed; z-index:80; left:12px; right:12px; top:auto; bottom:16px;
                                               box-sizing:border-box; width:auto; max-height:42vh; overflow:auto; }
        }

        .lic-sep { color:var(--muted); }
        .lic-link { color:var(--muted); font-weight:600; text-decoration:none; border-bottom:1px dotted var(--muted); }

        @media (max-width:640px) { .form { grid-template-columns:1fr; } .hero-inner { gap:16px; } }

        /* ---- 3P: the CANVAS layout (Ugur, 2026-09-24) ----
           The drawing first, then one Calculate bar, then the workbook's figures. The drawing
           itself is FloorCanvas's own DOM and brings its own rules; these place it and give it
           the page accent. Nothing here is per calculator. */
        .template-workspace.canvas { gap:var(--block-gap); }
        .cv-drawing { display:flex; flex-direction:column; gap:10px; min-width:0; }
        .cv-canvas-host, .cv-surface { min-width:0; }
        .cv-canvas-host .fc-root { --fc-accent:var(--accent-strong); }
        /* Basement reuses the labelled menu items without loading Deck's optional stylesheet. */
        .cv-canvas-host .fc-root .fc-menu .fc-menu-item .fc-menu-label { display:block; }
        .cv-canvas-host .fc-root .fc-menu .fc-menu-item .fc-menu-hint {
            display:block; margin-top:2px; color:var(--fc-muted); font-size:11.5px; font-weight:400;
        }
        /* Roof switches pair one checkbox with one caption; they are not stacked form fields. */
        .cv-canvas-host .fc-root :is(.fc-roof-framing, .fc-roof-livable, .fc-roof-attic) {
            display:inline-flex; flex-direction:row; align-items:center; gap:6px;
        }
        .cv-canvas-host .fc-root :is(.fc-roof-framing, .fc-roof-livable, .fc-roof-attic) > input[type=checkbox] {
            flex:none; width:auto; margin:0;
        }
        .cv-load-error { margin:0; padding:12px 14px; border-radius:var(--radius-sm); color:var(--required-ink);
                         background:color-mix(in srgb, var(--required-ink) 7%, var(--surface)); }
        .cv-access-note { margin:0; color:var(--muted); font-size:.8rem; line-height:1.45; }
        .cv-actions { display:flex; flex-wrap:wrap; align-items:center; gap:10px 16px; padding:14px 16px;
                      border:1px solid var(--line); border-radius:var(--radius); background:var(--surface);
                      box-shadow:var(--shadow-sm); }
        /* Worked examples and Start over at the TOP of the drawing, directly above its toolbar
           (Ugur, 2026-10-07, FLOORCANVAS-100726 item 7), so a picked example is seen landing in
           the drawing: the menu first, as wide as a name needs and no wider, then Start over at
           the row's end. On a narrow screen the menu takes the full width and Start over wraps
           below it. Nothing is ruled between them and the toolbar. */
        .cv-drawing > .calc-head { justify-content:flex-start; align-items:flex-end; gap:10px 14px; margin:0; }
        .cv-drawing > .calc-head .preset-bar { order:1; flex:1 1 260px; max-width:440px; padding:0; border-top:0; }
        .cv-drawing > .calc-head .reset-btn { order:2; margin-left:auto; }
        .cv-calculate { -webkit-appearance:none; appearance:none; min-height:44px; padding:0 24px;
                        border:1px solid var(--accent-strong); border-radius:var(--radius-sm);
                        background:var(--accent-strong); color:var(--hero-ink); font:inherit; font-weight:700;
                        cursor:pointer; }
        .cv-calculate:hover:not([aria-disabled="true"]):not(:disabled) { background:var(--accent); border-color:var(--accent); }
        .cv-calculate[aria-disabled="true"], .cv-calculate:disabled { background:var(--surface); color:var(--muted);
                                                                       border-color:var(--line-strong); cursor:default; }
        .cv-calculate:focus-visible { outline:3px solid color-mix(in srgb, var(--accent) 45%, white); outline-offset:2px; }
        .cv-status { flex:1 1 220px; margin:0; color:var(--muted); font-size:.88rem; }
        .cv-results { padding:18px 20px; border:1px solid var(--line); border-radius:var(--radius);
                      background:var(--surface); box-shadow:var(--shadow-sm); }
        .cv-results .results-head { justify-content:flex-start; gap:10px; margin-bottom:12px; }
        .cv-stale-badge { padding:2px 8px; border-radius:999px; background:#fef3c7; color:#92400e;
                          font-size:.7rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase; }
        /* Stale figures stay readable (muted ink keeps 4.5:1), and the badge says why. */
        .cv-results.results-stale .cv-figures [data-output] { color:var(--muted); font-style:italic; }
        .cv-results.results-stale .cv-cards .gauge { opacity:.6; }
        .cv-group + .cv-group { margin-top:16px; }
        .cv-group-title { margin:0 0 6px; color:var(--accent-strong); font-size:.72rem; font-weight:750;
                          letter-spacing:.1em; text-transform:uppercase; }
        /* The workbook's figures (Ugur, 2026-09-29: "cards as default for all canvas
           calculators"): each group's figures in one .cv-figures element, a grid of cards, one
           per figure (the fleet's .stat tile: label, value with its unit, the linear gauge
           where one is declared). A result-table block of figures ("From the drawing", on
           every canvas page with that group: all but the Area Calculator and 5.2) is a compact
           table instead: label beside value, only as wide as its
           contents, never a full-width row of empty space.
           ON A PHONE, TWO CARDS PER ROW (Coordination, 2026-09-29), measured on the card grid
           itself (each card group is a size container): the wide rule alone gives one column
           anywhere from 190 to 369 px, which is every phone, so below 370 px it is two equal
           columns, and one only below 260 px, a phone narrower than about 325 px (a 375 px
           phone's grid is 309 px wide, a 320 px phone's 254 px). A long label or value wraps
           inside its card and never widens the column. */
        .cv-figures { margin:0; }
        .cv-group:has(> .cv-cards) { container-type:inline-size; }
        .cv-cards { display:grid; grid-template-columns:repeat(auto-fill, minmax(min(100%, 180px), 1fr)); gap:10px; }
        @container (max-width: 369.98px) { .cv-cards { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
        @container (max-width: 259.98px) { .cv-cards { grid-template-columns:minmax(0, 1fr); } }
        .cv-card { min-width:0; }
        .cv-card .stat-name, .cv-card .stat-value { overflow-wrap:anywhere; }
        .cv-figures .cv-primary { font-size:1.3rem; font-weight:750; }
        .cv-group-table { min-width:0; max-width:100%; overflow-x:auto; }
        .cv-compact { border-collapse:collapse; width:auto; max-width:100%; font-size:.88rem; }
        .cv-compact caption { text-align:left; }
        .cv-compact th, .cv-compact td { padding:5px 0; border-bottom:1px solid var(--line); vertical-align:baseline; }
        .cv-compact tr:last-child th, .cv-compact tr:last-child td { border-bottom:0; }
        .cv-compact th { padding-right:28px; text-align:left; font-weight:500; color:var(--ink); }
        .cv-compact td { text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums; font-weight:650; }
        /* The contract's charts (ruling T18) sit after the figures, inside the results card,
           and go stale with them: faded, with the region's Stale badge saying why. */
        .cv-charts { display:grid; gap:16px; margin-top:16px; min-width:0; }
        .cv-results.results-stale .cv-charts { opacity:.6; }
        /* The form beside the drawing (3P template, G-A): a card like the actions bar, between
           the drawing and Calculate, holding the fleet's own field groups (.fields,
           .field-group), which bring their own columns and breakpoints. */
        .cv-form { min-width:0; padding:18px 20px; border:1px solid var(--line); border-radius:var(--radius);
                   background:var(--surface); box-shadow:var(--shadow-sm); }
        .cv-form .required-legend { margin:0 0 12px; }
        /* What a US/Metric switch did to the fields; empty, and so absent, until one. */
        .cv-swap-note { margin:0 0 14px; padding:10px 12px; border-radius:var(--radius-sm); color:var(--ink);
                        background:color-mix(in srgb, var(--accent) 8%, var(--surface)); font-size:.88rem; line-height:1.45; }
        .cv-swap-note:empty { display:none; }
        /* Floorplan mode (3P Wave B): the tables of what the reader types for each room, window
           and door, between the drawing and the form, as a card like the form. Each table
           scrolls inside its own box, so the page never scrolls sideways; the ID is the row
           header, the key the drawing labels. The selected window or door's row is tinted. */
        .cv-tables { display:flex; flex-direction:column; gap:14px; min-width:0; padding:18px 20px;
                     border:1px solid var(--line); border-radius:var(--radius); background:var(--surface);
                     box-shadow:var(--shadow-sm); }
        .cv-tables .required-legend, .cv-tables .cv-swap-note { margin:0; }
        /* Closed on load, with tabs (Ugur, 2026-10-07, FLOORCANVAS-100726 item 3): the whole
           heading row is the disclosure button; its summary says what the tables hold, and a
           badge says when one needs the reader. Open, the tabs sit over one panel. A hidden
           body or table stays in the page (its cells hold what is sent) and must not show:
           these rules give it a display of their own, so [hidden] is restated. */
        .cv-tables [hidden] { display:none !important; }
        .cv-tables-toggle { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px; width:100%; min-height:44px;
                            margin:-8px 0; padding:0; border:0; background:none; color:var(--ink); font:inherit;
                            text-align:left; cursor:pointer; }
        .cv-tables-toggle:focus-visible { outline:2px solid var(--accent-strong); outline-offset:4px; border-radius:var(--radius-sm); }
        .cv-tables-chevron { display:inline-block; width:0; height:0; border-style:solid; border-width:5px 0 5px 7px;
                             border-color:transparent transparent transparent var(--accent-strong); transition:transform .12s; }
        .cv-tables-toggle[aria-expanded="true"] .cv-tables-chevron { transform:rotate(90deg); }
        .cv-tables-title { color:var(--accent-strong); font-size:.78rem; font-weight:750; letter-spacing:.1em; text-transform:uppercase; }
        .cv-tables-summary { color:var(--muted); font-size:.85rem; }
        .cv-tables-alert { padding:2px 8px; border-radius:999px; color:var(--required-ink); font-size:.72rem; font-weight:700;
                           background:color-mix(in srgb, var(--required-ink) 9%, var(--surface)); }
        .cv-tables-body { display:flex; flex-direction:column; gap:14px; min-width:0; }
        .cv-tabs { display:flex; flex-wrap:wrap; gap:4px; border-bottom:1px solid var(--line); }
        .cv-tab { min-height:40px; margin-bottom:-1px; padding:6px 14px; border:1px solid transparent; border-bottom:0;
                  border-radius:var(--radius-sm) var(--radius-sm) 0 0; background:none; color:var(--muted);
                  font:inherit; font-size:.85rem; font-weight:600; cursor:pointer; }
        .cv-tab:hover { color:var(--accent-strong); }
        .cv-tab[aria-selected="true"] { color:var(--accent-strong); border-color:var(--line); background:var(--surface);
                                        box-shadow:inset 0 2px 0 var(--accent-strong); }
        .cv-tab:focus-visible { outline:2px solid var(--accent-strong); outline-offset:-2px; }
        .cv-tab-alert { color:var(--required-ink); }
        .cv-tab-panel { display:flex; flex-direction:column; gap:14px; min-width:0; }
        .cv-tab-empty { margin:0; color:var(--muted); font-size:.88rem; }
        .cv-carry-note { margin:0; color:var(--muted); font-size:.85rem; }
        .cv-carry-note:empty { display:none; }
        .cv-typed-wrap { min-width:0; max-width:100%; overflow-x:auto; }
        .cv-typed-wrap:focus-visible, .cv-table-scroll:focus-visible { outline:2px solid var(--accent-strong); outline-offset:2px; }
        .cv-typed { border-collapse:collapse; font-size:.88rem; }
        .cv-typed caption { margin:0 0 6px; color:var(--accent-strong); font-size:.72rem; font-weight:750;
                            letter-spacing:.1em; text-transform:uppercase; text-align:left; }
        .cv-typed th, .cv-typed td { padding:4px 10px 4px 0; text-align:left; vertical-align:middle; }
        .cv-typed thead th { color:var(--muted); font-weight:600; font-size:.8rem; white-space:nowrap; }
        .cv-typed tbody th { font-variant-numeric:tabular-nums; font-weight:650; }
        .cv-typed input, .cv-typed select { width:7.5em; min-height:36px; padding:4px 8px; border:1px solid var(--line-strong);
                                            border-radius:var(--radius-sm); font:inherit; background:var(--surface); color:var(--ink); }
        .cv-typed input[aria-invalid="true"], .cv-typed select[aria-invalid="true"] { border-color:var(--required-ink); }
        .cv-typed tr.cv-selected th, .cv-typed tr.cv-selected td { background:color-mix(in srgb, var(--accent) 12%, var(--surface)); }
        /* A grid output on a canvas page (M10): a table under the workbook's headings, after the
           figures and before the charts, in its own scroll box. */
        .cv-result-tables { display:grid; gap:16px; margin-top:16px; min-width:0; }
        .cv-result-table { min-width:0; }
        .cv-table-scroll { max-width:100%; overflow-x:auto; }
        .cv-table-scroll .grid-out { margin-top:0; }
        .cv-table-scroll .grid-out tbody th { text-align:left; font-weight:600; color:var(--ink); }
        .cv-table-missing { margin:0; color:var(--required-ink); font-size:.88rem; }
        .cv-results.results-stale .cv-result-tables { opacity:.6; }
        /* A phone: Calculate stays reachable while the reader draws, and takes the full width. */
        @media (max-width:640px) {
            .cv-actions { position:sticky; bottom:0; z-index:5; }
            .cv-calculate { flex:1 1 100%; }
            .cv-form { padding:16px; }
            /* The sticky bar must never cover the field being edited (ruling T6): a page with
               a form scrolls a focused field clear of it. --cv-actions-block is the bar's own
               height, which the island measures; the fallback covers the first frame. */
            html:has(.template-workspace.canvas .cv-form),
            html:has(.template-workspace.canvas .cv-tables) { scroll-padding-bottom:calc(var(--cv-actions-block, 220px) + 12px); }
            .cv-form .field, .cv-typed tr { scroll-margin-bottom:calc(var(--cv-actions-block, 220px) + 12px); }
            .cv-tables { padding:16px; }
        }
        /* A scenario-grid cell the workbook gave a reason: the reason, in place of a value. */
        .grid-out .grid-reason { color:var(--muted); font-style:italic; font-size:.82rem; white-space:normal; }
        /* The print summary: without the site chrome or the page's controls, and a stale result prints
           with its stale wording, because browser printing cannot be withheld. The 3R pages' own print
           rules (inputs, buttons and articles left out, the warnings kept) are in result-envelope.css. */
        @media print {
            .site-header, .crumbs, .hero-video-card, .hero-video-pill, .related, .powered-by,
            .beta-note, .feedback, .run-controls, .calc-head, .reset-btn, .input-view { display:none !important; }
            .run-page.results-stale::before { content:"These results do not match the inputs now on the page."; display:block;
                                             margin:0 0 8px; font-style:italic; }
        }
