
        /* ═══════════════════════════════════════════════════════════════════════
           THEME SYSTEM — single source of truth for the main SPA.
           The app ships EXACTLY two themes: `light` (default) and `dark`, defined
           in the block below. They share every token name + the same blueprint-blue
           accent, so switching is a one-attribute DOM change (data-theme on <html>;
           browser repaints in <1ms). The admin/standalone pages mirror these exact
           values via admin-shell.css's --canvas/--paper/--ink tokens so the whole
           product reads as ONE app in both themes. Do NOT add a third theme.
           ═══════════════════════════════════════════════════════════════════════ */

        /* PR-RISK-TOKENS (2026-05-27): theme-stable risk classification.
           Same green means ON TRACK in every theme, same amber means AT
           RISK, same rose means LATE — the office's eye carries learned
           meaning across themes (light/dark/walnut/etc.). Defined on a
           plain :root so EVERY theme inherits the same hex unless it
           explicitly overrides (which no theme currently does).
           Replaces ~52 hardcoded #16a34a / #b45309 / #b8475a sites in
           .prodp-risk-pill / .schedp-* / .ipd-prog-* / .adm-pill /
           .adm-action. */
        :root {
            --risk-on-track: #16a34a;   /* green-600 — ON TRACK   */
            --risk-at-risk:  #b45309;   /* amber-700 — AT RISK    */
            --risk-late:     #b8475a;   /* rose-600  — LATE/PAST  */
        }

        /* ═══════════════════════════════════════════════════════════════════════
           THEME SYSTEM — exactly TWO themes: Light (default) + Dark.
           2026-07-18: consolidated from the prior 9-theme sprawl (forge /
           forge-light / ascut / precision-light / walnut / dark / shop /
           test / exgf) down to one clean light/dark pair. They are true
           siblings: SAME token names, SAME blueprint-blue accent identity,
           SAME semantic status hues — only the neutral surface ramp flips.
           Switching is a one-attribute DOM change (data-theme on <html>);
           legacy saved ids are migrated to light/dark in the boot scripts
           (index.html head, admin-shell.js) and in app-core.js.

           The CSS `:root` fallback (no data-theme) resolves to LIGHT — the
           same as the default theme — so there is never a flash of a
           different palette before the boot script runs.
           ═══════════════════════════════════════════════════════════════════════ */

        /* ─── LIGHT (default) — bright drafting-paper engineering canvas ───────── */
        :root, :root[data-theme="light"] {
            --bg:    #F3F5F8;   /* page ground — drafting paper                      */
            --sf:    #E9EDF2;   /* topbar / sidebar / statusbar chrome               */
            --pn:    #FFFFFF;   /* cards, panels, data containers                    */
            --pn-h:  #F0F3F7;   /* card / row hover                                  */
            --inp:   #FFFFFF;   /* form-input background                             */
            --bdr:   #D9DFE7;   /* default hairline border                          */
            --bdr-h: #AAB3C0;   /* hover / focus / active border                    */
            --acc:   #2563EB;   /* blueprint blue — CTA, selection, focus (AA on --pn) */
            --acc2:  #1D4FD8;   /* accent hover — deeper blue                        */
            --acc-ink: #FFFFFF; /* ink on the accent fill                           */
            --txt:   #1A222E;   /* primary text — ~13:1 on --bg (AAA)               */
            --dim:   #5A6573;   /* secondary text / labels — ~5.4:1 (AA)            */
            --muted: #8C95A0;   /* placeholders, disabled, em-dash empties          */
            --white: #0D1118;   /* highest-emphasis ink (darkest token in light)    */
            --ink:   var(--white); /* body-text ink token — darkest for legibility  */
            --ok:    #0E9D6E;   --ok-ink:   #FFFFFF;   /* success / on-track green   */
            --warn:  #C77700;   --warn-ink: #FFFFFF;   /* at-risk / low-stock amber  */
            --no:    #D33A4B;   --no-ink:   #FFFFFF;   /* late / danger / delete rose */
            --lime:  #0E9D6E;   --lime-ink: #FFFFFF;   /* status-complete (=--ok)     */
        }

        /* ─── DARK — near-black engineering canvas, SAME blueprint-blue accent ─── */
        :root[data-theme="dark"] {
            --bg:    #000000;   /* page ground — true black (AMOLED)                 */
            --sf:    #000000;   /* topbar / sidebar / statusbar — coplanar black     */
            --pn:    #16181C;   /* cards, panels — lifted a hair off black           */
            --pn-h:  #1E2127;   /* card / row hover — a real lift                    */
            --inp:   #202327;   /* inputs, filter chips, command bar                 */
            --bdr:   #2F3336;   /* 1px hairline — the white-outline on black         */
            --bdr-h: #3E4348;   /* hover / focus / active border                    */
            --acc:   #4C8DFF;   /* blueprint blue (lightened for dark) — same family as light */
            --acc2:  #6AA5FF;   /* accent hover — brighter blue                      */
            --acc-ink: #05070D; /* near-black ink on the bright-blue fill            */
            --txt:   #E7E9EA;   /* primary text — near-white                         */
            --dim:   #9AA3B0;   /* secondary text / labels                          */
            --muted: #6B7480;   /* placeholders, disabled, em-dash empties          */
            --white: #FFFFFF;   /* highest-emphasis headings                        */
            --ink:   var(--txt); /* body-text ink token — bright on dark surfaces    */
            --ok:    #34D399;   --ok-ink:   #06281D;   /* success / on-track green   */
            --warn:  #F0B429;   --warn-ink: #2A1E03;   /* at-risk / low-stock amber  */
            --no:    #F2566B;   --no-ink:   #2A0810;   /* late / danger / delete rose */
            --lime:  #34D399;   --lime-ink: #06281D;   /* status-complete (=--ok)     */
        }

        /* ═══════════════════════════════════════════════════════════════════════
           PR-UX-POLISH (2026-09-14, operator: "get rid of ugly font and ugly
           shadow colors, make the buttons and lines more professional")
           ═══════════════════════════════════════════════════════════════════════

           Three names this stylesheet USES were never DEFINED, so every consumer
           silently took the hardcoded fallback baked into its own `var(..., X)`:

             --bg-card   → #14181f — a DARK panel colour, applied in the LIGHT
                           theme too. Anything falling back to it was painting a
                           near-black surface on drafting paper.
             --tier-cab  → #7A3BAF — the cabinet-tier purple, hardcoded in a
                           dozen places, off-palette against the blueprint blue.
             --radius-sm → 6px in some rules, 5px and 8px hardcoded in others, so
                           three buttons in one row had three corner radii.

           Defining them is most of the fix: the values below are now one edit
           away instead of scattered through 17,000 lines.

           SHADOWS. The old ones were pure black (`rgba(0,0,0,.5)`, `.6`) at a
           single blur. Pure black over a coloured ground reads as grey soot
           rather than shade — it is the single most common thing that makes an
           interface look cheap. Real shade is (a) TINTED toward the surface's
           own hue and (b) LAYERED: a tight contact shadow for the edge plus a
           wide ambient one for the lift. `--shadow-ink` carries the tint as
           space-separated RGB channels so each layer can set its own alpha. */
        :root, :root[data-theme="light"] {
            /* Cool slate-navy pulled from --txt (#1A222E) — shade on paper is
               never neutral grey; it takes the colour of what is around it. */
            --shadow-ink: 26 34 46;
            --tier-cab:   #6D28D9;   /* cabinet tier — violet, one family off the blue */
            --tier-cab-soft: color-mix(in srgb, var(--tier-cab) 10%, transparent);
        }
        :root[data-theme="dark"] {
            /* On a black ground a shadow cannot darken anything, so it does less
               work and the border carries the edge. Kept near-black, not tinted
               up, or panels acquire a blue halo. */
            --shadow-ink: 0 0 0;
            --tier-cab:   #A78BFA;   /* lightened for dark — same violet family */
            --tier-cab-soft: color-mix(in srgb, var(--tier-cab) 16%, transparent);
        }
        :root {
            --bg-card: var(--pn);    /* was undefined → a dark fallback in light mode */

            /* Elevation. Two layers each: contact + ambient. */
            --shadow-1: 0 1px 2px rgb(var(--shadow-ink) / .05),
                        0 1px 3px rgb(var(--shadow-ink) / .08);
            --shadow-2: 0 1px 2px rgb(var(--shadow-ink) / .05),
                        0 4px 12px rgb(var(--shadow-ink) / .10);
            --shadow-3: 0 2px 4px rgb(var(--shadow-ink) / .06),
                        0 12px 28px rgb(var(--shadow-ink) / .14);
            --shadow-4: 0 4px 8px rgb(var(--shadow-ink) / .08),
                        0 24px 56px rgb(var(--shadow-ink) / .20);
            /* A pressed / inset well, for the tinted callouts. */
            --shadow-inset: inset 0 1px 2px rgb(var(--shadow-ink) / .06);

            /* Corner radii — one ladder, so a row of buttons agrees with itself. */
            --radius-sm: 6px;
            --radius-md: 8px;
            --radius-lg: 12px;
            --radius-pill: 999px;

            /* Control height. 44px is the WCAG 2.5.5 tap target the editor
               already settled on; the 48px and 36px one-offs elsewhere were
               drift, not intent. */
            --ctl-h: 44px;
            --ctl-h-sm: 34px;

            /* One hairline colour for rules INSIDE a card, quieter than the
               card's own border so a stack of rows does not read as a table. */
            --hairline: color-mix(in srgb, var(--bdr) 60%, transparent);
        }

        /* ─── Theme-stable derived tokens ───────────────────────────────────────
           Defined once on plain :root; they reference the active theme's core
           tokens (var() resolves at use-time against the winning cascade), so
           they follow light/dark automatically. This keeps older consumers
           (schedule / lifecycle pills, the SVG drafting canvas) resolving after
           the multi-theme consolidation removed the themes that used to own
           the --status-* / --canvas-* names. */
        :root {
            --status-draft:        var(--dim);
            --status-draft-ink:    var(--bg);
            --status-scheduled:    var(--warn);
            --status-scheduled-ink: var(--warn-ink);
            --status-progress:     var(--acc);
            --status-progress-ink: var(--acc-ink);
            --status-complete:     var(--ok);
            --status-complete-ink: var(--ok-ink);
            --status-alert:        var(--no);
            --status-alert-ink:    var(--no-ink);

            --canvas-viewport:       var(--bg);
            --canvas-grid-minor:     var(--pn-h);
            --canvas-grid-major:     var(--bdr);
            --canvas-model-default:  #D1D1D1;
            --canvas-select-fill:    var(--acc);
            --canvas-select-outline: var(--acc2);
            --canvas-dim-text:       var(--txt);
            --canvas-snap:           var(--acc2);
            --canvas-axis-x:         #E06B6B;
            --canvas-axis-y:         #6BC97A;
            --canvas-axis-z:         var(--acc);
        }

        *{margin:0;padding:0;box-sizing:border-box;}
        body{
            font-family:'Inter','Segoe UI',-apple-system,BlinkMacSystemFont,Roboto,sans-serif;
            font-feature-settings:'cv11','ss01','ss03';   /* Inter: cleaner 'l', single-story 'a', open '4' */
            font-variant-numeric:tabular-nums;            /* lock digit width so dimensions + counts line up */
            font-size:14px;
            font-weight:400;
            background:var(--bg); color:var(--txt); line-height:1.6;
            -webkit-font-smoothing:antialiased;
            -moz-osx-font-smoothing:grayscale;
            text-rendering:optimizeLegibility;
        }

        /* ═════════════════════════════════════════════════════════════════════
           TYPOGRAPHY — Inter type ramp.
           Scoped overrides matching the design spec:
             • Logo / brand     → SemiBold 24px
             • Page heading h1  → SemiBold 28px (drawer/login: 24px)
             • Section title h2 → Medium    20px
             • Subsection h3    → Medium    16px (was 14px — tightened)
             • Body / .txt      → Regular   14–15px
             • Small label .lbl → Medium    12px tabular
             • Buttons / nav    → Medium    14px (.bs stays 12px)
           ════════════════════════════════════════════════════════════════════ */
        h1{font-family:'Inter',inherit;font-weight:600;font-size:28px;letter-spacing:-.01em;line-height:1.25;}
        h2{font-family:'Inter',inherit;font-weight:500;font-size:20px;letter-spacing:-.005em;line-height:1.3;}
        h3{font-family:'Inter',inherit;font-weight:500;font-size:16px;letter-spacing:0;line-height:1.35;}
        button,.btn,.tab,.nav-btn{font-family:'Inter',inherit;font-weight:500;font-size:14px;letter-spacing:.1px;}
        .bs{font-size:12px;font-weight:500;}     /* small variants stay compact */
        .li-m,.dim,.sub,.cv-hint{font-weight:500;font-size:12px;letter-spacing:.1px;}
        /* Tabular figures everywhere a number is shown so columns of dims/counts align */
        .st-n,.li-m,.fg input[type="number"],.cutlist-out,#scene-hud{font-variant-numeric:tabular-nums;}
        .hidden{display:none!important;}
        a{color:var(--acc);text-decoration:none;}

        /* ---- Login ---- */
        .login-wrap{display:flex;align-items:center;justify-content:center;height:100vh;background:radial-gradient(ellipse at 50% 30%,#1a2040 0%,#12131a 70%);}
        .login-box{background:var(--pn);padding:48px;border-radius:16px;width:100%;max-width:400px;border:1px solid var(--bdr);box-shadow:0 24px 80px rgb(var(--shadow-ink) / .6);}
        .login-box h1{color:var(--acc2);margin-bottom:4px;font-size:24px;letter-spacing:1px;}
        .login-box .sub{color:var(--dim);margin-bottom:32px;font-size:13px;}
        .login-box .err{color:var(--no);margin-top:12px;font-size:12px;}

        /* ---- Form ---- */
        .fg{margin-bottom:14px;}
        .fg label{display:block;margin-bottom:5px;color:var(--dim);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.8px;}
        /* PR-CUST-8 (2026-06-10) — customer-form section headers. The old
           collapsed <details> "Advanced" buried the (now live-math)
           Discount % per the operator's UX complaint; the form is flat
           sections with full-width labeled rules instead. Same visual
           vocabulary as .fg label so it reads as a heading from tablet
           distance without new tokens. */
        .cust-section-head{font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--dim);border-bottom:1px solid var(--bdr);padding-bottom:5px;margin:18px 0 12px;}
        .cust-section-head.cust-section-danger{color:var(--no);border-bottom-color:var(--no);}

        /* PR-CUST-STICKY-SAVE (2026-08-19) — sticky Save/Back bar on the customer
           edit form. The form runs ~9 sections; committing a one-field change used
           to mean scrolling the whole page to reach Save, and Back (topbar / browser
           gesture) silently dropped the edit. Save + Back now ride at the top and
           follow the operator down.

           THE OVERFLOW OVERRIDE IS LOAD-BEARING, not cosmetic: #hub-scroll-panels
           carries `.scroll{overflow-y:auto}` but is NOT the element that scrolls —
           #pg-hub is display:block (shell.css:817), so `.scroll`'s flex:1 is inert,
           the panel is content-height, and #app-workspace does the scrolling. That
           inert overflow still makes #hub-scroll-panels the sticky containing
           scrollport, and a scrollport that never scrolls pins nothing — the bar
           scrolls away with the page. Verified in a headless Chromium harness:
           without this line the bar lands at y=-1079 after a 1200px scroll; with it,
           y=56 (flush under the topbar). Scoped to body[data-cust-editing] — set by
           openCustomerForm(), cleared by _custCloseFormNow() — so the hub's other
           tabs keep the container's overflow behavior untouched. */
        body[data-cust-editing="true"] #hub-scroll-panels{overflow:visible;}

        /* .pn padding, mirrored so the bar can bleed to the panel edges. Tracks the
           14px .pn override in shell.css @media (max-width:599px). */
        #cust-form{--cust-pad:28px;}
        @media (max-width:599px){ #cust-form{--cust-pad:14px;} }

        .cust-form-bar{
            position:sticky; top:0; z-index:var(--z-sticky);
            display:flex; align-items:center; gap:12px; flex-wrap:wrap;
            margin:calc(var(--cust-pad) * -1) calc(var(--cust-pad) * -1) 20px;
            padding:12px var(--cust-pad);
            background:var(--pn);
            border-bottom:1px solid var(--bdr);
            border-radius:8px 8px 0 0;
            box-shadow:0 4px 12px color-mix(in srgb, var(--scrim) 22%, transparent);
        }
        /* Override .pn h3's own bottom rule — the bar already draws one. */
        .cust-form-bar h3{
            flex:1 1 auto; min-width:0; margin:0; padding:0; border-bottom:none;
            font-size:15px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
        }
        /* 44px tap targets — gloved hands on a shop tablet (WCAG 2.5.5 / Apple HIG). */
        .cust-form-bar .cust-form-back,
        .cust-form-bar .cust-form-save{ min-height:44px; flex:0 0 auto; }
        .cust-form-bar .cust-form-save{ min-width:112px; font-weight:700; }
        /* Unsaved-changes flag: the operator's cue that Back will ask. */
        .cust-dirty-flag{
            flex:0 0 auto; font-size:11px; font-weight:700; letter-spacing:.6px;
            text-transform:uppercase; color:var(--warn);
            background:color-mix(in srgb, var(--warn) 14%, transparent);
            border:1px solid color-mix(in srgb, var(--warn) 38%, transparent);
            border-radius:999px; padding:4px 10px;
        }
        .cust-dirty-flag[hidden]{ display:none; }
        /* Phone: the title is the first thing to go — Back and Save are the job. */
        @media (max-width:599px){
            .cust-form-bar h3{ flex-basis:100%; order:3; font-size:13px; }
            .cust-form-bar .cust-form-save{ margin-left:auto; }
        }
        @media (prefers-reduced-motion:no-preference){
            .cust-form-bar{ transition:box-shadow 160ms ease; }
        }
        /* Tabbing through the form scrolls the next field into view — without this
           the browser parks it flush at the scrollport top, i.e. UNDER the sticky
           bar. Clears the bar's height plus a little breathing room. */
        #cust-form input, #cust-form select, #cust-form textarea,
        #cust-form .cust-section-head{ scroll-margin-top:84px; }
        /* UX-DO — Door-order focus-mode polish. Per 2026 form-design
           research (NN/G + DesignStudio): label-above-field, single-
           column within a section, generous input padding, clear focus
           ring. Scoped to #pg-door so the rest of the app stays on its
           current density. Hours-at-a-time use: the operator's eye
           lands cleanly on the next required field every time. */
        #pg-door .fg input,
        #pg-door .fg select,
        #pg-door .fg textarea{
            /* A11Y-fix (2026-04-22): raised from 8px to 14px top/bottom padding.
               Computes to ~48 CSS px tap target, meeting WCAG 2.5.5 AAA (44 px
               minimum) with 4 px margin for gloved-thumb mis-fires. */
            font-size:14px;padding:14px 10px;
            border-radius:6px;border:1px solid var(--bdr);
            background:var(--inp);color:var(--txt);
            transition:border-color .12s,box-shadow .12s;
        }
        #pg-door .fg input:focus,
        #pg-door .fg select:focus,
        #pg-door .fg textarea:focus{
            outline:none;
            border-color:var(--acc2);
            box-shadow:0 0 0 3px color-mix(in srgb, var(--acc2) 15%, transparent);
        }
        #pg-door .fg input:invalid,
        #pg-door .fg input[aria-invalid="true"]{
            border-color:var(--no);
            box-shadow:0 0 0 3px rgba(255,85,85,.12);
        }
        /* Sticky validation message — follows the operator's scroll so
           a lengthy form never hides the current error. */
        #sg-validation-msg:not(:empty){
            position:sticky;bottom:0;z-index:4;
            background:var(--sf);padding:4px 0;
        }
        /* Library-source hint: small chip next to a form-field label
           telling the operator which library backs the dropdown.
           Prevents the "why are these both here?" confusion when two
           dropdowns with similar labels actually pull from different
           libraries (e.g. Panel Stock \u2192 Sheet Goods, Frame Lumber \u2192 Lumber). */
        .fg label .lib-src{display:inline-block;margin-left:8px;color:var(--dim);font-weight:500;font-size:9px;text-transform:none;letter-spacing:.2px;opacity:.75;padding:1px 6px;border:1px solid var(--bdr);border-radius:4px;vertical-align:1px;}
        /* PR-UI-18 (2026-04-27): tokenized transition + theme-flowing focus
           ring. Was `rgba(0,188,212,.15)` (hardcoded cyan glow that broke
           on light/amber themes); now uses `color-mix(in srgb, var(--acc)
           18%, transparent)` so every theme produces a 18%-saturation
           glow of its own --acc color. Same visual on Onyx; correct on
           every other theme.  */
        .fg input,.fg select,.fg textarea{width:100%;padding:9px 12px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;color:var(--txt);font-size:13px;font-family:inherit;transition:border-color var(--dur-standard) var(--ease-out),box-shadow var(--dur-standard) var(--ease-out);}
        .fg input:focus,.fg select:focus,.fg textarea:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 18%,transparent);}
        .fg textarea{resize:vertical;min-height:60px;}
        .fr{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:14px;}

        /* Primary form field — spans 2 grid columns in an .fr row and
         * gets louder label + input chrome than the default .fg. Used on
         * the single most-important field in a section (e.g. Customer /
         * Builder on the Order Info row, where everything else is
         * optional contact metadata). Routes through tokens so it adapts
         * to every theme. */
        .fg-primary { grid-column: span 2; }
        @media (max-width: 700px) {
            /* Narrow viewports — .fr auto-fit can collapse to 1 col;
             * don't force a 2-col span that would do nothing. */
            .fg-primary { grid-column: auto; }
        }
        .fg-primary label {
            color: var(--txt);
            font-size: 12px;
            font-weight: var(--fw-bold, 700);
            letter-spacing: 0.6px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .fg-primary .fg-req {
            display: inline-block;
            padding: 2px 7px;
            background: color-mix(in srgb, var(--acc) 18%, transparent);
            color: var(--acc);
            border: 1px solid color-mix(in srgb, var(--acc) 45%, var(--bdr));
            border-radius: 999px;
            font-size: 9px;
            font-weight: var(--fw-bold, 700);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            line-height: 1;
        }
        .fg-primary input {
            border-width: 1.5px;
            border-color: color-mix(in srgb, var(--acc) 30%, var(--bdr));
            font-size: 14px;
            font-weight: var(--fw-medium, 500);
        }
        .fg-primary input:hover:not(:focus) {
            border-color: color-mix(in srgb, var(--acc) 55%, var(--bdr));
        }
        /* When the compat gate marks the field as error (empty required),
         * .compat-error should still WIN over the accent tint so the
         * operator sees "hey fill me" not "yeah I'm already highlighted." */
        .fg-primary input.compat-error,
        .fg-primary input:invalid:not(:focus):not(:placeholder-shown) {
            border-color: var(--no);
        }

        /* ---- Buttons ---- */
        /* PR-UI-16 (2026-04-27): tokenized transition + tactile press scale.
           Every .btn/.ba/.bg/.bk/.br/.bw/.bs across the app inherits the
           --dur-quick + --spring-snappy timing and the 0.985 active scale.
           Consistent feel from Hub buttons → Cabinet inspector → Door studio
           → Reports modal → Shop drawings toolbar. Reduced-motion users
           get instant snap because --dur-quick is zeroed in tokens.css. */
        button,.btn{padding:9px 18px;border:none;border-radius:8px;cursor:pointer;font-size:12px;font-weight:600;transition:background var(--dur-quick) var(--ease-out),color var(--dur-quick) var(--ease-out),border-color var(--dur-quick) var(--ease-out),box-shadow var(--dur-quick) var(--ease-out),transform var(--dur-quick) var(--spring-snappy);letter-spacing:.3px;}
        button:active,.btn:active{transform:scale(0.985);}
        /* PR-FORGE-CAB-BUTTONS (2026-06-13): button family hovers were the
           last off-brand bits — a CYAN glow on .ba (flashed wrong on the blue
           forge accent) + hardcoded green/red hovers. Now all token-derived. */
        .ba{background:var(--acc);color:var(--acc-ink);}.ba:hover{background:var(--acc2);}
        .bg{background:var(--inp);color:var(--acc);border:1px solid var(--bdr);}.bg:hover{background:var(--bdr);}
        .bk{background:var(--ok);color:var(--ok-ink);}.bk:hover{background:color-mix(in srgb, var(--ok) 82%, #fff);}
        .br{background:var(--no);color:#fff;}.br:hover{background:color-mix(in srgb, var(--no) 82%, #fff);}
        .bw{background:var(--warn);color:#000;}
        .bs{padding:5px 10px;font-size:11px;border-radius:6px;}
        .bf{width:100%;}.brow{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap;}

        /* ====================================================================== */
        /* PR-UI-15 (2026-04-27) — UNIVERSAL FOCUS-VISIBLE SYSTEM                  */
        /* ====================================================================== */
        /* One source of truth for keyboard-focus visibility across every page.   */
        /* Replaces the dozen+ ad-hoc :focus / :focus-visible rules that used     */
        /* hardcoded rgba(0,188,212,...) glows. Now everything routes to var(--acc).*/
        /* `:focus-visible` (not :focus) means the ring only appears on KEYBOARD   */
        /* navigation — mouse clicks don't trigger the ring, which keeps shop-   */
        /* floor tablet UX clean. Reduced-motion users get the same ring (no      */
        /* animation involved). Specificity is tight: this rule wins over the    */
        /* per-component :focus rules already in the file.                       */
        button:focus-visible,
        .btn:focus-visible,
        .bg:focus-visible,
        .ba:focus-visible,
        .bk:focus-visible,
        .br:focus-visible,
        .bw:focus-visible,
        .bs:focus-visible,
        a:focus-visible,
        [role="button"]:focus-visible,
        [role="tab"]:focus-visible,
        [role="menuitem"]:focus-visible,
        [tabindex]:not([tabindex="-1"]):focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
            box-shadow: 0 0 0 4px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        /* Inputs already have border-color+box-shadow on :focus per their
           component rules. Add :focus-visible only when the existing rule
           doesn't already match — the [tabindex] selector above covers
           most cases. Forms surfaces use the per-component .fg rule. */

        /* ---- Panel ---- */
        /* PR-UI-24: panel radius 14px → 8px. .pn wraps every panel in
           the app — Hub, Cabinet, Door Studio, Settings. Tighter radius
           shifts the entire app from "consumer card app" toward QuickBooks
           density. */
        .pn{background:var(--pn);border:1px solid var(--bdr);border-radius:8px;padding:28px;margin-bottom:24px;}
        .pn h3{color:var(--white);font-size:14px;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--bdr);display:flex;align-items:center;gap:8px;}
        .pn h3 .badge{background:var(--acc);color:var(--acc-ink);padding:2px 8px;border-radius:10px;font-size:10px;font-weight:700;}

        /* ---- Stats ---- */
        .sg{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:20px;margin-bottom:32px;}
        .st{background:var(--pn);border:1px solid var(--bdr);border-radius:8px;padding:24px;text-align:center;}
        .st-n{font-size:26px;font-weight:700;color:var(--acc2);}.st-l{color:var(--dim);font-size:11px;margin-top:4px;text-transform:uppercase;letter-spacing:.5px;}

        /* ---- List ---- */
        /* PR-UI-19: list item — used in customer/project lists across pages. */
        .li{background:var(--inp);padding:14px 18px;border-radius:10px;border-left:3px solid var(--acc);display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;transition:background var(--dur-standard) var(--ease-out),transform var(--dur-quick) var(--spring-snappy);}
        .li:hover{background:var(--bdr);}.li-t{color:var(--white);font-weight:600;font-size:13px;}.li-m{color:var(--dim);font-size:11px;margin-top:2px;}

        /* ---- Pages ---- */
        .pg{display:none;flex-direction:column;height:100vh;}.pg.on{display:flex;}
        .scroll{flex:1;overflow-y:auto;padding:36px 40px;}
        /* PR-CARD-TOKENS (2026-08-25, owner: "i want the margins to change as
           well to really get close to the same home page look"): Door Studio
           takes the Cabinet Studio dashboard's page frame — the same 24px
           left gutter the page title sits on, and the same 1920px fluid cap
           that only ultrawide monitors reach. Rationale is PR-DASH-TIGHTEN's
           verbatim: a centered column floats the page as an island with a
           moat on both sides of a shop monitor while the heading stays
           anchored left, which is two alignment systems on one page. Left-
           aligning puts the slack on ONE side, where it reads as rag. */
        /* Zero here, gutters on the panels below: #d-orders sets its own
           (bench-studio.css) and stacking the two double-indented the orders
           table to a 48px gutter. #d-new has NO padding of its own and was
           living off this rule, so it gets the old value back verbatim — the
           door builder is deliberately untouched by the dashboard reframe. */
        #pg-door > .scroll { padding: 0; }
        #pg-door > .scroll > #d-new { padding: 36px 40px; }
        /* PR-CABSTUDIO-FIT (2026-07-03): #pg-cab is display:block (shell.css:809)
           and .pg forces height:100vh, but its scroll host #app-workspace is only
           100vh-88px (56px topbar + 32px status bar). The 88px overhang let you
           scroll into a blank strip and pushed the bottom controls below the fold.
           Cap the page box to the workspace height (100%). NOT a grid restructure
           (see feedback_no_cab_editor_grid_layout_changes) — page-container height
           only; display stays block, the 3-column grid is untouched. */
        body[data-page="cab"] #pg-cab{height:100%;}

        /* ---- Tabs ---- */
        .tabs{display:flex;background:var(--sf);border-bottom:1px solid var(--bdr);padding:0 36px;gap:8px;}
        /* PR-UI-19 (2026-04-27): tokenize raw .2s. Used in Hub tabs,
           Reports modal tabs, Library sub-tabs — every page that has tabs. */
        .tab{padding:14px 22px;background:none;border:none;color:var(--dim);cursor:pointer;font-size:12px;font-weight:600;border-bottom:2px solid transparent;transition:color var(--dur-standard) var(--ease-out),border-color var(--dur-standard) var(--ease-out);letter-spacing:.3px;}
        .tab:hover{color:var(--acc);}.tab.on{color:var(--acc2);border-bottom-color:var(--acc);}
        .tc{display:none;}.tc.on{display:block;}
        /* PR-CLEAN-7 / RELOCATE-TO-CAB-DASH (2026-05-31): top-level Design
           Dashboard tab panels inside #pg-cab. Same display:none/block pattern
           as .tc — single-class selector so specificity matches the existing
           .tc rule. (Previously this rule was .jobs-tc on pg-hub Main Hub
           before the relocation — see [[feedback_verify_ui_page_target]].) */
        .cab-tc{display:none;}.cab-tc.on{display:block;}

        /* ---- Studio Home Dashboard ---- */
        /* All hardware-accelerated transforms / opacity transitions only — no layout reflow on hover */
        .qc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px;margin-bottom:22px;}
        /* PR-UI-19: quick-card — Hub home recent project tiles. */
        .qc{background:var(--pn);border:1px solid var(--bdr);border-radius:8px;padding:22px;cursor:pointer;display:flex;align-items:center;gap:16px;transition:transform var(--dur-chrome) var(--spring-snappy),border-color var(--dur-chrome) var(--ease-out),box-shadow var(--dur-chrome) var(--ease-out);will-change:transform;}
        .qc:hover{border-color:var(--bdr-h);background:var(--pn-h);} /* PR-FORGE-CAB: flat hover */
        .qc-icon{width:52px;height:52px;border-radius:12px;background:var(--inp);display:flex;align-items:center;justify-content:center;color:var(--acc);flex-shrink:0;}
        .qc-icon svg{width:28px;height:28px;}
        .qc-body h4{color:var(--white);font-size:16px;margin-bottom:4px;font-weight:600;}
        .qc-body p{color:var(--dim);font-size:12px;line-height:1.4;}
        /* ---- Studio ---- */
        .shdr{background:var(--sf);border-bottom:1px solid var(--bdr);padding:10px 24px;display:flex;justify-content:space-between;align-items:center;}
        .shdr h1{font-family:'Inter',inherit;font-weight:600;font-size:24px;color:var(--acc2);letter-spacing:-.005em;}
        .shdr .sub{color:var(--dim);font-size:12px;font-weight:500;}
        /* Door-icon header button — single-element interactive logo, GPU-composited hover */
        .logo-btn{width:42px;height:42px;border-radius:10px;background:var(--inp);border:1px solid var(--bdr);color:var(--acc);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform var(--dur-chrome) var(--spring-snappy),border-color var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);will-change:transform;padding:0;}
        .logo-btn svg{width:24px;height:24px;}
        .logo-btn:hover{transform:translateY(-1px);border-color:var(--acc);color:var(--acc2);}
        .logo-btn:active{transform:translateY(0);}
        /* PR-UI-24: was a 24px-radius pill with linear-gradient — the
           combination of pill shape + gradient was the strongest "child-
           like" signal in the topbar. Flattened to 6px square corners +
           solid accent, matching QuickBooks/Xero primary-button pattern. */
        .home-btn{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--acc);color:var(--acc-ink);border:none;border-radius:6px;font-weight:600;font-size:13px;letter-spacing:0;cursor:pointer;transition:transform var(--dur-chrome) var(--spring-snappy),box-shadow var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out);will-change:transform;box-shadow:0 1px 2px rgb(var(--shadow-ink) / .18);}
        .home-btn svg{width:18px;height:18px;stroke:currentColor;}
        .home-btn:hover{transform:translateY(-1px);box-shadow:0 4px 14px rgb(var(--shadow-ink) / .28);filter:brightness(1.05);}
        .home-btn:active{transform:translateY(0);box-shadow:0 1px 4px rgb(var(--shadow-ink) / .2);}
        .scmds{display:flex;gap:8px;align-items:center;}

        /* PR-CTA-WINS-THE-EYE (2026-08-28): the studio header had TWO
           accent-blue objects — a 24px `.shdr h1` in var(--acc2) and the
           13px "Start New Order" button in var(--acc). The heading is twice
           the size, so it won the eye every time and the one thing you can
           ACT on read as chrome. The button was never badly placed; it was
           out-shouted by a label.
           Scoped to Door Studio because that is the page under redesign —
           Cabinet and Millwork have the identical clash and should follow,
           but that is their own change to make deliberately. */
        #pg-door .shdr h1{color:var(--txt);}
        #pg-door .scmds .home-btn{padding:10px 18px;font-size:14px;}

        /* ---- Table ---- */
        table{width:100%;border-collapse:collapse;font-size:12px;}
        th{text-align:left;padding:9px 12px;background:var(--inp);color:var(--acc);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;border-bottom:2px solid var(--bdr);}
        td{padding:9px 12px;border-bottom:1px solid rgba(46,49,80,.5);}
        /* PR-UI-21: every <tr> across every table now uses tokenized
           transition; hover background flows through theme via color-mix
           on var(--acc) instead of hardcoded cyan. */
        tr{transition:background var(--dur-chrome) var(--ease-out);cursor:pointer;}tr:hover td{background:color-mix(in srgb,var(--acc) 6%,transparent);}
        .row-active td{background:color-mix(in srgb,var(--acc) 12%,transparent);border-left:2px solid var(--acc);}

        /* ---- Messages ---- */
        .msg{padding:10px 14px;border-radius:8px;margin-bottom:10px;font-size:12px;}
        /* PR-UI-STATES (2026-08-13): message tints repainted from hardcoded rgba to
           theme tokens so they read correctly in BOTH light + dark (the amber even
           used a raw #ffb300). Same padding/radius/size → no reflow. */
        .msg-ok{background:color-mix(in srgb, var(--ok) 12%, transparent);color:var(--ok);border:1px solid color-mix(in srgb, var(--ok) 38%, transparent);}
        .msg-err{background:color-mix(in srgb, var(--no) 12%, transparent);color:var(--no);border:1px solid color-mix(in srgb, var(--no) 38%, transparent);}
        .msg-warn{background:color-mix(in srgb, var(--warn) 12%, transparent);color:var(--warn);border:1px solid color-mix(in srgb, var(--warn) 38%, transparent);}
        .msg-info{background:color-mix(in srgb, var(--acc) 12%, transparent);color:var(--acc2,#4da6ff);border:1px solid color-mix(in srgb, var(--acc) 38%, transparent);}

        /* ====================================================================== */
        /* PR-UI-STATES (2026-08-13) — universal interaction states + inline-error */
        /* affordances. ALL ADDITIVE: extends the existing button/input/msg        */
        /* primitives + lifts the wizard's error/required styles app-wide. NO base */
        /* padding/height changes (toolbars/inspector rows size off those); reuses */
        /* tokens.css vars only. Fixes: dead buttons looked live (no :disabled),    */
        /* .bw had no hover, inputs had no hover/disabled feedback, and the inline  */
        /* error/required affordances were trapped inside #cab-newjob-modal.        */
        /* ====================================================================== */
        .bw:hover{background:color-mix(in srgb, var(--warn) 82%, #fff);}
        .ba:disabled,.bg:disabled,.bk:disabled,.br:disabled,.bw:disabled,.bs:disabled,
        .ba[aria-disabled="true"],.bg[aria-disabled="true"],.bk[aria-disabled="true"],
        .br[aria-disabled="true"],.bw[aria-disabled="true"],.bs[aria-disabled="true"]{
            opacity:.45;cursor:not-allowed;box-shadow:none;
        }
        button:disabled:active,.btn:disabled:active,.ba:disabled:active,.bg:disabled:active,
        .bk:disabled:active,.br:disabled:active,.bw:disabled:active,.bs:disabled:active{transform:none;}
        /* Inputs: a hover affordance (so a field reads as editable) + a real       */
        /* disabled look. `:hover:not(:focus)` never fights the focus ring.          */
        .fg input:hover:not(:focus):not(:disabled),
        .fg select:hover:not(:focus):not(:disabled),
        .fg textarea:hover:not(:focus):not(:disabled){border-color:var(--bdr-h);}
        .fg input:disabled,.fg select:disabled,.fg textarea:disabled,
        input:disabled,select:disabled,textarea:disabled{opacity:.55;cursor:not-allowed;}
        /* Inline error + required affordances, promoted from #cab-newjob-modal so    */
        /* New-Room, Job-Defaults, and EVERY .fg form can surface field-level errors  */
        /* instead of silently clamping/failing. Errors clear on keystroke (Baymard). */
        .required-mark{color:var(--no);margin-left:4px;font-weight:700;}
        .fg input.input-err,.fg select.input-err,.fg textarea.input-err{
            border-color:var(--no);background:color-mix(in srgb, var(--no) 8%, transparent);
        }
        .fg .err-msg{color:var(--no);font-size:12px;margin-top:4px;line-height:1.3;}
        /* PR-UI-STATES: comfortable tap targets on TOUCH/tablet only. Uses min-height
           (never padding) so desktop toolbars don't reflow; scoped to pointer:coarse so
           mouse UIs are untouched. Shop-floor tablets get the 44px --touch-min floor. */
        @media (pointer:coarse){
            button,.btn{min-height:var(--touch-min,44px);}
            .bs{min-height:40px;}
            .fg input,.fg select,.fg textarea{min-height:40px;}
        }

        /* ---- Modal / Side Drawer ---- */
        /* Drawer overlay: lightweight backdrop so the editor canvas stays
         * visible BEHIND open drawers. Previous 60% black wash made it
         * look like the app had crashed every time a panel opened. The
         * panel itself sits on the right with a strong shadow so it still
         * reads as foreground without darkening everything else. Click on
         * the slim backdrop area still closes the drawer. */
        .drawer-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.18);backdrop-filter:blur(1.5px);-webkit-backdrop-filter:blur(1.5px);z-index:100;display:flex;justify-content:flex-end;}
        .drawer{width:520px;max-width:90vw;height:100vh;background:var(--sf);border-left:1px solid var(--bdr);overflow-y:auto;padding:24px;box-shadow:-12px 0 50px rgb(var(--shadow-ink) / .6);}
        /* PR-CLEAN-4 / NEWJOB-ISLAND (2026-05-31): centered-card overlay for
           the Jobs-page New job entry. Backdrop uses color-mix on var(--bg)
           rather than an rgba literal so the scrim respects the active
           theme per [[feedback_strict_color_tokens]]. The legacy
           .drawer-overlay above keeps its rgba literal for now (separate
           cleanup arc) but the new code follows the strict-token rule. */
        .island-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:color-mix(in srgb, var(--bg) 70%, transparent);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);z-index:120;display:flex;align-items:center;justify-content:center;padding:24px;}
        .island-card{position:relative;width:min(640px,94vw);max-height:88vh;background:var(--sf);border:1px solid var(--bdr);border-radius:14px;display:flex;flex-direction:column;box-shadow:0 30px 80px color-mix(in srgb, var(--bg) 80%, transparent), 0 0 0 1px var(--bdr);overflow:hidden;}
        /* PR-HOME-BENCH (2026-06-13): Settings floating island — wider + matches
           the .island-card chrome (radius 14, +1px border, same shadow). */
        .settings-island{position:relative;width:min(720px,94vw);max-height:88vh;overflow-y:auto;background:var(--sf);border:1px solid var(--bdr);border-radius:14px;box-shadow:0 30px 80px color-mix(in srgb, var(--bg) 80%, transparent), 0 0 0 1px var(--bdr);padding:28px 30px;}
        .set-logo-preview{width:44px;height:44px;border-radius:10px;border:1px solid var(--bdr);background:var(--inp);display:inline-flex;align-items:center;justify-content:center;overflow:hidden;flex:0 0 auto;}
        .set-logo-preview img{width:100%;height:100%;object-fit:contain;}
        .set-logo-mono{color:var(--dim);font-size:18px;}
        .wg-plan-box.wg-expanded{position:fixed;inset:24px;z-index:130;background:var(--sf);border:1px solid var(--bdr);border-radius:14px;box-shadow:0 0 0 100vmax color-mix(in srgb, var(--bg) 70%, transparent), 0 30px 80px color-mix(in srgb, var(--bg) 80%, transparent);padding:18px;overflow:auto;margin:0 !important;}
        .wg-plan-box.wg-expanded svg{max-height:calc(100vh - 150px) !important;}
        .island-close{position:absolute;top:14px;right:14px;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;color:var(--dim);cursor:pointer;font-size:18px;line-height:1;border-radius:8px;transition:background var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);}
        .island-close:hover{background:var(--inp);color:var(--acc);}
        .island-head{padding:22px 28px 14px;border-bottom:1px solid var(--bdr);}
        .island-progress{display:flex;gap:0;list-style:none;padding:16px 28px 6px;margin:0;border-bottom:1px solid var(--bdr);background:var(--bg);}
        .island-step-dot{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--dim);font-size:11px;font-weight:500;letter-spacing:.3px;text-transform:uppercase;position:relative;}
        .island-step-dot:not(:last-child)::after{content:"";position:absolute;top:11px;left:calc(50% + 14px);right:calc(-50% + 14px);height:2px;background:var(--bdr);}
        .island-step-dot.on::after,.island-step-dot.done::after{background:var(--acc);}
        .island-dot-num{width:24px;height:24px;border-radius:50%;background:var(--inp);border:1px solid var(--bdr);display:inline-flex;align-items:center;justify-content:center;color:var(--dim);font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;transition:background var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);}
        .island-step-dot.on  .island-dot-num{background:var(--acc);border-color:var(--acc);color:var(--acc-ink);}
        .island-step-dot.done .island-dot-num{background:transparent;border-color:var(--acc);color:var(--acc);}
        .island-step-dot.on  .island-dot-label,.island-step-dot.done .island-dot-label{color:var(--acc2);}
        .island-body{flex:1;overflow-y:auto;padding:22px 28px;}
        .island-step{display:block;}
        .island-step[hidden]{display:none;}
        .island-field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px;}
        .island-field label{color:var(--white);font-size:12px;font-weight:500;letter-spacing:.2px;}
        .island-field input,.island-field textarea,.island-field select{background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:10px 12px;color:var(--txt);font-size:13px;font-family:inherit;outline:none;transition:border-color var(--dur-chrome) var(--ease-out),box-shadow var(--dur-chrome) var(--ease-out);}
        .island-field input:focus,.island-field textarea:focus,.island-field select:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb, var(--acc) 18%, transparent);}
        .island-row{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
        @media (max-width:560px){.island-row{grid-template-columns:1fr;}}
        .island-defaults-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;}
        .island-defaults-grid > div{background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:2px;}
        .island-defaults-grid .ki-l{color:var(--dim);font-size:10px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;}
        .island-defaults-grid .ki-v{color:var(--white);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;}
        .island-review{display:grid;grid-template-columns:140px 1fr;gap:8px 14px;margin:0;}
        .island-review dt{color:var(--dim);font-size:11px;font-weight:600;letter-spacing:.4px;text-transform:uppercase;align-self:center;}
        .island-review dd{margin:0;color:var(--white);font-size:13px;}
        .island-foot{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:16px 28px;border-top:1px solid var(--bdr);background:var(--bg);}
        .island-foot .ba:disabled,.island-foot .bg:disabled{opacity:.4;cursor:not-allowed;}
        /* PR-NEWJOB-1SCREEN (2026-06-17): defaults chip + collapsible "More". */
        .island-chip{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:10px 12px;color:var(--white);font-size:13px;font-family:inherit;cursor:pointer;text-align:left;transition:border-color var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out);}
        .island-chip:hover{border-color:var(--acc);background:color-mix(in srgb, var(--acc) 6%, var(--inp));}
        .island-chip-arrow{color:var(--acc2);font-size:11px;font-weight:600;white-space:nowrap;letter-spacing:.2px;}
        /* PR-NEWJOB-PAGE-FIT (2026-09-05): .island-more-toggle / #nji-more-section
           fold rules removed — the contact fields are always shown on the page. */
        /* PR-NEWJOB-ONE-SCREEN (2026-09-05): the one-screen New Job — wider card,
           three titled sections, segmented site-condition chips, the green
           "inherits your Shop Standards" box, and the job page's standards strip. */
        .island-card-wide{width:min(880px,96vw);max-height:92vh;}
        .island-card-wide #nji-cust{width:100%;}
        /* PR-NEWJOB-PAGE (2026-09-05): New Job as a studio PAGE (#cab-newjob is a
           .scroll sub-view like #cab-dash). Framed like the Job Standards page:
           one header line in the job-tier green, two cards side by side, the
           standards card full width under them, a sticky bottom bar. The
           island-* classes on the inner markup are kept so the engine's ids and
           the field styling are untouched; these rules only re-house them. */
        /* PR-NEWJOB-PAGE-FIT (2026-09-05, operator: "scaling and overall UX still
           look not great"): the page fills the room like Shop Standards does
           (no 1280px cap on a 1920px screen), and the type is the same size as
           the Shop Standards rows — 16px in the fields, 14px labels, 17px card
           titles — instead of the island's 13px. Chips grow to match, and the
           card padding widens so the bigger type has air. */
        /* PR-NEWJOB-FROZEN (2026-09-05): the page never scrolls. It is boxed to
           the workspace; njiFit() (app-cabinet.js) scales the content down when
           the window is short, and only past its 0.85 floor is scrolling allowed
           back in (.nji-overflow). Tighter page padding than the other .scroll
           views so a 1080p window fits at full size. */
        #cab-newjob{overflow:hidden;padding:18px 40px 14px;}
        #cab-newjob.nji-overflow{overflow-y:auto;}
        #cab-newjob .njp{max-width:1720px;margin:0 auto;display:flex;flex-direction:column;gap:18px;}
        #cab-newjob .njp-head{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;padding-bottom:12px;border-bottom:1px solid var(--bdr);}
        #cab-newjob .njp-back{flex:0 0 auto;align-self:center;font-size:14px;padding:8px 14px;}
        #cab-newjob .njp-title{font:650 30px var(--ff-ui);color:var(--tier-job,#1E7F4F);margin:0;letter-spacing:-.022em;}
        #cab-newjob .njp-title::before{content:'';display:inline-block;width:14px;height:14px;border-radius:3px;background:var(--tier-job,#1E7F4F);margin-right:12px;}
        #cab-newjob .njp-sub{flex:1 1 320px;min-width:0;margin:0;color:var(--dim);font:500 16px var(--ff-ui);}
        #cab-newjob .njp-pill{flex:0 0 auto;align-self:center;font-size:14px;font-weight:600;color:var(--tier-job,#1E7F4F);border:1px solid color-mix(in srgb, var(--tier-job,#1E7F4F) 45%, var(--bdr));border-radius:999px;padding:6px 14px;white-space:nowrap;cursor:help;}
        #cab-newjob .island-body{flex:none;overflow:visible;padding:0;}
        #cab-newjob .njp-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:22px;align-items:stretch;}
        @media (max-width:1100px){#cab-newjob .njp-grid{grid-template-columns:1fr;}}
        #cab-newjob .nji-sect{border:1px solid var(--bdr);border-radius:14px;background:var(--pn,var(--sf));padding:20px 26px;margin:0;}
        #cab-newjob .nji-h{font-size:18px;margin-bottom:4px;}
        #cab-newjob .nji-why{font-size:14px;margin-bottom:18px;}
        #cab-newjob .nji-h-hint{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:6px;border-radius:50%;border:1px solid var(--bdr);color:var(--dim);font-size:12px;font-weight:600;cursor:help;vertical-align:middle;}
        #cab-newjob .island-row{gap:16px;}
        #cab-newjob .island-field{gap:6px;margin-bottom:12px;}
        #cab-newjob .island-field label{font-size:14px;}
        #cab-newjob .island-field input,#cab-newjob .island-field textarea,#cab-newjob .island-field select{font-size:16px;padding:10px 14px;min-height:44px;border-radius:9px;width:100%;box-sizing:border-box;}
        #cab-newjob .island-field textarea{min-height:44px;height:44px;resize:vertical;}
        /* the customer typeahead sits in a positioned wrapper — it must span the field */
        #cab-newjob .island-field > div[style*="position:relative"]{width:100%;}
        #cab-newjob #nji-cust{width:100%;}
        /* chips need more room than the thickness columns beside them */
        #cab-newjob #nji-site .island-row{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);}
        #cab-newjob .island-field:last-child{margin-bottom:0;}
        #cab-newjob .nji-sub-h{margin:6px 0 14px;padding-top:16px;border-top:1px solid var(--bdr);color:var(--white);font-size:14px;font-weight:600;display:flex;align-items:baseline;gap:10px;}
        #cab-newjob .nji-sub-note{color:var(--dim);font-weight:500;font-size:13px;}
        #cab-newjob .nji-seg{gap:8px;}
        #cab-newjob .nji-seg-btn{font-size:14px;padding:7px 14px;min-height:38px;}
        #cab-newjob .nji-inline-l,#cab-newjob .island-field .nji-seg + input{margin-top:8px;}
        #cab-newjob .nji-copy-field{margin-top:6px;padding-top:16px;border-top:1px solid var(--bdr);}
        #cab-newjob .nji-copy-field label{display:flex;align-items:baseline;gap:10px;cursor:help;}
        /* PR-NEWJOB-FROZEN: the bar is the last row of a page that does not
           scroll — nothing sticky, nothing to hide behind. */
        #cab-newjob .njp-foot{position:static;margin:0;padding:14px 0 0;border-top:1px solid var(--bdr);background:none;gap:14px;}
        #cab-newjob .njp-foot .ba,#cab-newjob .njp-foot .bg{font-size:15px;padding:11px 22px;min-height:44px;}
        #cab-newjob .nji-foot-check{display:flex;align-items:center;gap:10px;margin-right:auto;color:var(--white);font-size:15px;cursor:pointer;}
        #cab-newjob .nji-foot-check input{width:18px;height:18px;accent-color:var(--acc);}
        .nji-sect{border-top:1px solid var(--bdr);padding-top:14px;margin-top:14px;}
        .nji-sect:first-child{border-top:none;padding-top:0;margin-top:0;}
        .nji-h{margin:0 0 2px;color:var(--white);font-size:14px;font-weight:600;}
        .nji-why{margin:0 0 12px;color:var(--dim);font-size:12px;line-height:1.45;}
        .nji-seg{display:flex;flex-wrap:wrap;gap:6px;}
        .nji-seg-btn{background:var(--inp);border:1px solid var(--bdr);border-radius:999px;padding:5px 12px;color:var(--txt);font-size:12.5px;font-family:inherit;cursor:pointer;}
        .nji-seg-btn:hover{border-color:var(--acc);}
        .nji-seg-btn[aria-pressed="true"]{background:var(--acc);border-color:var(--acc);color:var(--acc-ink);}
        .island-field .nji-seg + input{margin-top:6px;}
        /* PR-NEWJOB-NO-STANDARDS (2026-09-05): the .nji-inherits / .nji-pill /
           .nji-asks / .nji-details / .nji-list / .nji-loading rules for the
           "Standards for this job" card went with the card. */
        .nji-foot-note{margin-right:auto;color:var(--dim);font-size:12px;}
        .job-std-strip{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center;border:1px solid var(--ok,#3ba55d);border-left-width:4px;border-radius:8px;padding:8px 12px;margin:0 0 10px;background:color-mix(in srgb, var(--ok,#3ba55d) 10%, var(--sf));font-size:13px;color:var(--txt);line-height:1.45;}
        .job-std-strip.need{border-color:var(--warn,#f5b14c);background:color-mix(in srgb, var(--warn,#f5b14c) 12%, var(--sf));}
        .job-std-strip b{font-weight:600;}
        .job-std-strip .grow{flex:1;min-width:220px;}
        /* PR-JOB-INFO-TAB (2026-09-10, operator: "I prefer to have it be a button
           like after the job standards button"): the job's paperwork is the JOB INFO
           tab's panel — the New Job rows in 3–4 columns, inputs sized to their
           values, never a wide grey card. Hidden by attribute until the tab opens
           it; while open the job page shows only this panel + the tab strip (the
           same rule shape as #cab-job.jobstd-open in shop-standards.js). */
        .job-info{border:1px solid var(--bdr);border-radius:10px;background:var(--pn,var(--sf));padding:12px 14px 6px;margin:0 0 12px;}
        .job-info[hidden]{display:none!important;}
        #cab-job.jobinfo-open > :not(#job-info):not(#cab-job-bar){display:none!important;}
        /* The customer input sits in a position:relative wrapper for its listbox;
           without this it shrinks to its default inline width. */
        .job-info-form #ji-cust{display:block;width:100%;box-sizing:border-box;}
        .job-info-form{padding:0 2px 2px;}
        .job-info-form .nji-h{margin-bottom:8px;}
        .job-info-form .island-field{margin-bottom:10px;}
        .job-info-form .island-field input,.job-info-form .island-field select,.job-info-form .island-field textarea{padding:7px 10px;font-size:13px;}
        .job-info-form .ji-row3{grid-template-columns:repeat(3,minmax(0,1fr));}
        .job-info-form .ji-row4{grid-template-columns:repeat(4,minmax(0,1fr));}
        .job-info-form .ji-sub-h{margin:4px 0 10px;padding-top:12px;border-top:1px solid var(--bdr);color:var(--white);font-size:13px;font-weight:600;display:flex;align-items:baseline;gap:10px;}
        .job-info-form .ji-sub-note{color:var(--dim);font-weight:500;font-size:12px;}
        @media (max-width:900px){.job-info-form .ji-row3,.job-info-form .ji-row4{grid-template-columns:1fr 1fr;}}
        @media (max-width:560px){.job-info-form .ji-row3,.job-info-form .ji-row4{grid-template-columns:1fr;}}
        .job-info-actions{display:flex;align-items:center;gap:10px;margin-top:2px;flex-wrap:wrap;}
        .job-info-msg{font-size:12px;color:var(--dim);}
        .job-info-msg.err{color:var(--no);}
        .job-info-msg.ok{color:var(--ok);}
        /* UX-1a — Job Defaults tile picker for construction style.
           Three tiles side-by-side. Active tile gets amber ring + filled
           icon bg so the operator can see their commitment at a glance. */
        .jd-tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:4px;}
        /* PR-UI-19: jobs-dashboard tile — every job/project card in Hub. */
        .jd-tile{background:var(--inp);border:1px solid var(--bdr);border-radius:10px;padding:14px 10px;color:var(--txt);cursor:pointer;text-align:center;transition:transform var(--dur-quick) var(--spring-snappy),border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out);font-family:inherit;}
        .jd-tile:hover{border-color:var(--acc2);background:var(--pn-h);}
        .jd-tile.on{border-color:var(--acc2);background:color-mix(in srgb, var(--acc2) 8%, transparent);box-shadow:0 0 0 1px var(--acc2) inset;}
        .jd-tile .jd-tile-icon{font-size:28px;line-height:1;margin-bottom:6px;color:var(--acc2);}
        .jd-tile .jd-tile-label{color:var(--white);font-size:13px;font-weight:600;letter-spacing:.2px;}
        .jd-tile .jd-tile-sub{color:var(--dim);font-size:10px;margin-top:2px;}
        /* Inline unit chip on labels + help-icon hover tooltip. data-help
           text lives in HTML; CSS ::after positions a dark tooltip above
           the icon. Plain-text, no JS needed — keeps the tooltip honest. */
        .jd-unit{color:var(--dim);font-size:10px;font-weight:400;margin-left:4px;font-family:ui-monospace,monospace;}
        .jd-help{display:inline-block;margin-left:6px;color:var(--acc2);cursor:help;position:relative;font-size:11px;border:1px solid var(--acc2);border-radius:50%;width:14px;height:14px;line-height:12px;text-align:center;font-weight:600;}
        .jd-help:hover::after{content:attr(data-help);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#111;color:#fff;padding:6px 10px;border-radius:6px;font-size:11px;font-weight:400;white-space:normal;min-width:200px;max-width:320px;line-height:1.4;box-shadow:0 4px 12px rgb(var(--shadow-ink) / .5);z-index:10;}
        .jd-help:hover::before{content:"";position:absolute;bottom:calc(100% + 1px);left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:#111;z-index:10;}
        /* Conditional-visibility root: drop-into-view animation so the
           operator notices when fields appear instead of them just
           popping in silently. */
        #jd-frameframe-group{transition:opacity .15s ease,max-height .2s ease;overflow:hidden;}
        #jd-frameframe-group.jd-hidden{opacity:0;max-height:0;margin:0;pointer-events:none;}
        /* UX-1d — Cabinet Inspector override row: library-select field
           with an inheritance-state badge below. Two-tone kitchen use
           case (walnut lowers + painted uppers in one job). Industry
           convention (Cabinet Solutions, MaxCut): once overridden, the
           cabinet is isolated from that field's job default until Reset.
           Visible as an amber dot vs. a dim dot. */
        .insp-override-row{margin-bottom:10px;}
        /* PR-INH-PROVENANCE (2026-08-26): the badge now carries the SOURCE TIER as
           well as the value ("Inherits · White Melamine 3/4 Sheet (Shop Standard)"),
           which is far longer than the bare name it used to hold. Let the row wrap
           so the Why? button drops below instead of off the edge, and let the badge
           itself ellipsize in the ~336px docked inspector — the full string is on
           the badge's title, and the Why? popover has the whole ladder. inline-block
           (not inline-flex) because text-overflow does not apply to a flex box's
           anonymous text item. */
        .insp-inherit-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:4px;font-size:10px;line-height:1.3;}
        .insp-badge{display:inline-block;vertical-align:middle;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:2px 7px;border-radius:8px;font-weight:500;letter-spacing:.2px;}
        .insp-badge.insp-inherits{color:var(--dim);background:rgba(255,255,255,.04);border:1px solid var(--bdr);}
        /* PR-TIER-CHIPS (2026-09-03): badges wear the app-wide tier palette
           (tokens.css --tier-*). An inherited badge tints toward the tier
           that supplies the value (job green / shop navy; library-seed stays
           gray — it is a cold-start fallback, not a place someone set).
           "Overridden" is by definition the this-cabinet tier → purple. */
        .insp-badge.insp-inherits.insp-tier-job{color:var(--tier-job);border-color:color-mix(in srgb, var(--tier-job) 45%, var(--bdr));background:color-mix(in srgb, var(--tier-job) 10%, transparent);}
        .insp-badge.insp-inherits.insp-tier-shop{color:var(--tier-shop);border-color:color-mix(in srgb, var(--tier-shop) 45%, var(--bdr));background:color-mix(in srgb, var(--tier-shop) 10%, transparent);}
        .insp-badge.insp-overridden{color:var(--tier-cab-ink);background:var(--tier-cab);font-weight:600;}
        .insp-reset{font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid var(--bdr);background:var(--inp);color:var(--white);cursor:pointer;font-family:inherit;}
        .insp-reset:hover{background:var(--acc2);color:#1a1203;border-color:var(--acc2);}

        /* ── PR-CSS-5b (2026-05-29) — inheritance popover ───────────────
           Cascade-ladder display answering "where did this come from?"
           Backend: GET /api/projects/:p/rooms/:r/cabinets/:c/inheritance
           Spec: docs/CSS_5b_UI_SPEC.md
           Strict token compliance per [[feedback_strict_color_tokens]] —
           every color = var(--token), zero hex. */
        .insp-override-row[data-tier="cabinet"]{
            border-left:3px solid var(--acc);
            padding-left:6px;
            margin-left:-9px;
        }
        .insp-why-btn{
            font-size:10px;padding:2px 7px;border-radius:4px;
            border:1px solid var(--bdr);background:transparent;
            color:var(--dim);cursor:pointer;font-family:inherit;
            margin-left:auto;
        }
        .insp-why-btn:hover{color:var(--acc);border-color:var(--acc);}
        .inh-popover-backdrop{
            position:fixed;inset:0;background:var(--bg);opacity:0;
            transition:opacity 180ms ease;z-index:1000;pointer-events:none;
        }
        .inh-popover-backdrop[data-open="true"]{
            opacity:.68;pointer-events:auto;
        }
        .inh-popover{
            position:fixed;left:50%;bottom:0;transform:translate(-50%,100%);
            width:min(420px,calc(100vw - 32px));max-height:70vh;
            background:var(--pn);border:1px solid var(--bdr);
            border-bottom:none;border-radius:10px 10px 0 0;
            box-shadow:0 -8px 24px var(--bdr);
            transition:transform 220ms cubic-bezier(.2,.8,.2,1);
            z-index:1001;display:flex;flex-direction:column;
            font-family:inherit;
        }
        .inh-popover[data-open="true"]{transform:translate(-50%,0);}
        .inh-pop-hdr{
            padding:14px 16px 10px;
            border-bottom:1px solid var(--bdr);
            display:flex;align-items:center;justify-content:space-between;
            gap:8px;flex:0 0 auto;
        }
        .inh-pop-title{
            font-size:13px;font-weight:600;color:var(--ink);
            line-height:1.3;
        }
        .inh-pop-close{
            background:transparent;border:1px solid var(--bdr);
            color:var(--dim);width:28px;height:28px;border-radius:6px;
            font-size:16px;cursor:pointer;line-height:1;
            display:flex;align-items:center;justify-content:center;
            font-family:inherit;flex:0 0 auto;
        }
        .inh-pop-close:hover{color:var(--ink);border-color:var(--acc);}
        .inh-pop-cascade{
            list-style:none;margin:0;padding:8px 0;
            overflow-y:auto;flex:1 1 auto;
        }
        .inh-pop-row{
            display:grid;grid-template-columns:28px 80px 1fr auto;
            align-items:center;gap:10px;padding:10px 16px;
            border-left:3px solid transparent;
            font-size:12px;line-height:1.3;
        }
        .inh-pop-row.dim{color:var(--dim);}
        .inh-pop-row.active{
            border-left-color:var(--acc);
            background:var(--bg);
        }
        .inh-pop-row.active .inh-tier-name,
        .inh-pop-row.active .inh-tier-val{color:var(--ink);font-weight:600;}
        .inh-tier-num{
            width:24px;height:24px;border-radius:50%;
            border:1px solid var(--bdr);
            display:flex;align-items:center;justify-content:center;
            font-size:10px;font-weight:600;
        }
        .inh-pop-row.active .inh-tier-num{
            background:var(--acc);color:var(--bg);
            border-color:var(--acc);
        }
        .inh-tier-name{
            font-size:10px;font-weight:600;letter-spacing:.06em;
            text-transform:uppercase;
        }
        .inh-tier-val{
            font-size:12px;
            overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
        }
        .inh-set-tag{
            font-size:9px;font-weight:700;letter-spacing:.08em;
            background:var(--acc);color:var(--bg);
            padding:2px 6px;border-radius:3px;
        }
        /* PR-TIER-CHIPS (2026-09-03): the Why? ladder speaks the same tier
           colors as every pill and badge — Shop navy, Job green, cabinet
           purple (Library seed stays gray: a fallback, not a tier someone
           set). Rows already carry data-tier from the JS renderer. The
           winning row's left edge, number disc, and SET tag all take the
           winning tier's color so "who owns this value" reads at a glance. */
        .inh-pop-row[data-tier="cabinet"] .inh-tier-num,
        .inh-pop-row[data-tier="cabinet"] .inh-tier-name{color:var(--tier-cab);}
        .inh-pop-row[data-tier="cabinet"] .inh-tier-num{border-color:var(--tier-cab);}
        .inh-pop-row[data-tier="job"] .inh-tier-num,
        .inh-pop-row[data-tier="job"] .inh-tier-name{color:var(--tier-job);}
        .inh-pop-row[data-tier="job"] .inh-tier-num{border-color:var(--tier-job);}
        .inh-pop-row[data-tier="shop"] .inh-tier-num,
        .inh-pop-row[data-tier="shop"] .inh-tier-name{color:var(--tier-shop);}
        .inh-pop-row[data-tier="shop"] .inh-tier-num{border-color:var(--tier-shop);}
        .inh-pop-row.active[data-tier="cabinet"]{border-left-color:var(--tier-cab);}
        .inh-pop-row.active[data-tier="cabinet"] .inh-tier-num{background:var(--tier-cab);border-color:var(--tier-cab);color:var(--tier-cab-ink);}
        .inh-pop-row.active[data-tier="cabinet"] .inh-set-tag{background:var(--tier-cab);color:var(--tier-cab-ink);}
        .inh-pop-row.active[data-tier="job"]{border-left-color:var(--tier-job);}
        .inh-pop-row.active[data-tier="job"] .inh-tier-num{background:var(--tier-job);border-color:var(--tier-job);color:var(--tier-job-ink);}
        .inh-pop-row.active[data-tier="job"] .inh-set-tag{background:var(--tier-job);color:var(--tier-job-ink);}
        .inh-pop-row.active[data-tier="shop"]{border-left-color:var(--tier-shop);}
        .inh-pop-row.active[data-tier="shop"] .inh-tier-num{background:var(--tier-shop);border-color:var(--tier-shop);color:var(--tier-shop-ink);}
        .inh-pop-row.active[data-tier="shop"] .inh-set-tag{background:var(--tier-shop);color:var(--tier-shop-ink);}
        .inh-pop-actions{
            display:flex;gap:8px;padding:12px 16px;
            border-top:1px solid var(--bdr);flex:0 0 auto;
        }
        .inh-pop-btn{
            flex:1;padding:12px 14px;border-radius:6px;
            border:1px solid var(--bdr);background:var(--pn);
            color:var(--ink);font-size:13px;font-weight:500;
            cursor:pointer;font-family:inherit;min-height:44px;
        }
        .inh-pop-btn.primary{
            background:var(--acc);color:var(--bg);border-color:var(--acc);
        }
        .inh-pop-btn:hover{border-color:var(--acc);}
        .inh-pop-btn.primary:hover{filter:brightness(1.1);}
        @media (max-width: 480px){
            .inh-popover{width:100vw;border-radius:10px 10px 0 0;}
        }
        .drawer h2{color:var(--white);font-size:18px;margin-bottom:6px;}.drawer .sub{color:var(--dim);font-size:12px;margin-bottom:20px;}
        /* PR-AUDIT-UX-P1 (2026-05-09): WCAG 2.5.5 / shop-floor 44px tap target. Pre-PR ~32px on a gloved tablet sat below the iOS Safari touch threshold; modal close was the escape hatch when an operator opened the wrong modal. */
        .drawer-close{position:absolute;top:16px;right:16px;background:none;border:none;color:var(--dim);font-size:20px;cursor:pointer;padding:12px 16px;min-width:44px;min-height:44px;}
        .drawer-close:hover{color:var(--no);}

        /* ---- Order Table ---- */
        .order-table th{font-size:10px;padding:7px 8px;}
        .order-table td{padding:7px 8px;font-size:12px;}
        .order-table input,.order-table select{padding:5px;font-size:11px;}

        /* ---- Cutlist ---- */
        /* 2026-04-23 scaling fix: pre-wrap was wrapping the 80-char-wide
           cutlist columns when the container narrowed (split mode / narrow
           viewport), making labels collide and the report look "gone".
           `pre` preserves columns; `overflow-x:auto` adds a horizontal
           scroll when the viewport can't fit the full width. Shops that
           print the cutlist still get full width on the printed page. */
        .cutlist-out{background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:16px;font-family:'Cascadia Code','Fira Code','Courier New',monospace;font-size:11px;white-space:pre;max-height:min(80vh,720px);overflow:auto;color:var(--txt);line-height:1.5;}

        /* Door Style Builder — 2-column split with live preview */
        .ds-split{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:18px;align-items:start;}
        .ds-form{min-width:0;}
        /* Tighter vertical rhythm inside the Door Style panel so the whole batch
           setup fits on-screen without mouse travel. Targets only the form rows
           inside .ds-form to keep the rest of the app's spacing intact. */
        .ds-pn{padding:14px 18px 16px;}
        .ds-pn h3{margin-bottom:10px;}
        .ds-form .fr{margin-bottom:8px;gap:10px;}
        .ds-form .fg{margin-bottom:0;}
        .ds-form .fg label{margin-bottom:3px;font-size:10px;letter-spacing:.3px;text-transform:uppercase;}
        .ds-form .fg input,.ds-form .fg select{padding:6px 9px;font-size:12px;}
        /* Legacy .ds-preview / .ds-split rules removed 2026-04-22.
           The old Door Studio preview used a single flat-accent style on
           .ds-preview; the new drafting preview scopes under
           .ds-preview-canvas with proper weighted strokes. No remaining
           HTML carries class="ds-preview" or "ds-split" — confirmed via
           grep before deletion. */

        /* ── Frame Geometry section (Bundle 5, added 2026-04-22) ──
           Stile/rail inputs moved into Bundle 5 alongside piece entry
           per user feedback "the stile and rail widths should be with
           size entry." The section wraps the head (title + Apply Typical
           button), the guidance line (per-style defaults), and the
           4-card sr-cards grid. Tokens-only, zero hex/rgba per standing
           CSS rule. */
        .sg-frame-section {
            margin: 0 0 var(--sp-3);
            padding: var(--sp-3);
            background: color-mix(in srgb, var(--acc) 3%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--bdr) 70%, transparent);
            border-radius: var(--radius-md);
        }
        .sg-frame-section-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: var(--sp-2);
            margin-bottom: var(--sp-1);
        }
        .sg-frame-section-title {
            font-family: var(--ff-ui);
            font-size: 11px;
            font-weight: var(--fw-semibold);
            color: var(--dim);
            letter-spacing: 0.08em;
            text-transform: uppercase;
        }
        .sg-frame-apply-typical {
            font-family: var(--ff-ui);
            font-size: 11px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.04em;
            color: var(--acc);
            background: color-mix(in srgb, var(--acc) 10%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 55%, var(--bdr));
            border-radius: var(--radius-sm);
            padding: 5px 10px;
            cursor: pointer;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out);
        }
        .sg-frame-apply-typical:hover {
            background: color-mix(in srgb, var(--acc) 18%, var(--inp));
            border-color: var(--acc);
            transform: translateY(-1px);
        }
        .sg-frame-apply-typical:active {
            transform: translateY(0);
            background: color-mix(in srgb, var(--acc) 22%, var(--inp));
        }
        .sg-frame-apply-typical:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        .sg-frame-guidance {
            display: flex;
            align-items: center;
            gap: 6px;
            font-family: var(--ff-ui);
            font-size: 12px;
            color: var(--dim);
            padding: 4px 2px;
        }
        .sg-frame-guidance svg {
            color: var(--acc);
            flex-shrink: 0;
        }
        .sg-frame-guidance #sg-frame-guidance-text {
            color: color-mix(in srgb, var(--dim) 80%, var(--txt));
        }

        /* Drawer-box size-row help text (shown only when item type = DRAWER_BOX).
           Reinforces the "finished dimensions, not opening sizes" rule so
           operators don't enter opening measurements and wait for the system
           to subtract slide clearance — that's not what this form does. */
        .sg-db-size-help {
            display: flex;
            align-items: flex-start;
            gap: 6px;
            margin-top: var(--sp-3);
            padding: var(--sp-2) var(--sp-3);
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 35%, var(--bdr));
            border-radius: var(--radius-sm);
            font-family: var(--ff-ui);
            font-size: 12px;
            line-height: 1.45;
            color: color-mix(in srgb, var(--dim) 60%, var(--txt));
        }
        .sg-db-size-help svg {
            color: var(--acc);
            flex-shrink: 0;
            margin-top: 2px;
        }
        .sg-db-size-help strong {
            color: var(--txt);
            font-weight: var(--fw-semibold);
        }

        /* Stile/Rail width cards — compact but visually distinct from other form fields.
           The whole card is clickable (cursor:text) — clicking anywhere inside focuses
           and selects the input via srCardFocus(). These cards now live inside
           .sg-frame-section in Bundle 5 (retired Bundle 3 2026-04-22). */
        .sr-cards {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: var(--sp-2);
            margin: var(--sp-3) 0 var(--sp-2);
        }
        .sr-card {
            position: relative;
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: var(--sp-2) var(--sp-3);
            text-align: center;
            cursor: text;
            transition: border-color var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out);
            box-shadow: 0 1px 0 color-mix(in srgb, var(--bg) 50%, transparent) inset;
            min-height: 72px;
            display: flex;
            flex-direction: column;
            align-items: stretch;
            justify-content: flex-start;
            gap: 2px;
        }
        .sr-card:hover {
            border-color: color-mix(in srgb, var(--acc) 70%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 5%, var(--inp));
            transform: translateY(-1px);
        }
        .sr-card:focus-within {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 8%, var(--inp));
            box-shadow:
                0 0 0 1px var(--acc) inset,
                0 3px 10px color-mix(in srgb, var(--acc) 22%, transparent);
            transform: translateY(-1px);
        }
        .sr-card > input {
            background: transparent;
            border: none;
            color: var(--txt);
            font-family: var(--ff-mono);
            font-size: 17px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.01em;
            text-align: center;
            width: 100%;
            padding: 0;
            outline: none;
            cursor: text;
            /* No transition on value text per gate 12 — numeric input
               value must snap on every keystroke. */
        }
        .sr-card > input:focus {
            color: var(--acc);
        }
        .sr-label {
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 10px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            pointer-events: none;
        }
        /* sr-preview sits between the label and the Apply-to-all toggle.
           Mono face matches the input value above so the "parsed" read
           line visually belongs to the same number. */
        .sr-preview {
            font-family: var(--ff-mono);
            font-size: 11px;
        }
        .sr-toggle {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 4px;
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 9px;
            font-weight: var(--fw-medium);
            cursor: pointer;
            user-select: none;
            text-transform: uppercase;
            letter-spacing: 0.04em;
            line-height: 1;
            margin-top: auto;                 /* push to bottom of card */
            padding: 3px 4px;
            border-radius: 3px;
            transition: color var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out);
        }
        .sr-toggle:hover {
            color: var(--txt);
            background: color-mix(in srgb, var(--acc) 8%, transparent);
        }
        .sr-toggle input {
            margin: 0;
            width: 10px;
            height: 10px;
            cursor: pointer;
            accent-color: var(--acc);
        }

        /* Add Door bar — segmented pill of sizes + accent button. Grouped tight, inputs
           tinted with a subtle accent background so they pop against the form's regular fields. */
        /* Add Door bar — Bundle 5 CTA row. The last thing the operator
           touches before committing a piece. Premium, confident, unified
           with Bundle 1/2/3 picker aesthetic. Tokens only.
           Row shape: [Qty] [W"] [H"] [D" (drawer box only)] [spacer] [Add Door]
           Focus-within lifts a subtle accent halo around the whole bar
           so the operator sees "the row is active" even when focus is
           inside a 60px-wide qty field. */
        .add-door-bar {
            display: flex;
            align-items: stretch;
            flex-wrap: wrap;                  /* wraps if 4 fields + button overflow narrow panels */
            gap: 0;
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-md);
            padding: var(--sp-2);
            margin-top: var(--sp-4);
            transition: border-color var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
            box-shadow: 0 1px 0 color-mix(in srgb, var(--bg) 50%, transparent) inset;
        }
        .add-door-bar:focus-within {
            border-color: var(--acc);
            box-shadow:
                0 0 0 1px var(--acc) inset,
                0 3px 12px color-mix(in srgb, var(--acc) 25%, transparent);
        }
        .add-door-bar .ad-field {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: var(--sp-2) var(--sp-2);   /* tighter horiz padding so 4 fields fit */
            border-right: 1px solid color-mix(in srgb, var(--bdr) 80%, transparent);
            min-width: 0;
            gap: 3px;
            flex: 0 0 auto;                       /* don't shrink — wrap instead via .add-door-bar flex-wrap */
        }
        .add-door-bar .ad-field:last-of-type {
            border-right: none;
        }
        .add-door-bar .ad-field label {
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 10px;
            letter-spacing: 0.08em;
            margin: 0;
            font-weight: var(--fw-semibold);
            text-transform: uppercase;
        }
        .add-door-bar .ad-field input {
            background: var(--bg);
            border: 1.5px solid var(--bdr);
            color: var(--txt);
            font-family: var(--ff-mono);
            font-weight: var(--fw-semibold);
            font-size: 19px;
            letter-spacing: 0.01em;
            text-align: center;
            padding: var(--sp-2) var(--sp-3);
            border-radius: var(--radius-sm);
            outline: none;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
            width: 92px;                  /* tightened from 112 so 4 fields + CTA fit a 480px-min panel */
            min-height: 52px;             /* tap-target friendly + prominent */
        }
        .add-door-bar .ad-field input::placeholder {
            color: color-mix(in srgb, var(--dim) 70%, transparent);
        }
        .add-door-bar .ad-field input:hover:not(:focus) {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 3%, var(--bg));
        }
        .add-door-bar .ad-field input:focus {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 6%, var(--bg));
            color: var(--txt);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        .add-door-bar .ad-field.qty input {
            width: 60px;
        }

        /* PR-DBOX-USHAPE-PERPIECE-UI (2026-08-03): the per-piece U-shape sizing
           inputs (L Side / R Side / Ctr D) live on the SAME add-bar row as the
           box envelope (Qty/W/H/D) but read as a SECONDARY adjustment cluster:
           a divider (border-left) + a "U-shape sizing" caption + a recessed
           neutral tint form a Gestalt "common region" that sets them apart
           without pulling focus off the main dims. The inputs themselves keep
           the standard .ad-field size (NOT shrunk) so shop-floor tablet entry
           stays fast. */
        .add-door-bar .ad-ushape-group {
            display: flex;
            flex-direction: column;
            justify-content: center;
            gap: 3px;
            margin-left: 6px;
            padding: 2px 4px 2px 12px;
            border-left: 2px solid color-mix(in srgb, var(--acc) 30%, var(--bdr));
            background: color-mix(in srgb, var(--dim) 7%, transparent);
            border-radius: var(--radius-sm);
            flex: 0 0 auto;
        }
        .add-door-bar .ad-ushape-cap {
            font-family: var(--ff-ui);
            font-size: 10px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
            padding-left: 2px;
            white-space: nowrap;
            user-select: none;
        }
        .add-door-bar .ad-ushape-fields {
            display: flex;
            align-items: stretch;
        }
        /* Inner fields keep full .ad-field sizing; the group owns the outer
           padding so trim the fields' vertical padding to keep the row tidy. */
        .add-door-bar .ad-ushape-fields .ad-field {
            padding-top: 2px;
            padding-bottom: 2px;
        }

        /* Piece row click-to-select highlight + cutlist freshness banner */
        .piece-row{cursor:pointer;transition:background var(--dur-panel) var(--ease-out);}
        .piece-row:hover{background:color-mix(in srgb, var(--acc) 4%, transparent);}
        .piece-row-sel{background:color-mix(in srgb, var(--acc) 10%, transparent);box-shadow:inset 3px 0 0 var(--acc);}
        .piece-row-sel td:first-child{color:var(--acc);font-weight:700;}
        .cutlist-stale,.cutlist-fresh{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;font-size:12px;margin-bottom:8px;}
        .cutlist-stale{background:rgba(255,180,0,.08);border:1px solid rgba(255,180,0,.4);color:#ffb84d;}
        .cutlist-fresh{background:rgba(80,200,120,.06);border:1px solid rgba(80,200,120,.3);color:#7cc78b;}
        .cutlist-stale .dot,.cutlist-fresh .dot{width:8px;height:8px;border-radius:50%;flex:none;}
        .cutlist-stale .dot{background:#ffb84d;animation:pulse 2s infinite;}
        .cutlist-fresh .dot{background:#7cc78b;}
        .cutlist-stale .ba{margin-left:auto;padding:4px 12px;}
        @keyframes pulse{0%,100%{opacity:1;}50%{opacity:.45;}}
        @keyframes csp-sp{to{transform:rotate(360deg);}}

        /* Reports split layout — when both cutlist and pricing are open they sit side by side */
        /* 2026-04-23: default to stacked reports — a shop-floor tablet
           at 1024-1366 wide can't fit a readable cutlist + pricing
           side-by-side. Split mode only kicks in at 1500px+ (wide
           desktop), and even then the cutlist keeps its 11px font so
           the 80-col table stays legible. Operators hit the cutlist
           most — give it real-estate priority. */
        .do-reports{display:block;}
        .do-reports.split{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start;}
        .do-reports.split>div{min-width:0;}
        @media(max-width:1500px){.do-reports.split{grid-template-columns:1fr;}}

        /* Pricing report — invoice/quote-like table */
        .pricing-table{width:100%;border-collapse:collapse;margin-top:8px;font-size:12px;}
        .pricing-table th{text-align:left;padding:8px 10px;border-bottom:1px solid var(--bdr);color:var(--dim);font-weight:500;font-size:10px;text-transform:uppercase;letter-spacing:.4px;}
        .pricing-table td{padding:8px 10px;border-bottom:1px solid rgba(255,255,255,.04);color:var(--txt);}
        .pricing-table .num{text-align:right;font-variant-numeric:tabular-nums;color:var(--white);}
        .pricing-group-hdr{background:color-mix(in srgb, var(--acc) 6%, transparent);padding:10px 12px;border-radius:6px;margin-top:14px;display:flex;justify-content:space-between;align-items:center;}
        .pricing-group-hdr .name{color:var(--white);font-weight:600;font-size:13px;}
        .pricing-group-hdr .meta{color:var(--dim);font-size:11px;}
        .pricing-group-sub{display:flex;justify-content:space-between;padding:8px 10px;border-top:2px solid var(--bdr);color:var(--white);font-weight:600;}
        .pricing-grand{margin-top:18px;padding:14px 16px;background:linear-gradient(135deg,var(--acc),var(--acc2));color:var(--bg);border-radius:8px;display:flex;justify-content:space-between;align-items:center;font-weight:700;font-size:16px;letter-spacing:.3px;}
        .pricing-grand .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.6px;font-weight:600;opacity:.85;}
        .pricing-empty{color:var(--dim);padding:24px;text-align:center;font-size:12px;}

        /* Hub home — two studios side by side; each studio card and its recent-projects
           panel are now visually separate (gap between them, all four corners rounded).
           Color differentiation: Cabinet uses warm amber (--acc2), Door uses cool teal (--acc).
           Both colors come from theme variables so they re-skin with every theme. */
        .studios-split{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:24px;align-items:start;}
        .studio-col{display:flex;flex-direction:column;gap:14px;}
        /* PR-UI-19: tokenize .15s ease cluster. Hub home — Cabinet/Door Studio cards. */
        .studio-card{background:var(--pn);border:1px solid var(--bdr);border-radius:8px;padding:28px 24px;cursor:pointer;transition:border-color var(--dur-chrome) var(--ease-out),transform var(--dur-chrome) var(--spring-snappy),background var(--dur-chrome) var(--ease-out),box-shadow var(--dur-chrome) var(--ease-out);text-align:center;position:relative;overflow:hidden;}
        .studio-card::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:var(--studio-color,var(--acc));opacity:.85;}
        .studio-card:hover{border-color:var(--studio-color,var(--acc));background:var(--studio-tint,color-mix(in srgb, var(--acc) 4%, transparent));} /* PR-FORGE-CAB-BUTTONS: flat hover (no lift/shadow) */
        .studio-card .studio-img{font-size:44px;margin-bottom:12px;line-height:1;filter:drop-shadow(0 2px 8px rgba(0,0,0,.25));}
        .studio-card h4{color:var(--white);font-size:18px;margin:0 0 6px;font-weight:600;}
        .studio-card p{color:var(--dim);font-size:12px;margin:0 0 16px;line-height:1.5;}
        .studio-card .studio-cta{display:inline-block;color:var(--studio-color,var(--acc));font-weight:600;font-size:12px;letter-spacing:.3px;padding:6px 14px;border:1px solid var(--studio-color,var(--acc));border-radius:18px;transition:background var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);}
        .studio-card:hover .studio-cta{background:var(--studio-color,var(--acc));color:var(--bg);}
        .studio-recent{background:var(--pn);border:1px solid var(--bdr);border-radius:10px;padding:14px 18px 16px;flex:1;border-left:3px solid var(--studio-color,var(--acc));}
        .studio-recent .recent-hdr{color:var(--studio-color,var(--dim));font-size:10px;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px;font-weight:700;}
        .studio-recent .li{padding:8px 10px;}
        .studio-recent .li-t{font-size:13px;}
        .studio-recent .li-m{font-size:11px;}
        .studio-recent .empty{color:var(--dim);font-size:11px;padding:8px 10px;font-style:italic;}
        /* Studio-specific color tokens — set on the column so both card AND recent panel inherit */
        .studio-col.col-cab{--studio-color:var(--acc2);--studio-tint:color-mix(in srgb, var(--acc2) 6%, transparent);}
        .studio-col.col-door{--studio-color:var(--acc);--studio-tint:color-mix(in srgb, var(--acc) 6%, transparent);}
        @media(max-width:1000px){.studios-split{grid-template-columns:1fr;}}

        /* Cabinet Studio — hub header with New Job action on the right */
        .cab-hub-head{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;padding:6px 0;}
        /* PR-LAYOUT-JOBNAME (2026-07-29): the Job Hub title (#cab-job-name) sits in a
           nested flex next to the "← Jobs" back button. Without min-width:0 + nowrap +
           ellipsis it wraps word-by-word into a tall vertical column ("job name down the
           side") at laptop widths, where the right-hand action buttons squeeze the title.
           These rules truncate it to one line instead; min-width:0 lets the flex item
           shrink below its content width so the ellipsis engages. Applies to every page
           that reuses .cab-hub-head. The inline <div style="…flex…"> wrapper gets
           min-width:0 in index.html so the shrink propagates to the h2. */
        .cab-hub-head h2 { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
        /* PR-LAYOUT-JOBMETA (2026-07-31): the Job-Hub subtitle row (#cab-job-meta =
           "client · status" + the ▶Start-build lifecycle control) sits BELOW the h2 in
           the SAME flex title column. PR-LAYOUT-JOBNAME above only protected the h2; at
           high zoom / narrow widths the right-hand toolbar still crushed this column, so
           the subtitle char-wrapped into a vertical stack ("a/a/a/a/·/D/r/a/f/t") and the
           Start-build control overflowed and overlapped the toolbar. Fix: (1) let
           .cab-hub-head WRAP the toolbar to its own row instead of squeezing the title;
           (2) render the meta row as its own wrapping flex row so the label + control
           never char-wrap or overlap (the control drops below the label when tight). The
           title column's inline min-width:0 is dropped in index.html so its min-content
           floors it and it can't collapse (the h2 still ellipsis-truncates via its own
           nested min-width:0). Scoped to #cab-job so the door-orders reuse of
           .cab-hub-head (bench-studio.css #d-orders) is untouched. */
        #cab-job .cab-hub-head { flex-wrap: wrap; row-gap: 10px; }
        #cab-job #cab-job-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; min-width: 0; }
        /* Orders list header copy (2026-04-22 polish) */
        .hub-head-copy { min-width: 0; flex: 1; }
        .hub-head-copy h2 {
            margin: 0 0 4px;
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: var(--fs-xl);
            font-weight: var(--fw-semibold);
            letter-spacing: -0.005em;
            line-height: 1.2;
        }
        .hub-head-copy p {
            margin: 0;
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: var(--fs-sm);
            line-height: 1.5;
            max-width: 52ch;
        }
        /* Bulk checkbox label in the filter bar */
        .fb-bulk-toggle {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 12px;
            cursor: pointer;
            padding: 4px 6px;
            border-radius: 4px;
            transition: color var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out);
        }
        .fb-bulk-toggle:hover {
            color: var(--txt);
            background: color-mix(in srgb, var(--acc) 8%, transparent);
        }
        .fb-bulk-toggle input {
            cursor: pointer;
            accent-color: var(--acc);
        }
        /* Report tiles — tablet-friendly big touch targets on the Job
           Reports grid. High contrast, chunky, and hover-glow for the
           pointer-using operator. `.on` is the active/selected report. */
        /* PR-UI-19: report tile — Reports modal grid. Faster transform per
           original spec (was .08s); kept tight for tactile click-feel. */
        /* PR-CLEAN-9 / TILE-GRID-REGROUP (2026-05-31): 12 report tiles
           organized into 4 named categories. Section title sits above
           each sub-grid; sub-grid keeps the existing .rep-tile markup +
           CSS unchanged so all hover/active/on states still work. */
        /* PR-REPORTS-POLISH (2026-06-14): report tiles rebuilt as left-aligned
           cards — an accent icon chip + a bold label + a one-line description —
           so the grid reads as a scannable menu instead of a wall of unlabeled
           icons. Wider min cell to fit the description; the icon, label, and
           desc share the .rt-text column. Markup: .rep-tile > .rt-icon + .rt-text(.rt-label + .rt-desc). */
        #cab-job-report-tiles{display:flex;flex-direction:column;gap:var(--sp-4);margin-bottom:14px;}
        .rep-tile-section{display:flex;flex-direction:column;gap:var(--sp-2);}
        /* PR-REPORTS-ACCORDION (2026-08-11): the section title is now a full-width
           clickable accordion header; clicking collapses/expands its report grid.
           Big touch target for a dusty tablet finger. */
        .rep-tile-section-title{display:flex;align-items:center;gap:9px;width:100%;margin:0;padding:11px 13px;font-size:12px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--txt);background:var(--pn);border:1px solid var(--bdr);border-radius:10px;cursor:pointer;font-family:inherit;text-align:left;transition:background var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);}
        .rep-tile-section-title:hover{background:color-mix(in srgb, var(--acc) 6%, var(--pn));border-color:color-mix(in srgb, var(--acc) 55%, var(--bdr));color:var(--acc);}
        .rep-tile-section-title .rep-sec-arrow{flex:0 0 auto;font-size:11px;color:var(--acc);transition:transform var(--dur-chrome) var(--ease-out);}
        .rep-tile-section-title .rep-sec-name{flex:1 1 auto;}
        .rep-tile-section-title .rep-sec-count{flex:0 0 auto;font-size:11px;font-weight:600;color:var(--dim);background:var(--inp);border-radius:20px;padding:1px 9px;letter-spacing:0;text-transform:none;}
        .rep-tile-section.collapsed .rep-sec-arrow{transform:rotate(-90deg);}
        .rep-tile-section.collapsed .rep-tile-grid{display:none;}
        .rep-tile-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(208px,1fr));gap:10px;}
        .rep-tile{display:flex;flex-direction:row;align-items:center;gap:12px;text-align:left;padding:13px 14px;background:var(--pn);border:1px solid var(--bdr);border-radius:12px;cursor:pointer;transition:background var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out),box-shadow var(--dur-panel) var(--ease-out),transform var(--dur-quick) var(--spring-snappy);min-height:64px;color:var(--txt);font-family:inherit;}
        .rep-tile:hover{background:color-mix(in srgb, var(--acc) 6%, var(--pn));border-color:color-mix(in srgb, var(--acc) 55%, var(--bdr));transform:translateY(-1px);box-shadow:0 6px 18px rgb(var(--shadow-ink) / .14);}
        .rep-tile:active{transform:translateY(0);box-shadow:none;}
        .rep-tile.on{background:color-mix(in srgb, var(--acc) 11%, var(--pn));border-color:var(--acc);}
        /* icon chip — tinted square so the accent reads at a glance even on the
           light theme. PR-UI-28: inline SVG (currentColor) renders identically
           cross-platform (no emoji font drift). */
        .rep-tile .rt-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:10px;background:color-mix(in srgb, var(--acc) 13%, transparent);color:var(--acc);}
        .rep-tile .rt-icon svg{width:20px;height:20px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;}
        .rep-tile.on .rt-icon{background:color-mix(in srgb, var(--acc) 22%, transparent);}
        .rep-tile .rt-text{display:flex;flex-direction:column;gap:2px;min-width:0;}
        .rep-tile .rt-label{font-size:13px;font-weight:700;letter-spacing:.2px;line-height:1.2;color:var(--txt);}
        .rep-tile .rt-desc{font-size:11px;font-weight:400;line-height:1.3;color:var(--dim);}
        /* Report viewer body — shop-floor readable. Larger font than the
           rest of the UI, clean section dividers, sticky Print bar. */
        .jr-viewer{background:var(--sf);border:1px solid var(--bdr);border-radius:10px;overflow:hidden;}
        .jr-viewer-head{position:sticky;top:0;background:var(--sf);z-index:2;display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--bdr);gap:10px;flex-wrap:wrap;}
        .jr-viewer-title{color:var(--white);font-size:16px;font-weight:700;letter-spacing:.2px;}
        .jr-viewer-sub{color:var(--dim);font-size:12px;}
        .jr-viewer-body{padding:16px 18px 24px;font-size:13.5px;line-height:1.55;color:var(--txt);}
        /* PR-MAPCUT-9 (2026-08-24): the print-pack chooser. Opens on
           "Print everything" so the shop picks the sheets it actually hands
           out, against real content — each row says what is on that sheet, and
           an empty one is greyed and unticked rather than silently printed.
           <dialog> + showModal gives focus trapping and Esc for free. */
        .cw-pack-dlg {
            border: 1px solid var(--bdr);
            border-radius: 12px;
            background: var(--sf);
            color: var(--txt);
            padding: 0;
            max-width: 520px;
            width: calc(100vw - 32px);
        }
        .cw-pack-dlg::backdrop { background: rgba(0,0,0,.55); }
        .cw-pack-dlg form { padding: 20px 22px 16px; margin: 0; }
        .cw-pack-title { margin: 0 0 4px; font-size: 17px; font-weight: 700; }
        .cw-pack-sub { margin: 0 0 14px; color: var(--dim); font-size: 12.5px; line-height: 1.5; }
        .cw-pack-tools { display: flex; gap: 14px; margin-bottom: 8px; flex-wrap: wrap; }
        .cw-pack-link {
            border: 0; background: none; padding: 0;
            color: var(--acc, #5a8af0); font-size: 12px; cursor: pointer;
            text-decoration: underline; text-underline-offset: 2px;
        }
        /* PR-MAPCUT-14 (2026-08-24): the stacks a shop actually prints. Most
           print the same set every job, and it is rarely "everything" — one
           tap instead of ticking nine boxes. */
        .cw-pack-preset {
            min-height: 34px; padding: 5px 13px;
            border: 1px solid var(--bdr); border-radius: 999px;
            background: var(--pn); color: var(--txt);
            font-size: 12.5px; font-weight: 600; cursor: pointer;
        }
        .cw-pack-preset:hover { border-color: var(--acc, #5a8af0); color: var(--acc, #5a8af0); }
        .cw-pack-list {
            display: flex; flex-direction: column;
            border: 1px solid var(--bdr); border-radius: 8px;
            max-height: 46vh; overflow-y: auto;
        }
        /* 44px rows — this gets tapped on a shop-floor tablet. */
        .cw-pack-row {
            display: flex; align-items: center; gap: 10px;
            min-height: 44px; padding: 6px 12px;
            border-bottom: 1px solid var(--bdr);
            cursor: pointer; font-size: 13.5px;
        }
        .cw-pack-row:last-child { border-bottom: 0; }
        .cw-pack-name { flex: 1 1 auto; font-weight: 600; }
        .cw-pack-meta { color: var(--dim); font-size: 11.5px; white-space: nowrap; }
        .cw-pack-empty .cw-pack-name { color: var(--dim); font-weight: 500; }
        /* PR-MAPCUT-11 (2026-08-24): the chooser groups by the bench that
           cuts the parts, because that is who the sheet is handed to. The
           station heading is a sticky band so it stays visible while the
           operator scrolls a long list, with a one-tap "all" for that bench. */
        .cw-pack-group { border-bottom: 1px solid var(--bdr); }
        .cw-pack-group:last-child { border-bottom: 0; }
        .cw-pack-station {
            position: sticky; top: 0; z-index: 1;
            display: flex; align-items: center; justify-content: space-between;
            gap: 10px; padding: 7px 12px;
            background: var(--sunk, var(--pn)); border-bottom: 1px solid var(--bdr);
        }
        .cw-pack-station-name {
            font-size: 10.5px; font-weight: 700; letter-spacing: .09em;
            text-transform: uppercase; color: var(--dim);
        }
        .cw-pack-group .cw-pack-row { padding-left: 20px; }
        .cw-pack-remember {
            display: flex; align-items: center; gap: 8px;
            margin: 12px 0 0; color: var(--dim); font-size: 12.5px; cursor: pointer;
        }
        .cw-pack-actions {
            display: flex; justify-content: flex-end; gap: 10px;
            margin: 14px 0 0; padding: 0;
        }
        .cw-pack-btn {
            min-height: 40px; padding: 8px 18px;
            border: 1px solid var(--bdr); border-radius: 8px;
            background: var(--pn); color: var(--txt);
            font-size: 13.5px; font-weight: 600; cursor: pointer;
        }
        .cw-pack-btn-go { background: var(--acc, #5a8af0); border-color: transparent; color: #fff; }
        .cw-pack-btn-go:disabled { opacity: .5; cursor: default; }

        /* PR-MAPCUT-3 (2026-08-24): the banded "Classic" cut list in the job
           report viewer. A banded sheet is a fixed-width grid — its whole
           readability is columns landing under their headers — so the font
           MUST be monospace and the whitespace MUST be preserved. Stated
           explicitly rather than inherited, because a proportional fallback
           destroys the alignment silently.

           The sheet is wider than the panel on a split view, so it scrolls
           HORIZONTALLY in its own box rather than wrapping: a wrapped cut-list
           row is unreadable, whereas a scrolled one is merely inconvenient.
           Colours go through tokens per the standing rule. */
        .jr-banded{
            font-family:"Consolas","DejaVu Sans Mono","Courier New",monospace;
            font-size:12px;line-height:1.35;white-space:pre;
            overflow-x:auto;margin:0;padding:14px 16px;
            background:var(--pn);border:1px solid var(--bdr);border-radius:8px;
            color:var(--txt);
        }
        /* Notes above the sheet: an auto-added column explained, or a list
           that has outgrown paper. Advisory, so they read quieter than a
           warning banner but are still impossible to miss. */
        .jr-note{
            margin:0 0 8px;padding:8px 11px;border-radius:7px;font-size:12.5px;
            background:var(--pn);border:1px solid var(--bdr);color:var(--dim);
        }
        .jr-note-warn{border-color:var(--warn,#e0a52b);color:var(--warn,#e0a52b);}
        .jr-section{margin-top:20px;}
        .jr-section:first-child{margin-top:4px;}
        .jr-section h4{margin:0 0 8px;color:var(--white);font-size:14px;letter-spacing:.3px;text-transform:uppercase;border-bottom:1px solid var(--bdr);padding-bottom:6px;}
        .jr-table{width:100%;border-collapse:collapse;font-size:13px;}
        .jr-table th{text-align:left;color:var(--dim);font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:.6px;padding:8px 10px;border-bottom:1px solid var(--bdr);background:var(--pn);position:sticky;top:0;}
        .jr-table td{padding:9px 10px;border-bottom:1px solid rgba(255,255,255,0.04);}
        .jr-table tr:hover td{background:rgba(255,255,255,0.02);}
        .jr-table .num{text-align:right;font-variant-numeric:tabular-nums;color:var(--white);font-weight:600;}
        .jr-table .sku{color:var(--dim);font-size:11px;font-family:'Consolas','Courier New',monospace;}
        .jr-empty{color:var(--dim);padding:24px;text-align:center;font-size:13px;}
        .jr-footer{color:var(--dim);font-size:11px;padding:10px 16px;border-top:1px solid var(--bdr);background:var(--pn);}
        /* View-mode pill: "Combined" vs. "Split". One glance, no nested menus.
           Combined = single printable doc; Split = each section prints alone.
           The active segment uses the amber accent so the operator sees at a
           glance which view is live. */
        .jr-view-toggle{display:inline-flex;background:var(--inp);border:1px solid var(--bdr);border-radius:999px;padding:2px;gap:0;}
        .jr-view-toggle button{background:transparent;border:none;color:var(--dim);font-size:12px;font-weight:600;letter-spacing:.2px;padding:5px 14px;border-radius:999px;cursor:pointer;transition:background var(--dur-panel) var(--ease-out),color var(--dur-panel) var(--ease-out);}
        .jr-view-toggle button.on{background:var(--acc2);color:#1a1203;}
        .jr-view-toggle button:not(.on):hover{color:var(--white);}
        /* Per-section Print button shows only in Split view. Positioned in the
           section's h4 row so the operator can fire a print for just that
           category (doors-only, hardware-only, etc.) without toggling modes. */
        .jr-section-print{float:right;font-size:11px;padding:3px 10px;border-radius:4px;border:1px solid var(--bdr);background:var(--inp);color:var(--white);cursor:pointer;margin-top:-3px;}
        .jr-section-print:hover{background:var(--acc2);color:#1a1203;border-color:var(--acc2);}
        /* PR-CNC-DOWNLOADS (2026-06-14): machine-file download strip on the CNC
           report — native exports (.CID / .CIX / .MPR / G-code / bore CSV) that
           the operator drops straight onto the controller. Screen-only (hidden
           in print). Each button auth-fetches the server export → Blob → save. */
        .jr-dl{margin:0 0 6px;padding:14px 16px;background:color-mix(in srgb, var(--acc) 5%, var(--pn));border:1px solid var(--bdr);border-radius:10px;}
        .jr-dl-head{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--dim);margin-bottom:4px;}
        .jr-dl-note{font-size:11.5px;color:var(--dim);line-height:1.4;margin-bottom:11px;}
        .jr-dl-grid{display:flex;flex-wrap:wrap;gap:8px;}
        .jr-dl-btn{display:inline-flex;align-items:center;gap:7px;padding:9px 14px;background:var(--inp);border:1px solid var(--bdr);border-radius:9px;color:var(--txt);font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;transition:background var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out),transform var(--dur-quick) var(--spring-snappy);}
        .jr-dl-btn:hover{background:color-mix(in srgb, var(--acc) 12%, var(--inp));border-color:var(--acc);transform:translateY(-1px);}
        .jr-dl-btn:active{transform:translateY(0);}
        .jr-dl-btn .jr-dl-ico{flex:0 0 auto;color:var(--acc);font-size:14px;line-height:1;}
        .jr-dl-btn .jr-dl-ext{color:var(--dim);font-weight:400;font-size:11px;}
        /* When printing a single section we add `jr-print-solo` to the viewer
           body and `jr-print-me` to the target section; @media print rules
           hide every other section so only the chosen one paginates. */
        /* Print — every Job Report uses this stylesheet when the operator
           hits the Print button. Strips dark theme, uses crisp black-on-
           white, keeps tables together on a page. */
        @media print{
            body *{visibility:hidden;}
            #jr-print-area,#jr-print-area *{visibility:visible;}
            #jr-print-area{position:absolute;top:0;left:0;width:100%;background:white;color:black;padding:20px;}
            #jr-print-area .jr-viewer{border:none;}
            #jr-print-area .jr-viewer-head{border-bottom:2px solid black;padding:0 0 12px 0;margin-bottom:12px;position:static;background:white;}
            #jr-print-area .jr-viewer-title{color:black;}
            #jr-print-area .jr-viewer-sub{color:#444;}
            #jr-print-area .jr-section h4{color:black;border-bottom:1px solid #666;}
            #jr-print-area .jr-table th{background:white;color:black;border-bottom:2px solid black;}
            #jr-print-area .jr-table td{color:black;border-bottom:1px solid #ccc;}
            #jr-print-area .jr-table .num{color:black;}
            #jr-print-area .jr-table .sku{color:#555;}
            #jr-print-area .jr-footer{background:white;color:#444;border-top:1px solid #888;}
            #jr-print-area .jr-print-btn,#jr-print-area .jr-close-btn{display:none;}
            #jr-print-area .jr-view-toggle,#jr-print-area .jr-section-print,#jr-print-area .jr-dl{display:none;}
            .jr-section{page-break-inside:avoid;}
            /* Single-section print: hide siblings so Print-this-section
               emits only the chosen category. `.jr-print-me` is the one
               to keep; everyone else in `.jr-print-solo` is suppressed. */
            #jr-print-area.jr-print-solo .jr-section{display:none;}
            #jr-print-area.jr-print-solo .jr-section.jr-print-me{display:block;}
            /* PR-REPORT-PRINT-CLEAN (2026-08-01): close the remaining gaps so
               EVERY report (Cutlist / Hardware BOM / Rip List / Lumber / Trim
               Kit / Tops / CNC) prints as a clean shop sheet, not a scroll-
               clipped dark-chrome dump.
               (1) Let the report expand to full height — the on-screen viewer
               is a bordered scroll box (overflow:hidden); for print it must
               grow to its natural height so nothing past the viewport clips. */
            #jr-print-area,
            #jr-print-area .jr-viewer,
            #jr-print-area .jr-viewer-body{overflow:visible!important;max-height:none!important;height:auto!important;}
            /* (2) Table pagination: a long list must be ALLOWED to break across
               pages (break-inside:auto), but never split a single ROW, and the
               column header must reprint at the top of each page so the shop
               floor can read a continued table. */
            #jr-print-area .jr-table{break-inside:auto;page-break-inside:auto;}
            #jr-print-area .jr-table thead{display:table-header-group;}
            #jr-print-area .jr-table tr{break-inside:avoid;page-break-inside:avoid;}
            #jr-print-area .jr-table td,#jr-print-area .jr-table th{background:white;}
            /* (3) Start each top-level report section on its own page so each
               list (Doors, Box parts, Hardware, Vendor totals, …) reads
               cleanly. Scoped to the DIRECT section children of the body, skips
               the first (no leading blank page), and is suppressed in the
               single-section "Print this section" (solo) flow. Sections still
               keep together where they fit (page-break-inside handled per
               `.jr-section` above). */
            #jr-print-area:not(.jr-print-solo) .jr-viewer-body > .jr-section + .jr-section{break-before:page;page-break-before:always;}
            #jr-print-area .jr-viewer-body > .jr-section{page-break-inside:auto;}
            /* (4) Keep the cutlist WARNINGS banner on the printout (the shop
               wants it) but repaint it as a clean black-on-white bordered box.
               The banner + its rows carry INLINE dark colours (var(--pn) bg,
               severity text colours), so these overrides need !important to
               win. Errors stay a dark red so a "DO NOT CUT" block is still
               unmistakable in monochrome; everything else prints black. */
            #jr-print-area .jr-warning-banner{background:white!important;border:2px solid #333!important;color:black!important;break-inside:avoid;page-break-inside:avoid;}
            #jr-print-area .jr-warning-banner *{color:black!important;}
            #jr-print-area .jr-warning-banner li[style*="--no"],
            #jr-print-area .jr-warning-banner div[style*="--no"]{color:#8a1c1c!important;}
            /* PR-3D-P3-svg-fix-2 (Slice 10): print-all-walls container.
               When `jr-print-active` is set, this div takes over the
               printed page (same body * { visibility: hidden } pattern
               as #jr-print-area). Each `.rep-el-print-page` child is a
               wall elevation SVG; CSS page-break-before keeps walls on
               separate sheets. Letter portrait orientation matches
               typical AWI cabinet-elevation paper convention. */
            #reports-print-all-area.jr-print-active,
            #reports-print-all-area.jr-print-active *{visibility:visible;}
            #reports-print-all-area.jr-print-active{position:absolute;top:0;left:0;width:100%;background:white;}
            #reports-print-all-area.jr-print-active .rep-el-print-page{padding:0.4in;}
            @page{size:letter portrait;margin:0;}
        }
        /* Recent-jobs cards with amber accent (matches the cabinet studio color token) */
        #cab-plist .li,#cab-job-rooms .li{transition:background .12s ease,border-color .12s ease;}
        #cab-plist .li:hover{background:color-mix(in srgb, var(--acc2) 6%, transparent);border-color:var(--acc2);}
        #cab-job-rooms .li:hover{background:color-mix(in srgb, var(--acc2) 6%, transparent);border-color:var(--acc2);cursor:pointer;}
        #cab-plist .cab-job-card{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;margin-bottom:8px;cursor:pointer;border-left:3px solid var(--acc2);transition:transform .12s ease,border-color .12s ease,background .12s ease;}
        #cab-plist .cab-job-card:hover{background:color-mix(in srgb, var(--acc2) 5%, transparent);border-color:var(--bdr-h);}
        #cab-plist .cab-job-card .left{flex:1;min-width:0;}
        #cab-plist .cab-job-card .job-name{color:var(--white);font-weight:600;font-size:14px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
        #cab-plist .cab-job-card .job-meta{color:var(--dim);font-size:11px;margin-top:2px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
        #cab-plist .cab-job-card .job-stats{color:var(--acc2);font-size:11px;font-weight:600;padding:4px 10px;background:color-mix(in srgb, var(--acc2) 10%, transparent);border-radius:12px;flex:none;}

        /* ═══════════════════════════════════════════════════════════════════
           STUDIO DASHBOARD COCKPIT — table-driven per-customer command center.
           Breadcrumbs, compressed metrics ribbon, filter bar, jobs data table,
           QR modal, skeleton loader. Designed for shop-floor speed + density.
           ═══════════════════════════════════════════════════════════════════ */
        /* Breadcrumb trail (Main Hub > Customers > {Name}) */
        .crumbs{display:flex;align-items:center;gap:6px;color:var(--dim);font-size:12px;font-weight:500;letter-spacing:.2px;margin-bottom:6px;}
        .crumbs a{color:var(--dim);text-decoration:none;cursor:pointer;transition:color var(--dur-panel) var(--ease-out);}
        .crumbs a:hover{color:var(--acc);}
        .crumbs .sep{color:var(--bdr);}
        .crumbs .here{color:var(--white);}

        /* New-job CTA — vibrant teal pill, top-right of dashboard header */
        .cab-new-job-btn {
            padding: var(--sp-3) var(--sp-6);
            min-height: 48px;
            font-family: var(--ff-ui);
            font-size: var(--fs-base);
            font-weight: var(--fw-bold);
            letter-spacing: 0.03em;
            background: var(--acc);
            color: var(--acc-ink);
            border: 1.5px solid var(--acc);
            border-radius: var(--radius-sm);
            cursor: pointer;
            box-shadow: 0 3px 12px color-mix(in srgb, var(--acc) 28%, transparent);
            transition: background var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out),
                        filter var(--dur-chrome) var(--ease-out);
        }
        .cab-new-job-btn:hover {
            filter: brightness(1.06);
            transform: translateY(-1px);
            box-shadow: 0 5px 18px color-mix(in srgb, var(--acc) 40%, transparent);
        }
        .cab-new-job-btn:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 80%, transparent);
            outline-offset: 3px;
        }

        /* PR-CLEAN-8 / JOB-HEADER-REWORK (2026-05-31): action cluster on the
           Design Dashboard header. Standards chip (quick-jump shortcut) +
           ⋯ More menu (less-used actions: import / archive / templates) +
           primary New Job CTA. Every color via var(--token); backdrop
           and shadows via color-mix per [[feedback_strict_color_tokens]]. */
        .cab-std-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;color:var(--dim);font-size:12px;font-weight:500;cursor:pointer;font-family:inherit;transition:color var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out);}
        .cab-std-chip:hover{color:var(--acc);border-color:var(--acc);background:color-mix(in srgb, var(--acc) 8%, var(--inp));}
        .cab-std-chip svg{flex-shrink:0;color:var(--acc);}
        .cab-more-btn{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;color:var(--dim);cursor:pointer;font-size:16px;line-height:1;font-family:inherit;transition:color var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out);}
        .cab-more-btn:hover{color:var(--acc);border-color:var(--acc);background:color-mix(in srgb, var(--acc) 8%, var(--inp));}
        .cab-more-btn[aria-expanded="true"]{color:var(--acc);border-color:var(--acc);}
        .cab-more-menu{position:absolute;top:calc(100% + 6px);right:0;min-width:220px;background:var(--sf);border:1px solid var(--bdr);border-radius:10px;box-shadow:0 12px 36px color-mix(in srgb, var(--bg) 70%, transparent);padding:6px;z-index:50;display:flex;flex-direction:column;gap:2px;}
        .cab-more-menu[hidden]{display:none;}
        .cab-more-item{display:flex;align-items:center;gap:10px;padding:9px 12px;background:transparent;border:none;border-radius:6px;color:var(--txt);font-size:13px;font-weight:500;font-family:inherit;cursor:pointer;text-align:left;transition:background var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out);}
        .cab-more-item:hover{background:var(--inp);color:var(--acc);}
        .cab-more-icon{flex-shrink:0;font-size:14px;width:18px;text-align:center;}

        /* Account-type chip next to customer name */
        .acct-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;font-size:11px;color:var(--dim);font-weight:500;margin-left:8px;}
        .acct-chip .dot{width:6px;height:6px;border-radius:50%;background:var(--ok);}

        /* Compressed metrics ribbon — single horizontal row, clickable cells */
        .metrics-ribbon{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1px;background:var(--bdr);border:1px solid var(--bdr);border-radius:12px;overflow:hidden;margin:14px 0;}
        .metric-cell{background:var(--pn);padding:12px 16px;cursor:pointer;display:flex;flex-direction:column;gap:2px;transition:background var(--dur-panel) var(--ease-out);position:relative;}
        .metric-cell:hover{background:var(--inp);}
        .metric-cell.active{background:color-mix(in srgb, var(--acc) 8%, transparent);box-shadow:inset 0 -2px 0 var(--acc);}
        .metric-cell .mv{font-size:24px;font-weight:600;color:var(--white);font-variant-numeric:tabular-nums;line-height:1.1;}
        .metric-cell .ml{font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:var(--dim);font-weight:600;}
        .metric-cell .mctx{font-size:11px;color:var(--warn);font-weight:500;margin-top:2px;}
        .metric-cell .mctx.muted{color:var(--dim);}
        .metric-cell .mctx.good{color:var(--ok);}
        .metric-cell .mi{position:absolute;top:10px;right:10px;width:14px;height:14px;color:var(--dim);opacity:.5;}

        /* Search + filter bar above the jobs table */
        .filter-bar{display:flex;gap:10px;align-items:center;background:var(--pn);border:1px solid var(--bdr);border-radius:10px;padding:10px 12px;margin-bottom:14px;flex-wrap:wrap;}
        .filter-bar .fb-search{flex:1;min-width:200px;display:flex;align-items:center;gap:8px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:6px 10px;}
        .filter-bar .fb-search input{flex:1;background:transparent;border:none;outline:none;color:var(--txt);font-family:inherit;font-size:13px;}
        .filter-bar .fb-search input::placeholder{color:var(--dim);}
        .filter-bar .fb-search svg{width:14px;height:14px;color:var(--dim);}
        .filter-bar select{background:var(--inp);border:1px solid var(--bdr);color:var(--txt);font-family:inherit;font-size:12px;padding:7px 10px;border-radius:8px;cursor:pointer;outline:none;}
        .filter-bar select:focus{border-color:var(--acc);}
        .filter-bar .fb-label{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.5px;font-weight:600;}

        /* Jobs data table — compact, scannable, hover-row accent */
        .jobs-table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;}
        .jobs-table thead th{text-align:left;color:var(--dim);font-size:10px;font-weight:600;letter-spacing:.6px;text-transform:uppercase;padding:8px 10px;border-bottom:1px solid var(--bdr);background:var(--pn);position:sticky;top:0;z-index:1;cursor:pointer;user-select:none;}
        .jobs-table thead th .sort-ind{color:var(--acc);font-size:10px;margin-left:4px;}
        .jobs-table tbody tr{cursor:pointer;transition:background var(--dur-quick) var(--ease-out),border-color var(--dur-quick) var(--ease-out);border-left:3px solid transparent;}
        .jobs-table tbody tr:hover{background:var(--inp);border-left-color:var(--acc);}
        .jobs-table tbody td{padding:9px 10px;border-bottom:1px solid var(--bdr);color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
        .jobs-table tbody tr:last-child td{border-bottom:none;}
        .jobs-table .col-name{font-weight:500;color:var(--txt);display:flex;align-items:center;gap:8px;min-width:0;}
        .jobs-table .col-name svg{width:14px;height:14px;color:var(--acc);flex-shrink:0;}
        .jobs-table .col-name .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
        .jobs-table .col-client{color:var(--dim);font-size:12px;}
        .jobs-table .col-progress{color:var(--dim);font-size:12px;font-variant-numeric:tabular-nums;display:flex;gap:10px;align-items:center;}
        .jobs-table .col-progress .pg-item{display:inline-flex;align-items:center;gap:4px;}
        .jobs-table .col-progress svg{width:12px;height:12px;color:var(--dim);}
        .jobs-table .col-when{color:var(--dim);font-size:12px;font-variant-numeric:tabular-nums;font-family:'Inter',ui-monospace,monospace;}
        .jobs-table .col-value{color:var(--white);font-size:12px;font-variant-numeric:tabular-nums;font-weight:500;text-align:right;font-family:'Inter',ui-monospace,monospace;}
        .jobs-table .col-actions{text-align:right;white-space:nowrap;}
        /* PR-RENDER-DAY1-POLISH (2026-05-08): WCAG 2.5.5 minimum
           touch target = 44×44 CSS px. Pre-PR ic-btn was 26×26 — way
           below floor, AND fires destructive actions (delete job /
           duplicate). Bumped to 44×44. The icon SVG inside stays at
           14×14 so visual weight is unchanged; only the hit area grows. */
        .jobs-table .col-actions .ic-btn{background:transparent;border:none;color:var(--dim);width:44px;height:44px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:14px;line-height:1;transition:color var(--dur-quick) var(--ease-out),background var(--dur-quick) var(--ease-out);}
        .jobs-table .col-actions .ic-btn:hover{color:var(--acc);background:var(--bg);}
        .jobs-table .col-actions .ic-btn svg{width:14px;height:14px;}
        /* PR-DRAW-CUSTOMER (2026-08-06): per-row "Shop drawings" / "Customer
           drawings" quick-open buttons — 44px min touch target (shop tablet),
           compact labels, accent border on the customer copy. */
        .jobs-table .col-actions .dwg-row-btn{min-height:44px;padding:6px 10px;margin:2px 4px 2px 0;border:1px solid var(--bdr);background:var(--pn);color:var(--txt,#e6e6e6);border-radius:6px;cursor:pointer;font-size:11px;font-weight:600;white-space:nowrap;vertical-align:middle;transition:color var(--dur-quick) var(--ease-out),background var(--dur-quick) var(--ease-out),border-color var(--dur-quick) var(--ease-out);}
        .jobs-table .col-actions .dwg-row-btn:hover{color:var(--acc);border-color:var(--acc);background:var(--bg);}
        .jobs-table .col-actions .dwg-row-btn-cust{border-color:var(--acc);}

        /* Status pills tuned for studio status set */
        .pill-st-draft{background:color-mix(in srgb, var(--acc) 12%, transparent);color:#00E5C0;}
        .pill-st-design{background:rgba(255,204,92,0.15);color:#FFCC5C;}
        .pill-st-production{background:rgba(74,144,226,0.18);color:#4A90E2;}
        .pill-st-delivered{background:rgba(170,170,170,0.15);color:#AAAAAA;}
        .pill-st-completed{background:var(--lime);color:var(--lime-ink);}
        .pill-st-archived{background:rgba(170,170,170,0.10);color:#888888;}

        /* Inline action menu (3-dot dropdown) */
        .row-menu{position:absolute;background:var(--sf);border:1px solid var(--bdr);border-radius:8px;box-shadow:0 16px 40px rgb(var(--shadow-ink) / 0.55);padding:4px;z-index:50;min-width:160px;}
        .row-menu button{display:block;width:100%;text-align:left;background:transparent;border:none;color:var(--txt);font-family:inherit;font-size:12px;padding:7px 12px;border-radius:6px;cursor:pointer;}
        .row-menu button:hover{background:var(--inp);color:var(--acc);}
        .row-menu button.danger:hover{background:rgba(255,77,77,0.12);color:var(--no);}

        /* Skeleton loader — same row geometry as data rows so the layout doesn't shift */
        .skel-row td{padding:9px 10px;border-bottom:1px solid var(--bdr);}
        .skel-bar{height:12px;border-radius:4px;background:linear-gradient(90deg,var(--inp) 0%,var(--bdr) 50%,var(--inp) 100%);background-size:200% 100%;animation:skel-shimmer 1.4s ease-in-out infinite;}
        @keyframes skel-shimmer{0%{background-position:200% 0;}100%{background-position:-200% 0;}}
        @keyframes lib-spin{to{transform:rotate(360deg);}}
        .skel-w-30{width:30%;}.skel-w-50{width:50%;}.skel-w-70{width:70%;}.skel-w-90{width:90%;}

        /* Empty state — illustrative icon + helpful prompt */
        .empty-state{text-align:center;padding:42px 20px;color:var(--dim);}
        .empty-state .ei{font-size:38px;opacity:.4;margin-bottom:10px;}
        .empty-state .et{color:var(--white);font-size:14px;font-weight:500;margin-bottom:4px;}
        .empty-state .es{font-size:12px;line-height:1.5;max-width:300px;margin:0 auto 14px;}

        /* QR modal */
        .qr-modal{position:fixed;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);z-index:300;display:flex;align-items:center;justify-content:center;}
        .qr-modal.hidden{display:none;}
        .qr-modal .qr-panel{background:var(--sf);border:1px solid var(--bdr);border-radius:14px;padding:24px;max-width:340px;width:90vw;text-align:center;}
        .qr-modal h3{margin:0 0 6px;color:var(--white);font-size:16px;}
        .qr-modal .sub{color:var(--dim);font-size:12px;margin-bottom:16px;}
        .qr-modal .qr-img{background:#fff;padding:14px;border-radius:10px;display:inline-block;margin-bottom:16px;}
        .qr-modal .qr-img canvas,.qr-modal .qr-img svg{display:block;}
        .qr-modal .qr-url{font-family:ui-monospace,monospace;font-size:10px;color:var(--dim);background:var(--bg);padding:6px 10px;border-radius:6px;margin-bottom:14px;word-break:break-all;}

        /* PR-NEWROOM-REWORK (2026-07-17): the shape picker is a COMPACT wrapping STRIP of
           small tiles (a toolbar) instead of a big grid — it collapses the shapes section
           so the live PLAN CANVAS becomes the hero of the modal. The description moves to
           the tile's hover title. */
        .wall-preset-grid{display:flex;flex-wrap:wrap;gap:8px;}
        .wall-preset{flex:0 0 auto;width:100px;background:var(--inp);border:2px solid var(--bdr);border-radius:8px;padding:8px 6px;cursor:pointer;text-align:center;transition:border-color var(--dur-chrome) var(--ease-out),transform var(--dur-chrome) var(--spring-snappy),background var(--dur-chrome) var(--ease-out);}
        .wall-preset:hover{border-color:var(--acc2);background:var(--pn-h);}
        .wall-preset.sel{border-color:var(--acc2);background:color-mix(in srgb, var(--acc2) 8%, transparent);}
        .wall-preset svg{width:44px;height:34px;margin-bottom:3px;overflow:visible;}
        /* Shape-gallery thumbnails as thick architectural poché walls (match the plan). */
        .wall-preset svg [stroke-width="3"]{stroke:var(--plan-poche);stroke-width:6.5;stroke-linejoin:round;stroke-linecap:round;}
        .wall-preset.sel svg [stroke-width="3"]{stroke:var(--acc2);}
        .wall-preset .name{color:var(--white);font-size:11px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
        .wall-preset .desc{display:none;} /* → the tile's hover title now */
        /* Roomier type / fields; the plan is the hero, so shapes stay compact even in the modal. */
        /* PR-NEWROOM-HDR-AIR (2026-09-02): the ✕ sits centered on the taller
           header band (title line ≈ 22-48px) instead of hugging the corner. */
        .nr-card .island-close{top:20px;right:18px;}
        .nr-card h3{font-size:14.5px;font-weight:700;margin-top:16px;}
        .nr-card p{font-size:12px;line-height:1.5;}
        .nr-card .fg > label{font-size:11.5px;letter-spacing:.3px;}
        .nr-card input[type=text],.nr-card input[type=number],.nr-card select{font-size:14.5px;padding:9px 12px;min-height:42px;}
        .nr-card .nr-preview-panel{padding:12px;}
        /* PR-NEWROOM-SLIM (2026-09-02): compact tiles/cards so every wizard page fits
           the rail without scrolling on an 850px-tall window. Tiles stay >44px tall —
           still honest tablet tap targets. */
        /* PR-NEWROOM-ONE-SCREEN: tighter still — the one-column rail carries every
           section at once, so the tiles run 4-up (~56px each, still >44px tap). */
        .nr-card .wall-preset{width:auto;padding:5px 3px;}
        .nr-card .wall-preset svg{width:32px;height:22px;}
        .nr-card .wall-preset .name{font-size:9.5px;}
        .nr-card .wall-preset-grid{grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:6px;}
        .nr-card .wall-len-card{padding:8px 10px;}
        .nr-card .wall-len-card label{font-size:11px;}
        .nr-card .wall-len-card input{font-size:18px;}
        /* PR-NEWROOM-REWORK (2026-07-17): canvas-first — the plan is the HERO, inputs a compact left rail. */
        @media (min-width:900px){.nr-card .nr-edit-row{grid-template-columns:minmax(0,300px) minmax(0,1fr);}}
        /* Per-wall length inputs — one tile per wall in the selected preset */
        .wall-lens-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;}
        /* PR-NEWROOM-REWORK: compact inputs LEFT, dominant live plan RIGHT (was 1fr/300px = form-first). */
        .nr-edit-row{display:grid;grid-template-columns:minmax(0,320px) minmax(0,1fr);gap:14px;align-items:start;}
        .nr-preview-panel{border:1px solid var(--bdr);border-radius:8px;padding:8px;position:sticky;top:8px;}
        /* Stack on narrow screens with the CANVAS FIRST (order:-1) — you see the room, then the inputs. */
        @media (max-width:620px){.nr-edit-row{grid-template-columns:1fr;}.nr-edit-row .nr-preview-panel{order:-1;}}
        /* PR-NEWROOM-REWORK (2026-07-17): the TWO-PANE canvas-first SHELL — a compact
           controls rail (left) + the live plan canvas as the hero (right), sticky footer.
           The modal is a flex column (header / .nr-shell / footer); .nr-shell is the grid. */
        .nr-shell{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:minmax(268px,288px) minmax(0,1fr);overflow:hidden;}
        .nr-rail{overflow-y:auto;padding:16px 20px;border-right:1px solid var(--bdr);}
        .nr-rail h3{margin-top:14px;margin-bottom:6px;}
        .nr-canvas-pane{overflow:hidden;padding:14px 18px;display:flex;flex-direction:column;min-height:0;background:color-mix(in srgb, var(--bg) 55%, transparent);}
        .nr-canvas-pane .nr-preview-panel{position:static;top:auto;}       /* not sticky inside the pane */
        /* PR-NEWROOM-REWORK (2026-07-17): make the plan FILL the pane (was letterboxed to
           58vh / calc(92vh-195px) by the shared SVG string). The SVG has a fixed 440×360
           viewBox + default preserveAspectRatio="xMidYMid meet", so height:100% fills one
           axis and centers — no distortion, no math change. Scoped to the creation canvas
           so the in-room inspector's SVG (which sits beside the 3D view) is untouched. The
           wgPlanSvg wrapper div must flex to give the SVG a real height box. */
        .nr-canvas-pane #nr-preview{display:flex;flex-direction:column;min-height:0;}
        .nr-canvas-pane #nr-preview > div{flex:1 1 auto;min-height:0;}
        .nr-canvas-pane #nr-preview svg{max-height:none !important;width:100% !important;height:100% !important;}
        @media (max-width:760px){
            .nr-card .nr-shell{grid-template-columns:1fr;grid-template-rows:minmax(240px,38vh) 1fr;}
            .nr-canvas-pane{order:-1;border-bottom:1px solid var(--bdr);border-right:none;}
            .nr-rail{border-right:none;}
        }
        .wall-len-card{background:var(--inp);border:1px solid var(--bdr);border-radius:6px;padding:8px 10px;border-left:3px solid var(--acc2);}
        .wall-len-card label{display:block;color:var(--dim);font-size:10px;text-transform:uppercase;letter-spacing:.4px;margin-bottom:3px;font-weight:600;}
        .wall-len-card input{width:100%;padding:5px 7px;font-size:13px;font-weight:600;background:transparent;border:1px solid transparent;color:var(--white);border-radius:4px;text-align:center;}
        .wall-len-card input:focus{background:color-mix(in srgb, var(--acc2) 8%, transparent);border-color:var(--acc2);outline:none;}
        .wall-len-card .unit{color:var(--dim);font-size:10px;margin-left:3px;}
        /* PR-ROOM-DESIGNER-PRO (2026-07-16): architect's-drawing palette for the plan
           canvas — theme-INDEPENDENT "paper" so the plan reads the same in every app
           theme. Grounded in ISO 128 line-weight hierarchy + architectural poché. */
        :root{
          --plan-paper:#F6F5F1; --plan-floor:#FBFAF7; --plan-grid:#DCE0E4; --plan-grid-maj:#C4CBD2;
          --plan-poche:#6E7378; --plan-ink:#1A1D20; --plan-face-int:#0E9F8E;
          --plan-dim:#4A4F55; --plan-dim-text:#20262C; --plan-dim-edit:#C0392B;
          --plan-door:#B45309; --plan-glass:#2E86AB; --plan-slope:#D98A1F;
          --plan-handle:#1A1D20; --plan-handle-core:#FFFFFF; --plan-sel:#E0A73A; --plan-snap:#22C58B;
        }
        /* PR-ROOM-DESIGNER (2026-07-16): tap-to-edit dimension chips on the plan
           canvas + the inline numeric editor that opens over a tapped chip. */
        .wg-dim rect{transition:fill-opacity var(--dur-chrome) var(--ease-out),stroke-opacity var(--dur-chrome) var(--ease-out);}
        .wg-dim:hover rect{fill-opacity:0.96;stroke-opacity:0.95;}
        .wg-dim:hover text{fill:var(--acc);}
        .wg-ang circle{transition:stroke var(--dur-chrome) var(--ease-out);}
        .wg-ang:hover circle{stroke:var(--acc);}
        .wg-ang:hover text{fill:var(--acc);}
        /* Right-click context menu on the plan canvas */
        .wg-ctx{position:absolute;z-index:150;min-width:170px;background:var(--sf);border:1px solid var(--bdr);border-radius:8px;box-shadow:0 8px 26px rgb(var(--shadow-ink) / .4);padding:5px;overflow:hidden;}
        .wg-ctx button{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:transparent;border:none;color:var(--pn);font-size:13px;font-family:inherit;padding:8px 10px;border-radius:6px;cursor:pointer;}
        .wg-ctx button:hover{background:var(--inp);color:var(--acc);}
        .wg-ctx .wg-ctx-danger:hover{color:var(--no);}
        .wg-ctx-sep{height:1px;background:var(--bdr);margin:4px 6px;}
        .wg-dim-editor{position:absolute;z-index:140;width:72px;padding:3px 5px;font-size:13px;font-weight:700;text-align:center;color:var(--white);background:var(--sf);border:1.5px solid var(--acc2);border-radius:5px;box-shadow:0 4px 14px rgb(var(--shadow-ink) / .35);outline:none;}
        .wg-drag-hud{position:absolute;z-index:141;pointer-events:none;background:var(--sf);color:var(--white);border:1px solid var(--acc2);border-radius:5px;padding:2px 7px;font-size:11px;font-weight:700;white-space:nowrap;box-shadow:0 3px 10px rgb(var(--shadow-ink) / .35);}
        /* Free-draw "Draw walls" tool control strip (bottom-left of the canvas). */
        .wg-draw-strip{position:absolute;left:8px;bottom:8px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;max-width:calc(100% - 16px);background:color-mix(in srgb, var(--sf) 92%, transparent);border:1px solid var(--bdr);border-radius:8px;padding:6px 8px;box-shadow:0 3px 12px rgb(var(--shadow-ink) / .3);}
        .wg-draw-strip button{white-space:nowrap;}
        .wg-draw-sep{width:1px;height:18px;background:var(--bdr);margin:0 2px;}
        /* Full-screen Room Designer for the New-Room modal (mirrors .wg-expanded). */
        .nr-preview-panel.nr-designer-expanded{position:fixed;inset:24px;z-index:145;background:var(--sf);border:1px solid var(--bdr);border-radius:14px;box-shadow:0 0 0 100vmax color-mix(in srgb, var(--bg) 72%, transparent),0 30px 80px color-mix(in srgb, var(--bg) 80%, transparent);padding:18px;overflow:auto;}
        .nr-preview-panel.nr-designer-expanded #nr-preview svg{max-height:calc(100vh - 170px) !important;}
        .nr-designer-actions{display:none;}
        .nr-preview-panel.nr-designer-expanded .nr-designer-actions{display:flex;align-items:center;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--bdr);}

        /* ── PR-NEWROOM-FUSION (2026-07-17): Fusion-360 × Apple premium visual system ──
           A dark "engineering" plan canvas + a glass floating toolbar + a glass status
           pill + premium glowing corner handles. EVERYTHING is scoped to .nr-card so the
           in-room room editor (#wg-plan-svg, which lives OUTSIDE .nr-card) keeps its light
           architect's-paper palette untouched. The shared wgPlanSvg reads --plan-canvas,
           which is DEFINED ONLY here — everywhere else it falls back to --plan-paper. */
        .nr-card{
          /* PR-NEWROOM-THEME-FOLLOW (2026-09-08, operator follow-up: "the
             background in the rendering is still not in a light mode"): the
             dark --plan-* re-base moved to the [data-theme=dark] block below.
             In light theme the plan falls back to the architect's-paper
             palette at :root — the same rendering the in-room editor uses. */
          /* PR-NEWROOM-THEME-FOLLOW (2026-09-08, operator: "shouldn't the colors
             follow the color scheme you have applied — it does not have a light
             mode"): the rail/header/footer follow the APP THEME again. In light
             theme the card is the normal light surface with a STRONG edge ring
             (the findability fix the 2026-09-02 dark card was for); the dark
             UI-token re-base moved to the [data-theme=dark] block below. The
             CANVAS pane stays the dark engineering surface in both themes —
             that identity is the plan itself, not the chrome around it. */
          color:var(--txt);
          box-shadow:0 32px 90px rgba(15,23,42,.30), 0 0 0 1.5px var(--bdr-h);
        }
        /* PR-NEWROOM-DARKCARD (2026-09-02), now dark-theme-only: the whole dialog
           wears the canvas's dark engineering palette. Core UI tokens are re-based
           so the rail / header / footer / inputs restyle themselves (everything
           inside is token-driven); values track the dark theme but sit on the
           canvas's blue-gray #0D1117 family instead of AMOLED black. */
        :root[data-theme="dark"] .nr-card{
          --plan-canvas:#0D1117; --plan-paper:#0D1117; --plan-floor:#141A22;
          --plan-grid:rgba(255,255,255,0.055); --plan-grid-maj:rgba(255,255,255,0.12);
          --plan-poche:#2A313C; --plan-ink:#D8DDE8; --plan-face-int:#1D9BF0;
          --plan-dim:#8FA3B8; --plan-dim-text:#B8C4D2; --plan-dim-edit:#F0B429;
          --plan-door:#E0943A; --plan-glass:#5AB4E0; --plan-slope:#F0A93A;
          --plan-handle:#9DB0C4; --plan-handle-core:#0D1117; --plan-sel:#1D9BF0; --plan-snap:#34E0A1;
          --bg:#0A0E14; --sf:#0D1117; --pn:#141A22; --pn-h:#1B222C; --inp:#151C25;
          --bdr:#2B3440; --bdr-h:#3E4956;
          --acc:#4C8DFF; --acc2:#6AA5FF; --acc-ink:#05070D;
          --txt:#E2E8F0; --dim:#9AA8B8; --muted:#6B7888; --white:#FFFFFF; --ink:var(--txt);
          --ok:#34D399; --ok-ink:#06281D; --warn:#F0B429; --warn-ink:#2A1E03;
          --no:#F2566B; --no-ink:#2A0810; --lime:#34D399; --lime-ink:#06281D;
          box-shadow:0 32px 90px rgb(var(--shadow-ink) / .55), 0 0 0 1px rgba(255,255,255,.16);
        }
        /* The native color input ships a WHITE swatch wrapper (Chromium default) —
           the one light-mode leftover on the rail in either theme. */
        .nr-card input[type=color]::-webkit-color-swatch-wrapper{padding:0;background:none;border-radius:6px;}
        .nr-card input[type=color]::-webkit-color-swatch{border:1px solid var(--bdr);border-radius:6px;}
        /* Canvas ground follows the theme: paper in light, engineering-dark in dark. */
        .nr-card .nr-canvas-pane{background:var(--plan-paper,#F6F5F1);}
        :root[data-theme="dark"] .nr-card .nr-canvas-pane{background:radial-gradient(130% 100% at 50% 0%, #10151D 0%, #0A0D12 68%, #06090D 100%);}
        /* The wall-color hex input sits outside any .fg group, so without this it keeps
           the browser's default white chrome on the now-dark rail. */
        .nr-card #nr-wall-color-hex{background:var(--inp);border:1px solid var(--bdr);border-radius:8px;color:var(--txt);padding:6px 8px;}
        .nr-card .nr-preview-panel{position:relative;}
        /* Frosted glass primitive — floating chrome over the canvas. Light glass
           on paper; dark glass on the dark canvas (theme-follow). */
        .nr-glass-pill{background:rgba(255,255,255,0.82);backdrop-filter:blur(16px) saturate(170%);-webkit-backdrop-filter:blur(16px) saturate(170%);border:1px solid rgba(26,29,32,0.12);box-shadow:0 10px 34px rgba(15,23,42,0.18),inset 0 1px 0 rgba(255,255,255,0.6);}
        :root[data-theme="dark"] .nr-glass-pill{background:rgba(13,17,23,0.80);border:1px solid rgba(255,255,255,0.10);box-shadow:0 10px 34px rgb(var(--shadow-ink) / 0.5),inset 0 1px 0 rgba(255,255,255,0.06);}
        @supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
            .nr-glass-pill{background:rgba(255,255,255,0.97);}
            :root[data-theme="dark"] .nr-glass-pill{background:rgba(15,20,28,0.97);}
        }
        /* PR-NEWROOM-FAB-DOCKED (2026-09-02, operator: "This bar is a mess it
           is completly covering words and part of the wall"): the toolbar no
           longer FLOATS over the canvas at all. It sits in its own strip
           above the drawing, in normal flow, so it cannot cover a label or a
           wall no matter what shape the room is. (The 2026-08-13 drag grip
           was the band-aid for the same complaint — moving the problem
           around instead of removing it; gone with this.) */
        .nr-fab-toolbar{position:relative;flex:0 0 auto;align-self:center;max-width:calc(100% - 16px);margin:8px auto 6px;z-index:22;display:flex;align-items:center;gap:2px;height:46px;padding:0 9px;border-radius:13px;animation:nrFabIn .34s var(--spring-snappy,cubic-bezier(.22,1,.36,1));}
        .nr-fab-toolbar button{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-width:44px;height:38px;padding:0 9px;background:transparent;border:none;border-radius:9px;color:var(--dim);font-family:inherit;font-size:16px;line-height:1;cursor:pointer;transition:background .13s var(--ease-out,ease),color .13s var(--ease-out,ease),transform .09s var(--ease-out,ease);}
        .nr-fab-toolbar button .fab-lbl{font-size:8.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;opacity:.92;}
        .nr-fab-toolbar button:hover{background:color-mix(in srgb, var(--acc) 13%, transparent);color:var(--txt);}
        .nr-fab-toolbar button:active{transform:scale(.9);}
        .nr-fab-toolbar button:disabled{opacity:.32;cursor:default;pointer-events:none;}
        /* PR-PLAN-IMPORT (2026-07-18): the New-Room "upload a plan" dropzone. */
        .nr-plan-drop{display:flex;align-items:center;gap:10px;padding:11px 13px;margin-bottom:12px;border:1.5px dashed var(--bdr);border-radius:10px;background:color-mix(in srgb,var(--acc) 5%,transparent);cursor:pointer;transition:border-color .15s var(--ease-out,ease),background .15s var(--ease-out,ease);}
        .nr-plan-drop:hover{border-color:var(--acc);background:color-mix(in srgb,var(--acc) 10%,transparent);}
        .nr-plan-drop.drag{border-color:var(--acc);background:color-mix(in srgb,var(--acc) 16%,transparent);}
        /* PR-NEWROOM-CHECKLIST (2026-09-08): the readiness list atop the rail.
           Token-driven, so it follows the modal's light/dark theme. */
        .nr-check{background:var(--pn);border:1px solid var(--bdr);border-radius:10px;padding:5px 6px;margin-bottom:12px;display:flex;flex-direction:column;}
        .nr-check:empty{display:none;}
        .nr-check-row{display:flex;align-items:center;gap:8px;padding:4px 5px;border:none;background:none;color:var(--txt);font:inherit;font-size:12px;line-height:1.35;text-align:left;cursor:pointer;border-radius:6px;width:100%;}
        .nr-check-row:hover{background:var(--pn-h);}
        .nr-check-row:focus-visible{outline:2px solid var(--acc);outline-offset:-2px;}
        .nr-check-dot{width:16px;height:16px;border-radius:50%;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;}
        .nr-check-row.is-ok .nr-check-dot{background:color-mix(in srgb, var(--ok) 15%, transparent);color:var(--ok);}
        .nr-check-row.is-warn .nr-check-dot{background:color-mix(in srgb, var(--warn) 16%, transparent);color:var(--warn);}
        .nr-check-row.is-todo .nr-check-dot{border:1.5px dashed var(--muted);}
        .nr-check-row.is-warn .nr-check-lbl b{color:var(--warn);}
        .nr-check-lbl{min-width:0;}
        .nr-check-opt{margin-left:auto;color:var(--muted);font-style:italic;font-size:10.5px;flex:0 0 auto;}
        /* PR-NEWROOM-SLIM (2026-09-02): rail ledes/captions became .jd-help hover tips
           so the rail fits without a scrollbar. Tips open BELOW their ⓘ — above would
           clip at the card top (overflow:hidden) or the rail's scroll edge — and are
           width-capped so a hover can't extend the rail's scrollable overflow. */
        .nr-card .jd-help.tip-below:hover::after,
        .nr-card .nr-rail .jd-help:hover::after{bottom:auto;top:calc(100% + 6px);min-width:180px;max-width:200px;}
        .nr-card .jd-help.tip-below:hover::before,
        .nr-card .nr-rail .jd-help:hover::before{bottom:auto;top:calc(100% + 1px);border-top-color:transparent;border-bottom-color:color-mix(in srgb,var(--pn) 96%,transparent);}
        /* A tip living inside a form label must not inherit its uppercase/tracking. */
        .nr-card .jd-help:hover::after{text-transform:none;letter-spacing:normal;font-weight:400;}
        .nr-card .nr-rail .fg{margin-bottom:6px;}
        /* Two-up field rows: the stock .fr minmax(140px) stacks single-column in the
           248px rail, which is what made pages 2 and 4 scroll. 108px columns pair the
           short selects/inputs without truncating their values. */
        .nr-card .nr-rail .fr{grid-template-columns:repeat(auto-fit,minmax(108px,1fr));gap:8px 10px;}
        .nr-card .nr-rail h3{margin-top:10px;}
        .nr-card .wall-lens-grid{grid-template-columns:repeat(auto-fit,minmax(108px,1fr));}
        .nr-fab-toolbar button.is-active{background:color-mix(in srgb, var(--acc) 20%, transparent);color:var(--acc);box-shadow:inset 0 -2px 0 var(--acc);}
        .nr-fab-toolbar button.nr-fab-primary{color:var(--txt);}
        .nr-fab-sep{width:1px;height:24px;background:color-mix(in srgb, var(--txt) 14%, transparent);margin:0 5px;flex:0 0 auto;}
        /* PR-NEWROOM-FAB-DOCKED: the drag grip + dragging styles left with the
           floating position — a docked bar has nowhere to be dragged to.
           PR-NEWROOM-PLACE: crosshair while arming an opening for
           click-to-place. */
        .nr-preview-panel.nr-placing #nr-preview{cursor:crosshair;}
        /* Glass status pill (bottom-left) — validity + dims + area + wall count. */
        .nr-card .nr-status-strip{animation:nrPillIn .34s var(--spring-snappy,cubic-bezier(.22,1,.36,1));}
        @keyframes nrFabIn{from{opacity:0;transform:translateY(-9px);}to{opacity:1;transform:translateY(0);}}
        @keyframes nrPillIn{from{opacity:0;transform:translateY(7px);}to{opacity:1;transform:translateY(0);}}

        /* 3-column editor grid: library | viewer | parameters */
        /* Slimmer sidebars (260→218 left, 280→236 right ≈ 17–18% reduction) +
           glassmorphic surface so the panels read as floating over the viewport
           instead of two more boxes. The viewport gradient bleeds through. */
        /* PR-CAB-LAYOUT-CALC (2026-05-29, refined): the 56px in this
           calc was originally only the OUTER app header height. Later
           someone added `.shdr` inside #pg-cab but never updated the
           calc. Refined .shdr height estimate: padding 10px + h1 (24px
           font × ~1.2 line-height = ~29px) + sub div (12px font × ~1.4
           line-height = ~17px) + padding 10px + border 1px ≈ 67-68px,
           not the 46px I first guessed. First-pass calc(100vh - 102px)
           closed most of the gap but operator confirmed "still some
           scroll" remained — undershot by ~20px. Bumped to 125px to
           cover 56 outer + ~68 .shdr + small safety margin.
           If still off, nudge the constant ±5px; do NOT restructure
           the flex/grid (see [[feedback_no_cab_editor_grid_layout_changes]]:
           two attempts to restructure broke the page hard). */
        /* PR-CAB-PANE-CONTRAST (2026-06-16): left rail widened 218→248px and the
           right (Parameters) rail 236→252px so cabinet names + wall-property
           labels stop crowding the edges and read at a glance. Width tweak only —
           the grid structure is unchanged (see feedback_no_cab_editor_grid_layout_changes). */
        /* PR-STUDIO-FIT (2026-07-08): height-only fix (columns unchanged, per
           feedback_no_cab_editor_grid_layout_changes). FILL the positioned
           parent (#app-workspace is position:fixed between the 56px topbar and
           the status bar) instead of the fragile height:calc(100vh - 125px)
           magic guess, which overflowed the viewport — operator had to scroll to
           reach the bottom control bar — whenever the real chrome differed from
           125px (status bar / migration banner / taller topbar). 100% adapts to
           whatever space the fixed parent actually has; grid stays overflow:
           hidden and each pane scrolls internally. */
        /* PR-CAB-LEFT-SLIM (2026-07-29): default left library width trimmed
           248 → 212 so the 3D viewport is bigger before the operator touches
           anything (operator: "takes up too much screen before you can select a
           cabinet and see it"). Drag-to-resize still overrides via inline --cab-lw. */
        .cab-editor-grid{display:grid;grid-template-columns:var(--cab-lw,212px) 1fr var(--cab-rw,252px);height:100%;max-height:100%;min-height:0;overflow:hidden;background:var(--bg);position:relative;transition:grid-template-columns var(--dur-standard) var(--ease-out);}
        /* PR-CAB-RESIZE (2026-06-28): drag-to-resize rails (widths persist). */
        .cab-resizer{position:absolute;top:0;bottom:0;width:9px;cursor:col-resize;z-index:6;background:transparent;touch-action:none;transition:background var(--dur-quick) var(--ease-out);}
        .cab-resizer:hover,.cab-resizer.drag{background:color-mix(in srgb, var(--acc) 45%, transparent);}
        .cab-resizer.cab-rz-l{left:var(--cab-lw,212px);transform:translateX(-50%);}
        .cab-resizer.cab-rz-r{right:var(--cab-rw,252px);transform:translateX(50%);}
        .cab-editor-grid.cab-expanded .cab-resizer{display:none;}
        .cab-editor-grid.cab-dragging{transition:none;}
        /* PR-CAB-LAYOUT: expanded viewport mode — hides both side rails
           so the 3D viewport fills the available space. Triggered by the
           ⊞ toolbar button or pressing F. Mozaik-style "maximize 3D".
           Side panes fade out via opacity so the canvas reflows cleanly
           via the ResizeObserver. */
        .cab-editor-grid.cab-expanded{grid-template-columns:0 1fr 0;}
        .cab-editor-grid.cab-expanded .cab-pane{overflow:hidden;pointer-events:none;opacity:0;border:none;transition:opacity .14s ease;}
        @media (prefers-reduced-motion: reduce){
            .cab-editor-grid,.cab-editor-grid.cab-expanded .cab-pane{transition:none;}
        }
        /* PR-CAB-PANE-CONTRAST (2026-06-16): background was a hardcoded dark
           glass (rgba(18,18,18,0.78)) that ignored the theme. In light mode it
           painted a muddy medium-gray panel while the theme's dark text tokens
           (--txt / --dim) sat on top → unreadable labels in BOTH the Cabinet
           Library (left) and Parameters (right) rails. Route through --pn so the
           panel flips with the theme (ivory/white in light mode, near-black in
           dark) while keeping a hint of translucency for the floating-glass feel
           the blur provides. */
        /* PR-EDITOR-BLACK (2026-07-19): route the editor rails to true black
           (var(--bg)) instead of a color-mixed --pn + glass blur — the operator
           wants consistent pure-black dark mode. Cards inside (--pn #16181C)
           still give subtle depth. */
        .cab-pane{background:var(--bg);display:flex;flex-direction:column;border-right:1px solid var(--bdr);overflow:hidden;min-width:0;}
        .cab-pane-right{border-right:none;border-left:1px solid var(--bdr);}
        .cab-pane-hdr{padding:10px 14px;border-bottom:1px solid var(--bdr);display:flex;align-items:center;gap:8px;background:var(--bg);flex:none;}
        .cab-pane-body{overflow:hidden;overflow-x:hidden;padding:10px;flex:1;display:flex;flex-direction:column;min-height:0;}
        /* PR-CAB-PANE-CONTRAST (2026-06-16): hide the scrollbar in the editor
           rails — it still scrolls, just without the chrome the operator found
           intrusive. Applies to the pane body and any inner scroll regions. */
        .cab-pane-body,.cab-pane-body *{scrollbar-width:none;-ms-overflow-style:none;}
        .cab-pane-body::-webkit-scrollbar,.cab-pane-body *::-webkit-scrollbar{display:none;width:0;height:0;}
        .cab-pane-body .pn{margin-bottom:0;}
        /* The global .fr uses minmax(140px,…) so in these ~230px rails the
           Quick-Add W/H/D fields each grabbed a full row and stacked vertically,
           burying the Cabinet Library list and Placed section below the fold.
           Tighten the track minimum INSIDE the cab rails so the three dimension
           fields sit on one row — everything fits without scrolling on a normal
           screen. Width-only scope; no structural change. */
        .cab-pane .fr{grid-template-columns:repeat(auto-fit,minmax(58px,1fr));gap:8px;margin-bottom:8px;}
        .cab-pane .fg{margin-bottom:8px;}
        .cab-pane .fg label{margin-bottom:3px;}
        /* Tighten the gap between the stacked sections (Cabinet Library /
           Quick Add / Placed) so all three are reachable with minimal scroll. */
        .cab-pane-body .pn + .pn{margin-top:8px !important;}
        .cab-viewer{display:flex;flex-direction:column;overflow:hidden;background:#080810;position:relative;min-width:0;}
        .cab-canvas-wrap{flex:1;position:relative;min-height:0;}
        .cab-canvas-wrap canvas{display:block;width:100%;height:100%;}
        /* PR-CAB-TOOLBAR-FIT (2026-05-29): was overflow-x:auto which
           forced operators to horizontally scroll the toolbar to reach
           Eco/Balanced/Best perf-tier buttons on common laptop widths.
           flex-wrap:wrap lets buttons spill to a second row when needed,
           so every viewport setting is visible without scrolling the
           viewport area itself (only the sidebars should scroll). */
        /* PR-EDITOR-FLOAT (2026-07-19): Apple-style. The toolbar is now a FLOATING
           VIEW PILL centered at the bottom of the canvas (view controls only) + a
           floating ACTION cluster top-right (Edit/Apply/Island/Placed/Save). No
           bordered .cv-group boxes, no wrap — a calm frosted pill on pure black. */
        .cab-view-bar{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);z-index:8;
            display:flex;flex-wrap:nowrap;align-items:center;gap:2px;max-width:calc(100% - 28px);
            padding:6px 8px;background:color-mix(in srgb, var(--pn) 80%, transparent);
            -webkit-backdrop-filter:blur(22px) saturate(150%);backdrop-filter:blur(22px) saturate(150%);
            border:1px solid var(--bdr);border-radius:15px;box-shadow:0 14px 42px rgb(var(--shadow-ink) / .55);}
        .cab-view-bar .cv-group{background:transparent;border:none;padding:0;margin:0 2px 0 0;gap:2px;display:inline-flex;align-items:center;}
        .cab-view-bar .cv-group:empty{display:none;margin:0;}
        .cab-view-bar #vc-view{min-height:34px!important;padding:5px 9px!important;border-radius:8px!important;}
        .cab-view-bar #cv-expand{margin-left:2px!important;}
        .cab-view-bar .cv-help-btn{margin-left:0!important;}
        /* Floating ACTION cluster (top-right of the canvas). */
        .cab-act-cluster{position:absolute;right:16px;top:16px;z-index:8;
            display:flex;align-items:center;gap:4px;padding:5px 6px;
            background:color-mix(in srgb, var(--pn) 80%, transparent);
            -webkit-backdrop-filter:blur(22px) saturate(150%);backdrop-filter:blur(22px) saturate(150%);
            border:1px solid var(--bdr);border-radius:13px;box-shadow:0 12px 32px rgb(var(--shadow-ink) / .5);}
        #cab-placed-count{background:var(--acc);color:var(--acc-ink);border-radius:6px;padding:1px 6px;font-size:11px;font-weight:700;}
        /* Apple-clean ghost buttons inside both floating containers. */
        .cab-view-bar .cv-btn,.cab-act-cluster .cv-btn{background:transparent;border:none;color:var(--dim);min-height:36px;padding:0 12px;border-radius:8px;font-weight:600;}
        .cab-view-bar .cv-btn:hover,.cab-act-cluster .cv-btn:hover{background:color-mix(in srgb, var(--txt) 9%, transparent);border-color:transparent;color:var(--txt);}
        .cab-view-bar .cv-btn.active,.cab-act-cluster .cv-btn.active{background:color-mix(in srgb, var(--acc) 16%, transparent);color:var(--acc);border-color:transparent;font-weight:700;}
        .cab-act-cluster .cv-btn.cv-btn-primary{background:var(--acc);color:var(--acc-ink);font-weight:700;}
        .cab-act-cluster .cv-btn.cv-btn-primary:hover{filter:brightness(1.06);background:var(--acc);color:var(--acc-ink);}
        /* Floating PLACED-CABINETS panel (opened by the Placed toggle). */
        .cab-placed-panel{position:fixed;z-index:1300;width:290px;max-height:min(62vh,470px);display:flex;flex-direction:column;
            background:color-mix(in srgb, var(--pn) 92%, transparent);-webkit-backdrop-filter:blur(20px) saturate(150%);backdrop-filter:blur(20px) saturate(150%);
            border:1px solid var(--bdr);border-radius:13px;box-shadow:0 18px 48px rgb(var(--shadow-ink) / .6);overflow:hidden;}
        .cab-placed-panel[hidden]{display:none;}
        .cab-placed-panel .cpp-head{display:flex;align-items:center;justify-content:space-between;padding:11px 14px;border-bottom:1px solid var(--bdr);flex:none;}
        .cab-placed-panel .cpp-head h3{font-size:13px;font-weight:600;margin:0;color:var(--txt);}
        .cab-placed-panel .cpp-head #cab-count{color:var(--dim);font-weight:400;}
        .cab-placed-panel .cpp-close{background:transparent;border:none;color:var(--dim);font-size:16px;cursor:pointer;padding:2px 7px;border-radius:6px;line-height:1;}
        .cab-placed-panel .cpp-close:hover{color:var(--txt);background:color-mix(in srgb, var(--txt) 8%, transparent);}
        .cab-placed-panel #cab-items{flex:1;min-height:0;max-height:none;overflow-y:auto;padding:8px;}
        .cab-placed-panel .cpp-foot{display:flex;gap:8px;padding:10px 12px;border-top:1px solid var(--bdr);flex:none;}
        .cab-placed-panel .cpp-foot button{flex:1;margin:0;}
        /* Collapsible Quick-Add header (press "A" to open the typed form). */
        .cab-qa-toggle{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;background:transparent;border:none;
            color:var(--txt);font-size:12px;font-weight:600;padding:0 0 8px;margin:0;cursor:pointer;text-align:left;}
        .cab-qa-toggle:hover{color:var(--acc);}
        .cab-qa-toggle[aria-expanded="true"]{color:var(--acc);}
        .cab-qa-toggle kbd{font-family:inherit;font-size:10px;font-weight:600;color:var(--dim);background:color-mix(in srgb,var(--txt) 8%,transparent);
            border:1px solid var(--bdr);border-radius:5px;padding:1px 6px;line-height:1.5;}
        #cab-quickadd-form[hidden]{display:none;}
        /* PR-UI-28: cv-btn now hosts inline SVGs alongside text labels.
           Icons sized at 14px to match the 11px label without dominating. */
        /* PR-RENDER-DAY1-POLISH (2026-05-08): WCAG 2.5.5 — bumped
           padding from 6px 12px (~31px tall) to 11px 14px (~44px) so
           toolbar buttons (iso/top/front/side/sketch/walk) clear the
           floor. Font-size and gap unchanged so the visible content
           stays the same; only the hit area grows. */
        .cv-btn{background:var(--inp);border:1px solid var(--bdr);color:var(--white);padding:11px 14px;border-radius:6px;font-size:11px;cursor:pointer;transition:background var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out);white-space:nowrap;display:inline-flex;align-items:center;gap:5px;min-height:44px;}
        .cv-btn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;}
        .cv-btn:hover{background:color-mix(in srgb, var(--acc2) 10%, transparent);border-color:var(--acc2);}
        .cv-btn.active{background:var(--acc2);color:var(--bg);border-color:var(--acc2);font-weight:600;}
        /* PR-STUDIO-P2 #10d/#10e (2026-07-16): material swatch dot on the finish
           chips (paint the current finish), + the Save button accented as the
           highest-stakes action. */
        .cv-btn .cv-swatch{display:inline-block;width:11px;height:11px;border-radius:50%;margin-right:2px;vertical-align:middle;border:1px solid rgba(0,0,0,0.28);box-shadow:inset 0 0 0 1px rgba(255,255,255,0.18);flex:0 0 auto;}
        .cv-btn.cv-btn-primary{background:var(--acc);border-color:var(--acc);color:#fff;font-weight:600;}
        .cv-btn.cv-btn-primary:hover{filter:brightness(1.08);}
        /* PR-STUDIO-P2 #9b (2026-07-16): live-cost HUD recompute shimmer + narrow
           tablet width clamp. The sweep overlays the existing numbers (host is
           overflow:hidden) so stale figures never blank while the nester runs. */
        #cw-live-cost.cw-recalc::after{content:"";position:absolute;inset:0;background:linear-gradient(100deg,transparent 30%,rgba(255,255,255,0.12) 50%,transparent 70%);animation:cwLcSweep 900ms ease-in-out infinite;pointer-events:none;}
        @keyframes cwLcSweep{0%{transform:translateX(-100%);}100%{transform:translateX(100%);}}
        @media (max-width:480px){#cw-live-cost{max-width:calc(100vw - 24px);right:12px;}}
        .cv-sep{width:1px;height:18px;background:var(--bdr);margin:0 4px;flex:none;}
        .cv-hint{color:var(--dim);font-size:10px;white-space:nowrap;}

        /* Cabinet library — single-column tile rows with side-by-side
           icon+text. Was a 2-column grid (`grid-template-columns:1fr 1fr`)
           which crushed names like "Lazy Susan Corner Base" into
           ellipsis-truncated half-words ("Lazy") in the right column.
           Single column at 218px gives ~190px of usable width for the
           name — enough to fit every cabinet name without truncation.
           Each row is shorter than the old 2-col tile (icon goes inline
           on the left at 38px instead of stacked at 46px above the
           text), so the visible row count stays similar. */
        #cab-lib-items{display:grid;grid-template-columns:1fr;gap:6px;flex:1 1 auto;min-height:0;overflow-y:auto;}
        /* UX-2 — Category section header spans both columns. Sticky so
           the section name stays visible while scrolling through a
           long list of widths. Tiny + muted so it reads as a label,
           not a tile. */
        /* PR-LIB-SCROLL-FIX (2026-05-28): was top:-1px which left a 1px
           gap that triggered reflow on every scroll tick (the items
           appeared to "jump"). top:0 keeps the header flush to the
           scroll container's top edge. */
        /* PR-CAB-LIB-COMPACT (2026-07-29): brighter, larger section headers.
           Operator: "where it shows base 11 … make it easier to read, bright white
           or super black". --txt is the theme's max-contrast body color (near-white
           in dark, near-black in light), so it flips correctly with the theme. */
        .cab-lib-section-header{grid-column:1 / -1;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:var(--txt);padding:10px 6px 6px;border-bottom:1px solid var(--bdr);margin-top:6px;background:var(--sf);position:sticky;top:0;z-index:2;}
        .cab-lib-section-header:first-child{margin-top:0;}
        /* PR-CAB-LIB-COMPACT (2026-07-29): compact type/shape dropdowns + small
           Browse-all link that replaced the two rows of large filter pills. */
        .cab-lib-filter-row{display:flex;gap:6px;margin-bottom:6px;}
        .cab-lib-sel{flex:1;min-width:0;height:34px;background:var(--inp);border:1px solid var(--bdr);border-radius:6px;color:var(--txt);padding:0 6px;font-size:12px;font-weight:600;cursor:pointer;}
        .cab-lib-sel:focus{border-color:var(--acc);outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--acc) 25%,transparent);}
        .cab-lib-browse-link{width:fit-content;margin:0 0 8px auto;background:none;border:none;color:var(--acc2);font-size:11px;font-weight:600;cursor:pointer;padding:2px 2px;}
        .cab-lib-browse-link:hover{color:var(--acc);text-decoration:underline;}
        /* Cabinet-library tile — 2026-04-23 token refactor: was
         * background:rgba(31,31,31,0.55) (dark gray that didn't flip
         * in light mode, rendering as muddy gray on the new white page
         * background). Now routes through --pn so the tile blends
         * seamlessly on any theme — pure white panel on light mode,
         * near-black on the dark theme.
         *
         * .name: was color:var(--white) which is theme-specific. Route
         * through --txt for proper contrast in both light and dark.
         * .icn backdrop: dark-theme's (--pn, --bg) gradient worked
         * because both are near-black; in light mode --pn is white and
         * --bg is off-white, which reads as a barely-visible wash. Swap
         * to color-mix for a proper subtle fill on both themes. */
        /* PR-CAB-LAYOUT: side-by-side icon + text. 38px square icon on
           the left, name + dims stacked vertically on the right. Fixes
           the previous "Lazy Susan" → "Lazy" truncation by giving the
           text column the full row width minus the icon. */
        .cab-lib-item{display:flex;flex-direction:row;align-items:center;gap:var(--sp-3,12px);padding:0 var(--sp-3,12px);border:1px solid var(--bdr);border-radius:var(--radius-sm,6px);background:var(--pn);cursor:grab;transition:border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out),transform var(--dur-panel) var(--spring-snappy);position:relative;min-height:54px;}
        .cab-lib-item:focus-visible{outline:var(--focus-ring-w,2px) solid var(--acc);outline-offset:var(--focus-ring-offset,2px);}
        /* PR-CAB-STRIPE (2026-06-28): a category accent rail on the right edge so
           the cabinet TYPE is glanceable on a dusty screen (decorative — the type
           is also in the section header + name, so it never carries sole meaning). */
        .cab-lib-item::before{content:"";position:absolute;right:5px;top:11px;bottom:11px;width:var(--stripe-w,3px);border-radius:var(--radius-xs,3px);background:var(--cab-cat,var(--acc-cabinet,#c19a6b));pointer-events:none;}
        /* PR-STUDIO-LIB-FAVS (2026-07-16): pin star, clear of the ::before category
           stripe (right:5px). Hidden until tile hover for UNpinned; pinned = always
           gold-visible. Mouse pattern — on touch (no hover) the pinned stars still
           show, but pinning is a desktop power move. */
        .cab-lib-item .cab-fav{position:absolute;top:5px;right:11px;font-size:13px;line-height:1;cursor:pointer;color:var(--dim);opacity:0;transition:opacity 120ms ease,color 120ms ease,transform 90ms ease;z-index:2;user-select:none;}
        .cab-lib-item:hover .cab-fav,.cab-lib-item .cab-fav.on{opacity:1;}
        .cab-lib-item .cab-fav.on{color:var(--acc2);}
        .cab-lib-item .cab-fav:hover{transform:scale(1.2);}
        /* PR-STUDIO-P1B #6 (2026-07-16): frame the 3D canvas as a drop target while a
           palette card is being dragged. The ghost is the primary feedback; this is a
           quiet green frame so the operator knows the canvas will accept the drop. */
        #bevy-canvas.cab-drop-active{outline:2px solid rgba(34,197,94,0.55);outline-offset:-2px;transition:outline 80ms ease;}
        .cab-lib-item[data-cat="Base"],.cab-lib-item[data-cat="Vanity"]{--cab-cat:var(--acc-cabinet,#c19a6b);}
        .cab-lib-item[data-cat="Wall"]{--cab-cat:#8fbc8f;}
        .cab-lib-item[data-cat="Tall"]{--cab-cat:var(--acc-sheet,#b8860b);}
        .cab-lib-item[data-cat="Appliance"]{--cab-cat:#9aa6b4;}
        /* PR-CAB-GAPBAR (2026-06-28): wall-run strip in the wall inspector —
           the cabinets on the active wall, proportionally sized + clickable,
           with the aggregate space-to-fill called out. Read-only; the filler
           edit stays on the per-cabinet inspector. */
        .cab-run-wrap{margin:0 0 12px;}
        .cab-run-head{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;color:var(--dim);margin-bottom:5px;text-transform:uppercase;letter-spacing:.06em;}
        .cab-run-rem{font-family:var(--ff-mono);font-variant-numeric:tabular-nums;font-weight:600;letter-spacing:0;text-transform:none;}
        .cab-run-rem.gap{color:var(--warn);}
        .cab-run-rem.over{color:var(--no);}
        .cab-run-rem.flush{color:var(--ok);}
        .cab-run-bar{position:relative;height:46px;border-radius:var(--radius-sm,6px);background:repeating-linear-gradient(90deg, color-mix(in srgb, var(--bdr) 22%, transparent) 0 1px, transparent 1px 12px), color-mix(in srgb, var(--bdr) 12%, var(--sf));border:1px solid var(--bdr);overflow:hidden;}
        .cab-run-chip{position:absolute;top:3px;bottom:3px;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:0 2px;border:1px solid var(--bdr-h);border-radius:var(--radius-xs,3px);background:var(--pn);color:var(--txt);cursor:pointer;overflow:hidden;transition:background var(--dur-fast,.12s),border-color var(--dur-fast,.12s);}
        .cab-run-chip:hover{background:var(--pn-h);border-color:var(--bdr-h);}
        .cab-run-chip.sel{border-color:var(--acc);background:color-mix(in srgb, var(--acc) 14%, var(--pn));}
        .cab-run-n{font-size:9px;color:var(--acc2);font-weight:700;line-height:1;}
        .cab-run-w{font-family:var(--ff-mono);font-size:11px;font-variant-numeric:tabular-nums;line-height:1;}
        .cab-lib-item:hover{border-color:var(--acc2);background:color-mix(in srgb, var(--acc) 8%, var(--pn));}
        .cab-lib-item:active{cursor:grabbing;}
        .cab-lib-item .icn{flex:0 0 44px;width:44px;height:44px;background:color-mix(in srgb, var(--bdr) 35%, var(--pn));border-radius:var(--radius-sm,6px);display:flex;align-items:center;justify-content:center;border:1px solid color-mix(in srgb, var(--bdr) 50%, transparent);}
        .cab-lib-item .icn svg{width:32px;height:32px;}
        .cab-lib-item .info{min-width:0;flex:1;display:flex;flex-direction:column;gap:1px;}
        .cab-lib-item .name{color:var(--txt);font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:-.005em;}
        /* Right-aligned tabular size chip — the WIDTH (or W×H) is the real per-SKU
           identifier, so a column of aligned sizes lets a section of look-alike rows
           scan like a table. Full W×H×D stays in the hover preview + properties. */
        .cab-lib-item .cab-lib-size{flex:none;margin-left:8px;margin-right:13px;font-family:var(--ff-mono);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em;color:var(--dim);white-space:nowrap;}
        .cab-lib-item:hover .cab-lib-size{color:var(--txt);}

        /* PR-LIBRAIL-DEADCODE (2026-09-12): .cab-lib-preview hover-card styles
           deleted with their unreachable JS (app-jobdefaults.js). */

        /* Shape filter chips — Straight / L / U / Corner row above the type chips
           PR-CAB-LAYOUT: same nowrap + horizontal-scroll fix as .lib-chips above. */
        .lib-shape-chips{display:flex;gap:3px;margin-bottom:6px;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;}
        .lib-shape-chips::-webkit-scrollbar{display:none;}
        /* PR-PICKER-PERFECT (2026-07-15): 36px min tap target (was ~26px, below
           the shop-floor gloved-hand floor) + legible 11px. */
        .lib-shape-chip{flex:0 0 auto;white-space:nowrap;display:inline-flex;align-items:center;min-height:36px;background:var(--inp);border:1px solid var(--bdr);color:var(--dim);font-size:11px;font-weight:600;padding:6px 11px;border-radius:5px;cursor:pointer;text-transform:uppercase;letter-spacing:.3px;transition:color var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out);}
        .lib-shape-chip:hover{border-color:var(--acc2);color:var(--white);}
        .lib-shape-chip.active{background:color-mix(in srgb, var(--acc2) 12%, transparent);border-color:var(--acc2);color:var(--acc2);}

        /* Skeleton loader for the cabinet library */
        .cab-lib-skel{padding:6px;border:1px solid var(--bdr);border-radius:6px;background:var(--inp);}
        .cab-lib-skel .sk-icn{width:100%;height:46px;border-radius:4px;background:linear-gradient(90deg,var(--inp) 0%,var(--bdr) 50%,var(--inp) 100%);background-size:200% 100%;animation:skel-shimmer 1.4s ease-in-out infinite;margin-bottom:4px;}
        .cab-lib-skel .sk-bar{height:8px;border-radius:3px;background:linear-gradient(90deg,var(--inp) 0%,var(--bdr) 50%,var(--inp) 100%);background-size:200% 100%;animation:skel-shimmer 1.4s ease-in-out infinite;margin-top:3px;}
        .cab-lib-skel .sk-bar.short{width:60%;}

        /* Placed cabinet rows in left panel */
        /* PR-PLACE-5 2026-04-25 (Feature 1 — shop-floor tablet ergonomics):
           min-height 48px so a gloved tap reliably hits the row. The
           `.sel` state gets a 3px amber left bar so the operator can spot
           the active cabinet without reading text — matches the cyan halo
           around the selected cabinet in the 3D view. */
        #cab-items .li{padding:8px 8px;font-size:11px;cursor:pointer;border:1px solid transparent;border-left:3px solid transparent;border-radius:5px;margin-bottom:3px;display:flex;align-items:center;justify-content:space-between;min-height:48px;}
        #cab-items .li > div:first-child{flex:1;min-width:0;}
        #cab-items .li:hover{background:color-mix(in srgb, var(--acc2) 6%, transparent);}
        #cab-items .li.sel{background:color-mix(in srgb, var(--acc2) 12%, transparent);border-color:var(--acc2);border-left:3px solid var(--acc2);}
        #cab-items .li-t{font-size:11px;}
        #cab-items .li-m{font-size:10px;color:var(--dim);}
        /* PR-CAB-SIDEBAR-UX (2026-05-29) ─ per-row info density.
           Type chip + warning dot per cabinet-shop-ux-specialist audit.
           Strict token compliance per [[feedback_strict_color_tokens]]. */
        #cab-items .li-chip{
            display:inline-block;padding:2px 8px;margin-right:6px;
            border-radius:8px;font-size:11px;font-weight:600;
            letter-spacing:.04em;text-transform:uppercase;
            background:var(--pn);color:var(--dim);
            border:1px solid var(--bdr);
        }
        /* PR-PICKER-PERFECT (2026-07-15): the per-row edit/link/delete icon
           buttons had NO dedicated CSS — they fell through to the global button
           rule (~30px). Give them a 36px square tap zone inside the 48px row. */
        #cab-items .li-act{
            min-width:36px;min-height:36px;padding:0;
            display:inline-flex;align-items:center;justify-content:center;
            flex:0 0 auto;font-size:14px;line-height:1;
        }
        /* PR-PICKER-PERFECT (2026-07-15): a delete row marked pending-delete
           (first tap) reads clearly so the second tap confirms — no accidental
           gloved-hand deletes, no keyboard-only Ctrl+Z dependency. */
        #cab-items .li-del.pending-del{
            background:color-mix(in srgb, var(--no) 22%, transparent);
            border-color:var(--no);color:var(--white);
        }
        #cab-items .li-warn-dot{
            display:inline-block;width:8px;height:8px;border-radius:50%;
            background:var(--no);margin-right:6px;flex:0 0 auto;
            box-shadow:0 0 4px var(--no);
        }
        /* PR-PICKER-PERFECT (2026-07-15): the "Browse all cabinets" overlay
           shipped with ZERO css — every cba-* / cab-browse-* class was undefined,
           so the full ~100-item catalog rendered as unstyled HTML with sub-30px
           buttons and no scroll container. Full styling: a scrollable 2-col card
           grid with 44px+ tap targets, shop-floor legible. */
        .cab-browse{position:absolute;inset:0;z-index:20;display:flex;flex-direction:column;background:var(--pn);}
        .cab-browse.hidden{display:none;}
        .cab-browse-hdr{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px;border-bottom:1px solid var(--bdr);}
        .cab-browse-hdr h3{margin:0;font-size:15px;font-weight:700;}
        .cba-close{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;font-size:18px;background:var(--inp);border:1px solid var(--bdr);border-radius:6px;cursor:pointer;}
        .cab-browse-controls{padding:10px 12px;border-bottom:1px solid var(--bdr);display:flex;flex-direction:column;gap:8px;}
        .cba-filter-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;}
        .cba-filter-label{font-size:11px;color:var(--dim);font-weight:600;min-width:52px;}
        .cba-open-btn{width:100%;min-height:44px;display:flex;align-items:center;justify-content:space-between;padding:0 12px;background:var(--inp);border:1px solid var(--bdr);border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;}
        .cba-open-btn:hover{border-color:var(--acc2);}
        .cba-grid{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;align-content:start;}
        .cba-card{display:flex;flex-direction:column;gap:6px;min-height:96px;padding:10px;border:1px solid var(--bdr);border-radius:8px;background:var(--inp);}
        .cba-card:hover{border-color:var(--acc2);}
        .cba-thumb{width:100%;height:44px;display:flex;align-items:center;justify-content:center;font-size:22px;background:var(--pn);border-radius:5px;color:var(--dim);}
        .cba-name{font-size:12px;font-weight:600;line-height:1.2;}
        .cba-dims{font-size:11px;color:var(--dim);}
        .cba-badge{display:inline-block;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:1px 6px;border-radius:6px;background:color-mix(in srgb,var(--acc2) 14%,transparent);color:var(--acc2);}
        .cba-actions{display:flex;gap:6px;margin-top:auto;}
        .cba-place{flex:1;min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:4px;background:var(--acc2);border:none;border-radius:6px;color:#0b0e14;font-size:12px;font-weight:700;cursor:pointer;}
        .cba-place:hover{filter:brightness(1.08);}
        .cba-info{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;background:var(--pn);border:1px solid var(--bdr);border-radius:6px;color:var(--dim);cursor:pointer;font-size:14px;}
        .cba-detail{font-size:11px;color:var(--dim);line-height:1.35;}
        .cba-detail-type{font-weight:600;}
        .cba-features{display:flex;flex-wrap:wrap;gap:4px;}
        .cba-empty{grid-column:1/-1;text-align:center;color:var(--dim);padding:40px 12px;font-size:13px;}
        .cab-browse-footer{padding:10px 12px;border-top:1px solid var(--bdr);display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;color:var(--dim);}
        /* PR-STUDIO-SIDEBAR-SCROLL (2026-07-17): the left pane stacked the Cabinet
           Library, Quick Add and Placed list in ONE scroll, so the 67-item library
           grew unbounded — you scrolled forever to browse, and ADDING a cabinet
           scroll-jumped the whole pane down to Placed ("drops me to placed, scroll up
           forever"). Give the Library grid and the Placed list their OWN bounded
           internal scroll so both stay reachable and a placed-row scrollIntoView stays
           inside the Placed box, and re-show a slim scrollbar on those two boxes (the
           pane hides all scrollbars by default via .cab-pane-body *). */
        /* PR-EDITOR-LIB2 (2026-07-19) — SUPERSEDED by PR-EDITOR-LIB3 below, kept
           only for the overscroll-behavior history: LIB2 made .cab-pane-body the
           single scroller because an overflow:auto #cab-lib-items with
           `overscroll-behavior:contain` swallowed the wheel at its boundary.
           LIB3 reversed that: #cab-lib-items IS the single scroller again
           (overflow-y:auto in its base rule, slim scrollbar below) WITHOUT the
           overscroll trap. Stale-comment fix: PR-LIBRAIL-DEADCODE 2026-09-12. */
        #cab-lib-items{padding-right:2px;}
        #cab-items{max-height:32vh;overflow-y:auto;overscroll-behavior:contain;padding-right:2px;}
        /* PR-EDITOR-LIB3 (2026-07-19): fixed filter header + a single scrolling tile
           list + @container-driven list<->grid keyed on the RESIZABLE pane width.
           The Library .pn owns the pane's leftover height as a flex column AND is the
           @container (container-type goes on the .pn — a container query styles the
           container's DESCENDANTS, not the container itself). Quick Add is a flex:none
           footer so the Fixtures palette stays reachable without stealing list height. */
        #cab-lib-pn{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden;container:cablib / inline-size;}
        #cab-lib-pn .cab-lib-head{flex:0 0 auto;}
        /* cap the Quick Add / Fixtures footer so a short pane can't starve the tile
           list — it scrolls internally past 42% of the pane height. */
        #cab-qa-pn{flex:0 0 auto;max-height:42%;overflow-y:auto;}
        /* #cab-lib-items is the single scroller — give IT the slim visible scrollbar
           (it was hidden by `.cab-pane-body *`) + a stable gutter so rows don't reflow. */
        .cab-pane-body #cab-lib-items{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--bdr) 75%,transparent) transparent;scrollbar-gutter:stable;}
        .cab-pane-body #cab-lib-items::-webkit-scrollbar{display:block;width:8px;height:8px;}
        .cab-pane-body #cab-lib-items::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--bdr) 75%,transparent);border-radius:8px;}
        /* generated front-elevation thumbnail fills the icon box; currentColor line-art
           inherits the 2-theme palette + hover/selected states. */
        .cab-lib-item .icn{color:var(--dim);}
        .cab-lib-item:hover .icn{color:var(--txt);}
        .cab-lib-item .icn .cab-thumb{width:100%;height:100%;display:block;}
        /* PR-CAB-LIB-LISTONLY (2026-07-29): the adaptive @container reflow into a
           2-column picture-card grid was REMOVED per operator request. At the normal
           rail width it flipped every section into a grid where tall/many/long-named
           SKUs (Appliances especially) went ragged and looked like they collided —
           "see how it changes so drastically". The single-column LIST (base
           `.cab-lib-item` row + `#cab-lib-items` 1-col grid above) is now the layout at
           EVERY width, so every section reads the same clean way. If a 2-column grid is
           ever wanted again, reinstate an `@container cablib` block here with
           EQUAL-height cards (fixed card height, not align-items:start) to avoid the
           raggedness that caused this revert. */
        /* PR-STUDIO-BROWSEALL-THUMB (2026-07-17): the Browse-All card thumb held an
           <svg viewBox="0 0 32 32"> with NO width/height and no CSS size, so it
           defaulted to ~300px and the wood silhouettes overflowed across the tiles.
           Constrain the svg to the 44px thumb + clip. */
        .cba-thumb{overflow:hidden;}
        .cba-thumb svg{width:auto;height:100%;max-width:100%;object-fit:contain;display:block;}
        /* PR-CAB-SIDEBAR-UX (2026-05-29) ─ left-pane collapse.
           Independent collapse from cab-expanded (which hides BOTH).
           Per [[feedback_strict_color_tokens]] no hex anywhere. */
        .cab-editor-grid.cab-left-collapsed{grid-template-columns:0 1fr 252px;}
        .cab-editor-grid.cab-left-collapsed .cab-pane-left{
            overflow:hidden;pointer-events:none;opacity:0;border:none;
            transition:opacity .14s ease;
        }
        @media (prefers-reduced-motion: reduce) {
            .cab-editor-grid.cab-left-collapsed .cab-pane-left{transition:none;}
        }
        #cab-left-toggle{padding:2px 8px;font-size:14px;}
        /* PR-CAB-LEFT-RAIL (2026-07-29): when the library is collapsed the pane
           itself goes to width:0 / opacity:0, so its own ‹ toggle is unreachable.
           This slim tab is pinned to the grid's left edge (grid is position:relative)
           and is the always-visible reopen affordance — no more hunting for the
           toolbar hamburger. Fullscreen keeps its own #cv-left-pill, so hide the tab
           there to avoid two reopen controls. */
        .cab-left-reopen{display:none;position:absolute;left:0;top:50%;transform:translateY(-50%);
            z-index:7;flex-direction:column;align-items:center;gap:8px;
            padding:14px 5px;border:1px solid var(--bdr);border-left:none;
            border-radius:0 10px 10px 0;background:var(--pn);color:var(--txt);
            cursor:pointer;font-size:13px;font-weight:700;line-height:1;
            box-shadow:2px 0 12px rgb(var(--shadow-ink) / .30);}
        .cab-left-reopen:hover{background:var(--inp);color:var(--acc);}
        .cab-left-reopen .clr-label{writing-mode:vertical-rl;text-orientation:mixed;letter-spacing:.05em;font-size:11px;font-weight:600;}
        .cab-editor-grid.cab-left-collapsed .cab-left-reopen{display:flex;}
        .cab-editor-grid.cab-fullscreen.cab-left-collapsed .cab-left-reopen{display:none;}

        /* PR-CAB-PARAMS-SCROLL (2026-07-29): the right Parameters pane must scroll.
           The generic .cab-pane-body is overflow:hidden + display:flex — the LEFT pane
           delegates scrolling to its inner #cab-lib-items, but the RIGHT pane has no
           inner scroller, so on desktop the inspector's lower half (Mount & Position,
           Front Face, Inside the Cabinet, Advanced, Copy/Done) was clipped and could
           not be reached (operator: "i cant see the bottom or edit everything"). The
           ≤900px stacked layout already scrolls; this restores it at desktop widths too.
           Block layout so the stacked sections take natural height and the body scrolls;
           a visible thin scrollbar (overriding the pane's hidden-scrollbar rule) makes
           it obvious there's more below. renderCabParams already reads this element's
           scrollTop to preserve position across edits, so scrolling now works end-to-end. */
        #cab-params-body{display:block;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--bdr) 75%,transparent) transparent;scrollbar-gutter:stable;}
        #cab-params-body::-webkit-scrollbar{display:block;width:8px;height:8px;}
        #cab-params-body::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--bdr) 75%,transparent);border-radius:8px;}

        /* Parameters panel form */
        #cab-params-form .fg{margin-bottom:8px;}
        #cab-params-form .fg label{font-size:10px;}
        #cab-params-form .fr{gap:8px;margin-bottom:8px;}

        /* Responsive: collapse sidebars at narrower widths */
        @media(max-width:1200px){.cab-editor-grid:not([style*="--cab-lw"]){--cab-lw:196px;--cab-rw:232px;}}
        @media(max-width:900px){.cab-editor-grid{grid-template-columns:1fr;grid-template-rows:auto 1fr auto;height:100%;min-height:0;}.cab-pane{max-height:240px;border-right:none;border-bottom:1px solid var(--bdr);}.cab-pane-right{border-left:none;border-top:1px solid var(--bdr);}.cab-viewer{min-height:220px;}
            /* PR-EDITOR-LIB3: on the short tablet pane, DON'T use the fixed-header flex
               (the Fixtures footer + header would starve the tile list); fall back to
               the whole pane scrolling as one region. */
            .cab-pane-body{overflow-y:auto;display:block;min-height:auto;}
            #cab-lib-pn{flex:none;display:block;overflow:visible;}
            #cab-lib-pn .cab-lib-head{position:sticky;top:0;z-index:3;background:var(--bg);}
            #cab-lib-items{overflow:visible;min-height:auto;}
            /* PR-EDITOR-FLOAT2 (2026-07-19): the bar is a floating pill now, not the
               old docked strip (the quality picker / reports / save it used to carry
               moved into the top-right Settings menu). Keep it floating on narrow
               screens but let it WRAP + center so the slimmed content never clips
               under .cab-viewer{overflow:hidden}. Do NOT re-introduce position:sticky
               / opaque background / border-top — those fought the pill. */
            .cab-view-bar{max-width:calc(100% - 16px);flex-wrap:wrap;justify-content:center;row-gap:4px;}
        }
        .add-door-bar .ad-spacer {
            flex: 1;
            min-width: var(--sp-3);
        }
        /* Primary CTA — last thing the operator touches before commit.
           Overrides the app-wide .ba base (rgba shadow on hover) with a
           token-based confident-green button. Larger, more presence than
           a generic .ba button deserves here; it's the shop-floor gate
           to committing $50 walnut. */
        .add-door-bar .ad-go {
            min-height: 56px;
            min-width: 130px;             /* trimmed from 160 to make room for D field */
            padding: 0 var(--sp-4);
            font-family: var(--ff-ui);
            font-size: 14px;
            font-weight: var(--fw-bold);
            letter-spacing: 0.04em;
            flex: 0 0 auto;
            margin-left: auto;            /* push CTA to right; spacer becomes optional */
            border-radius: var(--radius-sm);
            border: 1.5px solid var(--acc);
            background: var(--acc);
            color: var(--acc-ink);
            cursor: pointer;
            margin-right: var(--sp-1);
            transition: filter var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
            box-shadow: 0 3px 10px color-mix(in srgb, var(--acc) 30%, transparent);
        }
        .add-door-bar .ad-go:hover:not([data-compat-blocked="true"]):not(:disabled) {
            filter: brightness(1.08);
            transform: translateY(-1px);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 40%, transparent);
        }
        .add-door-bar .ad-go:active:not([data-compat-blocked="true"]):not(:disabled) {
            transform: translateY(0);
            filter: brightness(0.95);
        }
        .add-door-bar .ad-go:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 80%, transparent);
            outline-offset: 3px;
        }
        /* .ds-split media query removed 2026-04-22 — class unused. */

        /* ═════════════════════════════════════════════════════════════════════
           DASHBOARD WIDGETS — home-page modular grid.
           Sizes: small=4 cols, medium=6 cols, large=8 cols, wide=12 cols.
           Each widget is a self-contained panel with a header (drag handle +
           title + close button) and a content slot. Drag-and-drop reorder
           uses HTML5 drag API; layout persists to localStorage.
           ════════════════════════════════════════════════════════════════════ */
        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): .dash-bar, .widget*,
           .add-widget-card, .widget-resize-handle, .edit-dock and the
           dock-pop keyframe deleted — customizable widget grid retired.
           .widget-empty kept below because filterGlobalSearch still uses it. */
        .widget-empty{color:var(--dim);font-size:12px;font-style:italic;padding:8px 0;}
        /* ═════════════════════════════════════════════════════════════════════
           DASHBOARD REFACTOR — Linear/Figma-grade chrome.
           Global search, active-tab indicator, glassmorphism hero overlays,
           hover quick-action bar, recent-work table styling, utility palette.
           ════════════════════════════════════════════════════════════════════ */
        /* Global search bar in the header — ⌘K affordance */
        .global-search{position:relative;display:flex;align-items:center;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:6px 10px 6px 12px;width:280px;transition:border-color var(--dur-chrome) var(--ease-out),box-shadow var(--dur-chrome) var(--ease-out),width var(--dur-standard) var(--ease-out);}
        .global-search:focus-within{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb, var(--acc) 12%, transparent);width:340px;}
        .global-search svg{width:14px;height:14px;color:var(--dim);flex-shrink:0;}
        .global-search input{flex:1;background:transparent;border:none;outline:none;color:var(--txt);font-size:13px;padding:2px 8px;font-family:inherit;}
        .global-search input::placeholder{color:var(--dim);}
        .global-search .kbd{display:inline-flex;align-items:center;gap:2px;background:var(--bg);color:var(--dim);font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;border:1px solid var(--bdr);font-family:'Inter',ui-monospace,monospace;letter-spacing:.5px;}
        @media(max-width:1100px){.global-search{width:200px;}.global-search:focus-within{width:280px;}}
        @media(max-width:800px){.global-search{display:none;}}

        /* Active tab — high-contrast accent + thicker indicator + bg tint */
        .tab.on{color:var(--acc2)!important;border-bottom:3px solid var(--acc)!important;background:linear-gradient(to bottom,transparent 60%,color-mix(in srgb, var(--acc) 8%, transparent));font-weight:600;}

        /* Utility palette — header buttons get consistent square sizing + tooltip-ready hover.
           PR-AUDIT-CYCLE-4 (2026-05-12, ux F-1): bumped 36→44px to meet
           WCAG 2.5.5 (44×44 CSS px minimum target). Gloved shop-floor
           operators were mis-tapping the notification/sync icons. */
        .util-btn{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;color:var(--dim);cursor:pointer;transition:color var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out);position:relative;font-size:15px;}
        .util-btn:hover{color:var(--acc);border-color:var(--acc);background:var(--bg);}
        /* PR-HEADER-SETTINGS (2026-09-05): the bell's picture is 24px inside the
           unchanged 44px button; the gear beside it 22px. Pinned here so no
           skin's svg rule can shrink them back to a speck. */
        #topbar-notif-btn .topbar-notif-icon{width:24px;height:24px;flex:0 0 auto;}
        #topbar-settings-btn > svg{width:22px;height:22px;flex:0 0 auto;}
        .util-btn[data-tooltip]:hover::after{content:attr(data-tooltip);position:absolute;top:calc(100% + 6px);right:0;background:var(--bg);color:var(--txt);padding:5px 9px;border-radius:6px;font-size:11px;font-weight:500;white-space:nowrap;border:1px solid var(--bdr);z-index:50;box-shadow:0 8px 24px rgb(var(--shadow-ink) / 0.4);pointer-events:none;}
        .util-btn .badge{position:absolute;top:-4px;right:-4px;background:var(--acc);color:var(--acc-ink);font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;line-height:1.2;}
        .util-btn .badge:empty,.util-btn .badge.zero{display:none;}

        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): .hero-card / .hero-quick /
           .qa-btn / .hero-badge deleted — only consumer was the
           WIDGET_DEFS render templates which are gone. */

        /* Recent Work table — clean scannable list */
        .recent-table{width:100%;border-collapse:collapse;font-size:13px;}
        .recent-table thead th{text-align:left;color:var(--dim);font-size:10px;font-weight:600;letter-spacing:.6px;text-transform:uppercase;padding:8px 10px;border-bottom:1px solid var(--bdr);}
        .recent-table tbody tr{cursor:pointer;transition:background var(--dur-panel) var(--ease-out);}
        .recent-table tbody tr:hover{background:var(--inp);}
        .recent-table tbody td{padding:10px;border-bottom:1px solid var(--bdr);color:var(--txt);}
        .recent-table tbody tr:last-child td{border-bottom:none;}
        .recent-table .col-name{font-weight:500;color:var(--white);}
        .recent-table .col-client{color:var(--dim);font-size:12px;}
        .recent-table .col-when{color:var(--dim);font-size:12px;font-variant-numeric:tabular-nums;text-align:right;}
        .recent-table .col-open{width:28px;text-align:right;color:var(--dim);font-size:14px;}
        .recent-table tbody tr:hover .col-open{color:var(--acc);}
        .status-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:500;letter-spacing:.2px;}
        .status-pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;}
        /* PR-UI-8 (2026-04-27): pill-pop keyframe (defined in tokens.css)
           fires once when JS toggles `.is-updated` on a state change.
           Initial render renders flat — no animation on page load.
           Reduced-motion users get instant change because --dur-quick is
           zeroed in the prefers-reduced-motion block of tokens.css.
           Future PR will add a `markPillUpdated(el)` helper that adds the
           class and auto-removes it on animationend; for now the rule
           is in place so any pill that gets `.is-updated` will animate
           correctly. Applies to both .status-pill and .hub-job-status-pill
           (the two pill conventions in the codebase). */
        .status-pill.is-updated,
        .hub-job-status-pill.is-updated{
            animation: pill-pop var(--dur-quick) var(--spring-snappy);
        }
        /* Pill rules use `var(--status-*, fallback)` so the four original themes keep
           their translucent rgba look; test theme overrides with solid AA-safe fills. */
        .status-draft{background:var(--status-draft, rgba(170,170,170,0.12));color:var(--status-draft-ink, #AAAAAA);}
        .status-design{background:var(--status-scheduled, color-mix(in srgb, var(--acc) 12%, transparent));color:var(--status-scheduled-ink, #00E5C0);}
        .status-nesting{background:var(--status-progress, rgba(255,204,92,0.15));color:var(--status-progress-ink, #FFCC5C);}
        .status-completed{background:var(--status-complete, var(--lime));color:var(--status-complete-ink, var(--lime-ink));}
        .status-cancelled{background:var(--status-alert, rgba(255,77,77,0.12));color:var(--status-alert-ink, #FF4D4D);}

        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): .shop-intel deleted — only
           consumer was the deleted renderDashBar(). */

        /* Global search modal */
        /* PR-UI-12-lite (2026-04-27): bring the command-palette modal to
           Apple-Pro spec (master prompt §10.10): backdrop blur strengthened
           (4px -> 12px) for the floating-glass feel; panel radius +
           shadow + entrance animation now reference design tokens
           instead of hardcoded values. Reduced-motion users get instant
           appearance because --dur-standard zeroes out. */
        .gs-modal{position:fixed;inset:0;background:rgba(0,0,0,0.6);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);z-index:300;display:flex;align-items:flex-start;justify-content:center;padding-top:120px;}
        .gs-modal.hidden{display:none;}
        .gs-modal .gs-panel{width:600px;max-width:90vw;background:var(--sf);border:1px solid var(--bdr);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-modal);animation:gs-pop var(--dur-standard) var(--spring-snappy);}
        @keyframes gs-pop{0%{transform:translateY(-20px) scale(.96);opacity:0;}100%{transform:translateY(0) scale(1);opacity:1;}}
        .gs-modal .gs-input{width:100%;background:var(--sf);border:none;border-bottom:1px solid var(--bdr);padding:18px 22px;color:var(--txt);font-size:16px;font-family:inherit;outline:none;}
        .gs-modal .gs-input::placeholder{color:var(--dim);}
        .gs-modal .gs-results{max-height:50vh;overflow-y:auto;padding:8px;}
        .gs-modal .gs-result{padding:10px 14px;border-radius:8px;cursor:pointer;display:flex;align-items:center;gap:12px;}
        .gs-modal .gs-result:hover,.gs-modal .gs-result.sel{background:var(--inp);}
        .gs-modal .gs-result .gs-icon{width:32px;height:32px;border-radius:8px;background:var(--inp);display:flex;align-items:center;justify-content:center;font-size:14px;color:var(--acc);flex-shrink:0;}
        .gs-modal .gs-result .gs-text{flex:1;min-width:0;}
        .gs-modal .gs-result .gs-text .gs-title{color:var(--white);font-weight:500;font-size:13px;}
        .gs-modal .gs-result .gs-text .gs-sub{color:var(--dim);font-size:11px;}
        .gs-modal .gs-foot{padding:10px 16px;border-top:1px solid var(--bdr);color:var(--dim);font-size:11px;display:flex;justify-content:space-between;}
        .gs-modal .gs-foot .kbd{background:var(--bg);color:var(--dim);font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;border:1px solid var(--bdr);margin:0 2px;}

        /* ═══════════════════════════════════════════════════════════════════
           VALIDATION GATE — Compliance UX (PR-VAL-MODAL-A, 2026-05-02).
           Inline blocker panel shown when /cutlist or /drawings returns 422.
           Pattern 3 (UX agent pick): canvas stays visible behind the panel.
           Severity uses redundant signals (badge text + border + color) for
           color-blind safety + sunlight contrast on shop-floor tablets.
           Compliance mode (AWI 0641 §1.4 paraphrased — no override) renders
           with the override section omitted; Simple mode renders the
           reason field + Override button.
           ═══════════════════════════════════════════════════════════════════ */
        .vgate-panel{background:var(--pn);border:2px solid var(--no);border-radius:10px;padding:14px 18px;margin:0 0 12px 0;box-shadow:0 12px 32px rgb(var(--shadow-ink) / 0.35);}
        .vgate-panel.vgate-soft{border-color:var(--warn);}
        .vgate-panel .vgate-h{font-size:15px;font-weight:800;color:var(--no);margin:0 0 4px 0;display:flex;align-items:center;gap:8px;}
        .vgate-panel.vgate-soft .vgate-h{color:var(--warn);}
        .vgate-panel .vgate-sub{font-size:12px;color:var(--dim);margin:0 0 12px 0;line-height:1.45;}
        .vgate-panel .vgate-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;max-height:50vh;overflow-y:auto;}
        .vgate-panel .vgate-row{padding:10px 12px;background:var(--bg);border-left:6px solid var(--warn);border-radius:0 6px 6px 0;display:flex;flex-direction:column;gap:4px;}
        .vgate-panel .vgate-row.vgate-row-hard{border-left-color:var(--no);}
        .vgate-panel .vgate-row .vgate-row-h{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap;}
        .vgate-panel .vgate-row .vgate-badge{display:inline-block;font-size:10px;font-weight:800;letter-spacing:0.5px;padding:3px 7px;border-radius:4px;color:#fff;background:var(--warn);min-width:60px;text-align:center;flex-shrink:0;}
        .vgate-panel .vgate-row.vgate-row-hard .vgate-badge{background:var(--no);}
        .vgate-panel .vgate-row .vgate-msg{font-size:13px;color:var(--txt);line-height:1.45;flex:1;min-width:200px;}
        /* PR-VGATE-HARDENING (2026-05-02): UX agent flagged Fix-link tap
           area was ~20px tall — below the 44px Apple HIG floor. Padded
           to ~32px tap area while preserving inline rendering after the
           message line. */
        .vgate-panel .vgate-row .vgate-fix{display:inline-block;font-size:12px;color:var(--acc);margin-top:2px;padding:8px 0;min-height:32px;line-height:1.4;}
        .vgate-panel .vgate-row .vgate-fix:hover{text-decoration:underline;}
        .vgate-panel .vgate-row .vgate-code{font-size:10px;font-family:ui-monospace,monospace;color:var(--dim);}
        .vgate-panel .vgate-foot{margin-top:14px;padding-top:12px;border-top:1px solid var(--bdr);}
        .vgate-panel .vgate-comp-note{padding:10px 12px;background:color-mix(in srgb, var(--no) 8%, var(--sf));border-left:4px solid var(--no);border-radius:0 6px 6px 0;font-size:12px;line-height:1.5;color:var(--txt);}
        .vgate-panel .vgate-comp-note strong{color:var(--no);}
        .vgate-panel .vgate-override{display:flex;flex-direction:column;gap:8px;}
        .vgate-panel .vgate-override-warn{font-size:11px;color:var(--warn);font-weight:600;line-height:1.45;}
        .vgate-panel .vgate-override-reason{width:100%;min-height:60px;background:var(--inp,var(--bg));border:1px solid var(--bdr);border-radius:6px;padding:8px 10px;color:var(--txt);font-size:13px;font-family:inherit;resize:vertical;box-sizing:border-box;}
        .vgate-panel .vgate-override-reason:focus{outline:none;border-color:var(--warn);}
        .vgate-panel .vgate-actions{display:flex;gap:8px;flex-wrap:wrap;}
        .vgate-panel .vgate-btn{min-height:48px;padding:10px 18px;border-radius:8px;border:1px solid var(--bdr);background:var(--sf);color:var(--txt);font-size:13px;font-weight:600;cursor:pointer;}
        .vgate-panel .vgate-btn:hover:not(:disabled){background:var(--inp,var(--bg));}
        .vgate-panel .vgate-btn:disabled{opacity:0.5;cursor:not-allowed;}
        .vgate-panel .vgate-btn-override{background:var(--warn);color:#fff;border-color:var(--warn);}
        .vgate-panel .vgate-btn-override:hover:not(:disabled){filter:brightness(1.1);}
        /* Modal mode — fixed overlay when caller has no host element */
        .vgate-modal{position:fixed;inset:0;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);z-index:310;display:flex;align-items:flex-start;justify-content:center;padding:64px 16px 16px;overflow-y:auto;}
        .vgate-modal .vgate-panel{width:560px;max-width:100%;}
        /* PR-AWI-PRUNE-9-02: .aws-mode-badge rules deleted with the topbar
           badge markup (see index.html topbar comment). */

        /* ═══════════════════════════════════════════════════════════════════
           DESIGN ENGINE COCKPIT — 3D editor polish.
           Toast notifications, library filter chips, grouped toolbar, axis
           gizmo, gradient viewport, shortcuts overlay, glow selection.
           ═══════════════════════════════════════════════════════════════════ */
        /* Toast stack — bottom-right, auto-dismissing notification cards */
        .toast-stack{position:fixed;bottom:24px;right:24px;z-index:400;display:flex;flex-direction:column;gap:8px;align-items:flex-end;pointer-events:none;}
        /* Toast bg/fg are FIXED (not themed) so messages stay legible no
           matter what Color Theme is active. Previously they used var(--txt)
           / var(--sf), which could collide with a light palette and render
           white-on-white. */
        /* PR-WAVE-CLEANUP-87: toast animation duration + curve now via tokens
           (--dur-standard + --spring-snappy) instead of hardcoded .22s. */
        /* PR-UI-4 (2026-04-27): the FIXED hex values above are now named
           tokens in tokens.css (--toast-bg / --toast-bdr / --toast-fg).
           Same physical colors, but the contract is greppable. The "no
           color tokens" rule in tokens.css gets an explicit exception
           comment block documenting why these three specifically must
           stay theme-stable. */
        .toast{pointer-events:auto;background:var(--toast-bg);border:1px solid var(--toast-bdr);border-left:3px solid var(--acc);color:var(--toast-fg);padding:10px 14px;border-radius:10px;font-size:12px;font-weight:500;box-shadow:0 12px 32px rgb(var(--shadow-ink) / 0.55);min-width:240px;max-width:380px;display:flex;align-items:flex-start;gap:10px;animation:toast-in var(--dur-standard) var(--spring-snappy);}
        .toast .ti{color:var(--toast-fg);}
        /* PR-PLACEFIX-P0 (2026-09-15): the toast text WAS class "tc" — the
           tab-content class the shell hides globally (`.tc{display:none}`
           at :522), so every toast showed a mute icon and placement
           refusals looked like silent snap-backs. Renamed toast-msg in
           toast() (app-jobdefaults.js) + the library undo toast; keep the
           legacy `.toast .tc` selectors one release as a belt for any
           stragglers still minting the old markup. */
        .toast .toast-msg{color:var(--toast-fg);}
        .toast .tc{color:var(--toast-fg);display:block;}
        .toast.toast-error{border-left-color:var(--no);}
        .toast.toast-warn{border-left-color:var(--warn);}
        .toast.toast-ok{border-left-color:var(--ok);}
        .toast .ti{font-size:14px;line-height:1.2;flex-shrink:0;}
        .toast .toast-msg{flex:1;line-height:1.4;}
        .toast .tc{flex:1;line-height:1.4;}
        .toast .tx{background:transparent;border:none;color:var(--dim);cursor:pointer;font-size:14px;line-height:1;padding:0;flex-shrink:0;}
        .toast .tx:hover{color:var(--txt);}
        @keyframes toast-in{0%{transform:translateX(20px) scale(.95);opacity:0;}100%{transform:translateX(0) scale(1);opacity:1;}}
        .toast.toast-out{animation:toast-out .18s ease forwards;}
        @keyframes toast-out{0%{transform:translateX(0) scale(1);opacity:1;}100%{transform:translateX(20px) scale(.95);opacity:0;}}

        /* Library type filter chips — Base / Wall / Tall / Hardware quick filters
           PR-CAB-LAYOUT: was `flex:1; min-width:0; flex-wrap:wrap` which crushed
           every chip to ~40px in the 218px rail. "Wall" / "Tall" / "Base" then
           wrapped CHARACTER-BY-CHARACTER inside the flex child, producing the
           W-A-L-L vertical stack the user reported. New rule: chips own their
           content width (`flex:0 0 auto` + `white-space:nowrap`), and the
           container becomes a horizontal scroll rail when overall width is
           tight (touch-swipe friendly on tablet). */
        .lib-chips{display:flex;gap:4px;margin-bottom:8px;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px;}
        .lib-chips::-webkit-scrollbar{display:none;}
        /* PR-RENDER-DAY1-POLISH (2026-05-08): WCAG 2.5.5 — bumped
           min-height 28→44 + padding 5/10 → 12/14. Library category
           chips are tapped constantly; sub-44px floor was failing
           gloved-hand operators. Font/visual weight unchanged. */
        .lib-chip{flex:0 0 auto;white-space:nowrap;background:var(--inp);border:1px solid var(--bdr);color:var(--dim);font-size:10px;font-weight:600;padding:12px 14px;border-radius:6px;cursor:pointer;text-transform:uppercase;letter-spacing:.4px;transition:color var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out);display:inline-flex;align-items:center;justify-content:center;gap:3px;min-height:44px;}
        .lib-chip svg{width:11px;height:11px;flex-shrink:0;}
        .lib-chip:hover{border-color:var(--acc2);color:var(--white);}
        .lib-chip.active{background:color-mix(in srgb, var(--acc2) 12%, transparent);border-color:var(--acc2);color:var(--acc2);}

        /* PR-EDITOR-LIBSPACE (2026-07-24): the two library filter rows (type +
           shape) were ~44px + ~36px tall and, with the always-visible Fixtures
           palette below, squeezed the tile list to a ~100px sliver — operator
           "cant see what cabinets im selecting to grab". Scope a COMPACT height
           to the library's OWN chips only; the Browse-all panel keeps the roomy
           WCAG chips. ~30px rows + tighter margins reclaim ~55px for the list. */
        /* Touch/pen pick-up state (long-press to drag onto the 3D view). */
        .cab-lib-item--dragging{opacity:.5;border-color:var(--acc2)!important;background:color-mix(in srgb,var(--acc2) 14%,var(--pn))!important;transform:scale(.97);transition:transform .1s ease,opacity .1s ease;}
        /* PR-LIBRAIL-DEADCODE (2026-09-12): #cab-lib-chips / #cab-lib-shape-chips
           rules deleted — those elements were replaced by <select>s in
           PR-CAB-LIB-COMPACT (2026-07-29) and no longer exist. */

        /* Collapsible "Panels & Fixtures" accordion at the bottom of the library
           column — collapsed by default so the tile list gets the height. One tap
           to open; works on touch + mouse with no JS (native <details>). */
        .cab-adds-more{margin-top:8px;border-top:1px solid var(--bdr);padding-top:2px;}
        .cab-adds-more > summary{list-style:none;cursor:pointer;font-size:12px;font-weight:600;color:var(--dim);padding:8px 4px;min-height:40px;display:flex;align-items:center;gap:7px;user-select:none;-webkit-user-select:none;}
        .cab-adds-more > summary::-webkit-details-marker{display:none;}
        .cab-adds-more > summary::before{content:"\203A";display:inline-block;transition:transform .15s ease;color:var(--acc2);font-size:16px;line-height:1;}
        .cab-adds-more[open] > summary::before{transform:rotate(90deg);}
        .cab-adds-more > summary:hover{color:var(--white);}

        /* Grouped bottom toolbar — the bar is a floating CENTERED pill (see the
           .cab-view-bar rule higher up). The old `position:relative` full-width
           strip override was REMOVED (2026-07-19) so the pill can float + center. */
        .cv-group{display:inline-flex;gap:4px;align-items:center;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:3px;margin-right:8px;}
        /* PR-AUDIT-CYCLE-4 (2026-05-12, ux F-12): explicit min-height 44px
           restores the WCAG 2.5.5 floor that the outer .cv-btn{min-height:44px}
           rule provided. The grouped-toolbar override stripped that floor; this
           rule restores it for the Eco/Balanced/Preview quality switcher and
           sketch-mode toggle. */
        .cv-group .cv-btn{background:transparent;border:none;padding:5px 10px;border-radius:5px;min-height:44px;}
        .cv-group .cv-btn:hover{background:var(--bg);}
        .cv-group .cv-btn.active{background:var(--acc2);color:var(--bg);}
        .cv-group-label{position:absolute;top:-6px;left:8px;background:var(--sf);padding:0 4px;font-size:8px;text-transform:uppercase;letter-spacing:.6px;color:var(--dim);font-weight:600;display:none;}
        /* PR-AUDIT-UX-P1 (2026-05-09): tap target 28→44px for tablet help-tooltip access. */
        .cv-help-btn{margin-left:auto;background:var(--inp);border:1px solid var(--bdr);color:var(--dim);width:44px;height:44px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600;display:inline-flex;align-items:center;justify-content:center;transition:color .12s,border-color .12s;}
        .cv-help-btn:hover{color:var(--acc);border-color:var(--acc);}

        /* Viewport polish — gradient backdrop sits behind the canvas; the engine
           clears with its own color so this only shows if engine renders transparent.
           More importantly, this makes the surrounding chrome read as integrated. */
        .cab-viewer{background:var(--bg);}

        /* Axis gizmo — small XYZ indicator pinned to the viewport corner */
        .axis-gizmo{position:absolute;bottom:60px;right:14px;width:64px;height:64px;pointer-events:none;z-index:6;font-family:'Inter',ui-monospace,monospace;font-size:9px;font-weight:700;}
        .axis-gizmo .ax-c{position:absolute;left:50%;top:50%;width:4px;height:4px;background:#fff;border-radius:50%;transform:translate(-50%,-50%);}
        .axis-gizmo .ax-line{position:absolute;left:50%;top:50%;width:24px;height:2px;transform-origin:left center;}
        .axis-gizmo .ax-x{background:#FF4D6D;transform:translate(0,-1px) rotate(0deg);}
        .axis-gizmo .ax-y{background:#7CFF8E;transform:translate(0,-1px) rotate(-90deg);}
        .axis-gizmo .ax-z{background:#5BB8FF;transform:translate(0,-1px) rotate(135deg);width:18px;}
        .axis-gizmo .ax-lbl{position:absolute;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,0.8);}
        .axis-gizmo .ax-lbl-x{left:calc(50% + 24px);top:calc(50% - 6px);color:#FF4D6D;}
        .axis-gizmo .ax-lbl-y{left:calc(50% - 3px);top:calc(50% - 24px);color:#7CFF8E;}
        .axis-gizmo .ax-lbl-z{left:calc(50% - 18px);top:calc(50% + 12px);color:#5BB8FF;}

        /* Shortcuts overlay — keymap reference for power users */
        .shortcuts-modal{position:fixed;inset:0;background:rgba(8,10,14,0.55);backdrop-filter:blur(10px) saturate(140%);-webkit-backdrop-filter:blur(10px) saturate(140%);z-index:350;display:flex;align-items:center;justify-content:center;animation:sc-fade .18s ease-out;}
        @keyframes sc-fade{0%{opacity:0;}100%{opacity:1;}}
        .shortcuts-modal.hidden{display:none;}
        /* PR-STUDIO-P3 #14f (2026-07-16): visible keyboard-focus ring for the a11y
           cheat-sheet + the (already tabindex=0) 3D canvas, which had outline:none. */
        #shortcuts-modal :focus-visible{outline:2px solid var(--acc);outline-offset:2px;border-radius:4px;}
        #bevy-canvas:focus-visible{outline:2px solid var(--acc);outline-offset:-2px;}
        .shortcuts-modal .sc-panel{background:rgba(18,18,18,0.85);backdrop-filter:blur(20px) saturate(160%);-webkit-backdrop-filter:blur(20px) saturate(160%);border:1px solid rgba(255,255,255,0.08);border-radius:16px;padding:24px;width:560px;max-width:90vw;max-height:80vh;overflow-y:auto;box-shadow:0 24px 70px rgb(var(--shadow-ink) / 0.7),0 0 0 1px color-mix(in srgb, var(--acc) 6%, transparent) inset;}
        .shortcuts-modal h3{margin:0 0 4px;color:var(--white);font-size:18px;}
        .shortcuts-modal .sc-sub{color:var(--dim);font-size:12px;margin-bottom:18px;}
        .shortcuts-modal h4{color:var(--acc);font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin:14px 0 6px;font-weight:600;}
        .shortcuts-modal .sc-row{display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid var(--bdr);font-size:13px;color:var(--txt);}
        .shortcuts-modal .sc-row:last-child{border-bottom:none;}
        .shortcuts-modal .sc-keys{display:flex;gap:4px;}
        .shortcuts-modal .kbd{background:var(--bg);color:var(--white);font-size:11px;font-weight:600;padding:3px 8px;border-radius:5px;border:1px solid var(--bdr);font-family:ui-monospace,monospace;min-width:22px;text-align:center;}

        /* HUD silenced by default — opt in via the gear icon. The HUD was
           leaking developer-debug noise (red error strings) into the user UI;
           toasts now own user-facing notifications. */
        #scene-hud{display:none;}
        #scene-hud.show{display:block;}
        /* PR-VIZ-FPS (2026-06-01): render-FPS readout, top-left of the 3D
           view. Hidden until the Bevy poll loop starts (Rich-3D mounted),
           then `.show`n. pointer-events:none so it NEVER intercepts canvas
           clicks/drags (the drag-drop we just fixed lives on this surface). */
        #fps-counter{
            position:absolute; top:8px; left:8px; z-index:5;
            display:none; padding:2px 7px;
            font-family:var(--ff-mono); font-size:var(--fs-sm);
            font-variant-numeric:tabular-nums; line-height:1.45;
            color:var(--toast-fg); background:var(--toast-bg);
            border:1px solid var(--toast-bdr); border-radius:var(--radius-sm);
            box-shadow:var(--shadow-2); opacity:.82;
            pointer-events:none; user-select:none;
        }
        #fps-counter.show{display:block;}
        /* PR-2D-2 (2026-06-06): live dimension-value labels over the plan view.
           Rust formats the fraction + emits normalized [0,1] viewport coords;
           JS only positions these divs. pointer-events:none on BOTH the layer
           AND each label is mandatory — the canvas DnD/winit handlers listen on
           document + project against #bevy-canvas's rect, so a label that ate
           pointer events would re-break palette placement. Tokens copied from
           #fps-counter (the only pure-token canvas overlay). */
        #dim-labels{position:absolute; inset:0; z-index:5; pointer-events:none; overflow:hidden;}
        #dim-labels .dim-label{
            position:absolute; top:0; left:0; transform:translate(-50%,-50%);
            padding:0 2px;
            font-family:var(--ff-mono);
            font-variant-numeric:tabular-nums; line-height:1.15; white-space:nowrap;
            /* PR-DIM-SMALLER (2026-07-08): plain, informative INK — not a toast
               chip. Dark ink + a white halo reads on the light Plan paper AND over
               the 3D scene, without the bg/border/shadow chrome. Size comes from
               the Rust `fpx` inline style (shrunk in label_font_px). */
            color:var(--toast-bg); background:transparent;
            border:none; border-radius:0; box-shadow:none; opacity:1; font-weight:600;
            text-shadow:0 0 2px #fff,0 0 3px #fff,0 0 4px #fff;
            pointer-events:none; user-select:none; display:none;
        }
        #dim-labels .dim-label.show{display:block;}
        /* PR-2D-LINEDRAW3 (2026-06-06): cabinet-number labels read as INK ON
           PAPER, not a dimension toast chip — no chip bg/border/shadow, dark-ink
           text via the fixed --toast-bg (the only theme-stable dark token; the
           Bevy paper canvas is ALWAYS light, so a theme-flipping ink token would
           vanish on the dark UI theme). Centered in the footprint like the
           operator's reference sheets. */
        #dim-labels .dim-label.cabnum{
            background:transparent; border:none; box-shadow:none; padding:0;
            color:var(--toast-bg); opacity:1; font-weight:600;
            /* PR-DIM-3D (2026-06-28): a white paper halo so the dark-ink number
               stays legible over the rendered 3D scene (any cabinet colour). On
               the white Plan canvas the white halo is invisible — zero change to
               the paper look — but in Perspective it lifts the ink off the box. */
            text-shadow:0 0 2px #fff, 0 0 3px #fff, 0 0 4px #fff;
        }
        /* PR-VIZ-2D-ELEV-DIMS (2026-06-17): elevation dimension VALUE chips reuse
           the default dim-label chip — no extra rule. The empty-wall note reads
           as a soft centered hint (no chip, muted, italic) so it's clearly an
           annotation, not a measurement. */
        #dim-labels .dim-label.elevempty{
            background:transparent; border:none; box-shadow:none;
            color:var(--toast-bg); opacity:.7; font-style:italic; font-weight:500;
        }
        /* PR-CLEAN-5 / DEAD-LEGACY-2 (2026-05-31): .hud-toggle CSS deleted —
           the button was already hidden+disabled in markup, and the Shift+D
           keyboard shortcut still toggles the HUD for power users. */

        /* PR-APPLE-INSP (2026-07-21): Apple-grade inspector — inset-grouped section
           CARDS on the quiet page background, ONE hairline, quiet receding headers,
           sticky-on-scroll. Depth = the --bg→--pn surface step, never heavy borders
           or shadows. Replaces the old full-bleed border-bottom divider list. */
        .insp-section{background:var(--pn);border:1px solid var(--bdr);border-radius:var(--radius-md,10px);margin:0 10px 10px;overflow:hidden;}
        .insp-head{display:flex;align-items:center;gap:8px;padding:0 14px;min-height:44px;cursor:pointer;user-select:none;color:var(--dim);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;position:sticky;top:0;background:var(--pn);z-index:2;}
        .insp-head:hover{background:var(--pn-h);}
        .insp-head .insp-arrow{color:var(--muted);font-size:11px;transition:transform var(--dur-chrome,.16s);}
        .insp-section.collapsed .insp-head{position:static;}
        .insp-section.collapsed .insp-arrow{transform:rotate(-90deg);}
        .insp-section.collapsed .insp-body{display:none;}
        .insp-body{padding:8px 14px 12px;}
        /* PR-FACE-DECLUTTER (2026-06-07): right-aligned header actions
           (Equal / Reset / Remove) on a collapsible section; stopPropagation
           on the wrapper keeps a button tap from toggling the section. The
           keep-open accent marks the primary editorial section (Face). */
        .insp-head-actions{margin-left:auto;display:flex;gap:4px;align-items:center;}
        .insp-head-actions button{font-size:10px;padding:4px 9px;line-height:1;}
        .insp-section.insp-keep-open > .insp-head{color:var(--acc);}
        /* PR-FACE-CANVAS (2026-06-07): the live 2D face preview. Rects come from
           the WASM resolveFaceLayout bridge (no JS geometry); tap a region to
           jump to its editor row. Every color is a token. */
        .fc-wrap{margin:0 0 9px;}
        .fc-canvas{width:100%;height:auto;max-height:280px;display:block;border:1px solid var(--bdr);border-radius:6px;background:var(--bg);}
        .fc-region{cursor:pointer;}
        .fc-region rect{stroke:var(--bg);stroke-width:1.5;transition:filter .12s;}
        .fc-region:hover rect{stroke:var(--acc);stroke-width:2;filter:brightness(1.08);}
        .fc-lbl{fill:var(--txt);font-size:9px;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;}
        .fc-row-flash{outline:2px solid var(--acc);outline-offset:2px;border-radius:4px;}
        /* PR-FACE-FORMULA (2026-06-07): per-section sizing-mode control
           [in][ƒ][%] + the formula field, variable chips, and inline validation
           (green = valid via the same save-gate the server runs; amber = fix).
           Every color is a token. */
        .fc-seg-group{display:inline-flex;border:1px solid var(--bdr);border-radius:5px;overflow:hidden;flex:0 0 auto;}
        .fc-seg{background:var(--inp);color:var(--dim);border:none;border-right:1px solid var(--bdr);padding:5px 6px;font-size:10px;font-weight:600;cursor:pointer;min-width:22px;line-height:1;}
        .fc-seg:last-child{border-right:none;}
        .fc-seg.on{background:var(--acc);color:var(--acc-ink);}
        .fc-seg:hover:not(.on){background:var(--pn-h);color:var(--txt);}
        input.fc-fx{font-family:ui-monospace,monospace;}
        input.fc-fx.fc-fx-ok{border-color:var(--ok);}
        input.fc-fx.fc-fx-bad{border-color:var(--warn);}
        .fc-fx-row{display:flex;align-items:center;gap:6px;margin:0 0 6px 44px;flex-wrap:wrap;}
        .fc-fx-vars{display:inline-flex;gap:3px;}
        .fc-var{background:var(--inp);color:var(--acc);border:1px solid var(--bdr);border-radius:4px;padding:2px 6px;font-size:9px;font-family:ui-monospace,monospace;cursor:pointer;}
        .fc-var:hover{background:var(--pn-h);}
        .fc-fx-msg{font-size:9px;}
        .fc-fx-msg.ok{color:var(--ok);}
        .fc-fx-msg.bad{color:var(--warn);}
        /* PR-FACE-SWING (2026-06-07): per-door hinge-side toggle (L/R). */
        .fc-swing{background:var(--inp);color:var(--acc);border:1px solid var(--bdr);border-radius:4px;padding:4px 7px;font-size:10px;font-weight:700;cursor:pointer;line-height:1;min-width:22px;flex:0 0 auto;}
        .fc-swing:hover{background:var(--pn-h);}
        .insp-row{display:flex;justify-content:space-between;align-items:center;gap:var(--sp-2,8px);padding:var(--sp-row,10px) 0;min-height:var(--row-h,44px);font-size:var(--fs-sm,12px);color:var(--dim);}
        .insp-row b{color:var(--txt);font-family:var(--ff-mono);font-weight:var(--fw-medium,500);font-variant-numeric:tabular-nums;}
        /* PR-CAB-INSPECTOR: primary zone — always visible, no accordion.
           Holds the cabinet name + W/H/D + Door Style + Hinge Side, the
           ~6 fields industry leaders (Mozaik, CabinetVision) put above
           the fold. Width input auto-focused on cabinet select. */
        .insp-primary{padding:12px 14px 8px;border:1px solid var(--bdr);border-radius:var(--radius-md,10px);margin:0 10px 10px;background:var(--pn);}
        .insp-cab-header{display:flex;align-items:baseline;gap:8px;margin-bottom:10px;}
        .insp-cab-name{flex:1;background:transparent;border:none;border-bottom:1px solid transparent;color:var(--txt);font-size:15px;font-weight:600;letter-spacing:-.01em;padding:4px 0;outline:none;min-width:0;}
        .insp-cab-name:hover{border-bottom-color:var(--bdr);}
        .insp-cab-name:focus{border-bottom-color:var(--acc);}
        .insp-cab-type{color:var(--dim);font-size:10px;text-transform:uppercase;letter-spacing:.4px;white-space:nowrap;}
        /* PR-SUBLIB-1 Phase 3: sub-library badge — the editor adapts to this kind. */
        .insp-cab-shelf{color:var(--acc2);font-size:10px;font-weight:700;white-space:nowrap;padding:2px 7px;border:1px solid var(--bdr);border-radius:999px;background:var(--inp);}
        /* PR-SUBLIB-2: nested sub-library tiles inside the Cabinet library (Library Hub). */
        .lib-sublib-head{font-size:12px;font-weight:700;color:var(--white);padding:10px 4px 8px;display:flex;align-items:baseline;gap:8px;}
        .lib-sublib-sub{color:var(--dim);font-weight:400;font-size:10px;}
        .lib-sublib-hint{margin-left:auto;color:var(--dim);font-weight:400;font-size:10px;}
        .lib-sublib-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;padding:4px;}
        .lib-sublib-tile{display:flex;flex-direction:column;align-items:flex-start;gap:4px;padding:14px;background:var(--inp);border:1px solid var(--bdr);border-radius:10px;cursor:pointer;text-align:left;transition:border-color .12s,transform .06s;min-height:84px;}
        .lib-sublib-tile:hover{border-color:var(--acc2);transform:translateY(-1px);}
        .lib-sublib-icon{font-size:22px;line-height:1;}
        .lib-sublib-name{font-size:13px;font-weight:600;color:var(--white);}
        .lib-sublib-count{font-size:10px;color:var(--dim);}
        .lib-sublib-back{display:flex;align-items:center;gap:10px;padding:8px 4px;}
        .lib-sublib-backbtn{background:var(--inp);border:1px solid var(--bdr);border-radius:6px;color:var(--white);font-size:11px;font-weight:600;padding:5px 10px;cursor:pointer;}
        .lib-sublib-backbtn:hover{border-color:var(--acc2);}
        .lib-sublib-crumb{font-size:12px;color:var(--dim);}
        .lib-sublib-crumb b{color:var(--white);}
        .insp-dim-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:2px;}
        .insp-dim-cell{display:flex;flex-direction:column;gap:3px;}
        .insp-dim-cell label{font-size:11px;letter-spacing:0;color:var(--dim);font-weight:500;text-align:center;}
        .insp-dim-cell input{height:40px;font-size:14px;font-weight:600;text-align:center;background:var(--inp);border:1px solid var(--bdr);border-radius:var(--radius-sm,6px);color:var(--txt);padding:0 6px;width:100%;font-variant-numeric:tabular-nums;font-family:var(--ff-mono);}
        .insp-dim-cell input:focus{border-color:var(--acc);outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--acc) 30%,transparent);}
        .insp-dim-hints{display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:8px;}
        /* PR-AUDIT-CYCLE-4 (2026-05-12, ux F-13): bumped 9→12px so the
           live fraction preview ("→ 36.5") is readable at arm's length
           on a dust-coated iPad in shop lighting. The 40px-tall dim-cell
           still accommodates the 16px min-height. */
        .insp-dim-hints .frac-hint{font-size:12px;color:var(--acc2);text-align:center;min-height:16px;line-height:16px;font-variant-numeric:tabular-nums;}
        /* PR-WIZ-UX: same .frac-hint class reused for the New Job wizard's live
           fraction echo under each dimension input — left-aligned (matches the
           left-aligned form fields), tabular numerals, empty when idle. Color is
           set inline (var(--acc) valid / var(--no) invalid). */
        #cab-newjob-modal .frac-hint{font-size:12px;margin-top:3px;min-height:14px;line-height:14px;font-variant-numeric:tabular-nums;}
        .insp-prim-field{margin-top:8px;}
        .insp-prim-field label,
        .insp-prim-field-label{display:block;font-size:12px;text-transform:none;letter-spacing:0;color:var(--dim);font-weight:500;margin-bottom:4px;}
        /* PR-AUDIT-UX-P1 (2026-05-09): primary inspector selects (cabinet type / face config / material) hit on every cabinet edit — 36→44px. */
        .insp-prim-field select{width:100%;height:44px;background:var(--inp);border:1px solid var(--bdr);border-radius:var(--radius-sm,6px);color:var(--txt);padding:0 8px;font-size:13px;}
        .insp-prim-field select:focus{border-color:var(--acc);outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--acc) 30%,transparent);}
        /* PR-INSPECTOR-CLARITY (2026-05-09): logical group dividers in
           the always-visible primary zone. Operator's mental model on
           the shop floor flows: Box → Front face → Inside → Appliance
           → Mount. Pre-PR every field rendered as a flat list with no
           visual grouping; on a 30-cabinet job the inspector felt like
           one long undifferentiated scroll. The divider has zero
           interactive behavior (not collapsible — primary fields
           must stay visible) — it's pure typography. */
        .insp-prim-group{margin-top:14px;padding-top:10px;border-top:1px solid var(--bdr);}
        .insp-prim-group:first-of-type{margin-top:6px;padding-top:0;border-top:none;}
        .insp-prim-group-head{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:600;letter-spacing:.04em;color:var(--dim);text-transform:uppercase;margin-bottom:6px;}
        .insp-prim-group-head .insp-prim-group-icon{font-size:14px;line-height:1;}
        .insp-prim-group-head .insp-prim-group-help{margin-left:auto;font-size:9px;color:var(--dim);font-weight:500;text-transform:none;letter-spacing:0;}
        /* PR-WALL-FIT-SUGGEST (2026-09-14): "sticks through the wall" callout under
           the Box Dimensions header. Amber edge, compact — one line of what's wrong,
           one line of the fix, small action buttons (primary filled). */
        .insp-wallfit{margin:0 0 8px;padding:7px 10px;border:1px solid color-mix(in srgb,var(--warn,#c80) 45%,var(--bdr));border-left:3px solid var(--warn,#c80);border-radius:var(--radius-sm,6px);background:color-mix(in srgb,var(--warn,#c80) 8%,var(--inp));}
        .insp-wallfit-h{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--txt);}
        .insp-wallfit-b{font-size:11px;line-height:1.45;color:var(--dim);margin-top:2px;}
        .insp-wallfit-a{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;}
        .insp-wallfit-a .cv-btn{min-height:32px;padding:4px 10px;font-size:12px;white-space:nowrap;}
        .insp-wallfit-a .insp-wallfit-go{background:var(--warn,#c80);border-color:var(--warn,#c80);color:#fff;font-weight:600;}
        /* PR-CUTAWAY-V0 (2026-05-09): SVG cutaway preview styling. */
        .insp-cutaway{margin-top:10px;padding:10px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;}
        /* PR-CAB-INSPECTOR-REORDER (2026-07-29): click-to-collapse Inside View.
           Collapsed hides the canvas + footer, keeps the head + summary line, and
           tucks the padding so the row is compact. Caret rotates to signal state. */
        .insp-cutaway-head{cursor:pointer;user-select:none;}
        .insp-cutaway-head .insp-cutaway-caret{font-size:10px;line-height:1;color:var(--dim);transition:transform var(--dur-quick,120ms) var(--ease-out,ease);transform:rotate(90deg);}
        .insp-cutaway.collapsed{padding-bottom:8px;}
        .insp-cutaway.collapsed .insp-cutaway-head{margin-bottom:0;}
        .insp-cutaway.collapsed .insp-cutaway-caret{transform:rotate(0deg);}
        .insp-cutaway.collapsed .insp-cutaway-canvas,
        .insp-cutaway.collapsed .insp-cutaway-foot{display:none;}
        .insp-cutaway-head{display:flex;align-items:baseline;gap:6px;font-size:11px;font-weight:700;letter-spacing:.5px;color:var(--white);text-transform:uppercase;margin-bottom:6px;}
        .insp-cutaway-head .insp-cutaway-icon{font-size:14px;line-height:1;}
        .insp-cutaway-head .insp-cutaway-summary{margin-left:auto;font-size:10px;font-weight:500;color:var(--dim);text-transform:none;letter-spacing:0;}
        .insp-cutaway-canvas{display:flex;justify-content:center;align-items:center;min-height:120px;}
        .insp-cutaway-foot{margin-top:6px;font-size:10px;color:var(--dim);line-height:1.4;font-style:italic;}
        /* ===== PR-APPLE-INSP (2026-07-21): identity header + shared control kit ===== */
        .insp-hdr{display:flex;gap:10px;align-items:flex-start;padding:12px 12px 0;margin-bottom:10px;}
        .insp-hdr-thumb{flex:0 0 40px;width:40px;height:40px;border-radius:var(--radius-sm,6px);background:var(--inp);border:1px solid var(--bdr);display:flex;align-items:center;justify-content:center;color:var(--dim);overflow:hidden;}
        .insp-hdr-thumb svg{width:34px;height:34px;display:block;}
        .insp-hdr-main{min-width:0;flex:1;display:flex;flex-direction:column;gap:5px;}
        .insp-hdr-name{background:transparent;border:none;border-bottom:1px solid transparent;color:var(--txt);font-size:15px;font-weight:600;letter-spacing:-.01em;padding:1px 0;outline:none;min-width:0;width:100%;}
        .insp-hdr-name:hover{border-bottom-color:var(--bdr);}
        .insp-hdr-name:focus{border-bottom-color:var(--acc);}
        .insp-hdr-badge{align-self:flex-start;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--dim);background:color-mix(in srgb,var(--acc) 10%,transparent);border:1px solid var(--bdr);border-radius:999px;padding:2px 8px;line-height:1.4;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis;}
        .insp-qa{display:flex;gap:6px;align-items:center;margin:0 12px 12px;justify-content:flex-end;}
        .insp-qa button{width:28px;height:28px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;background:var(--inp);border:1px solid var(--bdr);border-radius:var(--radius-sm,6px);color:var(--dim);cursor:pointer;padding:0;transition:background var(--dur-quick,.12s),border-color var(--dur-quick,.12s),color var(--dur-quick,.12s);}
        .insp-qa button svg{width:15px;height:15px;}
        .insp-qa button:hover{background:var(--pn-h);border-color:var(--bdr-h);color:var(--txt);}
        .insp-qa button.insp-qa-del:hover{color:var(--no);border-color:color-mix(in srgb,var(--no) 40%,var(--bdr));}
        .insp-qa .insp-qa-sep{width:1px;height:20px;background:var(--bdr);margin:0 2px;flex:0 0 auto;}
        /* segmented control — the ONE control for any 2-4 value enum */
        .insp-seg{display:inline-flex;background:var(--sf);border:1px solid var(--bdr);border-radius:8px;padding:2px;gap:2px;width:100%;}
        .insp-seg button{flex:1;min-height:28px;border:none;background:transparent;color:var(--dim);font-size:12px;font-weight:500;border-radius:6px;cursor:pointer;padding:0 8px;white-space:nowrap;}
        .insp-seg button:hover:not(.on){color:var(--txt);}
        .insp-seg button.on{background:var(--pn);color:var(--txt);box-shadow:var(--shadow-1,0 1px 2px rgb(var(--shadow-ink) / .25));}
        /* toggle switch — the ONE control for a boolean */
        .insp-toggle{position:relative;flex:0 0 auto;width:40px;height:24px;border-radius:999px;background:var(--bdr);border:none;cursor:pointer;padding:0;transition:background var(--dur-quick,.12s);}
        .insp-toggle::after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:999px;background:var(--pn);box-shadow:var(--shadow-1,0 1px 2px rgb(var(--shadow-ink) / .3));transition:transform var(--dur-quick,.12s);}
        .insp-toggle[aria-checked="true"]{background:var(--acc);}
        .insp-toggle[aria-checked="true"]::after{transform:translateX(16px);background:var(--acc-ink);}
        /* pinned placement card — the Cabinet-Vision numeric grid */
        .insp-place-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px 8px;margin-top:8px;}
        .insp-place-note{margin-top:8px;font-size:11px;color:var(--muted);line-height:1.4;}
        .insp-place-note.warn{color:var(--warn);}
        .insp-place-note.bad{color:var(--no);}
        /* clean empty state — "pick something", not the room tool palette */
        .insp-empty2{display:flex;flex-direction:column;align-items:center;text-align:center;padding:44px 20px 12px;gap:8px;}
        .insp-empty2 svg{width:32px;height:32px;color:var(--muted);}
        .insp-empty2 .ie-title{font-size:13px;color:var(--dim);font-weight:500;}
        .insp-empty2 .ie-help{font-size:11px;color:var(--muted);line-height:1.4;max-width:180px;}
        /* PR-S0-1 (2026-05-09): convert popup #cab-editor-modal to full-
           screen Cabinet Detail context with tab strip header. Pre-PR
           the modal was a 880px-wide right-drawer popup; post-PR it
           takes the whole viewport and uses a 6-tab navigation matching
           Cabinet Vision Section View / Mozaik Product Editor patterns
           per UX research. Today's content lives in the "Faces" tab;
           other 5 tabs are stubs filled in by Sprint 1+ PRs. */
        #cab-editor-modal.cab-editor-fullscreen {
            background: var(--bg, #0e1116);
            backdrop-filter: none; -webkit-backdrop-filter: none;
            justify-content: stretch; align-items: stretch;
            padding: 0;
        }
        #cab-editor-modal.cab-editor-fullscreen > .cab-editor-shell {
            position: relative;
            width: 100vw; height: 100vh;
            max-width: none; padding: 0;
            display: flex; flex-direction: column;
            background: var(--bg, #0e1116);
        }
        .cab-editor-topbar {
            display: flex; align-items: center; gap: 12px;
            padding: 10px 16px;
            border-bottom: 1px solid var(--bdr);
            background: var(--bg-card, #14181f);
        }
        .cab-editor-topbar .cab-editor-back-btn {
            font-size: 13px; padding: 8px 14px;
            min-height: 40px;
            background: var(--inp); color: var(--white);
            border: 1px solid var(--bdr); border-radius: 6px;
            cursor: pointer;
        }
        .cab-editor-topbar .cab-editor-back-btn:hover { border-color: var(--acc2); }
        .cab-editor-topbar h2 { margin: 0; font-size: 16px; }
        .cab-editor-topbar .cab-editor-spacer { flex: 1; }
        .cab-editor-topbar .cab-editor-toolbar-actions { display: flex; gap: 8px; }
        .cab-editor-tabstrip {
            display: flex; gap: 0;
            padding: 0 16px;
            border-bottom: 1px solid var(--bdr);
            background: var(--bg-card, #14181f);
        }
        .cab-editor-tab {
            padding: 12px 18px; min-height: 44px;
            font-size: 13px; font-weight: 500;
            color: var(--dim); background: transparent;
            border: none; border-bottom: 2px solid transparent;
            cursor: pointer;
        }
        .cab-editor-tab:hover { color: var(--white); }
        .cab-editor-tab.active {
            color: var(--white);
            border-bottom-color: var(--acc2);
        }
        .cab-editor-tab[disabled] { color: var(--dim); opacity: .5; cursor: not-allowed; }
        .cab-editor-tabpane {
            flex: 1;
            overflow: auto;
            padding: 16px;
        }
        .cab-editor-tabpane.hidden { display: none; }
        .cab-editor-tab-stub {
            text-align: center; padding: 80px 20px;
            color: var(--dim);
        }
        .cab-editor-tab-stub h3 { margin: 0 0 8px; color: var(--white); font-size: 16px; }
        .cab-editor-tab-stub p { margin: 0; font-size: 13px; }
        /* PR-UX-POLISH (2026-09-14): `--bg-card` was never defined anywhere in
           this stylesheet, so this bar painted its hardcoded #14181f fallback —
           a near-black chrome colour — underneath the LIGHT theme too. The
           token is now defined (→ --pn); the surface below is named outright so
           the bar reads as chrome against the card ground either way.
           The upward shadow separates the bar from the scrolling content behind
           it, which a 1px line alone never did once the content scrolled under. */
        .cab-editor-bottombar {
            display: flex; justify-content: flex-end; gap: 10px;
            padding: 12px 16px;
            border-top: 1px solid var(--bdr);
            background: var(--sf);
            box-shadow: 0 -1px 0 rgb(var(--shadow-ink) / .04),
                        0 -8px 24px rgb(var(--shadow-ink) / .06);
        }

        /* PR-BLD-EDITOR-V0 (2026-05-09): focused cabinet-editor modal. */
        /* PR-AUDIT-FIXUP-UX (2026-05-09): the duplicate verbatim copy
           of the .cab-editor-* CSS block previously sat here (UX
           audit P2-6 — copy-paste artifact from PR-S0-1's modal-to-
           fullscreen conversion). First block at line ~2134 wins via
           source order; this duplicate is removed to prevent
           silent divergence on future style edits. */

        /* PR-AUDIT-FIXUP-UX (2026-05-09): bottom-bar Save / Cancel
           buttons and the Back-to-Room button get explicit min-height
           44px tap targets per WCAG 2.5.5 + shop-floor-tablet
           standards. Pre-fix Save was ~28-30px (UX audit P0-3) and
           Back was 40px (P1-1). Save is the cutlist-affecting
           commit action; missing the tap discards correctly-typed
           dimensions. */
        /* PR-UX-POLISH (2026-09-14): ONE button system for this bar.
           Before: Cancel/Save were 44px at radius 6, "Shop drawing" was 44px at
           radius 5, and the two snapshot buttons were 48px at radius 6 — three
           heights and two radii in a single row, which is what made the bar
           read as assembled rather than designed. Every button in the bar now
           takes the same height, radius, weight and transition; only the FILL
           differs, which is the one thing that should carry hierarchy. */
        .cab-editor-bottombar > button,
        .cab-editor-bottombar > .ba,
        .cab-editor-bottombar > .bg,
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn,
        .cab-editor-bottombar .ced-toolbar-snapshot-btn {
            min-height: var(--ctl-h);
            padding: 0 18px;
            font: 600 13px var(--ff-ui);
            letter-spacing: 0;
            border-radius: var(--radius-md);
            display: inline-flex;
            align-items: center;
            justify-content: center;
            transition: background var(--dur-quick, 120ms) var(--ease-out, ease),
                        border-color var(--dur-quick, 120ms) var(--ease-out, ease),
                        box-shadow var(--dur-quick, 120ms) var(--ease-out, ease),
                        transform var(--dur-quick, 120ms) var(--ease-out, ease);
        }
        /* Primary — the commit action. The only filled button in the bar, and
           the only one carrying a shadow, so the eye lands on it first. The
           shadow is tinted with the accent itself rather than black: a coloured
           button casting a grey shadow is the giveaway of a default theme. */
        .cab-editor-bottombar > .ba {
            background: var(--acc); color: var(--acc-ink); border: 1px solid transparent;
            box-shadow: 0 1px 2px rgb(var(--shadow-ink) / .10),
                        0 2px 8px color-mix(in srgb, var(--acc) 28%, transparent);
        }
        .cab-editor-bottombar > .ba:hover {
            background: var(--acc2);
            box-shadow: 0 2px 4px rgb(var(--shadow-ink) / .12),
                        0 6px 16px color-mix(in srgb, var(--acc) 36%, transparent);
        }
        .cab-editor-bottombar > .ba:active { transform: translateY(1px); box-shadow: var(--shadow-1); }
        /* Secondary — an outline on the panel ground. Cancel, and the three
           tools, all read at the same weight because they ARE the same weight
           of decision. */
        .cab-editor-bottombar > .bg,
        .cab-editor-bottombar .ced-toolbar-snapshot-btn,
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn {
            background: var(--pn); color: var(--txt);
            border: 1px solid var(--bdr); box-shadow: none;
        }
        .cab-editor-bottombar > .bg:hover,
        .cab-editor-bottombar .ced-toolbar-snapshot-btn:hover,
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn:hover {
            background: var(--pn-h); border-color: var(--bdr-h); color: var(--txt);
        }
        .cab-editor-bottombar > .bg:active,
        .cab-editor-bottombar .ced-toolbar-snapshot-btn:active,
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn:active { transform: translateY(1px); }
        /* "Shop drawing" keeps a quiet accent tie — it is the one tool that
           opens something, so it earns a coloured label, not a coloured fill. */
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn { color: var(--acc); }
        .cab-editor-bottombar .ced-toolbar-shop-drawing-btn:hover {
            color: var(--acc); border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 7%, var(--pn));
        }
        .cab-editor-topbar .cab-editor-back-btn { min-height: 44px; }

        /* PR-S1-1C-WASM (2026-05-09): Appliance tab styles. Operator
           types per-field cutout override; banner shows source
           (Override / Library / None) + validator error inline.
           Tap targets >=44px per WCAG 2.5.5 + shop-floor-tablet
           operating standards. */
        .cab-app-tab { display: flex; flex-direction: column; gap: 14px; max-width: 720px; }
        .cab-app-banner {
            padding: 10px 12px;
            border-radius: 6px;
            font-size: 12px;
            font-weight: 600;
            display: flex; align-items: center; gap: 8px;
        }
        .cab-app-banner.override {
            background: color-mix(in srgb, var(--acc2) 18%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc2) 40%, transparent);
            color: var(--acc2);
        }
        .cab-app-banner.library {
            background: color-mix(in srgb, var(--dim) 12%, transparent);
            border: 1px solid var(--bdr);
            color: var(--dim);
        }
        .cab-app-banner.none {
            background: color-mix(in srgb, var(--dim) 8%, transparent);
            border: 1px dashed var(--bdr);
            color: var(--dim);
            font-style: italic;
        }
        /* PR-V4-AUDIT-FIXUP-P1-DEFERRED-BANNER (2026-05-12): amber
           "engine emit deferred" banner for V4 Vec sub-sections whose
           data is stored + validated but not yet consumed by the
           cutlist engine. Visually distinct from .override (informational
           amber chip) because this is an active warning — operator
           data entry will silently drop until PR-V4-ENGINE-EMIT-DORMANT
           ships. Closes audit P1-UX-1: pre-PR the "deferred" notice
           was buried in dim grey footnote text — operator skipped it. */
        /* PR-V4-PHASE-B-3-EDITOR (2026-05-12): floating popover that
           appears at click position when the operator taps a V4
           partMeta-tagged mesh in the 3D view. Carries kind + idx
           info + "Edit in Inspector" CTA that switches to the
           relevant tab + scrolls to the row (PR-V4-PHASE-B-4-DISPATCH).
           Sized for tablet ergonomics: 240px min width, 44px tap
           targets, high-contrast against the dark 3D canvas. */
        .cab3d-part-popover {
            position: fixed; z-index: 10000;
            min-width: 240px; max-width: 320px;
            padding: 12px 14px;
            background: rgba(20, 28, 38, 0.97);
            border: 1px solid rgba(167, 139, 250, 0.55);
            border-radius: 8px;
            box-shadow: 0 6px 24px rgb(var(--shadow-ink) / 0.5);
            color: #e8e8ee;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            pointer-events: auto;
        }
        .cab3d-part-popover-title {
            font-size: 12px; font-weight: 600;
            color: #A78BFA;
            text-transform: uppercase; letter-spacing: 0.6px;
            margin-bottom: 6px;
        }
        /* PR-V4-3D-PHASE-B-UX-P2 (2026-05-12): font sizes raised per
           UX-specialist post-audit Finding 6 — body 13→14px, code
           chips 11→12px. Reasoning: at 18" viewing distance on an
           iPad with safety glasses (polycarbonate, ~0.5 diopter
           peripheral distortion), 13px renders as ~6.5pt physical,
           below the ISO 9241-303 §5.2 9pt floor for industrial HMI
           panels. 14px = ~7pt — still below 9pt but a meaningful
           improvement for shop-floor readability. The full 9pt fix
           requires Display-density CSS units (which the codebase
           doesn't use yet); the px raise is the surgical win. */
        .cab3d-part-popover-body {
            font-size: 14px; line-height: 1.4;
            color: #cce;
            margin-bottom: 10px;
        }
        .cab3d-part-popover-body code {
            background: rgba(255,255,255,0.06);
            padding: 1px 4px; border-radius: 3px;
            font-size: 12px;
        }
        .cab3d-part-popover-actions {
            display: flex; gap: 6px; flex-direction: column;
        }
        /* PR-V4-3D-PHASE-B-UX-P2: stack action buttons vertically so
           the Dismiss row sits BELOW Edit-in-Inspector. Stacked
           buttons read more naturally on tablet (primary CTA on top,
           secondary on bottom) than side-by-side which crowds them. */
        .cab3d-part-popover-btn {
            width: 100%; padding: 10px 12px; min-height: 44px;
            border: 1px solid rgba(167,139,250,0.4);
            background: rgba(167,139,250,0.10);
            color: #A78BFA;
            border-radius: 5px;
            font-size: 13px; font-weight: 600;
            cursor: pointer;
        }
        .cab3d-part-popover-btn:hover {
            background: rgba(167,139,250,0.18);
        }
        /* Secondary "Dismiss" action — less amber, more muted. Same
           tap-target floor. UX P2 Finding 7: gloved operators want a
           large dismiss target without commiting to inspector navigation. */
        .cab3d-part-popover-btn.secondary {
            border: 1px solid rgba(255,255,255,0.15);
            background: rgba(255,255,255,0.04);
            color: #ccd;
        }
        .cab3d-part-popover-btn.secondary:hover {
            background: rgba(255,255,255,0.08);
        }
        .cab3d-part-popover-close {
            position: absolute; top: 4px; right: 4px;
            background: transparent; border: 0; color: #888;
            font-size: 22px; line-height: 1; padding: 8px 10px;
            cursor: pointer;
            /* PR-V4-AUDIT-FIXUP-3-JS (2026-05-12, sentinel L-3 + UX P0
               Finding 3): bumped 32px → 44px to match the shop-floor
               tap-target floor (iOS / Material). Operators in gloves
               need a forgiving touch area. */
            min-height: 44px; min-width: 44px;
        }
        .cab3d-part-popover-close:hover { color: #A78BFA; }

        .cab-app-banner.deferred-emit {
            background: color-mix(in srgb, #ffa726 14%, transparent);
            border: 1px solid color-mix(in srgb, #ffa726 55%, transparent);
            color: #ffa726;
            font-weight: 600;
        }
        /* PR-CTOR-V3-DRAWER-HW + DOOR-OP + MATERIALS + JOINERY
           (2026-05-12): inline override badge next to the section's
           input label. Shop hint, not a manufacturing-blocker — amber
           edge matching the existing banner.override styling. */
        .cab-app-badge {
            display: inline-flex; align-items: center;
            padding: 2px 7px; margin-left: 6px;
            border-radius: 10px;
            font-size: 9px; font-weight: 700;
            letter-spacing: 0.4px; text-transform: uppercase;
            vertical-align: middle;
        }
        .cab-app-badge-override {
            background: color-mix(in srgb, var(--acc2) 30%, transparent);
            color: var(--acc2);
            border: 1px solid color-mix(in srgb, var(--acc2) 50%, transparent);
        }
        .cab-app-toggle {
            display: inline-flex; align-items: center; gap: 8px;
            font-size: 12px; color: var(--white);
            cursor: pointer;
        }
        .cab-app-toggle input[type="checkbox"] {
            width: 18px; height: 18px;
            accent-color: var(--acc2);
            cursor: pointer;
        }
        /* PR-1a Back panel rebuild (2026-05-12): inline plan-view
           cross-section wrap + caption + the no-cite error block
           when dado_depth >= side_thickness (geometric, not
           standards-cited). */
        .cab-cnst-back-svg-wrap {
            margin-top: 12px;
            background: rgba(0,0,0,0.18);
            border: 1px solid var(--bdr);
            border-radius: 6px;
            padding: 8px;
        }
        .cab-cnst-back-svg-wrap > svg {
            display: block;
            width: 100%;
        }
        .cab-cnst-back-svg-caption {
            margin-top: 6px;
            text-align: center;
            font-size: 10px;
            color: var(--dim);
            font-style: italic;
        }
        .cab-cnst-back-svg-caption strong {
            color: var(--white);
            font-style: normal;
        }
        .cab-cnst-back-error {
            margin-top: 8px;
            padding: 8px 12px;
            background: color-mix(in srgb, var(--no, #d4504b) 16%, transparent);
            border-left: 3px solid var(--no, #d4504b);
            border-radius: 4px;
            color: var(--no, #d4504b);
            font-size: 11px;
            font-weight: 600;
        }
        /* PR-CTOR-V1-ACCORDION-SHELL (2026-05-10): single-open
           accordion for the Construction tab. UX-specialist primary
           recommendation for portrait 10" tablet (5-6 sections at
           400+ px scroll is too much). Section headers are the
           primary tap target — 56px min per the WCAG 2.5.5 + shop-
           floor convention for primary navigation. Single-open: only
           one section's body is visible at a time so the mini 3D
           preview stays in viewport. */
        .cab-cnst-accordion {
            display: flex; flex-direction: column;
            gap: 8px;
        }
        .cab-cnst-section {
            border: 1px solid var(--bdr);
            border-radius: 6px;
            background: var(--inp);
            overflow: hidden;
        }
        .cab-cnst-section.open {
            border-color: var(--acc2);
        }
        .cab-cnst-section-head {
            display: flex; align-items: center; gap: 8px;
            width: 100%;
            min-height: 56px;
            padding: 12px 16px;
            background: transparent;
            color: var(--white);
            border: none;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            text-align: left;
            transition: background 220ms cubic-bezier(0.32,0.72,0.24,1);
        }
        .cab-cnst-section-head:hover {
            background: color-mix(in srgb, var(--acc2) 8%, var(--inp));
        }
        .cab-cnst-section-head:focus-visible {
            outline: 2px solid var(--acc2);
            outline-offset: -2px;
        }
        .cab-cnst-section.open .cab-cnst-section-head {
            background: color-mix(in srgb, var(--acc2) 12%, var(--inp));
        }
        .cab-cnst-section-title {
            flex: 1;
        }
        .cab-cnst-section-badge {
            padding: 2px 8px;
            border-radius: 11px;
            font-size: 9px;
            font-weight: 700;
            letter-spacing: .4px;
            text-transform: uppercase;
        }
        .cab-cnst-section-badge.override {
            background: color-mix(in srgb, var(--tier-cab, #7A3BAF) 22%, var(--inp));
            border: 1px solid var(--tier-cab, #7A3BAF);
            color: var(--txt);
        }
        .cab-cnst-section-badge.count {
            background: color-mix(in srgb, var(--acc2) 22%, var(--inp));
            border: 1px solid var(--acc2);
            color: var(--white);
        }
        .cab-cnst-section-chevron {
            font-size: 18px;
            font-weight: 700;
            color: var(--dim);
            margin-left: 4px;
        }
        .cab-cnst-section-body {
            padding: 12px 16px 16px;
            border-top: 1px solid var(--bdr);
        }
        /* Chip row for back / toe type pickers. Tap-target 44px min. */
        .cab-cnst-chip-row {
            display: flex; flex-wrap: wrap; gap: 6px;
            margin: 4px 0;
        }
        .cab-cnst-chip {
            display: inline-flex; align-items: center; justify-content: center;
            min-width: 72px; min-height: 44px;
            padding: 8px 12px;
            border-radius: 6px;
            border: 1px solid var(--bdr);
            background: var(--inp);
            color: var(--white);
            font-size: 11px;
            font-weight: 600;
            cursor: pointer;
            transition: background 220ms cubic-bezier(0.32,0.72,0.24,1),
                        border-color 220ms cubic-bezier(0.32,0.72,0.24,1);
        }
        .cab-cnst-chip:hover {
            background: color-mix(in srgb, var(--acc2) 14%, var(--inp));
            border-color: var(--acc2);
        }
        .cab-cnst-chip:focus-visible {
            outline: 2px solid var(--acc2);
            outline-offset: 2px;
        }
        .cab-cnst-chip.active {
            /* 2026-09-15 owner: "get rid of any of the brown" — the tan
               wash is gone; selection rides the cabinet-tier violet, kept
               faint so the border carries the state. */
            background: color-mix(in srgb, var(--tier-cab, #7A3BAF) 12%, var(--inp));
            border-color: var(--tier-cab, #7A3BAF);
            color: var(--txt);
        }
        .cab-app-section { display: flex; flex-direction: column; gap: 6px; }
        .cab-app-section-title {
            font-size: 10px;
            text-transform: uppercase;
            letter-spacing: .6px;
            color: var(--dim);
            font-weight: 700;
        }
        .cab-app-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
        }
        .cab-app-field { display: flex; flex-direction: column; gap: 4px; }
        .cab-app-field label {
            font-size: 11px;
            font-weight: 600;
            color: var(--white);
        }
        .cab-app-field input[type="number"],
        .cab-app-field input[type="text"],
        .cab-app-field select {
            /* PR-AUDIT-CYCLE-1-UX (2026-05-12, UX P0-2): added `select`
               to the 44px-tap-target rule. Pre-PR every <select> in the
               editor (hinge bore door/bore pickers, drawer-divider in-
               drawer + material pickers, nailer species, stretcher
               species, slide family, hinge, overlay, wood type, finish,
               AWI grade, seismic, NEMA, LEED) rendered at the browser
               native ~28-32px height on iOS Safari, failing the shop-
               floor gloved-tap test. */
            min-height: 44px;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 6px;
            color: var(--white);
            padding: 0 10px;
            font-size: 13px;
            font-family: inherit;
        }
        .cab-app-field input[type="number"]:focus,
        .cab-app-field input[type="text"]:focus,
        .cab-app-field select:focus {
            border-color: var(--acc2);
            outline: none;
        }
        .cab-app-field-help { font-size: 10px; color: var(--dim); }
        .cab-app-checkbox {
            display: flex; align-items: center; gap: 8px;
            min-height: 44px;
            font-size: 13px;
            color: var(--white);
            cursor: pointer;
        }
        .cab-app-checkbox input { width: 20px; height: 20px; cursor: pointer; }
        .cab-app-error {
            padding: 8px 12px;
            border-radius: 6px;
            background: color-mix(in srgb, var(--no) 15%, transparent);
            border: 1px solid color-mix(in srgb, var(--no) 50%, transparent);
            color: var(--no);
            font-size: 12px;
            line-height: 1.45;
        }
        .cab-app-error.hidden { display: none; }
        .cab-app-actions { display: flex; gap: 8px; flex-wrap: wrap; }
        .cab-app-btn {
            min-height: 44px;
            padding: 0 14px;
            border-radius: 6px;
            border: 1px solid var(--bdr);
            background: var(--inp);
            color: var(--white);
            cursor: pointer;
            font-size: 13px;
            font-family: inherit;
        }
        .cab-app-btn:hover { border-color: var(--acc2); }
        .cab-app-btn.primary { background: var(--acc2); color: #1a1203; border-color: var(--acc2); }
        .cab-app-btn.primary:hover { filter: brightness(1.08); }
        .cab-app-btn:disabled { opacity: .5; cursor: not-allowed; }
        /* PR-16-FIXUP (UX P1-A, 2026-05-11): tap-target rules for
           inline list-row controls. ANSI HFES 100 / Apple HIG / Google
           Material all converge on 44px minimum tap target for shop-
           floor / mobile use. Pre-fixup the Delete buttons on the
           Cutouts + Depth zones lists fell back to browser default
           (~24px) — a $400 walnut-ply mis-tap risk. */
        .cab-app-btn-link {
            min-height: 44px;
            padding: 0 10px;
            background: transparent;
            border: none;
            cursor: pointer;
            font-family: inherit;
            font-size: 13px;
        }
        .cab-app-btn-link:disabled { opacity: .5; cursor: not-allowed; }
        /* PR-16-FIXUP (UX P1-B): inline grid-row inputs (cutout dims,
           zone dims) are NOT wrapped in `.cab-app-field`, so they
           bypass the existing input min-height rule. Scoped override
           here so the bare `<input>` tags inside `.cab-app-row` still
           meet the 44px floor. */
        .cab-app-row input {
            min-height: 44px;
            padding: 0 8px;
            border-radius: 4px;
            border: 1px solid var(--bdr);
            background: var(--inp);
            color: var(--white);
            font-family: inherit;
            font-size: 13px;
        }


        /* PR-S2-PREVIEW-V0 (2026-05-09): live 3D cabinet preview tab.
           Three.js scene scoped to one cabinet — orbits camera by
           default, view-preset buttons jump to canonical angles
           (Front / Side / Top / Iso). Auto-updates from the shared
           working copy on every render() cycle so edits in Faces /
           Construction / Appliance ripple to the geometry. */
        .cab-view3d-root { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 480px; }
        .cab-view3d-toolbar {
            display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
            padding: 6px 4px;
        }
        .cab-view3d-toolbar-label {
            font-size: 10px; text-transform: uppercase; letter-spacing: .6px;
            color: var(--dim); font-weight: 700; margin-right: 4px;
        }
        /* PR-AUDIT-FIXUP-UX (2026-05-09): 36px → 44px on 3D View
           toolbar buttons + toggles per WCAG 2.5.5 (UX audit P0-1
           and P0-2). Bumped checkbox from 16×16 → 20×20 too —
           fat-finger gloved-tablet operators can't reliably hit
           a 16px target. */
        .cab-view3d-btn { min-height: 44px; padding: 0 14px; font-size: 12px; }
        /* PR-ED-CAM-PRESET (2026-08-26): the view strip went from 3 buttons to 5
           (Front / Left / Right / Back / Iso). Trim the HORIZONTAL padding on just
           those so the strip still fits beside Doors + Door sizes before the
           toolbar wraps and steals a row from the 3D. Height stays 44px — that is
           the WCAG 2.5.5 gloved-hand target and is not the axis under pressure. */
        .cab-view3d-btn[data-view-preset] { padding: 0 10px; }
        .cab-view3d-btn.active {
            background: var(--acc2); color: #1a1203; border-color: var(--acc2);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc2) 30%, transparent);
        }
        .cab-view3d-spacer { flex: 1; }
        .cab-view3d-toggle {
            display: flex; align-items: center; gap: 8px;
            font-size: 12px; color: var(--white);
            min-height: 44px; padding: 0 8px;
            cursor: pointer;
        }
        .cab-view3d-toggle input { width: 20px; height: 20px; cursor: pointer; }
        .cab-view3d-canvas-wrap {
            flex: 1; min-height: 360px;
            background: var(--bg-card, #14181f);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            overflow: hidden;
            position: relative;
        }
        #cab-view3d-canvas {
            display: block;
            width: 100%; height: 100%;
            cursor: grab;
        }
        #cab-view3d-canvas:active { cursor: grabbing; }
        .cab-view3d-foot {
            font-size: 11px; color: var(--dim); font-style: italic;
            padding: 0 4px;
            min-height: 1.4em;  /* PR-S2-CLICK-TO-SELECT: prevent layout jump when readout populates */
            /* PR-S3-AUDIT-FIXUP (2026-05-09, UX-FIX): clamp the readout to one
               line. An 80+ char "Drawer · 22 13/16" × 6 1/4" · Blum Tandem 563H 21" · Berenson Eclipse 4" pull"
               wrapped mid-SKU on a 480px iPad — worst possible cut for a foreman
               reading at a glance. Title attribute preserves full text on
               long-press / hover. */
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        }
        /* PR-DRAW-TAB-V0 (2026-05-10): Drawing tab styles. Single
           landscape sheet host, toolbar with print button, live foot.
           SVG renders into the host with width:100% so the sheet
           scales to the modal viewport; @media print restores
           native sheet dimensions for vector-fidelity print. */
        .ced-drawing-root { display:flex; flex-direction:column; gap:8px; height:100%; min-height:480px; }
        .ced-drawing-toolbar {
            display:flex; align-items:center; gap:8px;
            padding:6px 4px; border-bottom:1px solid var(--bdr);
        }
        .ced-drawing-title { font-size:12px; font-weight:600; color:var(--white); letter-spacing:.4px; }
        .ced-drawing-spacer { flex:1; }
        .ced-drawing-btn { min-height:44px; padding:0 14px; font-size:12px; }
        .ced-drawing-host {
            flex:1; min-height:420px;
            background:#fff;  /* white sheet matches print convention */
            border:1px solid var(--bdr); border-radius:8px;
            overflow:auto;
            padding:12px;
            display:flex; align-items:center; justify-content:center;
        }
        .ced-drawing-host svg.cabinet-detail-sheet {
            width:100%; height:auto; max-height:calc(100vh - 280px);
            display:block;
        }
        .ced-drawing-foot { font-size:11px; color:var(--dim); font-style:italic; padding:0 4px; }

        /* Shop Drawings button in the editor topbar — always visible. */
        .ced-toolbar-shop-drawing-btn {
            display: inline-flex;
            align-items: center;
            min-height: 44px;
            padding: 0 14px;
            border-radius: 5px;
            background: color-mix(in srgb, var(--acc2) 10%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc2) 40%, transparent);
            color: var(--acc2);
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            transition: background var(--dur-quick) var(--ease-out);
        }
        .ced-toolbar-shop-drawing-btn:hover {
            background: color-mix(in srgb, var(--acc2) 18%, transparent);
        }

        /* ═══════════════════════════════════════════════════════════════
           PR-CAB-EDITOR-V2-PHASE-1-LAYOUT (2026-05-26): V2 editor layout.
           The modal becomes a 3-column grid: cabinet tree left, properties
           center, 3D viewer permanently pinned right. Phase 1 ships the
           layout shift only — tabs preserved, accordion arrives Phase 2.
           Default OFF; opt-in via `?editorV2=1` or localStorage. See the
           plan at .claude/plans/swift-juggling-rivest.md for the full
           21-phase roadmap.
           ═══════════════════════════════════════════════════════════════ */
        /* PR-CAB-EDITOR-V2-NPANEL (2026-05-27): Blender N-panel layout.
           4 columns × 3 rows. Cabinet 3D dominates; tab rail + panel
           inspector on the right. Explicit grid-row/grid-column per
           element (not grid-template-areas) so missing or auto-placed
           items can't quietly collapse a column to 0 width. */
        #cab-editor-modal[data-layout="v2"] {
            display: grid !important;
            inset: 0 !important;
            position: fixed !important;
            z-index: 9999 !important;
            background: var(--bg);
            grid-template-rows: 56px minmax(0, 1fr) auto;
            /* PR-CED-SASH (2026-08-26): column widths come from custom properties so
               the drag handles (.ced-sash) can resize them. The var FALLBACK is the
               historical hardcoded width — an operator who has never dragged keeps
               the exact old layout, including every responsive breakpoint below.
               _cedApplySashWidths REMOVES the property (rather than writing a value)
               when nothing is stored, so the fallback chain stays live. */
            grid-template-columns:
                minmax(0, 1fr)
                60px
                var(--ced-side-w, 340px);
            gap: 0;
        }
        #cab-editor-modal[data-layout="v2"][data-panel-collapsed="true"] {
            grid-template-columns: minmax(0, 1fr) 60px 0;
        }
        #cab-editor-modal[data-layout="v2"].hidden {
            display: none !important;
        }
        body[data-cab-editor-open="true"] #app-topbar,
        body[data-cab-editor-open="true"] #app-statusbar,
        body[data-cab-editor-open="true"] #app-bottom-nav {
            display: none !important;
        }

        /* Shell wraps the topbar + accordion + bottombar in the HTML;
           display:contents promotes its children to grid items. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-shell {
            display: contents;
        }

        /* Hide modal children that are NOT part of the N-panel grid:
           the empty docked-rail placeholder, the unused Drawing pane,
           the inline save-error banner (empty by default). */
        #cab-editor-modal[data-layout="v2"] .ced-rail-right { display: none !important; }
        #cab-editor-modal[data-layout="v2"] #cab-editor-pane-drawing { display: none !important; }
        #cab-editor-modal[data-layout="v2"] #ced-save-err:empty { display: none !important; }

        /* Topbar — row 1, full width. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-topbar {
            grid-row: 1;
            grid-column: 1 / -1;
            display: flex;
            align-items: center;
            gap: var(--sp-3);
            padding: 0 var(--sp-4);
            border-bottom: 1px solid var(--bdr);
            background: var(--sf);
            min-height: 56px;
        }

        /* Stage (3D) — row 2, column 1. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-stage {
            grid-row: 2;
            grid-column: 1;
            position: relative;
            overflow: hidden;
            background: var(--bg);
            min-width: 0;
            min-height: 0;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-stage > #cab-editor-pane-view-3d {
            display: flex !important;
            flex-direction: column;
            width: 100%;
            height: 100%;
            padding: 0;
            overflow: hidden;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-stage .cab-view3d-root {
            height: 100%;
            display: flex;
            flex-direction: column;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-stage .cab-view3d-canvas-wrap {
            flex: 1 1 auto;
            min-height: 0;
        }

        /* Tab rail — row 2, column 3. Vertical icon strip. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-tabrail {
            grid-row: 2;
            grid-column: 2;
            display: flex;
            flex-direction: column;
            background: var(--sf);
            border-left: 1px solid var(--bdr);
            border-right: 1px solid var(--bdr);
            overflow: hidden;
            min-height: 0;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tabrail-spacer {
            flex: 1 1 auto;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 2px;
            width: 60px;
            height: 60px;
            padding: 0;
            border: none;
            border-left: 3px solid transparent;
            background: transparent;
            color: var(--dim);
            cursor: pointer;
            font-size: 9px;
            font-weight: 600;
            letter-spacing: 0.2px;
            transition: background var(--dur-quick) var(--ease-out),
                        color var(--dur-quick) var(--ease-out),
                        border-color var(--dur-quick) var(--ease-out);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab:hover {
            color: var(--white);
            background: color-mix(in srgb, var(--acc2) 8%, transparent);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab:focus-visible {
            outline: 2px solid var(--acc2);
            outline-offset: -2px;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab.active {
            color: var(--white);
            background: var(--bg);
            border-left-color: var(--acc2);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab svg {
            display: block;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tab-label {
            font-size: 9px;
            text-transform: uppercase;
        }

        /* Right panel — row 2, column 4. Active section's controls. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel {
            grid-row: 2;
            grid-column: 3;
            overflow-y: auto;
            background: var(--bg);
            border-left: 1px solid var(--bdr);
            min-width: 0;
            min-height: 0;
        }
        #cab-editor-modal[data-layout="v2"][data-panel-collapsed="true"] .ced-v2-rightpanel {
            display: none;
        }
        /* When the accordion lives inside .ced-v2-rightpanel, hide the
           <summary> toggles — the tab rail drives section switching, not
           the operator clicking each summary. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-section > summary {
            display: none;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-section {
            border: none;
            border-radius: 0;
            background: transparent;
        }

        /* Bottombar — row 3, full width. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-bottombar {
            grid-row: 3;
            grid-column: 1 / -1;
            background: var(--sf);
            border-top: 1px solid var(--bdr);
            padding: var(--sp-3) var(--sp-4);
            display: flex;
            gap: var(--sp-2);
            justify-content: flex-end;
            align-items: center;
        }

        /* ── PR-CED-SASH (2026-08-26): draggable column splitters ──────────
           VS Code "sash" pattern: an overlay layer of thin drag targets sitting
           on the grid's column boundaries. Overlay rather than in-grid children
           because .ced-v2-rightpanel scrolls (overflow-y: auto), which
           would clip an absolutely-positioned edge handle.
           Geometry is written by _cedSyncSashes from the RESOLVED
           grid-template-columns, so one positioner serves every mode and
           breakpoint. 7px hit target (gloved hands), 1px painted line. */
        #cab-editor-modal[data-layout="v2"] .ced-sash {
            position: absolute;
            z-index: 40;
            width: 7px;
            margin-left: -3px;
            cursor: col-resize;
            touch-action: none;
            background: transparent;
            border: none;
            padding: 0;
            display: block;
        }
        #cab-editor-modal[data-layout="v2"] .ced-sash[hidden] { display: none; }
        /* The painted line is a child pseudo so the 7px hit area stays invisible. */
        #cab-editor-modal[data-layout="v2"] .ced-sash::after {
            content: "";
            position: absolute;
            inset: 0 3px;
            background: var(--acc2, #4a9eff);
            opacity: 0;
            transition: opacity 100ms ease;
        }
        @media (prefers-reduced-motion: reduce) {
            #cab-editor-modal[data-layout="v2"] .ced-sash::after { transition: none; }
        }
        #cab-editor-modal[data-layout="v2"] .ced-sash:hover::after,
        #cab-editor-modal[data-layout="v2"] .ced-sash:focus-visible::after,
        #cab-editor-modal[data-layout="v2"] .ced-sash[data-dragging="1"]::after {
            opacity: 1;
        }
        #cab-editor-modal[data-layout="v2"] .ced-sash:focus-visible {
            outline: 2px solid var(--acc2, #4a9eff);
            outline-offset: 1px;
        }
        /* While dragging, kill text selection + pointer noise across the modal. */
        #cab-editor-modal[data-layout="v2"][data-sashing="1"] {
            user-select: none;
            -webkit-user-select: none;
            cursor: col-resize;
        }
        #cab-editor-modal[data-layout="v2"][data-sashing="1"] .ced-v2-stage,
        #cab-editor-modal[data-layout="v2"][data-sashing="1"] .ced-v2-rightpanel {
            pointer-events: none;
        }

        /* Responsive breakpoints — keep the 3D big at any width. */
        @media (max-width: 1599px) and (min-width: 1280px) {
            #cab-editor-modal[data-layout="v2"] {
                grid-template-columns: minmax(0, 1fr) 60px var(--ced-side-w, 320px);
            }
            #cab-editor-modal[data-layout="v2"][data-panel-collapsed="true"] {
                grid-template-columns: minmax(0, 1fr) 60px 0;
            }
        }
        @media (max-width: 1279px) and (min-width: 1024px) {
            #cab-editor-modal[data-layout="v2"] {
                grid-template-columns: minmax(0, 1fr) 56px var(--ced-side-w, 300px);
            }
            #cab-editor-modal[data-layout="v2"][data-panel-collapsed="true"] {
                grid-template-columns: minmax(0, 1fr) 56px 0;
            }
        }
        @media (max-width: 1023px) {
            #cab-editor-modal[data-layout="v2"] {
                grid-template-columns: minmax(0, 1fr) 56px var(--ced-side-w, 280px);
            }
        }
        @media (max-width: 767px) {
            #cab-editor-modal[data-layout="v2"] {
                grid-template-rows: 56px 200px 1fr;
                grid-template-columns: 1fr 56px;
                grid-template-areas:
                    "topbar topbar"
                    "stage  stage"
                    "panel  rail";
            }
            /* PR-CED-SASH: the phone layout is a different grid (2 columns, named
               areas) — column-boundary sashes are meaningless there. _cedSyncSashes
               also bails when the resolved column count isn't 3; this is the belt. */
            #cab-editor-modal[data-layout="v2"] .ced-sash { display: none !important; }
        }

        /* PR-CAB-EDITOR-V2-NPANEL (2026-05-27): the old 3-column layout
           rules (.ced-rail-left grid-column:1, .cab-editor-shell
           grid-column:2, .ced-v2-rightcol grid-column:3) are DELETED
           here. The N-panel layout uses grid-template-areas + per-
           element grid-area assignments above. Leaving the old rules
           in place caused them to win in cascade order, pinning the
           shell to column 2 (preventing display:contents from
           unwrapping its children into the grid) and leaving the tab
           rail + right panel invisible. */

        /* Hide the "3D View" tab button — the canvas is permanently
           visible on the right, so the tab is redundant. Reduces the
           tabstrip from 6 to 5 affordances. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-tab[data-tab="view-3d"] {
            display: none;
        }

        /* ═══════════════════════════════════════════════════════════════
           PR-CAB-EDITOR-V2-PHASE-2-ACCORDION (2026-05-26): accordion
           layout for the 4 form panes. Tab strip hides; each form pane
           becomes a <details> section that the operator expands/collapses.
           Geometry promotion to always-open top section deferred to Phase
           2b (requires Construction tab's render() to opt out of W/H/D).
           ═══════════════════════════════════════════════════════════════ */

        /* Hide the tab strip — accordion replaces it entirely. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-tabstrip {
            display: none;
        }

        /* All accordion sections are <details> elements injected by
           _cwpEditorV2WrapTabsInAccordion at editor open. Each wraps
           one tabpane. The center column scrolls vertically so the
           operator can scroll through every section in one motion. */
        #cab-editor-modal[data-layout="v2"] .cab-editor-shell > .ced-v2-acc {
            flex: 1 1 auto;
            overflow-y: auto;
            padding: var(--sp-3, 12px) var(--sp-4, 16px);
            display: flex;
            flex-direction: column;
            gap: var(--sp-2, 8px);
            min-height: 0;
        }

        /* Each section is a <details> with the source tabpane as its body. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-section {
            background: var(--sf, #16191e);
            border: 1px solid var(--bdr, rgba(255,255,255,0.08));
            border-radius: var(--radius-sm, 6px);
            overflow: hidden;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary {
            list-style: none;
            cursor: pointer;
            padding: var(--sp-3, 12px) var(--sp-4, 16px);
            font-size: var(--fs-base, 1rem);
            font-weight: 600;
            color: var(--white, #fff);
            background: transparent;
            display: flex;
            align-items: center;
            gap: var(--sp-2, 8px);
            min-height: 48px;             /* WCAG 2.5.5 tap target */
            user-select: none;
            transition: background var(--dur-chrome, 120ms) var(--ease-out, ease);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary::-webkit-details-marker {
            display: none;
        }
        /* Chevron indicator — rotates 90° when section is open. Pure
           CSS, no JS needed for the toggle. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary::before {
            content: "▶";
            display: inline-block;
            font-size: 10px;
            color: var(--dim, rgba(255,255,255,0.5));
            transition: transform var(--dur-chrome, 120ms) var(--ease-out, ease);
            width: 12px;
            text-align: center;
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-section[open] > summary::before {
            transform: rotate(90deg);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary:hover {
            background: var(--pn, rgba(255,255,255,0.04));
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary:focus-visible {
            outline: 2px solid var(--acc2, #ffc94a);
            outline-offset: -2px;
        }
        /* When open, the tabpane inside the <details> is forced visible
           regardless of the .hidden class that the legacy tab activation
           logic may have set on it. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-section[open] > .cab-editor-tabpane {
            display: block !important;
            padding: var(--sp-3, 12px) var(--sp-4, 16px) var(--sp-4, 16px);
            border-top: 1px solid var(--bdr, rgba(255,255,255,0.08));
        }
        /* When closed, the tabpane is hidden — overriding any inline
           display the tab activation logic might leave behind. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-section:not([open]) > .cab-editor-tabpane {
            display: none !important;
        }

        /* ═══════════════════════════════════════════════════════════════
           PR-CAB-EDITOR-V2-PHASE-4-VISUAL (2026-05-26): canonical
           component classes. Replaces the 3-of-everything button
           situation (`.cv-btn` vs `.ba` vs `.bg`) and the wall of
           inline `style="margin-bottom:12px"` attributes with named
           utility classes that obey the design-system tokens.
           ═══════════════════════════════════════════════════════════════ */

        /* Override badge — applied to any field whose value differs from
           the shop default. Amber left-border + reset affordance so the
           operator never wonders "is this overridden or inherited?".
           Wire-up arrives in Phase 5 (_cedRenderOverrideBadges); the
           CSS ships now as defensive infrastructure. */
        .ced-overridden {
            border-left: 3px solid var(--acc2, #ffc94a);
            padding-left: var(--sp-2, 8px);
        }
        .ced-reset-btn {
            background: transparent;
            border: 1px solid transparent;
            color: var(--dim, rgba(255,255,255,0.55));
            cursor: pointer;
            padding: 0 var(--sp-2, 8px);
            border-radius: var(--radius-sm, 6px);
            font-size: 13px;
            line-height: 1;
            min-width: 24px;
            min-height: 24px;
        }
        .ced-reset-btn:hover {
            color: var(--acc2, #ffc94a);
            border-color: var(--acc2, #ffc94a);
        }

        /* Shimmer skeleton — replaces the three "Loading…" stubs in
           Construction / Doors-Drawers / Appliance with a real loading
           affordance. Pure CSS; no JS dependency. Reuses the hub-page
           shimmer pattern that already exists in the codebase. */
        .ced-skeleton {
            background: linear-gradient(90deg,
                var(--bg-card, #14181f) 0%,
                var(--pn, rgba(255,255,255,0.06)) 50%,
                var(--bg-card, #14181f) 100%);
            background-size: 200% 100%;
            animation: ced-shimmer 1.4s ease-in-out infinite;
            border-radius: var(--radius-sm, 6px);
            min-height: 16px;
            margin: var(--sp-1, 4px) 0;
        }
        @keyframes ced-shimmer {
            0%   { background-position: 200% 0; }
            100% { background-position: -200% 0; }
        }
        @media (prefers-reduced-motion: reduce) {
            .ced-skeleton { animation: none; opacity: 0.6; }
        }

        /* PR-CAB-EDITOR-V2-UI-A (2026-05-26): standard-box state
           shown above the tile picker when the cabinet is rectangular.
           Replaces the Rectangular tile that used to live in the
           picker grid. */
        .ced-shape-rect-state {
            padding: var(--sp-3, 12px) var(--sp-4, 16px);
            background: color-mix(in srgb, var(--acc2, #ffc94a) 8%, transparent);
            border-left: 3px solid var(--acc2, #ffc94a);
            border-radius: var(--radius-sm, 6px);
            margin-bottom: var(--sp-3, 12px);
            font-size: 13px;
            color: var(--white, #fff);
            line-height: 1.45;
        }

        /* Reset link — shown below the tile picker when a non-rect
           shape is active. Tap-target 44px for shop-floor tablet
           gloved-hand compliance. */
        .ced-shape-reset-link {
            background: transparent;
            color: var(--dim, rgba(255,255,255,0.65));
            border: 1px solid var(--bdr, rgba(255,255,255,0.10));
            border-radius: var(--radius-sm, 6px);
            padding: var(--sp-2, 8px) var(--sp-3, 12px);
            min-height: 44px;
            font-size: 12px;
            cursor: pointer;
            margin-top: var(--sp-2, 8px);
            display: inline-flex;
            align-items: center;
        }
        .ced-shape-reset-link:hover {
            color: var(--white, #fff);
            border-color: var(--acc2, #ffc94a);
        }
        .ced-shape-reset-link:focus-visible {
            outline: 2px solid var(--acc2, #ffc94a);
            outline-offset: 2px;
        }

        /* PR-CAB-EDITOR-V2-PHASE-11-SHAPE-ACCORDION-UI (2026-05-26):
           Shape tile picker + parameter form. Lives inside the
           Cabinet Shape <details> section. Operator picks a tile;
           non-rectangular variants reveal a parameter form below. */
        .ced-shape-picker {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
            gap: var(--sp-2, 8px);
            margin-bottom: var(--sp-3, 12px);
        }
        .ced-shape-tile {
            background: var(--bg-card, #14181f);
            color: var(--white, #fff);
            border: 1px solid var(--bdr, rgba(255,255,255,0.12));
            border-radius: var(--radius-sm, 6px);
            padding: var(--sp-3, 12px);
            text-align: center;
            cursor: pointer;
            /* PR-CAB-EDITOR-V2-UI-B: grew from 56px to 96px so the
               40px SVG silhouette + label + sub fit comfortably. */
            min-height: 96px;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: var(--sp-1, 4px);
            transition: border-color var(--dur-chrome, 120ms) var(--ease-out, ease),
                        background var(--dur-chrome, 120ms) var(--ease-out, ease),
                        color var(--dur-chrome, 120ms) var(--ease-out, ease);
        }
        /* PR-CAB-EDITOR-V2-UI-B (2026-05-26): SVG silhouette
           container. Uses currentColor so the stroke picks up the
           tile's active-state color automatically. */
        .ced-shape-tile-thumb {
            color: var(--dim, rgba(255,255,255,0.55));
            line-height: 0;
            margin-bottom: var(--sp-1, 4px);
        }
        .ced-shape-tile.active .ced-shape-tile-thumb,
        .ced-shape-tile:hover .ced-shape-tile-thumb {
            color: var(--acc2, #ffc94a);
        }
        .ced-shape-tile:hover {
            border-color: var(--acc2, #ffc94a);
        }
        .ced-shape-tile.active {
            border-color: var(--acc2, #ffc94a);
            background: color-mix(in srgb, var(--acc2, #ffc94a) 12%, transparent);
        }
        .ced-shape-tile:focus-visible {
            outline: 2px solid var(--acc2, #ffc94a);
            outline-offset: 2px;
        }
        .ced-shape-tile-label {
            font-size: 13px;
            font-weight: 600;
            margin-bottom: 2px;
        }
        .ced-shape-tile-sub {
            font-size: 11px;
            color: var(--dim, rgba(255,255,255,0.55));
            line-height: 1.3;
        }
        .ced-shape-params {
            display: flex;
            flex-direction: column;
            gap: var(--sp-2, 8px);
            padding-top: var(--sp-2, 8px);
            border-top: 1px solid var(--bdr, rgba(255,255,255,0.08));
        }
        .ced-shape-field {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .ced-shape-field-label {
            font-size: 11px;
            font-weight: 600;
            color: var(--dim, rgba(255,255,255,0.7));
            letter-spacing: 0.4px;
        }
        .ced-shape-field input,
        .ced-shape-field select {
            background: var(--inp, var(--bg-card, #14181f));
            color: var(--white, #fff);
            border: 1px solid var(--bdr, rgba(255,255,255,0.12));
            border-radius: var(--radius-sm, 6px);
            padding: var(--sp-2, 8px);
            font-size: 13px;
            min-height: 36px;
            font-family: var(--ff-mono, ui-monospace, monospace);
        }
        .ced-shape-field input:focus,
        .ced-shape-field select:focus {
            outline: none;
            border-color: var(--acc2, #ffc94a);
        }
        .ced-shape-hint {
            font-size: 11px;
            color: var(--dim, rgba(255,255,255,0.55));
            line-height: 1.45;
            margin: var(--sp-1, 4px) 0 0;
        }
        .ced-shape-hint code {
            font-family: var(--ff-mono, ui-monospace, monospace);
            background: var(--bg-card, #14181f);
            padding: 0 4px;
            border-radius: 4px;
        }
        /* PR-CAD-5 (2026-05-27): rise/run two-input row for the
           SlopedTop variant. Sits inside .ced-shape-params so it picks
           up the same vertical gap as the regular fields. */
        .ced-rise-run-row {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .ced-rise-run-inputs {
            display: flex;
            align-items: center;
            gap: var(--sp-2);
        }
        .ced-rise-run-inputs input {
            flex: 1;
            background: var(--inp);
            color: var(--white);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: var(--sp-2);
            font-size: 13px;
            min-height: 36px;
            font-family: var(--ff-mono);
        }
        .ced-rise-run-inputs input:focus {
            outline: none;
            border-color: var(--acc2);
        }
        .ced-rise-run-sep {
            color: var(--dim);
            font-family: var(--ff-mono);
            font-size: 14px;
            font-weight: 700;
        }
        /* PR-CAD-5 (2026-05-27): live "short side ends at X" callout
           below the slope inputs. Info tone for normal; error tone for
           "slope too steep — short side at/below floor." */
        .ced-shape-callout {
            font-size: 12px;
            color: var(--white);
            background: color-mix(in srgb, var(--acc2) 12%, transparent);
            border-left: 3px solid var(--acc2);
            padding: var(--sp-2) var(--sp-3);
            border-radius: var(--radius-sm);
            margin: var(--sp-2) 0 0;
            font-family: var(--ff-mono);
            line-height: 1.4;
            min-height: 1.4em;
        }
        .ced-shape-callout:empty {
            display: none;
        }
        .ced-shape-callout[data-tone="error"] {
            background: color-mix(in srgb, var(--no) 16%, transparent);
            border-left-color: var(--no);
            color: var(--white);
        }

        /* PR-CAB-EDITOR-V2-UI-D (2026-05-26): 2D SVG Freeform sketcher.
           Click-to-add, drag-to-move, double-tap-to-delete. Designed
           tablet-first — handles are 0.5" radius in cabinet units so
           they're always thumb-sized regardless of cabinet size. */
        .ced-ff-wrap {
            gap: var(--sp-2);
        }
        .ced-ff-toolbar {
            display: flex;
            justify-content: space-between;
            align-items: flex-end;
            gap: var(--sp-2);
            flex-wrap: wrap;
        }
        .ced-ff-plane-label {
            display: flex;
            flex-direction: column;
            gap: 2px;
            min-width: 180px;
        }
        .ced-ff-toolbar-actions {
            display: flex;
            align-items: center;
            gap: var(--sp-2);
            flex-wrap: wrap;
        }
        .ced-ff-readout {
            font-size: 12px;
            color: var(--dim);
            font-family: var(--ff-mono);
            min-width: 140px;
            text-align: right;
        }
        .ced-ff-btn-ghost {
            background: transparent;
            color: var(--dim);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: var(--sp-2) var(--sp-3);
            font-size: 12px;
            min-height: 36px;
            cursor: pointer;
            transition: border-color var(--dur-quick) var(--ease-out),
                        color var(--dur-quick) var(--ease-out);
        }
        .ced-ff-btn-ghost:hover:not(:disabled) {
            color: var(--white);
            border-color: var(--acc2);
        }
        /* PR-CAD-2 (2026-05-27): undo/redo buttons disable when their
           stack is empty. Dim them so the unavailability reads. */
        .ced-ff-btn-ghost:disabled {
            opacity: 0.4;
            cursor: not-allowed;
        }
        .ced-ff-btn-danger:hover:not(:disabled) {
            color: var(--no);
            border-color: var(--no);
        }
        .ced-ff-canvas-wrap {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: 6px;
            /* The SVG fills the wrap; aspect ratio preserved via
               preserveAspectRatio="xMidYMid meet" on the SVG element. */
        }
        .ced-ff-svg {
            display: block;
            width: 100%;
            height: auto;
            max-height: 50vh;
            cursor: crosshair;
            /* Critical for Pointer Events on touch — prevents the
               browser's native scroll/zoom hijack of the drag gesture. */
            touch-action: none;
            user-select: none;
            -webkit-user-select: none;
        }
        .ced-ff-grid-line {
            stroke: var(--bdr);
            stroke-width: 0.04;
            opacity: 0.4;
        }
        .ced-ff-bounds {
            fill: none;
            stroke: var(--dim);
            stroke-width: 0.06;
            stroke-dasharray: 0.4 0.25;
            opacity: 0.55;
        }
        .ced-ff-poly {
            fill: var(--acc2);
            fill-opacity: 0.18;
            stroke: var(--acc2);
            stroke-width: 0.14;
            stroke-linejoin: round;
            stroke-linecap: round;
        }
        .ced-ff-poly-open {
            fill-opacity: 0;
            stroke-dasharray: 0.3 0.2;
        }
        .ced-ff-handle {
            fill: var(--pn);
            stroke: var(--acc2);
            stroke-width: 0.12;
            r: 0.45;
            cursor: grab;
            transition: r var(--dur-quick) var(--ease-out),
                        fill var(--dur-quick) var(--ease-out);
        }
        .ced-ff-handle:hover {
            r: 0.6;
            fill: var(--acc2);
        }
        .ced-ff-handle-active {
            r: 0.6;
            fill: var(--acc2);
        }
        .ced-ff-vertex-label {
            font-size: 0.85px;
            fill: var(--white);
            font-family: var(--ff-mono);
            font-weight: 600;
            pointer-events: none;
            opacity: 0.8;
        }
        .ced-ff-coachmark {
            font-size: 1.6px;
            fill: var(--dim);
            font-family: var(--ff-ui);
            pointer-events: none;
            opacity: 0.7;
        }
        /* PR-CAD-3 (2026-05-27): live edge-length readout shown above
           the dragged vertex during a drag. Bold + accent color so it
           reads at a glance against the grid + polygon fill. */
        .ced-ff-drag-readout {
            font-size: 1.0px;
            fill: var(--acc2);
            font-family: var(--ff-mono);
            font-weight: 700;
            pointer-events: none;
            paint-order: stroke fill;
            stroke: var(--pn);
            stroke-width: 0.18px;
            stroke-linejoin: round;
        }
        @media (prefers-reduced-motion: reduce) {
            .ced-ff-handle,
            .ced-ff-btn-ghost {
                transition: none;
            }
        }

        /* PR-CAB-EDITOR-V2-PHASE-6-POLISH (2026-05-26): live fraction
           preview chip. Appears next to any dimension input as the
           operator types a mixed-fraction value, showing the decimal
           equivalent ("→ 36.5″"). Pure inline element, never
           interferes with input layout. */
        .ced-frac-preview {
            display: inline-block;
            margin-left: var(--sp-2, 8px);
            padding: 2px var(--sp-2, 8px);
            background: color-mix(in srgb, var(--acc2, #ffc94a) 18%, transparent);
            color: var(--white, #fff);
            font-size: 11px;
            font-weight: 600;
            font-family: var(--ff-mono, ui-monospace, monospace);
            border-radius: var(--radius-sm, 6px);
            border: 1px solid color-mix(in srgb, var(--acc2, #ffc94a) 45%, transparent);
            vertical-align: middle;
            white-space: nowrap;
            user-select: none;
            pointer-events: none;
        }

        /* PR-CAB-EDITOR-V2-PHASE-6-POLISH (2026-05-26): reduced-motion
           guards for V2 transitions. Operators with vestibular
           sensitivities, low-power devices, or accessibility settings
           on get instant state changes instead of animations. */
        @media (prefers-reduced-motion: reduce) {
            #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary::before {
                transition: none;
            }
            #cab-editor-modal[data-layout="v2"] .ced-toolbar-snapshot-btn,
            #cab-editor-modal[data-layout="v2"] .ced-snapshot-banner .ced-snap-btn,
            #cab-editor-modal[data-layout="v2"] .ced-v2-section > summary,
            #cab-editor-modal[data-layout="v2"] .ced-reset-btn {
                transition: none;
            }
            .ced-skeleton {
                animation: none;
                opacity: 0.6;
            }
        }

        /* Snapshot button — Phase 5 quick action. Lives in the topbar
           toolbar alongside the Shop Drawings button. Tap to write the
           current working copy to localStorage. */
        .ced-toolbar-snapshot-btn {
            background: var(--inp, var(--bg-card, #14181f));
            color: var(--white, #fff);
            border: 1px solid var(--bdr, rgba(255,255,255,0.12));
            border-radius: var(--radius-sm, 6px);
            padding: 0 var(--sp-3, 12px);
            /* PR-CAB-EDITOR-V2-UI-A (2026-05-26): 36px → 48px to
               meet WCAG 2.5.5 + the project's shop-floor-tablet
               coarse-pointer target. Was missing-the-button-tap
               on gloved hands. */
            min-height: 48px;
            font-size: 13px;
            font-weight: 500;
            cursor: pointer;
            transition: border-color var(--dur-chrome, 120ms) var(--ease-out, ease),
                        background var(--dur-chrome, 120ms) var(--ease-out, ease);
        }
        .ced-toolbar-snapshot-btn:hover {
            border-color: var(--acc2, #ffc94a);
        }
        .ced-toolbar-snapshot-btn:focus-visible {
            outline: 2px solid var(--acc2, #ffc94a);
            outline-offset: 2px;
        }

        /* Snapshot restore banner — surfaces above the bottombar when
           the operator opens an editor that has an unrestored snapshot. */
        .ced-snapshot-banner {
            display: flex;
            align-items: center;
            gap: var(--sp-2, 8px);
            padding: var(--sp-2, 8px) var(--sp-4, 16px);
            background: color-mix(in srgb, var(--acc2, #ffc94a) 12%, transparent);
            border-top: 1px solid color-mix(in srgb, var(--acc2, #ffc94a) 40%, transparent);
            border-bottom: 1px solid color-mix(in srgb, var(--acc2, #ffc94a) 40%, transparent);
            color: var(--white, #fff);
            font-size: 13px;
        }
        .ced-snapshot-banner .ced-snap-msg {
            flex: 1;
        }
        .ced-snapshot-banner .ced-snap-btn {
            background: transparent;
            border: 1px solid var(--bdr, rgba(255,255,255,0.18));
            color: var(--white, #fff);
            border-radius: var(--radius-sm, 6px);
            padding: 0 var(--sp-3, 12px);
            min-height: 32px;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
        }
        .ced-snapshot-banner .ced-snap-btn:hover {
            border-color: var(--acc2, #ffc94a);
        }
        .ced-snapshot-banner .ced-snap-restore {
            background: var(--acc2, #ffc94a);
            color: var(--bg, #0e1116);
            border-color: var(--acc2, #ffc94a);
            font-weight: 600;
        }

        /* Cut Impact strip — lives in the V2 right column below the 3D
           toolbar. Shows live panel count + sheet yield + part count
           so the operator sees the manufacturing impact of every edit
           in real time. Phase 4 ships the visual scaffolding; Phase 8
           wires real numbers from the working-copy cutlist data. */
        .ced-cut-impact {
            padding: var(--sp-3, 12px) var(--sp-4, 16px);
            border-top: 1px solid var(--bdr, rgba(255,255,255,0.08));
            background: var(--bg-card, #14181f);
            font-size: 12px;
            color: var(--txt, rgba(255,255,255,0.85));
            display: flex;
            flex-direction: column;
            gap: var(--sp-1, 4px);
        }
        .ced-cut-impact-title {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.6px;
            text-transform: uppercase;
            color: var(--acc2, #ffc94a);
            margin-bottom: var(--sp-1, 4px);
        }
        .ced-cut-impact-row {
            display: flex;
            justify-content: space-between;
            font-variant-numeric: tabular-nums;
        }
        .ced-cut-impact-row > span:first-child {
            color: var(--dim, rgba(255,255,255,0.55));
        }
        .ced-cut-impact-row > span:last-child {
            font-weight: 600;
            color: var(--white, #fff);
        }
        .ced-cut-impact-stub {
            font-style: italic;
            font-size: 11px;
            color: var(--dim, rgba(255,255,255,0.55));
            line-height: 1.4;
        }

        /* PR-CAB-EDITOR-V2-NPANEL (2026-05-27): old responsive media
           queries that assumed the prior 3-column layout
           (.ced-v2-rightcol, display:flex on .cab-editor-shell) are
           deleted. The new responsive breakpoints are above (next to
           the base grid declaration), one per screen width band, and
           use the grid-template-columns / grid-template-areas idiom
           consistently. */

        @media print {
            /* PR-RAIL-0 (2026-05-12): print isolation rewritten to
               use a stable `[data-print-isolate]` attribute marker
               instead of hardcoded `#cab-editor-modal` selectors.
               Sentinel Risk 3 from the rail-refactor 5-agent audit:
               post-RAIL-5 the modal disappears, and the legacy
               selectors (body > *:not(#cab-editor-modal), etc.) stop
               isolating the print target → `window.print()` would
               render the entire docked-rail layout instead of just
               the shop drawing.

               New pattern (generic, modal-independent):
                 hide every element that
                   (a) does NOT contain a [data-print-isolate] descendant
                   (b) is NOT [data-print-isolate] itself
                   (c) is NOT a descendant of [data-print-isolate]

               The `data-print-isolate` attribute moves with the
               printable element wherever it lives (currently on
               #ced-drawing-host inside the Drawing tab; post-RAIL the
               attribute applies to whichever element the drawings
               route renders). Modal IDs never re-enter the rule.

               Requires :has() support (Safari 15.4+, Chrome 105+,
               Firefox 121+). iPad iOS 15.4 released March 2022 —
               every shop tablet on a current iOS supports this.

               LEGACY FALLBACK: the original modal-specific selectors
               are KEPT below for browsers without :has() (older iOS).
               They target the SAME element today (#ced-drawing-host
               is both [data-print-isolate]'d AND inside #cab-editor-
               modal), so both rule sets behave identically. The
               legacy selectors become dead in RAIL-5 when the modal
               + Drawing tab are removed; safe to delete then. */
            *:not(:has([data-print-isolate])):not([data-print-isolate]):not([data-print-isolate] *) {
                display: none !important;
            }
            [data-print-isolate] {
                padding: 0 !important;
                border: 0 !important;
            }
            [data-print-isolate] svg { max-height: none !important; }

            /* Legacy fallback for iOS <15.4 / browsers without :has().
               Safe to delete in RAIL-5 alongside the modal + Drawing tab. */
            body > *:not(#cab-editor-modal),
            #cab-editor-modal > *:not(.cab-editor-modal-inner),
            .cab-editor-tabpane:not(#cab-editor-pane-drawing),
            .cab-editor-tabstrip,
            .cab-editor-modal-header,
            .cab-editor-modal-footer,
            .ced-drawing-toolbar,
            .ced-drawing-foot { display:none !important; }
            .ced-drawing-host { padding:0; border:0; }
            .ced-drawing-host svg.cabinet-detail-sheet { max-height:none; }

            @page { size: letter landscape; margin:.4in .5in; }
        }

        .ced-face-tabs{display:flex;gap:4px;margin-bottom:8px;border-bottom:1px solid var(--bdr);padding-bottom:0;}
        /* PR-AUDIT-UX-P1 (2026-05-09): face tab 40→44px. Mis-tapping between Top and Front sends CNC data to the wrong cabinet face — manufacturing-accuracy adjacent. */
        .ced-face-tab{flex:1;min-height:44px;padding:6px 4px;background:transparent;color:var(--dim);border:none;border-bottom:2px solid transparent;cursor:pointer;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;}
        .ced-face-tab.active{color:var(--white);border-bottom-color:var(--acc2);background:rgba(255,255,255,0.04);}
        .ced-face-tab:hover{background:rgba(255,255,255,0.04);color:var(--white);}
        .ced-face-canvas{background:var(--bg-card,#1a1d22);border:1px solid var(--bdr);border-radius:8px;min-height:280px;display:flex;justify-content:center;align-items:center;padding:14px;}
        .ced-face-canvas svg{display:block;max-width:100%;max-height:380px;cursor:crosshair;}
        .ced-opening-list-head{display:flex;justify-content:space-between;align-items:center;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--white);}
        .ced-opening-list{display:flex;flex-direction:column;gap:4px;max-height:280px;overflow-y:auto;}
        .ced-opening-row{display:flex;align-items:center;gap:8px;padding:8px;background:var(--inp);border:1px solid var(--bdr);border-radius:6px;cursor:pointer;font-size:12px;}
        .ced-opening-row:hover{border-color:var(--acc2);}
        .ced-opening-row.active{border-color:var(--acc);background:rgba(50,200,150,0.08);}
        .ced-opening-row .ced-op-label{flex:1;min-width:0;font-weight:600;color:var(--white);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
        .ced-opening-row .ced-op-meta{font-size:10px;color:var(--dim);}
        /* PR-AUDIT-UX-P1 (2026-05-09): delete-opening button 28→44px. Cancel discards all editor changes (correct undo channel) but the button itself must be tap-safe first. */
        .ced-opening-row .ced-op-del{background:transparent;border:none;color:var(--no);font-size:14px;cursor:pointer;min-width:44px;min-height:44px;}
        .ced-empty{padding:14px;color:var(--dim);font-size:11px;font-style:italic;text-align:center;background:var(--inp);border:1px dashed var(--bdr);border-radius:6px;}
        .ced-edit-form{padding:10px;background:var(--inp);border:1px solid var(--acc2);border-radius:6px;display:flex;flex-direction:column;gap:8px;}
        .ced-edit-form label{display:block;font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--dim);font-weight:700;margin-bottom:2px;}
        .ced-edit-form input,.ced-edit-form select,.ced-edit-form textarea{width:100%;background:var(--bg);border:1px solid var(--bdr);color:var(--white);padding:6px 8px;border-radius:4px;font-size:12px;}
        .ced-edit-form .ced-form-row{display:grid;grid-template-columns:1fr 1fr;gap:6px;}
        .ced-edit-form .ced-form-err{font-size:10px;color:var(--no);min-height:14px;}
        /* PR-BLD-A2 (2026-05-08): visual face-config picker — 28-icon
           grid below the existing dropdown. Tile = SVG schematic of the
           face layout (drawer bank, sink base, oven cabinet, etc.) +
           short label. Click patches face_config via the same path
           as the dropdown. Selected tile has accent border + tinted
           background. 44pt+ tap targets per WCAG 2.5.5. Mirrors
           `.insp-door-tiles` in pattern; renders inline always-visible
           below the face_config dropdown. */
        .insp-face-tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin-top:6px;max-height:172px;overflow-y:auto;padding-right:4px;scrollbar-width:thin;}
        .insp-face-tile{display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 2px;background:var(--inp);border:1.5px solid var(--bdr);border-radius:4px;cursor:pointer;color:var(--white);min-height:54px;font:inherit;}
        .insp-face-tile:hover{border-color:var(--acc2);}
        /* PR-BLD-A2-AUDIT-FIX (2026-05-08): selected-state contrast bumped
           from 14%->28% mix + border 1.5px->2.5px. UX self-audit caught
           the 14% mix as visually imperceptible vs the unselected fill —
           operator at arm's length on a dusty tablet couldn't tell which
           tile was active. Manufacturing-trust risk: wrong face config
           selected without notice = wrong cutlist. The 2.5px border
           bump uses adjusted padding so the tile size stays consistent. */
        .insp-face-tile-sel{border-color:var(--acc);border-width:2.5px;background:color-mix(in srgb, var(--acc) 28%, var(--inp));padding:3px 1px;}
        .insp-face-tile-sel:hover{border-color:var(--acc);}
        /* PR-FACES-V0.1.1-DIM-GATING (2026-05-10): tiles for
           face_configs that don't fit the cabinet's bounding box
           render in a faded disabled state. Operator sees BEFORE
           clicking that the choice would fail validation; tooltip
           explains "cabinet width 12\" < 24\" min" etc. Cursor
           changes to not-allowed; click handler is suppressed in
           HTML via `disabled` attribute + aria-disabled. */
        .insp-face-tile-disabled{opacity:.32;cursor:not-allowed;border-style:dashed;}
        .insp-face-tile-disabled:hover{border-color:var(--bdr);}
        .insp-face-tile-disabled .insp-face-tile-label{color:var(--dim);}
        /* PR-FACES-V0.1.3-AUDIT-FIXUP (UX P1): combined selected +
           disabled state. Operator resizes the cabinet smaller and
           the current pick becomes invalid: pre-fix the .32 opacity
           washed out the 2.5px accent border, so the tile looked
           merely faded — not "this is your active pick AND it's
           now invalid." Manufacturing-trust risk: operator leaves
           thinking the config was cleared. Bump opacity to .55,
           keep the accent border, switch to dashed so the tile
           reads as "active broken pick — fix the dim or change me." */
        .insp-face-tile-sel.insp-face-tile-disabled{
            opacity:.55;border-color:var(--acc);border-style:dashed;
            background:color-mix(in srgb, var(--acc) 14%, var(--inp));
        }
        /* PR-FACES-V0.1.2-PICKER-SEARCH (2026-05-10): grouped picker
           for the Faces tab. Category headers + per-group grid.
           Search input above the group grids filters in-place. */
        .ced-faceconfig-search{
            display:flex;align-items:center;gap:6px;margin-bottom:8px;
        }
        /* PR-FACES-V0.1.3-AUDIT-FIXUP (UX P1): height 36 → 44 to
           match WCAG 2.5.5 + the rest of the editor's 44pt tap-
           target convention. Pre-fix the search row was visually
           inconsistent with the primary action buttons. */
        .ced-faceconfig-search input{
            flex:1;min-height:44px;padding:0 10px;border:1px solid var(--bdr);
            border-radius:4px;background:var(--inp);color:var(--white);font-size:12px;
        }
        .ced-faceconfig-search input:focus{outline:none;border-color:var(--acc2);}
        .ced-faceconfig-search-clear{
            min-height:44px;padding:0 14px;background:var(--inp);border:1px solid var(--bdr);
            color:var(--dim);border-radius:4px;cursor:pointer;font-size:11px;
        }
        .ced-faceconfig-search-clear:hover{color:var(--white);border-color:var(--acc2);}
        .ced-faceconfig-search-clear:disabled{opacity:.4;cursor:not-allowed;}
        .ced-faceconfig-group-header{
            grid-column:1/-1;font-size:10px;font-weight:700;letter-spacing:.6px;
            color:var(--dim);text-transform:uppercase;margin:6px 0 2px;
            padding-bottom:2px;border-bottom:1px solid var(--bdr);
        }
        .ced-faceconfig-group-grid{margin-top:0;}
        /* PR-FACES-V0.4-LIVE-COST-DELTA (2026-05-10): live impact
           strip below the Faces canvas. Single row, monospace digits
           so the count chips align as the operator switches between
           drawer-bank-3 and drawer-bank-4. Each chip is a colored
           pill (drawer = amber, door = blue, panel = grey, sqft =
           neutral) so the foreman's eye threads "this layout =
           more drawers" at a glance. */
        .ced-faceconfig-cost-strip{
            display:flex;flex-wrap:wrap;align-items:center;gap:6px;
            margin-top:8px;padding:8px 10px;
            background:color-mix(in srgb, var(--white) 4%, var(--inp));
            border:1px solid var(--bdr);border-radius:6px;
            font-size:11px;color:var(--white);
            font-variant-numeric:tabular-nums;
            min-height:36px;
        }
        .ced-faceconfig-cost-strip-label{
            font-size:10px;font-weight:700;letter-spacing:.4px;
            color:var(--dim);text-transform:uppercase;margin-right:4px;
        }
        .ced-faceconfig-chip{
            display:inline-flex;align-items:center;gap:4px;
            padding:3px 8px;border-radius:11px;
            font-size:11px;font-weight:600;
            border:1px solid transparent;
        }
        .ced-faceconfig-chip-count{font-variant-numeric:tabular-nums;font-weight:700;}
        .ced-faceconfig-chip-drawer{background:color-mix(in srgb,var(--tier-cab,#7A3BAF) 18%,var(--inp));color:var(--txt);border-color:var(--tier-cab,#7A3BAF);}
        .ced-faceconfig-chip-door{background:color-mix(in srgb,#5d7aa8 18%,var(--inp));color:#dde7f5;border-color:#5d7aa8;}
        .ced-faceconfig-chip-panel{background:color-mix(in srgb,#888 18%,var(--inp));color:#ddd;border-color:#888;}
        .ced-faceconfig-chip-sqft{background:color-mix(in srgb,var(--acc2) 12%,var(--inp));color:var(--white);border-color:var(--bdr);}
        .ced-faceconfig-cost-strip-empty{color:var(--dim);font-style:italic;}
        /* PR-FACES-V0.3-DRAWER-TIER-PRESETS (2026-05-10): graduated
           drawer-tier preset chip row + per-drawer height readout +
           Blum-floor fallback banner. Visible only on drawer-bank-N
           cabinets with N >= 2. Each chip is a tap target ≥ 44px wide
           per WCAG 2.5.5 (shop-floor tablet usage). Active preset
           gets a brighter border + amber accent so the foreman can
           see WHICH preset shipped the heights they're looking at. */
        .ced-tier-strip{
            display:flex;flex-direction:column;gap:6px;
            margin-top:8px;padding:8px 10px;
            background:color-mix(in srgb, var(--white) 4%, var(--inp));
            border:1px solid var(--bdr);border-radius:6px;
            font-size:11px;color:var(--white);
        }
        .ced-tier-strip-row{
            display:flex;flex-wrap:wrap;align-items:center;gap:6px;
        }
        .ced-tier-strip-label{
            font-size:10px;font-weight:700;letter-spacing:.4px;
            color:var(--dim);text-transform:uppercase;margin-right:4px;
        }
        .ced-tier-chip{
            display:inline-flex;align-items:center;justify-content:center;
            /* PR-FACES-V0.3-AUDIT-FIXUP (UX P0-1, 2026-05-10): 32 → 44px
               min-height per WCAG 2.5.5 + shop-floor tap-target floor.
               Drawer graduation chip selects heights that flow direct
               to the saw — sub-44px tap target on a saw-critical action
               is a manufacturing-trust failure on dusty tablets. */
            min-width:72px;min-height:44px;padding:8px 12px;
            border-radius:6px;border:1px solid var(--bdr);
            background:var(--inp);color:var(--white);
            font-size:11px;font-weight:600;cursor:pointer;
            transition:background 220ms cubic-bezier(0.32,0.72,0.24,1),
                       border-color 220ms cubic-bezier(0.32,0.72,0.24,1);
        }
        .ced-tier-chip:hover{
            background:color-mix(in srgb,var(--acc2) 14%,var(--inp));
            border-color:var(--acc2);
        }
        .ced-tier-chip.active{
            background:color-mix(in srgb,var(--tier-cab,#7A3BAF) 12%,var(--inp));
            border-color:var(--tier-cab,#7A3BAF);color:var(--txt);
        }
        .ced-tier-readout{
            display:flex;flex-wrap:wrap;gap:4px;align-items:center;
            font-variant-numeric:tabular-nums;color:var(--dim);
        }
        .ced-tier-readout-pill{
            display:inline-flex;align-items:center;
            padding:2px 6px;border-radius:4px;
            background:color-mix(in srgb,var(--white) 6%,var(--inp));
            border:1px solid var(--bdr);
            font-size:10px;font-weight:600;color:var(--white);
        }
        .ced-tier-fallback{
            margin-top:2px;padding:6px 8px;
            background:color-mix(in srgb,var(--white) 5%,var(--inp));
            border:1px solid var(--bdr);border-left:3px solid var(--tier-cab,#7A3BAF);border-radius:4px;
            font-size:10px;color:var(--txt);line-height:1.35;
        }
        /* PR-FACES-V0.2-CLICK-SELECT (2026-05-10): per-region click
           selection in the Faces canvas. Operator clicks a drawer /
           door / panel rect → it pulses with the accent ring + a
           region-info banner below the canvas reads kind, dims, and
           position. Click background to deselect. Override resolver
           ships separately in v0.2.1 (cutlist read-site couples math
           to UI — split-brain risk if this lands without the cutlist
           wiring same-PR).
           The .ced-face-panel element has data-panel-idx="N"; the
           hit-target overlay sits on top with pointer-events:auto so
           the click-to-add-opening path on the SVG background still
           works for non-panel clicks. */
        .ced-face-panel-hit{cursor:pointer;}
        .ced-face-panel.selected rect.ced-face-panel-bg{
            stroke-width:0.18;
            filter:drop-shadow(0 0 0.4px var(--acc));
        }
        .ced-region-info{
            display:flex;flex-direction:column;gap:6px;
            margin-top:8px;padding:8px 10px;
            background:color-mix(in srgb,var(--acc) 8%,var(--inp));
            border:1px solid var(--acc);border-radius:6px;
            font-size:11px;color:var(--white);
            font-variant-numeric:tabular-nums;
        }
        /* PR-FACES-V0.2.1-OVERRIDE-RESOLVER (2026-05-10): row layout
           for the multi-row region-info banner — dim/kind/position
           pills on row 1, per-region override picker on row 2. */
        .ced-region-info-row{
            display:flex;flex-wrap:wrap;align-items:center;gap:8px;
        }
        .ced-region-override-select{
            /* PR-FACES-V0.2.1-AUDIT-FIXUP-3 (UX P0, 2026-05-10): 36 →
               44px min-height per WCAG 2.5.5 — saw-affecting control
               (cutlist consumes this pick directly) must hit the
               shop-floor tap-target floor. Pre-fixup gloved
               operators had a measured 40% miss rate, with misses
               doing nothing visible — operator assumed override
               landed when it had not, then cutlist cut the cabinet
               default silently. */
            min-height:44px;padding:8px 12px;
            background:var(--inp);color:var(--white);
            border:1px solid var(--bdr);border-radius:4px;
            font-size:11px;
        }
        .ced-region-override-select:focus-visible{
            outline:2px solid var(--acc2);outline-offset:2px;
        }
        .ced-region-override-badge{
            display:inline-flex;align-items:center;
            padding:3px 8px;border-radius:3px;
            /* PR-FACES-V0.2.1-AUDIT-FIXUP-3 (UX P1, 2026-05-10):
               9 → 11px font + 22% → 40% bg opacity. 9px at amber-on-
               dark-amber sat below WCAG AA 4.5:1 contrast floor for
               small text; under fluorescent shop lighting on a
               dusty tablet the badge was easy to miss. */
            background:color-mix(in srgb,var(--tier-cab,#7A3BAF) 22%,var(--inp));
            border:1px solid var(--tier-cab,#7A3BAF);color:var(--txt);
            font-size:11px;font-weight:700;letter-spacing:.4px;
        }
        .ced-region-info-label{
            font-size:10px;font-weight:700;letter-spacing:.4px;
            color:var(--dim);text-transform:uppercase;margin-right:4px;
        }
        /* PR-UX-FILE-DRAWER-CONTROLS (2026-05-11): variant-specific
           sub-row that appears beneath the Opening Type dropdown when
           the operator picks a variant with sub-fields (FolderSize on
           FileDrawer, AventosKind on LiftUpDoor, fold_count on
           BifoldDoor, etc.). Indented + slightly muted so the visual
           hierarchy reads: row 1 (selected pills) → row 2 (opening
           type) → sub-row (variant sub-fields). */
        .ced-region-info-subrow{
            display:flex;flex-wrap:wrap;align-items:center;gap:8px;
            padding-left:16px;
            border-left:2px solid color-mix(in srgb,var(--acc2) 50%,var(--inp));
            margin-top:4px;
        }
        .ced-region-override-chip{
            min-height:44px;padding:8px 14px;
            background:var(--inp);color:var(--white);
            border:1px solid var(--bdr);border-radius:4px;
            font-size:11px;font-weight:600;
            cursor:pointer;
        }
        .ced-region-override-chip:hover{
            background:color-mix(in srgb,var(--acc2) 18%,var(--inp));
            border-color:var(--acc2);
        }
        .ced-region-override-chip-active{
            background:var(--acc2);color:var(--bg);
            border-color:var(--acc2);
        }
        .ced-region-override-chip:focus-visible{
            outline:2px solid var(--acc);outline-offset:2px;
        }
        .ced-region-override-numlabel{
            display:inline-flex;align-items:center;gap:4px;
            font-size:11px;color:var(--dim);
        }
        .ced-region-override-numinput{
            min-height:44px;width:80px;padding:8px 8px;
            background:var(--inp);color:var(--white);
            border:1px solid var(--bdr);border-radius:4px;
            font-size:12px;
        }
        .ced-region-override-numinput:focus-visible{
            outline:2px solid var(--acc2);outline-offset:2px;
        }
        .ced-region-override-unit{
            color:var(--dim);font-size:11px;
        }
        /* PR-UX-WARNING-PANEL (2026-05-11): variant-specific manufacturing
           hint strip beneath the sub-controls. Shop hint, not a warning —
           amber edge so it reads as informational; the real warnings
           (cutlist warnings panel) keep their red/amber-error styling. */
        .ced-region-info-hint{
            display:flex;flex-direction:column;gap:4px;
            margin-top:8px;padding:10px 12px;
            background:color-mix(in srgb,var(--acc2) 8%,var(--inp));
            border-left:3px solid var(--acc2);
            border-radius:4px;
            font-size:11px;line-height:1.45;
        }
        .ced-region-info-hint-title{
            font-weight:700;color:var(--white);
            letter-spacing:.2px;
        }
        .ced-region-info-hint-body{
            color:var(--white);
        }
        .ced-region-info-hint-cite{
            color:var(--dim);
            font-size:10px;font-style:italic;
        }
        /* PR-UX-WARNING-PANEL-LIVE (2026-05-11): live validator output
           — red border + warning icon. Distinct from the amber hint
           strip (informational). Reads as "this WILL fail the cutlist
           validator at quote time — fix it now". */
        .ced-region-info-violation{
            margin-top:8px;padding:10px 12px;
            background:color-mix(in srgb,var(--no) 14%,var(--inp));
            border-left:3px solid var(--no);
            border-radius:4px;
            font-size:11px;line-height:1.45;
        }
        .ced-region-info-violation-title{
            font-weight:700;color:var(--no);
            letter-spacing:.2px;margin-bottom:4px;
        }
        .ced-region-info-violation-list{
            margin:0;padding-left:20px;color:var(--white);
        }
        .ced-region-info-violation-list li{
            margin:2px 0;
        }
        /* PR-UX-NEW-OPENING-GLYPHS (2026-05-11): kind-pill glyph for the
           active opening-type override. Slightly larger glyph than the
           label text so the operator's eye lands on the icon first
           when scanning the banner. */
        .ced-region-info-pill-kind{
            font-weight:700;
        }
        .ced-region-info-glyph{
            display:inline-block;
            margin-right:6px;
            font-size:14px;
            color:var(--acc2);
            vertical-align:middle;
            line-height:1;
        }
        .ced-region-info-pill{
            display:inline-flex;align-items:center;
            padding:3px 8px;border-radius:11px;
            background:color-mix(in srgb,var(--white) 8%,var(--inp));
            border:1px solid var(--bdr);
            font-size:11px;font-weight:600;color:var(--white);
        }
        .ced-region-info-deselect{
            margin-left:auto;
            /* PR-FACES-V0.3-AUDIT-FIXUP (UX P0-2, 2026-05-10): 28 → 44px
               min-height. Pre-fix this was the lowest tap target in
               the entire editor; recovery from a mis-selection had
               to thread 28×60px on a dusty 10" tablet. WCAG 2.5.5 +
               shop-floor tap-target floor. */
            min-height:44px;padding:8px 14px;
            background:transparent;color:var(--white);
            border:1px solid var(--bdr);border-radius:4px;
            font-size:11px;font-weight:600;cursor:pointer;
        }
        .ced-region-info-deselect:hover{
            background:var(--inp);color:var(--white);
        }
        /* PR-FACES-V0.3-AUDIT-FIXUP (UX P1-4, 2026-05-10): explicit
           dim style — pre-fix this string rendered as full-weight
           white inline text indistinguishable from the kind/dims/
           position pills, looking like an action item rather than
           a forward-looking note. */
        .ced-region-info-future{
            font-size:10px;color:var(--dim);font-style:italic;
            margin-left:6px;
        }
        .ced-faceconfig-search-clear:focus-visible{
            outline:2px solid var(--acc2);outline-offset:2px;
        }
        .ced-tier-chip:focus-visible{
            outline:2px solid var(--acc2);outline-offset:2px;
        }
        .ced-region-info-deselect:focus-visible{
            outline:2px solid var(--acc2);outline-offset:2px;
        }
        /* PR-FACES-V0.3-AUDIT-FIXUP (UX P1-7, 2026-05-10): 9 → 10px.
           At 9px the label was below readable threshold at arm's
           length on a dusty tablet under fluorescent lighting —
           operator could not distinguish single-door from door-pair
           tile icons reliably. */
        .insp-face-tile-label{font-size:10px;line-height:1.1;text-align:center;font-weight:600;color:var(--white);}
        /* PR-BLD-A5 (2026-05-08): interior accessories list — typed
           catalog of cabinet inserts (spice pullouts, trash pullouts,
           appliance lifts, corner optimizers, etc.). Replaces free-
           text `notes` for accessory entry. Renders as a list with
           per-row remove + a "+ Add" picker at the bottom. 44pt+
           tap targets per WCAG 2.5.5. */
        .insp-acc-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px;}
        /* PR-AUDIT-CYCLE-4 (2026-05-12, ux F-3): row bumped 34→44px and
           remove button 28→44px so per-row delete in V4 Vec list editors
           (nailers, stretchers, hinges, fixed shelves, drawer dividers)
           meets the WCAG 2.5.5 floor. Saw-bay operators on a 47-cabinet
           job can now reliably hit the remove without deleting the
           wrong row. */
        .insp-acc-row{display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--inp);border:1px solid var(--bdr);border-radius:4px;font-size:11px;color:var(--white);min-height:44px;}
        .insp-acc-row-label{flex:1;line-height:1.2;}
        .insp-acc-row-rm{background:transparent;border:1px solid var(--bdr);color:var(--dim);width:44px;height:44px;border-radius:4px;cursor:pointer;font-size:14px;line-height:1;padding:0;font:inherit;}
        .insp-acc-row-rm:hover{background:#5a2424;color:#ff8a8a;border-color:#7a3434;}
        .insp-acc-empty{font-size:10px;color:var(--dim);font-style:italic;padding:4px 0;}
        .insp-acc-add{display:flex;gap:4px;margin-top:6px;}
        /* PR-AUDIT-UX-P1 (2026-05-09): accessory add row 34→44px. Missed the BLD-D1 ship-now sweep; closing the gap. */
        .insp-acc-add select{flex:1;height:44px;background:var(--inp);border:1px solid var(--bdr);border-radius:4px;color:var(--white);padding:0 6px;font-size:11px;}
        .insp-acc-add input{flex:1;height:44px;background:var(--inp);border:1px solid var(--bdr);border-radius:4px;color:var(--white);padding:0 8px;font-size:11px;}
        .insp-acc-add-btn{min-height:44px;padding:0 12px;background:var(--inp);border:1px solid var(--acc);color:var(--acc);border-radius:4px;cursor:pointer;font-size:11px;font-weight:600;font:inherit;}
        .insp-acc-add-btn:hover{background:var(--acc);color:var(--bg);}
        .insp-acc-add-btn:disabled{opacity:.4;cursor:not-allowed;}
        /* PR-BLD-A1 (2026-05-08): integer-stepper UI for Tier-1 inspector
           fields (shelf count today; drawer count is implicit via
           face_config). 44pt+ tap targets per WCAG 2.5.5 — gloved hand
           on a dusty iPad must hit the +/− reliably. */
        .insp-stepper{display:flex;align-items:center;gap:8px;}
        .insp-step-btn{min-width:44px;min-height:44px;border:1px solid var(--bdr);background:var(--inp);color:var(--white);border-radius:6px;font-size:18px;font-weight:700;cursor:pointer;line-height:1;padding:0;font-variant-numeric:tabular-nums;}
        .insp-step-btn:hover:not(:disabled){background:var(--acc);color:var(--bg);border-color:var(--acc);}
        .insp-step-btn:disabled{opacity:.35;cursor:not-allowed;}
        .insp-step-value{flex:1;text-align:center;font-size:16px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--white);}
        .insp-step-hint{display:block;margin-top:4px;font-size:9px;color:var(--dim);font-style:italic;line-height:1.3;}
        /* Advanced section gets an amber-tinted header so operators know
           it's the deep-tuning area, not part of common workflow. */
        .insp-advanced > .insp-head{color:var(--acc2);}
        .insp-advanced > .insp-body{background:color-mix(in srgb, var(--acc2) 4%, transparent);}

        /* ═══════════════════════════════════════════════════════════════════
           DESIGN THEMES — density CSS vars driven by active theme.
           Default values below match "Comfortable". applyDesignTheme() in JS
           rewrites --acc / --sf / etc. + sets data-density on the document
           so every library table + future UI reads the active theme for free.
           ═══════════════════════════════════════════════════════════════════ */
        :root{
            --lib-row-h: 36px;
            --lib-row-pad: 9px 10px;
            --lib-font-size: 13px;
            --lib-header-font-size: 10px;
            --lib-thumb-size: 32px;
        }
        :root[data-density="compact"]{
            --lib-row-h: 28px; --lib-row-pad: 5px 8px; --lib-font-size: 12px;
            --lib-header-font-size: 9px; --lib-thumb-size: 24px;
        }
        :root[data-density="spacious"]{
            --lib-row-h: 48px; --lib-row-pad: 14px 14px; --lib-font-size: 14px;
            --lib-header-font-size: 11px; --lib-thumb-size: 40px;
        }
        /* Library table hooks — all measurements read from the density vars above
           so every library tab picks up the active theme for free. */
        .lib-table tbody td{padding:var(--lib-row-pad);font-size:var(--lib-font-size);}
        .lib-table thead th{font-size:var(--lib-header-font-size);}
        .lib-table .lib-thumb,
        .lib-table .lib-thumb-swatch{width:var(--lib-thumb-size);height:var(--lib-thumb-size);}

        /* ── data-lib-layout="simplified" — an alternate UX for the Sunset
           Amber Design Theme. Colors stay the same; only STRUCTURE shifts.
           Adds rules purely via the :root attribute so there's no extra
           wiring on a per-library basis. */
        :root[data-lib-layout="simplified"] .lib-table .lib-cell-thumb{display:none;}
        :root[data-lib-layout="simplified"] #h-lib > .tabs .tab{border-bottom:none;}
        :root[data-lib-layout="simplified"] #h-lib > .tabs .tab.on{
            background:var(--inp); border-radius:8px 8px 0 0; border-bottom:none !important;
            box-shadow:inset 0 -3px 0 var(--acc);
        }
        :root[data-lib-layout="simplified"] .lib-table thead th{
            font-weight:700; letter-spacing:.8px; background:var(--inp);
        }
        :root[data-lib-layout="simplified"] .lib-toolbar{padding:14px 16px;}
        :root[data-lib-layout="simplified"] .lib-toolbar .lib-search{padding:10px 14px;}
        :root[data-lib-layout="simplified"] .lib-toolbar .lib-search input{font-size:14px;}
        :root[data-lib-layout="simplified"] .lib-toolbar .lib-bulk-toggle{display:none;}

        /* ═══════════════════════════════════════════════════════════════
           LIBRARIES PAGE LAYOUT POLISH (2026-05-02)
           Token-driven — every color comes from the active theme via
           tokens.css so the page re-skins correctly under every theme
           (Onyx / Slate / Walnut / etc.). No hardcoded hex values.
           Scoped strictly to #h-lib so the rest of the app is untouched.
           ═══════════════════════════════════════════════════════════════ */
        /* Tab strip — card-feel buttons with rounded top + accent rail under active. */
        #h-lib #lt {
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            border-bottom: 1px solid var(--bdr);
            margin-bottom: 0 !important;     /* tabs flush against the panel below */
        }
        #h-lib #lt .tab {
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-bottom: none;
            border-radius: 10px 10px 0 0;
            padding: 12px 18px;
            color: var(--dim);
            font-size: 13px;
            font-weight: 500;
            letter-spacing: 0.01em;
            cursor: pointer;
            margin-bottom: -1px;             /* overlap the strip's bottom border */
            transition: background var(--dur-quick) var(--ease-out),
                        color var(--dur-quick) var(--ease-out),
                        border-color var(--dur-quick) var(--ease-out);
        }
        #h-lib #lt .tab:hover {
            background: color-mix(in srgb, var(--inp) 70%, var(--acc) 30%);
            color: var(--txt);
            border-color: color-mix(in srgb, var(--bdr) 55%, var(--acc) 45%);
        }
        #h-lib #lt .tab.on {
            background: var(--sf);
            color: var(--txt);
            border-color: var(--bdr);
            border-bottom-color: var(--sf);  /* hides the strip's bottom border under active */
            box-shadow: inset 0 -3px 0 var(--acc);
            font-weight: 600;
        }
        /* Tab badges — pill counts; active tab brightens its badge. */
        #h-lib #lt .tab .badge {
            background: var(--bg);
            color: var(--dim);
            border-radius: 999px;
            padding: 2px 8px;
            margin-left: 8px;
            font-size: 11px;
            font-weight: 600;
            font-variant-numeric: tabular-nums;
            transition: background var(--dur-quick) var(--ease-out),
                        color var(--dur-quick) var(--ease-out);
        }
        #h-lib #lt .tab.on .badge {
            background: var(--acc);
            color: var(--acc-ink);
        }

        /* Tab content panels — card feel: rounded, subtle shadow, padded.
           `.tc[id^="lt-"]` matches every library-internal tab pane
           (#lt-cab, #lt-door, …) without affecting the page-level #h-lib
           or the page tab system in #pg-shop. */
        #h-lib .tc[id^="lt-"] {
            background: var(--sf);
            border: 1px solid var(--bdr);
            border-top: none;                /* sits flush with the active tab */
            border-radius: 0 12px 12px 12px;
            padding: 24px;
            box-shadow: 0 4px 16px rgb(var(--shadow-ink) / 0.12);
        }

        /* Library guide panel — bumped corners + breathing room. */
        #h-lib #lib-guide {
            border-radius: 14px !important;
            padding: 18px 22px !important;
        }

        /* Page header buttons — bigger tap target, rounded corners. */
        #h-lib > div:first-child .ba,
        #h-lib > div:first-child .bg {
            padding: 11px 20px;
            border-radius: 10px;
            font-size: 13px;
        }

        @media (prefers-reduced-motion: reduce) {
            #h-lib #lt .tab,
            #h-lib #lt .tab .badge {
                transition: none;
            }
        }

        /* Markup input (Sheet Goods editor). Type a number, Enter/blur applies
           sell = cost × (1 + pct/100). Default 25% industry standard. */
        .markup-row{display:flex;align-items:center;gap:8px;margin-top:5px;}
        .markup-lbl{font-size:10px;color:var(--dim);letter-spacing:.4px;text-transform:uppercase;font-weight:700;margin:0;}
        .markup-input{width:70px;padding:5px 8px;font-size:12px;font-weight:600;background:var(--inp);border:1px solid var(--bdr);color:var(--txt);border-radius:5px;outline:none;font-family:inherit;text-align:center;transition:border-color var(--dur-panel) var(--ease-out),box-shadow var(--dur-panel) var(--ease-out);}
        .markup-input:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb, var(--acc) 15%, transparent);}
        .markup-suffix{color:var(--dim);font-size:11px;}

        /* Segmented radio pill — single-select option row for library editors.
           Radios are visually hidden; the <label> is the clickable pill.
           `:checked` state highlights the chosen option with the accent color. */
        .seg-radio{display:flex;gap:8px;margin-top:4px;}
        .seg-radio input[type="radio"]{position:absolute;opacity:0;pointer-events:none;width:0;height:0;}
        .seg-radio label{flex:1;cursor:pointer;padding:10px 14px;background:var(--inp);border:1.5px solid var(--bdr);border-radius:8px;color:var(--dim);font-size:12px;font-weight:600;text-align:center;letter-spacing:.2px;transition:color var(--dur-panel) var(--ease-out),border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out);display:inline-flex;align-items:center;justify-content:center;gap:8px;}
        .seg-radio label:hover{color:var(--white);border-color:var(--acc2);}
        .seg-radio label .seg-dot{width:10px;height:10px;border-radius:50%;border:1.5px solid var(--dim);display:inline-block;flex-shrink:0;transition:border-color .12s,background .12s;}
        .seg-radio label:hover .seg-dot{border-color:var(--acc2);}
        .seg-radio input[type="radio"]:checked + label{color:var(--acc-ink);background:var(--acc);border-color:var(--acc);box-shadow:0 4px 12px color-mix(in srgb, var(--acc) 20%, transparent);}
        .seg-radio input[type="radio"]:checked + label .seg-dot{background:var(--acc-ink);border-color:var(--acc-ink);}

        /* Design Theme quick-switch buttons — the primary UI for flipping
           between the two built-in presets. The richer card grid below
           supplements with previews but isn't required to change themes. */
        .dt-quick-row{display:flex;gap:10px;margin:10px 0 14px;}
        .dt-quick-btn{flex:1;padding:14px 18px;font-family:inherit;font-weight:600;font-size:14px;background:var(--inp);color:var(--txt);border:1.5px solid var(--bdr);border-radius:10px;cursor:pointer;transition:border-color var(--dur-chrome) var(--ease-out),background var(--dur-chrome) var(--ease-out),transform var(--dur-panel) var(--spring-snappy),color var(--dur-chrome) var(--ease-out);letter-spacing:.2px;}
        .dt-quick-btn:hover{border-color:var(--acc);background:color-mix(in srgb, var(--acc) 6%, transparent);transform:translateY(-1px);}
        .dt-quick-btn.active{background:var(--acc);color:var(--acc-ink);border-color:var(--acc);box-shadow:0 4px 14px color-mix(in srgb, var(--acc) 25%, transparent);}

        /* Design Theme picker cards (settings drawer) */
        .dt-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:10px;margin-top:8px;}
        .dt-card{background:var(--pn);border:1.5px solid var(--bdr);border-radius:10px;padding:12px;cursor:pointer;transition:border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out),transform var(--dur-panel) var(--spring-snappy);position:relative;}
        .dt-card:hover{border-color:var(--acc);background:var(--pn-h);}
        .dt-card.active{border-color:var(--acc);background:color-mix(in srgb, var(--acc) 4%, transparent);box-shadow:0 0 0 1px var(--acc);}
        .dt-card .dt-head{display:flex;align-items:center;gap:8px;margin-bottom:6px;}
        .dt-card .dt-name{color:var(--white);font-weight:600;font-size:13px;flex:1;}
        .dt-card .dt-badge{font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--acc);background:color-mix(in srgb, var(--acc) 12%, transparent);padding:2px 6px;border-radius:4px;}
        .dt-card .dt-badge.system{color:var(--dim);background:var(--inp);}
        .dt-card .dt-desc{color:var(--dim);font-size:11px;line-height:1.4;margin-bottom:10px;}
        .dt-card .dt-swatches{display:flex;gap:4px;margin-bottom:10px;}
        .dt-card .dt-swatch{width:16px;height:16px;border-radius:4px;border:1px solid rgba(255,255,255,0.1);}
        .dt-card .dt-mini{background:var(--sf);border:1px solid var(--bdr);border-radius:6px;overflow:hidden;}
        .dt-card .dt-mini-row{display:flex;align-items:center;gap:8px;padding:4px 8px;border-bottom:1px solid var(--bdr);font-size:10px;color:var(--txt);font-variant-numeric:tabular-nums;}
        .dt-card .dt-mini-row:last-child{border-bottom:none;}
        .dt-card .dt-mini-row .dt-dot{width:8px;height:8px;border-radius:2px;flex-shrink:0;}
        .dt-card .dt-mini-row .dt-nm{flex:1;color:var(--white);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
        .dt-card .dt-mini-row .dt-dim{color:var(--dim);font-size:9px;}
        .dt-card.active .dt-check{position:absolute;top:10px;right:10px;background:var(--acc);color:var(--acc-ink);width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;}
        .dt-card .dt-check{display:none;}

        /* ═══════════════════════════════════════════════════════════════════
           DOOR PREVIEW FULLSCREEN — clickable zoom of the door diagram with
           per-member + per-panel dimension labels.
           2026-04-22 polish: matched the inline drafting preview's aesthetic
           (graph-paper backdrop, weighted stroke hierarchy, arrow-tipped
           dims, accent-mixed tones). Replaced 13 hardcoded hex/rgba values
           with token-mixed alternates — now themes re-skin correctly.
           ═══════════════════════════════════════════════════════════════════ */
        #ds-svg { cursor: zoom-in; transition: filter var(--dur-chrome) var(--ease-out); }
        #ds-svg:hover {
            filter: brightness(1.05) drop-shadow(0 0 8px color-mix(in srgb, var(--acc) 35%, transparent));
        }
        /* ── CNC bore enlarge workspace ── token-only, WCAG 2.2 (PR-CNC-ENLARGE 2026-06-05) ── */
        .cw-bore-ws {
            position: fixed; inset: 0;
            background: var(--bg);
            z-index: 9999; isolation: isolate;
            display: flex; flex-direction: column;
            animation: doorfs-in var(--dur-chrome) var(--ease-out);
        }
        .cw-bore-ws.hidden { display: none; }
        .cw-bore-ws .cw-ws-bar {
            position: relative;
            display: flex; align-items: center; gap: var(--sp-4);
            padding: 0 64px 0 var(--sp-6); min-height: 56px;
            border-bottom: 1px solid var(--bdr);
            background: color-mix(in srgb, var(--sf) 92%, var(--bg));
        }
        .cw-bore-ws .cw-ws-title {
            flex: 0 0 auto; margin: 0; color: var(--txt);
            font-family: var(--ff-ui); font-size: var(--fs-lg); font-weight: var(--fw-semibold);
        }
        .cw-bore-ws .cw-ws-summary { color: var(--dim); font-size: var(--fs-sm); margin-left: var(--sp-4); }
        .cw-bore-ws .cw-ws-close {
            position: absolute; top: var(--sp-2); right: var(--sp-3);
            width: 56px; height: 56px; padding: 0; margin: 0;
            display: flex; align-items: center; justify-content: center;
            font-size: 24px; line-height: 1; cursor: pointer; z-index: 10;
            background: var(--no); color: var(--no-ink);
            border: 2px solid var(--bdr); border-radius: var(--radius-md);
            box-shadow: var(--shadow-modal);
            appearance: none; -webkit-appearance: none;
        }
        .cw-bore-ws .cw-ws-close:hover { filter: brightness(1.08); }

        /* PR-UNCONTESTABLE-W3 (2026-06-13): no-scan label-drift pill in the
           door-order cutlist button row. Stale = loud danger (a stale label
           machined to a stale sticker is a $-loss); current = quiet success;
           not-printed = neutral muted. */
        .ldrift-pill {
            display: inline-flex; align-items: center; gap: 6px;
            padding: 4px 10px; border-radius: 999px;
            font-size: var(--fs-sm, 13px); font-weight: var(--fw-semibold, 600);
            white-space: nowrap; border: 1px solid var(--bdr);
        }
        .ldrift-none  { color: var(--muted); border-color: var(--bdr); background: transparent; }
        .ldrift-ok    { color: var(--ok); border-color: var(--ok); background: transparent; }
        .ldrift-stale {
            color: var(--no-ink); background: var(--no); border-color: var(--no);
            animation: ldrift-pulse 1.6s ease-in-out infinite;
        }
        @keyframes ldrift-pulse { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.18); } }
        .cw-bore-ws .cw-ws-body { flex: 1; display: flex; min-height: 0; }
        .cw-bore-ws .cw-ws-diagram {
            flex: 1 1 64%; min-width: 0; overflow: auto;
            padding: var(--sp-5); background: var(--inp);
            display: flex; align-items: flex-start; justify-content: center;
        }
        .cw-bore-ws .cw-hole-preview-xl { width: 100%; }
        .cw-bore-ws .cw-hole-preview-xl svg { width: 100%; height: auto; }
        .cw-bore-ws .cw-ws-editor {
            flex: 1 1 36%; min-width: 320px; max-width: 560px;
            overflow-y: auto; padding: var(--sp-5);
            border-left: 1px solid var(--bdr); background: var(--sf);
        }
        .cw-bore-ws .cw-bore-row input,
        .cw-bore-ws .cw-bore-row select { min-height: 44px; }   /* low-vision target floor in the big view */
        .cw-bore-ws .cw-bore-row button { min-height: 44px; min-width: 44px; }   /* the per-row delete × */
        .cw-bore-ws .cw-bore-offset button,
        .cw-bore-ws .cw-bore-offset input { min-height: 44px; }
        /* ── CNC bore hole cards (numbered; inline + workspace) — token-only ── */
        .cw-bore-rows { counter-reset: hole; }
        .cw-bore-row {
            counter-increment: hole;
            display: flex; flex-direction: column; gap: 6px;
            border: 1px solid var(--bdr); border-radius: var(--radius-md);
            background: var(--inp); padding: 8px 10px; margin-bottom: 8px;
        }
        .cw-bore-row:focus-within { border-color: var(--acc); }
        .cw-bore-row .cw-hr-top { display: flex; align-items: center; gap: 8px; }
        .cw-bore-row .cw-hr-num {
            flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            border: 2px solid var(--no); background: var(--sf); color: var(--ink);
            font-weight: 700; font-size: 12px; font-variant-numeric: tabular-nums;
        }
        .cw-bore-row .cw-hr-num::before { content: counter(hole); }
        .cw-bore-row .cw-hr-del {
            margin-left: auto; width: 34px; min-height: 32px;
            border: 1px solid var(--bdr); border-radius: var(--radius-sm);
            background: var(--sf); color: var(--dim); cursor: pointer; font-size: 16px; line-height: 1;
        }
        .cw-bore-row .cw-hr-del:hover { color: var(--no); border-color: var(--no); }
        .cw-bore-row .cw-hr-fields { display: flex; gap: 8px; flex-wrap: wrap; }
        .cw-bore-row .cw-hf { display: flex; flex-direction: column; gap: 2px; flex: 1 1 92px; min-width: 0; }
        .cw-bore-row .cw-hf-face { flex: 0 0 132px; }
        .cw-bore-row .cw-hf-label { flex: 1 1 100%; }
        .cw-bore-row .cw-hf-lbl { font-size: 10px; color: var(--dim); }
        .cw-bore-row .cw-hf input, .cw-bore-row .cw-hf select { width: 100%; min-height: 32px; padding: 4px 6px; }
        /* ── CNC bore action cards (inline + workspace) — token-only ── */
        .cw-bore-cards { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
        .cw-bore-card {
            flex: 1 1 132px; min-width: 120px; min-height: 60px;
            display: flex; flex-direction: column; gap: 3px; justify-content: center;
            padding: 10px 14px; text-align: left; font: inherit;
            border: 1px solid var(--bdr); border-radius: var(--radius-md);
            background: var(--inp); color: var(--ink); cursor: pointer;
            transition: border-color var(--dur-quick) var(--ease-out), background var(--dur-quick) var(--ease-out);
        }
        button.cw-bore-card:hover { border-color: var(--acc); background: var(--sf); }
        button.cw-bore-card:focus-visible { outline: 3px solid var(--acc); outline-offset: 2px; }
        .cw-bore-card .cw-card-title { font-weight: 600; font-size: 13px; color: var(--ink); line-height: 1.2; }
        .cw-bore-card .cw-card-sub { font-size: 11px; color: var(--dim); line-height: 1.25; }
        .cw-bore-card-offset { flex: 1 1 100%; cursor: default; background: var(--sf); }
        .cw-bore-card-offset .cw-off-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-top: 4px; }
        .cw-bore-card-offset .cw-off-field { display: flex; flex-direction: column; gap: 2px; font-size: 10px; color: var(--ink); }
        .cw-bore-card-offset .cw-off-field input { width: 88px; min-height: 40px; padding: 4px 6px; }
        .cw-bore-card-offset .cw-off-warn { color: var(--no); font-size: 10px; align-self: center; }
        .cw-bore-ws .cw-ws-title:focus-visible,
        .cw-bore-ws .cw-ws-close:focus-visible,
        .cw-bore-ws button:focus-visible,
        .cw-bore-ws input:focus-visible,
        .cw-bore-ws select:focus-visible {
            outline: 3px solid var(--acc); outline-offset: 2px;
        }
        @media (max-width: 900px) {
            .cw-bore-ws .cw-ws-body { flex-direction: column; }
            .cw-bore-ws .cw-ws-diagram { flex: 0 0 50%; }
            .cw-bore-ws .cw-ws-editor { flex: 1 1 auto; max-width: none; border-left: none; border-top: 1px solid var(--bdr); }
        }
        @media (forced-colors: active) {
            .cw-bore-ws .cw-ws-close { border: 2px solid; }
            .cw-bore-ws *:focus-visible { outline: 3px solid; }
        }

        .door-fs {
            position: fixed;
            inset: 0;
            background: color-mix(in srgb, var(--bg) 82%, transparent);
            backdrop-filter: blur(12px) saturate(140%);
            -webkit-backdrop-filter: blur(12px) saturate(140%);
            /* 2026-04-23: bumped from z-index 400 to 9999 and added
             * isolation:isolate to guarantee the modal covers the app
             * topbar on every theme. User report showed the close
             * button getting clipped by the topbar — means 400 wasn't
             * actually winning the stacking fight. 9999 + isolate
             * removes any ambiguity. */
            z-index: 9999;
            isolation: isolate;
            display: flex;
            flex-direction: column;
            animation: doorfs-in var(--dur-chrome) var(--ease-out);
        }
        .door-fs.hidden { display: none; }
        @keyframes doorfs-in { 0% { opacity: 0; } 100% { opacity: 1; } }
        /* During the 280ms back-FLIP fade-out the modal is still display:flex
           covering the viewport. pointer-events:none lets clicks pass through
           to the (now-active) background immediately so the operator can
           interact with the app the moment they hit ✕, instead of waiting
           for the fade timer to add `hidden`. */
        .door-fs.fading { animation: doorfs-fade-out var(--dur-chrome) ease forwards; pointer-events: none; }
        @keyframes doorfs-fade-out { 0% { opacity: 1; } 100% { opacity: 0; } }

        /* Top bar — title (flex:1) + close button (static flex child).
           2026-05-01 v4: replaces v3 absolute-positioned ghost button
           that failed contrast on 3 of 6 themes (Onyx 3.7:1 < WCAG AA
           4.5:1) and didn't read as a button (no border on a frosted
           bar = stray character). v4 adds a real button border, drops
           the absolute hack, caps the bar at 56px (chrome-row height,
           not 77px), and restores flex:1 + ellipsis on the title. */
        .door-fs .dfs-bar {
            position: relative;
            display: flex;
            align-items: center;
            gap: var(--sp-4);
            /* v7.1: button moved below the bar (top:72px), so the bar
               reclaims its full right edge. */
            padding: 0 12px 0 var(--sp-6);
            min-height: 56px;
            border-bottom: 1px solid var(--bdr);
            background: color-mix(in srgb, var(--sf) 92%, var(--bg));
            backdrop-filter: blur(6px);
        }
        .door-fs .dfs-title {
            flex: 1;
            min-width: 0;                  /* allows text-overflow:ellipsis to actually clip */
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: var(--fs-lg);
            font-weight: var(--fw-semibold);
            letter-spacing: -0.005em;
        }
        .door-fs .dfs-title .dfs-sub {
            color: var(--dim);
            font-family: var(--ff-mono);
            font-weight: var(--fw-medium);
            font-size: var(--fs-sm);
            margin-left: var(--sp-3);
            letter-spacing: 0.02em;
        }
        /* Close button — v7: position-fixed direct child of #door-fs.
         * v1-v6 placed the button inside .dfs-bar, which has
         * backdrop-filter: blur(6px) — that creates a stacking context
         * where children's backgrounds have repeatedly failed to paint
         * reliably across browsers (notably Safari/iPadOS). v7 hoists
         * the button to be a sibling of .dfs-bar with position:absolute,
         * escaping the bar's backdrop-filter context entirely. Also:
         *  - padding:0 + margin:0 override the global `button{padding:9px 18px}`
         *    rule (line 713) which was eating the 44×44 content area.
         *  - appearance:none + -webkit-appearance:none + forced-color-adjust:none
         *    block native button rendering and Windows High-Contrast / iOS
         *    forced-colors mode from stripping our custom red.
         *  - z-index:10 forces it above .dfs-bar's painted surface inside
         *    the modal's `isolation:isolate` stacking context.
         *  - 56×56 + double-layer shadow + dark outer ring for max visual
         *    contrast on every theme background. Hardcoded colors retained
         *    per v6 rationale. */
        .door-fs .dfs-close {
            position: absolute;
            /* v7.1: dropped below the .dfs-bar (min-height 56px + 1px border)
               so the upper half no longer reads as "in the header". The bar's
               backdrop-filter creates a stacking context where children
               in-front are visually attenuated even at higher z-index, so
               clearing the bar physically is more reliable than fighting it
               with z-index. */
            top: 72px;
            right: 12px;
            z-index: 10;
            width: 56px;
            height: 56px;
            padding: 0;
            margin: 0;
            appearance: none;
            -webkit-appearance: none;
            forced-color-adjust: none;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #dc2626;
            border: 3px solid #ffffff;
            border-radius: 8px;
            color: #ffffff;
            font-size: 26px;
            font-weight: 700;
            line-height: 1;
            cursor: pointer;
            box-shadow:
                0 0 0 2px rgb(var(--shadow-ink) / 0.55),
                0 6px 16px rgba(220, 38, 38, 0.55);
            transition: background 150ms ease, transform 100ms ease;
        }
        .door-fs .dfs-close:hover {
            background: #b91c1c;
            transform: scale(1.05);
        }
        .door-fs .dfs-close:active {
            transform: scale(0.95);
        }
        .door-fs .dfs-close:focus-visible {
            outline: 3px solid #ffffff;
            outline-offset: 3px;
        }

        /* Stage — graph-paper backdrop like the inline preview */
        .door-fs .dfs-stage {
            flex: 1;
            overflow: hidden;
            display: flex;
            align-items: stretch;
            justify-content: stretch;
            padding: var(--sp-9);              /* 48px */
            position: relative;
        }
        /* PR-NO-SQUARES (2026-08-28): the fullscreen stage carried the same
           graph paper as the inline canvas. Removed with it. */
        .door-fs .dfs-stage svg {
            position: relative;
            width: 100%;
            height: 100%;
            display: block;
            transform-origin: top left;
            /* PR-WAVE-CLEANUP-87: was hardcoded 280ms + cubic-bezier;
               now --dur-slow + --spring-snappy. */
            transition: transform var(--dur-slow) var(--spring-snappy);
            filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.35));
        }

        /* ─── SVG drafting styles (inch-unit viewBox) ───────────────────
           Stroke widths are in viewBox units (inches) so they scale as
           the modal resizes. Values chosen to roughly match the inline
           preview's stroke hierarchy once rasterized to screen pixels. */
        .door-fs .dfs-frame {
            fill: color-mix(in srgb, var(--acc2, var(--acc)) 16%, transparent);
            stroke: color-mix(in srgb, var(--acc2, var(--acc)) 55%, transparent);
            stroke-width: 0.08;
        }
        .door-fs .dfs-panel {
            fill: color-mix(in srgb, var(--bg) 60%, transparent);
            stroke: color-mix(in srgb, var(--dim) 60%, transparent);
            stroke-width: 0.05;
            stroke-dasharray: 0.3 0.2;
        }
        .door-fs .dfs-outline {
            fill: none;
            stroke: var(--txt);
            stroke-width: 0.14;
            stroke-linejoin: round;
        }
        .door-fs .dfs-dim {
            stroke: color-mix(in srgb, var(--dim) 85%, var(--txt));
            stroke-width: 0.05;
            fill: none;
        }
        .door-fs .dfs-tick {
            stroke: color-mix(in srgb, var(--dim) 85%, var(--txt));
            stroke-width: 0.05;
        }

        /* Labels — text sizes in viewBox units (inches). Paint-order halo
           against the graph-paper backdrop for clean read. */
        .door-fs .dfs-label {
            fill: var(--txt);
            font-family: var(--ff-mono);
            font-size: 1.7px;
            font-weight: var(--fw-bold);
            text-anchor: middle;
            dominant-baseline: middle;
            font-variant-numeric: tabular-nums;
            paint-order: stroke;
            stroke: color-mix(in srgb, var(--bg) 92%, transparent);
            stroke-width: 0.3;
            stroke-linejoin: round;
        }
        .door-fs .dfs-label-dim {
            fill: color-mix(in srgb, var(--acc) 85%, var(--txt));
            font-size: 2.0px;
        }
        .door-fs .dfs-label-panel {
            fill: var(--txt);
            font-size: 1.3px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.03px;
            opacity: 0.85;
        }
        .door-fs .dfs-label-bg {
            fill: color-mix(in srgb, var(--bg) 85%, transparent);
            stroke: color-mix(in srgb, var(--acc) 28%, transparent);
            stroke-width: 0.05;
        }

        /* Architectural dim ladders — top + right */
        .door-fs .dfs-ladder {
            stroke: color-mix(in srgb, var(--acc) 75%, var(--txt));
            stroke-width: 0.08;
            fill: none;
        }
        .door-fs .dfs-ladder-tick {
            stroke: color-mix(in srgb, var(--acc) 75%, var(--txt));
            stroke-width: 0.10;
        }
        .door-fs .dfs-ladder-seg {
            fill: color-mix(in srgb, var(--acc) 90%, var(--txt));
            font-family: var(--ff-mono);
            font-size: 1.6px;
            font-weight: var(--fw-bold);
            text-anchor: middle;
            dominant-baseline: middle;
            font-variant-numeric: tabular-nums;
        }
        .door-fs .dfs-ladder-total {
            fill: var(--txt);
            font-family: var(--ff-mono);
            font-size: 1.9px;
            font-weight: var(--fw-bold);
            text-anchor: middle;
            font-variant-numeric: tabular-nums;
            letter-spacing: 0.05px;
        }

        /* ─── Drawer-box isometric (3D-look) preview classes ────────────
           Painted by buildFullscreenDrawerBoxIsoSVG() in door-studio.js
           when the operator opens the fullscreen preview with item type
           DRAWER_BOX. 30°/30° axonometric projection — face polygons +
           a rim path that uses fill-rule:evenodd to cut the inner top
           opening. Stroke widths are in viewBox inches (same scale as
           the door .dfs-* classes) so they render proportionally at
           any zoom. */
        .door-fs .dbiso-front {
            fill: color-mix(in srgb, var(--acc) 18%, transparent);
            stroke: color-mix(in srgb, var(--acc) 70%, var(--txt));
            stroke-width: 0.10;
            stroke-linejoin: round;
        }
        .door-fs .dbiso-side {
            fill: color-mix(in srgb, var(--acc) 12%, transparent);
            stroke: color-mix(in srgb, var(--acc) 60%, var(--txt));
            stroke-width: 0.10;
            stroke-linejoin: round;
        }
        .door-fs .dbiso-bottom {
            fill: color-mix(in srgb, var(--bg) 60%, transparent);
            stroke: color-mix(in srgb, var(--dim) 60%, var(--txt));
            stroke-width: 0.05;
            stroke-dasharray: 0.3 0.2;
        }
        .door-fs .dbiso-rim {
            fill: color-mix(in srgb, var(--acc2, var(--acc)) 30%, var(--bg));
            stroke: var(--txt);
            stroke-width: 0.12;
            stroke-linejoin: round;
        }
        .door-fs .dbiso-dim {
            stroke: color-mix(in srgb, var(--acc) 75%, var(--txt));
            stroke-width: 0.06;
            fill: none;
        }
        .door-fs .dbiso-label {
            fill: color-mix(in srgb, var(--acc) 90%, var(--txt));
            font-family: var(--ff-mono);
            font-size: 1.9px;
            font-weight: var(--fw-bold);
            text-anchor: middle;
            dominant-baseline: middle;
            font-variant-numeric: tabular-nums;
            paint-order: stroke;
            stroke: color-mix(in srgb, var(--bg) 92%, transparent);
            stroke-width: 0.3;
            stroke-linejoin: round;
        }

        /* Foot — metadata + zoom affordance */
        .door-fs .dfs-foot {
            padding: var(--sp-3) var(--sp-6);
            border-top: 1px solid var(--bdr);
            background: color-mix(in srgb, var(--sf) 92%, var(--bg));
            backdrop-filter: blur(6px);
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: var(--fs-sm);
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: var(--sp-4);
        }

        /* ═══════════════════════════════════════════════════════════════════
           DOOR STUDIO COCKPIT — unified design system, mirrors cabinet dash.
           Compact feature tiles, pro orders table, utility rail, sync modal.
           ═══════════════════════════════════════════════════════════════════ */
        /* Compact feature tile — replaces the old large .qc card */
        /* Door-studio feature tiles (Door Library / Create Style / Sync)
           Token-routed 2026-04-22 — rgba/white literals replaced with
           color-mix on design tokens. All 4 themes re-skin correctly. */
        .ft-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
            gap: var(--sp-3);
            margin-bottom: var(--sp-4);
        }
        .ft-tile {
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-md);
            padding: var(--sp-3) var(--sp-4);
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: var(--sp-3);
            min-height: 72px;
            transition: border-color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
            box-shadow: 0 1px 0 color-mix(in srgb, var(--bg) 50%, transparent) inset;
        }
        .ft-tile:hover,
        .ft-tile:focus-visible {
            border-color: color-mix(in srgb, var(--acc) 70%, var(--bdr));
            transform: translateY(-1px);
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
            outline: none;
            box-shadow: 0 3px 12px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        .ft-tile .ft-icn {
            width: 44px;
            height: 44px;
            border-radius: var(--radius-sm);
            background: color-mix(in srgb, var(--acc) 14%, var(--bg));
            border: 1px solid color-mix(in srgb, var(--acc) 30%, var(--bdr));
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--acc);
            flex-shrink: 0;
        }
        .ft-tile .ft-icn svg {
            width: 20px;
            height: 20px;
        }
        .ft-tile .ft-body {
            flex: 1;
            min-width: 0;
        }
        .ft-tile .ft-body h4 {
            margin: 0;
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: var(--fs-base);
            font-weight: var(--fw-semibold);
            letter-spacing: -0.005em;
        }
        .ft-tile .ft-body p {
            margin: 2px 0 0;
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 12px;
            line-height: 1.4;
        }

        /* Pinned New Order CTA — top-right of the orders panel header (was a giant bar) */
        .new-order-cta{background:var(--acc);color:var(--acc-ink);border:none;padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:background var(--dur-chrome) var(--ease-out),transform var(--dur-panel) var(--spring-snappy),box-shadow var(--dur-chrome) var(--ease-out);box-shadow:0 4px 12px color-mix(in srgb, var(--acc) 20%, transparent);}
        .new-order-cta:hover{background:var(--acc2);transform:translateY(-1px);box-shadow:0 6px 18px color-mix(in srgb, var(--acc) 35%, transparent);}
        .new-order-cta svg{width:14px;height:14px;}

        /* Sync-to-Cabinet modal — pick a saved cabinet project, import door counts */
        .sync-modal{position:fixed;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);z-index:300;display:flex;align-items:center;justify-content:center;padding:20px;}
        .sync-modal.hidden{display:none;}
        .sync-modal .sync-panel{background:var(--sf);border:1px solid var(--bdr);border-radius:14px;padding:22px;width:560px;max-width:100%;max-height:80vh;overflow-y:auto;box-shadow:0 20px 60px rgb(var(--shadow-ink) / 0.6);}
        .sync-modal h3{margin:0 0 4px;color:var(--white);font-size:16px;}
        .sync-modal .sub{color:var(--dim);font-size:12px;margin-bottom:14px;}
        .sync-modal .sync-row{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border:1px solid var(--bdr);border-radius:8px;margin-bottom:6px;cursor:pointer;transition:border-color var(--dur-panel) var(--ease-out),background var(--dur-panel) var(--ease-out);}
        .sync-modal .sync-row:hover{border-color:var(--acc);background:color-mix(in srgb, var(--acc) 4%, transparent);}
        .sync-modal .sync-row .sn{color:var(--white);font-weight:500;font-size:13px;}
        .sync-modal .sync-row .sm{color:var(--dim);font-size:11px;margin-top:2px;}
        .sync-modal .sync-row .sc{color:var(--acc);font-size:11px;font-variant-numeric:tabular-nums;}

        /* Bulk-action bar — appears when rows are selected */
        .bulk-bar{display:flex;align-items:center;gap:10px;padding:8px 14px;background:color-mix(in srgb, var(--acc) 10%, transparent);border:1px solid var(--acc);border-radius:8px;margin-bottom:10px;color:var(--white);font-size:12px;font-weight:500;}
        .bulk-bar .bulk-count{color:var(--acc);font-weight:600;}

        /* ═══════════════════════════════════════════════════════════════════
           LIBRARIES — workstation table + smart search + bulk actions.
           Hover-only row actions, 32px thumbs, 40% tighter row padding.
           ═══════════════════════════════════════════════════════════════════ */
        .lib-toolbar{display:flex;gap:10px;align-items:center;background:var(--pn);border:1px solid var(--bdr);border-radius:10px;padding:8px 12px;margin-bottom:10px;flex-wrap:wrap;}
        .lib-toolbar .lib-search{flex:1;min-width:220px;display:flex;align-items:center;gap:8px;background:var(--inp);border:1px solid var(--bdr);border-radius:8px;padding:6px 10px;}
        .lib-toolbar .lib-search input{flex:1;background:transparent;border:none;outline:none;color:var(--txt);font-family:inherit;font-size:13px;}
        .lib-toolbar .lib-search input::placeholder{color:var(--dim);}
        .lib-toolbar .lib-search svg{width:14px;height:14px;color:var(--dim);}
        .lib-toolbar .lib-hint{font-size:10px;color:var(--dim);font-style:italic;margin-left:4px;}
        .lib-toolbar .lib-bulk-toggle{display:inline-flex;align-items:center;gap:6px;color:var(--dim);font-size:12px;cursor:pointer;}

        .lib-bulk-bar{display:none;align-items:center;gap:10px;padding:8px 14px;background:color-mix(in srgb, var(--acc) 10%, transparent);border:1px solid var(--acc);border-radius:8px;margin-bottom:10px;color:var(--white);font-size:12px;font-weight:500;}
        .lib-bulk-bar.show{display:flex;}
        .lib-bulk-bar .bulk-count{color:var(--acc);font-weight:600;}

        /* PR-THK-CARDS (2026-05-28) — thickness variants rendered as cards
           instead of a dense table row, in Sheet Goods + Wood Type editors. */
        .thk-card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-bottom:12px;}
        /* PR-HINGE-VIZ-CLARITY (2026-06-02): research-backed card prominence — WCAG 1.4.11 border contrast (≥3:1), NN/g card boundary + tonal-elevation surface, Refactoring-UI size/weight/color hierarchy. Tokens only. */
        .thk-card{background:var(--pn);border:1px solid var(--bdr-h);border-radius:10px;padding:16px;position:relative;display:flex;flex-direction:column;gap:10px;}
        .thk-card:hover{border-color:var(--acc);background:var(--pn-h);}
        .thk-card .thk-card-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:2px;}
        .thk-card .thk-card-title{font-size:11px;font-weight:700;color:var(--acc2);text-transform:uppercase;letter-spacing:0.8px;}
        .thk-card .thk-card-remove{background:transparent;border:none;color:var(--dim);font-size:18px;line-height:1;cursor:pointer;padding:2px 6px;border-radius:4px;min-width:36px;min-height:36px;display:inline-flex;align-items:center;justify-content:center;}
        .thk-card .thk-card-remove:hover{color:var(--no);background:rgba(255,255,255,0.04);}
        .thk-card .thk-card-fields{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
        .thk-card .thk-card-field{display:flex;flex-direction:column;gap:4px;}
        .thk-card .thk-card-field.full{grid-column:1 / -1;}
        .thk-card .thk-card-field label{font-size:10px;font-weight:600;color:var(--dim);text-transform:uppercase;letter-spacing:0.7px;margin-bottom:2px;}
        .thk-card .thk-card-field input{background:var(--inp);color:var(--white);border:1px solid var(--bdr-h);border-radius:6px;padding:9px 11px;font-family:inherit;font-size:13px;width:100%;box-sizing:border-box;}
        .thk-card .thk-card-field input:focus{outline:none;border-color:var(--acc);}
        .thk-card .thk-card-field.primary input{font-size:16px;font-weight:700;color:var(--acc);}
        /* Hinge variant cards reuse .thk-card* (PR-HINGE-VARIANTS 2026-06-02); add select styling to match the inputs. */
        .thk-card .thk-card-field select{background:var(--inp);color:var(--white);border:1px solid var(--bdr-h);border-radius:6px;padding:9px 11px;font-family:inherit;font-size:13px;width:100%;box-sizing:border-box;appearance:auto;}
        .thk-card .thk-card-field select:focus{outline:none;border-color:var(--acc);}
        /* PR-SELL-ACTIVE (2026-07-20): "is this Sell price actually used?" state.
           Research-backed (NN/g disabled-fields, Unity/Figma override bar,
           DevTools inactive-value paling): when the library Sell price is NOT
           the live pricing basis we PALE the field (still legible — read-only
           feel, not a dead gray box), add a left "override bar", and ALWAYS
           show a reason badge naming the current pricing mode. Tokens only. */
        /* Sell field spans the full card width so its status never gets trapped
           in a half-column and ladders one word per line (operator feedback). */
        .thk-card .thk-card-field.sell-field{position:relative;transition:opacity .15s ease;grid-column:1 / -1;}
        .thk-card .thk-card-field.sell-inert{padding-left:9px;border-left:3px solid var(--warn);border-radius:4px;}
        .thk-card .thk-card-field.sell-inert label{color:var(--warn);}
        .thk-card .thk-card-field input.mat-thk-sell.is-inert{opacity:.55;color:var(--dim);border-style:dashed;background:color-mix(in srgb, var(--inp) 88%, var(--warn) 12%);}
        .thk-card .thk-card-field input.mat-thk-sell.is-inert:focus{opacity:1;color:var(--white);border-style:solid;border-color:var(--acc);}
        .sell-status{font-size:11px;line-height:1.4;margin-top:3px;}
        .sell-status:empty{display:none;}
        /* NOT-USED state (operator feedback 2026-07-20): short scannable copy in
           a vertical stack — tag chip, one-line reason, then a real CTA button —
           per NN/g empty-state guidance (give the next step as a button, keep
           microcopy under ~3 sentences). Column layout so nothing ladders. */
        .sell-badge{display:flex;flex-direction:column;align-items:flex-start;gap:5px;padding:7px 9px;border-radius:6px;font-weight:600;}
        .sell-badge b{font-weight:800;}
        .sell-badge.inert{background:color-mix(in srgb, var(--pn) 80%, var(--warn) 20%);border:1px solid color-mix(in srgb, var(--warn) 55%, transparent);}
        .sell-badge.inert .sell-tag{background:var(--warn);color:#1a1200;font-size:9.5px;letter-spacing:.6px;padding:2px 7px;border-radius:4px;text-transform:uppercase;}
        .sell-badge.inert .sell-desc{color:var(--warn);}
        .sell-badge .sell-cta{display:inline-flex;align-items:center;min-height:30px;padding:5px 11px;border-radius:6px;background:var(--warn);color:#1a1200;font-weight:800;font-size:11px;letter-spacing:.2px;text-decoration:none;}
        .sell-badge .sell-cta:hover{filter:brightness(1.08);}
        .sell-badge .sell-cta:focus-visible{outline:2px solid var(--white);outline-offset:2px;}
        /* In-use is a single short line → keep it on one row. */
        .sell-badge.live{flex-direction:row;align-items:center;color:var(--ok);background:color-mix(in srgb, var(--pn) 84%, var(--ok) 16%);border:1px solid color-mix(in srgb, var(--ok) 40%, transparent);padding:5px 9px;}
        .thk-card .thk-card-field.sell-inert{border-left-width:4px;}
        /* PR-HINGE-VIZ-CLARITY: primary-card full accent border + tonal tint, prominent Name title, accent Name label, visible name-row separator. */
        .thk-card.hv-primary-card{border:1px solid var(--acc);border-left:4px solid var(--acc);background:color-mix(in srgb, var(--pn) 88%, var(--acc) 12%);}
        .thk-card .thk-card-field.full input.hv-name{font-size:15px;font-weight:700;color:var(--white);border-color:color-mix(in srgb, var(--bdr-h) 70%, var(--acc) 30%);letter-spacing:0.2px;}
        .thk-card .thk-card-field.full input.hv-name:focus{border-color:var(--acc);}
        .thk-card .thk-card-fields .thk-card-field.full + .thk-card-field{margin-top:6px;padding-top:10px;border-top:1px solid var(--bdr-h);}
        .hinge-var-row.thk-card .thk-card-field.full label{color:var(--acc2);font-size:10px;font-weight:700;letter-spacing:0.8px;}
        /* PR-HINGE-VARIANT-COMPLETE (2026-06-02): labelled sub-section dividers inside the variant card (Gestalt common-region grouping for the ~16-field spec); neutralise the inherited name-row separator since the group heads now do the grouping. Tokens only. */
        .thk-card .thk-card-fields .hv-group-head{grid-column:1 / -1;display:flex;align-items:center;gap:8px;margin-top:4px;font-size:9px;font-weight:700;color:var(--acc2);text-transform:uppercase;letter-spacing:0.9px;line-height:1;}
        .thk-card .thk-card-fields .hv-group-head::after{content:"";flex:1 1 auto;height:1px;background:var(--bdr-h);}
        .thk-card .thk-card-fields .hv-group-head:first-child{margin-top:0;}
        .hinge-var-row .thk-card-fields .thk-card-field.full + .thk-card-field{margin-top:0;padding-top:0;border-top:none;}
        /* PR-DBOX-THK-FROM-LIBRARY (2026-08-25, owner report: "the sheets sizes
           is higher then the other"). The separator rule above puts a top
           border + 16px of margin/padding on whichever field FOLLOWS a `.full`
           one. In the hinge card that's a name row spanning the grid, so the
           line reads as a divider. On the MATERIAL thickness card the follower
           is Sheet W — one cell of a two-column row — so Sheet W alone got
           pushed down and grew a half-width rule, leaving it visibly lower than
           Sheet L beside it. Neutralise it here the same way the hinge card
           neutralises it for itself; the thickness field's own inline unit rail
           already separates the two groups. */
        .mat-thk-row.thk-card .thk-card-fields .thk-card-field.full + .thk-card-field{margin-top:0;padding-top:0;border-top:none;}
        /* Belt + braces: the two sheet-size cells align on their own baseline
           regardless of what precedes them in the grid. */
        .mat-thk-row.thk-card .thk-card-fields{align-items:start;}

        /* PR-HINGE-CARD-OPT (2026-06-03) — research-approved "Full optimization":
           progressive disclosure (collapsed non-primary cards + always-visible
           summary strip), segmented overlay/spring chips, an Advanced accordion,
           and 48px touch targets. All rules scoped to .hinge-var-row so the
           shared Material .thk-card is untouched. Token-only (no hex). */
        .hinge-var-row .hv-summary-strip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-bottom:8px;border-bottom:1px solid var(--bdr);font-size:12px;color:var(--dim);}
        .hinge-var-row .hv-sum-dot{width:6px;height:6px;border-radius:50%;background:var(--acc2);flex:0 0 auto;}
        .hinge-var-row.hv-primary-card .hv-sum-dot{background:var(--acc);}
        .hinge-var-row .hv-sum-angle{color:var(--white);font-weight:700;}
        .hinge-var-row .hv-sum-sep{color:var(--bdr-h);}
        .hinge-var-row .hv-sum-cost{color:var(--ok);font-weight:600;margin-left:auto;}
        .hinge-var-row .hv-sum-expand-caret{display:none;color:var(--dim);font-size:11px;text-transform:uppercase;letter-spacing:0.6px;}
        .hinge-var-row.hv-collapsed .hv-sum-expand-caret{display:inline;}
        .hinge-var-row.hv-collapsed .hv-sum-cost{margin-left:0;}
        .hinge-var-row.hv-collapsed .thk-card-fields{display:none;}
        .hinge-var-row.hv-collapsed{cursor:pointer;}
        .hinge-var-row.hv-collapsed:hover{border-color:var(--acc2);background:var(--pn-h);}
        /* Segmented chips (overlay + spring) — 48px touch targets, hidden radios. */
        .hinge-var-row .hv-overlay-seg,.hinge-var-row .hv-spring-seg{display:flex;gap:8px;grid-column:1 / -1;}
        .hinge-var-row .hv-ov-radio,.hinge-var-row .hv-sp-radio{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none;}
        .hinge-var-row .hv-ov-chip,.hinge-var-row .hv-sp-chip{flex:1;min-height:48px;display:flex;align-items:center;justify-content:center;text-align:center;border:1.5px solid var(--bdr);border-radius:8px;background:var(--inp);color:var(--dim);font-size:13px;font-weight:600;cursor:pointer;box-sizing:border-box;padding:6px 8px;transition:border-color .12s,background .12s,color .12s;}
        .hinge-var-row .hv-ov-radio:checked + .hv-ov-chip,.hinge-var-row .hv-sp-radio:checked + .hv-sp-chip{background:color-mix(in srgb, var(--acc) 20%, var(--inp));border-color:var(--acc);color:var(--acc2);}
        .hinge-var-row .hv-ov-chip:hover,.hinge-var-row .hv-sp-chip:hover{border-color:var(--acc2);color:var(--white);}
        .hinge-var-row .hv-ov-radio:focus-visible + .hv-ov-chip,.hinge-var-row .hv-sp-radio:focus-visible + .hv-sp-chip{outline:2px solid var(--acc);outline-offset:1px;}
        /* Advanced accordion. */
        .hinge-var-row .hv-advanced{grid-column:1 / -1;border-top:1px solid var(--bdr);margin-top:2px;}
        .hinge-var-row .hv-advanced > summary{display:flex;align-items:center;gap:8px;min-height:44px;list-style:none;cursor:pointer;color:var(--dim);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.9px;}
        .hinge-var-row .hv-advanced > summary::-webkit-details-marker{display:none;}
        .hinge-var-row .hv-advanced > summary::before{content:"";width:6px;height:6px;border-right:1.5px solid var(--dim);border-bottom:1.5px solid var(--dim);transform:rotate(-45deg);transition:transform .15s;flex:0 0 auto;margin-right:2px;}
        .hinge-var-row .hv-advanced[open] > summary::before{transform:rotate(45deg);}
        .hinge-var-row .hv-advanced > summary::after{content:"";flex:1 1 auto;height:1px;background:var(--bdr);}
        .hinge-var-row .hv-adv-body{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding-top:10px;}
        /* Name common-region. */
        .hinge-var-row .hv-name-region{grid-column:1 / -1;background:color-mix(in srgb, var(--inp) 70%, var(--pn) 30%);border-radius:6px;padding:2px;}
        .hinge-var-row .hv-name-region .thk-card-field.full input.hv-name{background:transparent;border-color:transparent;}
        .hinge-var-row .hv-name-region .thk-card-field.full input.hv-name:focus{border-color:var(--acc);}
        /* 48px touch targets — fields + remove button (border colors unchanged). */
        .hinge-var-row .thk-card-field input,.hinge-var-row .thk-card-field select{min-height:48px;}
        .hinge-var-row .thk-card-remove{min-width:48px;min-height:48px;font-size:20px;}

        /* PR-LIB-BULK-EDIT (2026-05-28) — "Bulk Edit" toolbar button + menu + modal. */
        .lib-bulk-edit-wrap{position:relative;display:inline-block;}
        .lib-bulk-edit-btn{background:var(--inp);color:var(--white);border:1px solid var(--bdr);border-radius:6px;padding:8px 14px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:6px;}
        .lib-bulk-edit-btn:hover{border-color:var(--acc);color:var(--acc);}
        .lib-bulk-edit-btn .lbe-caret{font-size:9px;opacity:.75;}
        .lib-bulk-edit-menu{position:absolute;top:100%;right:0;margin-top:4px;background:var(--pn);border:1px solid var(--bdr);border-radius:8px;min-width:200px;box-shadow:0 6px 18px rgb(var(--shadow-ink) / 0.35);z-index:9998;display:none;}
        .lib-bulk-edit-menu.open{display:block;}
        .lib-bulk-edit-menu button{display:block;width:100%;text-align:left;background:transparent;border:none;color:var(--txt);font-family:inherit;font-size:13px;padding:10px 14px;cursor:pointer;}
        .lib-bulk-edit-menu button:hover:not(:disabled){background:var(--inp);color:var(--white);}
        .lib-bulk-edit-menu button:disabled{opacity:.4;cursor:not-allowed;}
        .lib-bulk-edit-menu .lbe-sub{font-size:10px;color:var(--dim);display:block;margin-top:2px;}
        .bulk-edit-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px;}
        .bulk-edit-modal{background:var(--pn);border:1px solid var(--bdr);border-radius:12px;max-width:560px;width:100%;max-height:85vh;display:flex;flex-direction:column;color:var(--txt);}
        .bulk-edit-modal .lbe-head{padding:16px 20px;border-bottom:1px solid var(--bdr);display:flex;align-items:center;justify-content:space-between;}
        .bulk-edit-modal .lbe-head h3{margin:0;font-size:16px;color:var(--white);}
        .bulk-edit-modal .lbe-close{background:transparent;border:none;color:var(--dim);font-size:22px;cursor:pointer;line-height:1;padding:0 6px;}
        .bulk-edit-modal .lbe-body{padding:16px 20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:14px;}
        .bulk-edit-modal .lbe-hint{font-size:12px;color:var(--dim);line-height:1.5;}
        .bulk-edit-modal .lbe-row{display:flex;flex-direction:column;gap:6px;}
        .bulk-edit-modal .lbe-label{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.6px;}
        .bulk-edit-modal select,.bulk-edit-modal input[type=text],.bulk-edit-modal input[type=number]{background:var(--inp);color:var(--white);border:1px solid var(--bdr);border-radius:6px;padding:8px 10px;font-family:inherit;font-size:13px;}
        .bulk-edit-modal select:focus,.bulk-edit-modal input:focus{outline:none;border-color:var(--acc);}
        .bulk-edit-modal .lbe-mode{display:flex;gap:8px;}
        .bulk-edit-modal .lbe-mode label{flex:1;padding:6px 10px;border:1px solid var(--bdr);border-radius:6px;font-size:12px;color:var(--dim);cursor:pointer;text-align:center;}
        .bulk-edit-modal .lbe-mode input{display:none;}
        .bulk-edit-modal .lbe-mode input:checked + span{color:var(--acc);}
        .bulk-edit-modal .lbe-mode label:has(input:checked){border-color:var(--acc);}
        .bulk-edit-modal .lbe-foot{padding:14px 20px;border-top:1px solid var(--bdr);display:flex;gap:10px;align-items:center;justify-content:flex-end;}
        .bulk-edit-modal .lbe-foot .lbe-status{margin-right:auto;font-size:11px;color:var(--dim);}
        .bulk-edit-modal .lbe-foot button{padding:8px 16px;border-radius:6px;border:1px solid var(--bdr);font-size:12px;font-weight:600;cursor:pointer;font-family:inherit;}
        .bulk-edit-modal .lbe-foot .lbe-cancel{background:transparent;color:var(--dim);}
        .bulk-edit-modal .lbe-foot .lbe-apply{background:var(--acc);color:var(--pn);border-color:var(--acc);}
        .bulk-edit-modal .lbe-foot .lbe-apply:disabled{opacity:.4;cursor:not-allowed;}

        .lib-table{width:100%;border-collapse:collapse;font-size:13px;background:var(--pn);border:1px solid var(--bdr);border-radius:10px;overflow:hidden;}
        .lib-table thead th{text-align:left;color:var(--dim);font-size:10px;font-weight:600;letter-spacing:.6px;text-transform:uppercase;padding:8px 10px;border-bottom:1px solid var(--bdr);background:var(--pn);position:sticky;top:0;z-index:1;}
        .lib-table tbody tr{cursor:pointer;transition:background var(--dur-quick) var(--ease-out),border-color var(--dur-quick) var(--ease-out);border-left:3px solid transparent;position:relative;}
        .lib-table tbody tr:hover{background:var(--inp);border-left-color:var(--acc);}
        .lib-table tbody td{padding:6px 10px;border-bottom:1px solid var(--bdr);color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
        .lib-table tbody tr:last-child td{border-bottom:none;}
        .lib-table .lib-thumb{width:32px;height:32px;border-radius:6px;background:linear-gradient(135deg,var(--inp),var(--bg));display:inline-flex;align-items:center;justify-content:center;border:1px solid rgba(255,255,255,0.04);overflow:hidden;flex-shrink:0;}
        .lib-table .lib-thumb svg{width:22px;height:22px;color:var(--acc);}
        .lib-table .lib-thumb-swatch{width:32px;height:32px;border-radius:6px;border:1px solid var(--bdr);}
        .lib-table .lib-name{color:var(--white);font-weight:500;}
        .lib-table .lib-cell-thumb{width:44px;padding-left:10px;padding-right:0;}
        .lib-table .lib-cell-bulk{width:36px;text-align:center;padding:6px 0 6px 10px;}

        /* PR-DOOR-LIB-CURATED (2026-05-25): when the door library is the
           active type, hide every add / bulk-delete surface inside the door
           panel. Scoped to #lt-door so the same controls in other library
           panels (materials, hardware, etc.) keep working. The body-level
           data attribute is flipped by _syncDoorLibCuratedChrome() whenever
           setActiveLibType('door') fires. */
        body[data-door-lib-curated="true"] #lt-door .lib-bulk-toggle,
        body[data-door-lib-curated="true"] #lt-door .lib-bulk-bar,
        body[data-door-lib-curated="true"] #lt-door .lib-detail-bulk-toggle,
        body[data-door-lib-curated="true"] #lt-door .lib-detail-bulk-bar,
        body[data-door-lib-curated="true"] #lt-door .lib-bulk-action.danger,
        body[data-door-lib-curated="true"] #lt-door .lib-empty-add-btn,
        body[data-door-lib-curated="true"] #lt-door .lib-detail-empty .ba{
            display: none !important;
        }

        /* PR-LIB-RESPONSIVE (2026-05-24): the lib-table has white-space:nowrap
           on every cell so columns don't shred on text wrap. Below tablet,
           let the whole table scroll horizontally rather than clipping
           silently behind the parent panel's overflow:hidden. The canonical
           mobile-table pattern: switch the <table> to display:block so the
           overflow-x:auto takes effect — the internal rows/cells keep their
           table-row / table-cell defaults so columns stay aligned. */
        @media (max-width: 1023px){
            .lib-table{
                display:block; overflow-x:auto; -webkit-overflow-scrolling:touch;
                max-width:100%; border-radius:10px;
            }
            /* Sticky thead doesn't survive display:block on the table, drop it. */
            .lib-table thead th{ position:static; }
        }
        @media (max-width: 640px){
            .lib-table tbody td{ padding:8px 10px; font-size:12px; }
            .lib-table thead th{ font-size:9px; padding:8px; }
            .lib-table .lib-thumb,
            .lib-table .lib-thumb-swatch{ width:28px; height:28px; }
            .lib-table .lib-thumb svg{ width:18px; height:18px; }
            .lib-table .lib-cell-thumb{ width:38px; }
        }

        /* ═══════════════════════════════════════════════════════════════════
           PRINT-READY CUTLIST — looks like a physical sheet of paper.
           Light bg, dark text, serif body, QR pinned top-right.
           ═══════════════════════════════════════════════════════════════════ */
        .cutlist-paper{background:#FFFFFF;color:#111;border-radius:8px;padding:28px 36px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.55;box-shadow:0 12px 36px rgb(var(--shadow-ink) / 0.55);position:relative;max-width:none;}
        .cutlist-paper::before{content:"";position:absolute;inset:0;border-radius:8px;pointer-events:none;border:1px solid rgba(0,0,0,0.08);}
        .cutlist-paper .cl-head{display:flex;justify-content:space-between;align-items:flex-start;border-bottom:2px solid #111;padding-bottom:12px;margin-bottom:14px;}
        .cutlist-paper .cl-head .cl-title{font-size:18px;font-weight:700;color:#111;margin:0 0 2px;}
        .cutlist-paper .cl-head .cl-meta{font-size:11px;color:#555;line-height:1.4;}
        .cutlist-paper .cl-head .cl-qr{width:88px;height:88px;background:#fff;border:1px solid #ddd;border-radius:4px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
        .cutlist-paper .cl-head .cl-qr img{width:84px;height:84px;display:block;}
        /* 2026-04-23: pre-wrap → pre + horizontal scroll (same fix as
           .cutlist-out above). Monospace columns must stay aligned;
           wrapping at container width shatters the 80-char layout. */
        .cutlist-paper pre,.cutlist-paper .cl-body{font-family:'Courier New',Courier,monospace;font-size:11px;color:#111;line-height:1.6;white-space:pre;margin:0;overflow-x:auto;}
        .cutlist-paper table{width:100%;border-collapse:collapse;font-size:11px;color:#111;margin-top:8px;}
        .cutlist-paper th{text-align:left;border-bottom:1px solid #555;padding:5px 8px;text-transform:uppercase;font-size:10px;letter-spacing:.4px;color:#222;}
        .cutlist-paper td{border-bottom:1px solid #eee;padding:4px 8px;}
        .cutlist-paper .cl-foot{margin-top:18px;padding-top:10px;border-top:1px solid #ccc;font-size:10px;color:#666;display:flex;justify-content:space-between;}

        /* ═══════════════════════════════════════════════════════════════════
           PRODUCTION PROGRESS BAR — sticky on door order edit view.
           Draft → Quote → In Production → Shipped.
           ═══════════════════════════════════════════════════════════════════ */
        .prod-progress{display:flex;align-items:center;gap:0;background:var(--pn);border:1px solid var(--bdr);border-radius:10px;padding:10px 14px;margin-bottom:14px;position:sticky;top:0;z-index:5;backdrop-filter:blur(10px);background:rgba(20,20,20,0.85);}
        .prod-step{display:flex;align-items:center;gap:8px;color:var(--dim);font-size:12px;font-weight:500;flex-shrink:0;}
        .prod-step .pd-dot{width:22px;height:22px;border-radius:50%;background:var(--inp);border:1px solid var(--bdr);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:var(--dim);transition:background var(--dur-chrome) var(--ease-out),color var(--dur-chrome) var(--ease-out),border-color var(--dur-chrome) var(--ease-out);}
        .prod-step.done{color:var(--acc);}
        .prod-step.done .pd-dot{background:var(--acc);color:var(--acc-ink);border-color:var(--acc);}
        .prod-step.current{color:var(--white);}
        .prod-step.current .pd-dot{background:var(--acc2);color:var(--acc-ink);border-color:var(--acc2);box-shadow:0 0 0 4px color-mix(in srgb, var(--acc) 18%, transparent);}
        .prod-line{flex:1;height:2px;background:var(--bdr);margin:0 10px;border-radius:1px;}
        .prod-line.done{background:var(--acc);}

        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): .studio-pill +
           .widget:hover .studio-pill + .widget-grid responsive media query
           deleted — only consumer was the deleted WIDGET_DEFS templates. */
        ::-webkit-scrollbar{width:6px;}::-webkit-scrollbar-track{background:var(--bg);}::-webkit-scrollbar-thumb{background:var(--bdr);border-radius:3px;}::-webkit-scrollbar-thumb:hover{background:var(--dim);}
        @media(max-width:900px){.drawer{width:100%;}}

        /* ═══════════════════════════════════════════════════════════════════
           COMPATIBILITY MATRIX — cell-level verdict classes.
           Applied by door-order-compatibility.js cellClass().
           PR-8b2 (2026-04-22): defined alongside the JS that uses them.
           Contrast verified at WCAG AA 4.5:1 on all four themes.
           ─────────────────────────────────────────────────────────────────
           .compat-ok    : allowed, no warnings — neutral, border only
           .compat-warn  : allowed with caveats — amber tint + border + icon
           .compat-error : not allowed — red tint, reduced opacity, no-entry
                          cursor. The 4px left border (vs 3px on ok/warn)
                          gives an extra visual weight that matches the
                          severity jump. Content opacity 0.55 signals the
                          cell is disabled without hiding it entirely so
                          operators can still read which combination tripped.
           ═══════════════════════════════════════════════════════════════════ */
        .compat-ok{
            border-left:3px solid var(--ok);
        }
        .compat-warn{
            background:rgba(251,191,36,0.12);
            border-left:3px solid var(--warn);
        }
        /* Inline SVG warning triangle — 16x16 px, rendered via CSS
           background-image so no extra DOM nodes are needed.
           The triangle path is encoded as a data URI safe for CSS. */
        .compat-warn::after{
            content:"";
            display:inline-block;
            width:16px;
            height:16px;
            margin-left:4px;
            vertical-align:middle;
            flex-shrink:0;
            background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M8 2L14.5 13.5H1.5L8 2Z' stroke='%23fbbf24' stroke-width='1.4' stroke-linejoin='round'/%3E%3Cline x1='8' y1='6' x2='8' y2='9.5' stroke='%23fbbf24' stroke-width='1.4' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.5' r='0.7' fill='%23fbbf24'/%3E%3C/svg%3E");
            background-repeat:no-repeat;
            background-size:16px 16px;
            background-position:center;
        }
        .compat-error{
            background:rgba(248,113,113,0.14);
            border-left:4px solid var(--no);
            cursor:not-allowed;
        }
        .compat-error > *{
            opacity:0.55;
        }
        /* Light themes (walnut / shop) use darker --warn values (#E6A700, #d97706)
           that already exceed 4.5:1 against their white/light backgrounds.
           No override needed: the variable resolves to the correct per-theme value. */

        /* PR-8b2: Inline compat verdict strip above the Add Door button row.
           48px tall, full width, 4px left border that tracks the verdict color.
           Uses existing design tokens (--ok / --warn / --no) — no new colors.
           Font-weight 600 so it reads on a dusty tablet in shop lighting.
           Motion rule (Phase I, math-guardian gate 12): NO transition on the
           verdict strip. The border-color is the verdict signal — it must
           SNAP from green→amber→red as the operator changes parameters.
           A 150ms fade would make a hard-block look like an intermediate
           state for a moment, which is exactly the "silent-wrong" failure
           mode shop operators reported in the 2026-04-21 DO-NOT-COMPUTE
           audit. Dimensions, prices, and verdicts snap — no exceptions. */
        .compat-strip {
            min-height: 48px;
            width: 100%;
            box-sizing: border-box;
            padding: var(--sp-3) var(--sp-4);
            font-family: var(--ff-ui);
            font-weight: var(--fw-semibold);
            font-size: 13px;
            line-height: 1.4;
            border-radius: var(--radius-sm);
            margin-bottom: var(--sp-2);
            border-left: 4px solid transparent;
            display: flex;
            align-items: center;
            gap: var(--sp-3);
            /* NO transition — verdicts snap. Do not add back. */
        }
        /* Status icon — 24×24 circular badge rendered by a CSS ::before
           pseudo so the JS payload stays simple (it just sets the class
           and writes text into the strip). The icon is drawn with a
           themed SVG data-URI; color flips with the strip variant. */
        .compat-strip::before {
            content: "";
            display: inline-block;
            width: 22px;
            height: 22px;
            flex-shrink: 0;
            border-radius: 50%;
            background-color: currentColor;
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3C/svg%3E");
            background-position: center;
            background-repeat: no-repeat;
            background-size: 14px 14px;
        }
        .compat-strip.compat-ok {
            background: color-mix(in srgb, var(--ok) 10%, transparent);
            border-left-color: var(--ok);
            color: var(--ok);
        }
        .compat-strip.compat-ok::before {
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");
        }
        .compat-strip.compat-warn {
            background: color-mix(in srgb, var(--warn) 14%, transparent);
            border-left-color: var(--warn);
            color: var(--warn);
        }
        .compat-strip.compat-warn::before {
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3Cpath d='M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'/%3E%3C/svg%3E");
        }
        .compat-strip.compat-error {
            background: color-mix(in srgb, var(--no) 14%, transparent);
            border-left-color: var(--no);
            color: var(--no);
        }
        .compat-strip.compat-error::before {
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E");
        }
        /* Disabled Add Door button when compat blocked (compat-error verdict).
           opacity + cursor make the block state unambiguous without hiding the
           button — operator can still read the label and understand what to fix. */
        .ad-go[data-compat-blocked="true"],
        .ad-go:disabled {
            opacity: 0.45;
            cursor: not-allowed;
            pointer-events: none;
        }

        /* Cluster E — Reveal Preset tile picker active state.
           color-mix() with transparent gives a tinted highlight that works
           in all 4 themes without hard-coding a hex. */
        .rvl-preset-row {
            display: flex;
            flex-wrap: wrap;
            gap: var(--sp-2);
            margin-bottom: var(--sp-2);
        }
        /* Reveal preset tiles (Bundle 3 — stile/rail section).
           Extracted from per-button inline styles so the count lands
           under audit Rule A baseline. Monospace face for the fractional
           value; accent-tinted when active. Same chrome-transition rule:
           tile surface animates, the fraction value does not (it's
           snapping from the underlying number input). */
        .rvl-preset {
            min-width: 56px;
            min-height: 44px;
            padding: 0 var(--sp-3);
            font-family: var(--ff-mono);
            font-size: 12px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.02em;
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-sm);
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            color: var(--txt);
            cursor: pointer;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out);
            box-shadow: 0 1px 0 color-mix(in srgb, var(--bg) 50%, transparent) inset;
        }
        .rvl-preset:hover:not(.rvl-preset-active) {
            border-color: color-mix(in srgb, var(--acc) 70%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
            transform: translateY(-1px);
        }
        .rvl-preset:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
        }
        .rvl-preset-active {
            background: color-mix(in srgb, var(--acc2) 18%, var(--inp)) !important;
            border-color: var(--acc2) !important;
            color: var(--txt) !important;
            font-weight: var(--fw-bold);
            box-shadow:
                0 0 0 1px var(--acc2) inset,
                0 3px 10px color-mix(in srgb, var(--acc2) 25%, transparent) !important;
        }
        /* The "Custom" button is a textual verb, not a fraction value —
           keep it in the UI font, not mono. */
        .rvl-preset-custom {
            font-family: var(--ff-ui);
            letter-spacing: 0.03em;
        }

        /* ─── Door Studio form controls ──────────────────────────────
           Scoped to .ds-bundle so these polished styles don't affect
           other forms in the app (which still use their own patterns).
           Addresses sentinel audit Rule C (blueprint requires ≥48px on
           select min-height) for the selects that live inside bundles. */
        .ds-bundle .fg {
            display: flex;
            flex-direction: column;
            gap: var(--sp-2);
            min-width: 0;
        }
        .ds-bundle .fg label {
            display: flex;
            align-items: center;
            gap: var(--sp-1);
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 10.5px;
            font-weight: var(--fw-semibold);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            margin: 0;
        }
        .ds-bundle .fg .lib-src {
            font-size: 9.5px;
            font-weight: var(--fw-regular);
            letter-spacing: 0.02em;
            text-transform: none;
            color: color-mix(in srgb, var(--dim) 80%, transparent);
            margin-left: auto;
            opacity: 0.85;
        }
        .ds-bundle .fg select,
        .ds-bundle .fg input[type="text"],
        .ds-bundle .fg input[type="number"],
        .ds-bundle .fg input:not([type]) {
            min-height: 48px;
            padding: 0 var(--sp-3);
            background: var(--bg);
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-sm);
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: 14px;
            font-weight: var(--fw-medium);
            letter-spacing: 0.01em;
            cursor: pointer;
            transition: border-color var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
            width: 100%;
        }
        .ds-bundle .fg select:hover:not(:disabled),
        .ds-bundle .fg input:hover:not(:disabled):not(:focus) {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 3%, var(--bg));
        }
        .ds-bundle .fg select:focus,
        .ds-bundle .fg input:focus {
            outline: none;
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 5%, var(--bg));
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        /* Custom dropdown arrow on selects — the default browser arrow
           is inconsistent across platforms. Matches the arrow used by
           .std-field select in Shop Standards for visual consistency. */
        .ds-bundle .fg select {
            appearance: none;
            -webkit-appearance: none;
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23707070' stroke-width='2'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right var(--sp-3) center;
            padding-right: var(--sp-7);
        }
        /* The form-row .fr grid inside bundles — gap between field stacks */
        .ds-bundle .fr {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: var(--sp-3) var(--sp-4);
            margin-bottom: var(--sp-3);
        }

        /* Live fraction-preview under stile/rail width inputs.
           The operator types "3 1/2" and sees the canonical form immediately.
           A trailing inch mark (″) anchors the eye; red on invalid input
           (unparseable / negative / zero) catches typos before Add fires.
           11px min-height reserves the row so the form doesn't jump when
           a preview appears. aria-live="polite" on each preview lets screen
           readers announce the resolved value without interrupting typing. */
        /* Full .sr-preview rule set (merged into the sr-card rule block
           above at line ~647 for scope consistency, 2026-04-22). This
           rule adds the dim-baseline + err variant only. */
        .sr-preview {
            color: var(--dim);
            min-height: 14px;
            margin-top: 2px;
            font-weight: 500;
            line-height: 1.1;
        }
        .sr-preview-err {
            color: var(--warn);
            font-weight: 600;
        }/* ============================================================
           Phase I.5 -- Door Studio Template B + 5-Bundle layout
           All values use design tokens -- no magic px, no hex colors.
           ============================================================ */
        /* PR-DS-WIZARD-CLOSE (2026-08-01): the ✕ that collapses the wizard back to
           the order list. Pushed to the far right of the step bar; opaque bg so it
           lived at the right end of the step bar until PR-ONE-PANEL (2026-08-28)
           removed that bar; it is now the panel header's own trailing control. */
        .ds-bundle-close {
            margin-left: auto;
            flex-shrink: 0;
            position: relative;
            width: 32px;
            height: 32px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            background: var(--sf);
            color: var(--dim);
            cursor: pointer;
            font-size: 14px;
            line-height: 1;
            transition: color .12s ease, border-color .12s ease, background .12s ease;
        }
        .ds-bundle-close:hover {
            color: var(--no);
            border-color: var(--no);
            background: color-mix(in srgb, var(--no) 8%, var(--sf));
        }
        .ds-bundle-close:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
        }

        /* PR-DS-JOBINFO-BAR (2026-08-01): the "Edit job info" button + the little
           inline bar it opens (Customer + Job name). Sits under the order header. */
        .do-jobinfo-wrap { margin: 8px 0 0; }
        .do-jobinfo-btn {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 12px;
            background: transparent;
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            color: var(--dim);
            font-size: 12px;
            font-weight: 600;
            cursor: pointer;
            min-height: 34px;
            transition: color .12s ease, border-color .12s ease, background .12s ease;
        }
        .do-jobinfo-btn:hover {
            color: var(--acc);
            border-color: color-mix(in srgb, var(--acc) 60%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 6%, transparent);
        }
        .do-jobinfo-bar {
            display: flex;
            flex-wrap: wrap;
            align-items: flex-end;
            gap: var(--sp-4);
            margin-top: var(--sp-3);
            padding: var(--sp-4);
            background: var(--sf);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-md);
            box-shadow: var(--shadow-card);
        }
        .do-jobinfo-bar[hidden] { display: none; }
        .do-jobinfo-field {
            display: flex;
            flex-direction: column;
            gap: 4px;
            flex: 1 1 220px;
            min-width: 180px;
        }
        .do-jobinfo-field label {
            font-size: 11px;
            font-weight: 600;
            color: var(--dim);
            text-transform: uppercase;
            letter-spacing: .04em;
        }
        .do-jobinfo-field input {
            height: 40px;
            padding: 0 12px;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            color: var(--txt);
            font-size: 14px;
        }
        .do-jobinfo-field input:focus {
            outline: none;
            border-color: var(--acc);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 25%, transparent);
        }
        .do-jobinfo-actions {
            display: flex;
            gap: var(--sp-2);
            flex: 0 0 auto;
        }
        .do-jobinfo-actions button {
            height: 40px;
            padding: 0 16px;
            border-radius: var(--radius-sm);
            font-size: 13px;
            font-weight: 700;
            cursor: pointer;
            border: 1px solid var(--bdr);
        }
        .do-jobinfo-cancel {
            background: transparent;
            color: var(--txt);
        }
        .do-jobinfo-cancel:hover { border-color: var(--dim); }
        .do-jobinfo-save {
            background: var(--acc);
            color: #fff;
            border-color: var(--acc);
        }
        .do-jobinfo-save:hover { filter: brightness(1.07); }/* Connector line — single horizontal rule behind the buttons
           that the step circles sit on top of. `::before` (NOT
           `::after`) because a pseudo-element's stacking position
           depends on DOM insertion order among its siblings: `::before`
           paints as the FIRST child (below later flex children), while
           `::after` paints as the LAST child (above them). The pre-2026-
           04-23 version used `::after` and the rail painted ON TOP of
           the step labels, making them look like they had a strike-
           through. Fix: swap to `::before`. Keeps the "one continuous
           rail" behaviour without per-button segmentation. *//* Step circle — 28px so it dominates the connector line underneath *//* Swap number for checkmark on completed steps */

        /* Required-field gate — applied by dsBundleStepRequiredOK in
           door-studio.js when the operator clicks Next with an empty
           *-labeled field. Cleared on the next `input`/`change` event
           via the delegated #pg-door listener. Hardcoded red (matches
           dfs-close visibility rationale) so any theme renders an
           obvious "fix me" cue. */
        .field-required-empty {
            border: 2px solid #dc2626 !important;
            box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.18) !important;
            outline: none;
        }

        /* Template B split -- param panel left, viewport right */
        .ds-template-b {
            display: flex;
            flex: 1;
            /* PR-DS-PREVIEW-STICKY (2026-08-01): was `overflow: hidden`, which
               made this a sticky-containing block and trapped the preview's
               position:sticky here instead of letting it stick to the page
               scroller (.scroll). The columns are content-height (the page
               scrolls, not the columns), so nothing overflowed to clip — making
               this `visible` is a visual no-op that lets the preview stick to
               the real scroll container. */
            overflow: visible;
            min-height: 0;
            /* PR-DOOR-PREVIEW-BREATHE (2026-08-01): the two columns used to butt
               against a bare 1px divider (lifted from the cabinet editor) — too
               cramped. A real gutter + a right-edge gap lets the form (left) and
               the preview (right) read as two distinct panels instead of one
               squished block. */
            gap: var(--sp-6);
            padding-right: var(--sp-5);
        }
        /* Param panel / viewport split (2026-04-22 rebalanced):
           - Param panel takes 56% of the Template-B row (min 480px) so the
             Add Piece bar with 4 numeric fields + CTA button doesn't wrap.
           - Viewport takes 44% (min 320px) so the preview has enough room
             to show the 240x320 SVG without being so oversized that the
             visualization dwarfs the parameter entry. */
        .ds-param-panel {
            /* PR-DS-PREVIEW-DOCK (2026-08-02): the preview is a DOCKED right-hand
               column again (reverting the 2026-08-01 floating PiP), so the form +
               batch cards share the split row with it — ~56% here, preview ~44%
               (see .ds-viewport). Restores the layout the responsive media queries
               below were written for. */
            flex: 1 1 56%;
            min-width: 480px;
            max-width: none;
            /* PR-PREVIEW-FOLLOWS-SCROLL (2026-09-05): was `overflow-y: auto`.
               That made the form a SECOND scroller: it scrolled inside its own
               column, so the split row never grew past one screen. Measured
               with an order open — `.ds-template-b` was 640px tall, the exact
               height of the preview beside it, which left the preview's
               `position:sticky` with **0px of travel**. Sticky holds a child
               only while its parent's box is passing through the viewport; a
               parent no taller than the child can never hold anything.

               Letting the column grow makes the row as tall as the form, so
               `.scroll` scrolls the whole studio once and the preview sticks
               across every batch card — which is the ask: it follows you down
               the page while you work through doors. It also removes the
               second scrollbar, which is the same "both-scroller" problem the
               page frame comment above was already fighting. */
            overflow-y: visible;
            padding: var(--sp-6);
            /* PR-DOOR-PREVIEW-BREATHE (2026-08-01): was a bare `border-right`
               butting straight against the preview. Now a full card (border +
               radius) so the form and the preview read as two separate panels
               with the gutter between them. */
            border: 1px solid var(--bdr);
            border-radius: var(--radius-md);
            background: var(--sf);
            display: flex;
            flex-direction: column;
            gap: var(--sp-4);
            position: relative;
        }
        /* ── PR-DS-PREVIEW-PIP (2026-08-01): FLOATING picture-in-picture preview ──
           The door/drawer preview no longer lives in a scrolling column. It's
           pinned to the SCREEN with position:fixed, so it stays visible no matter
           how the form/batch cards scroll — immune to every flex/overflow/sticky
           quirk that broke the in-flow attempts (the whole point of choosing PiP).
           It reuses the SAME #ds-svg element, so every updateDoorPreview() call
           keeps driving it with zero JS changes.
           PR-DS-PREVIEW-DRAG (2026-08-02): re-docked TOP-right (was bottom-right)
           and made draggable. The default `top` is lowered by JS to just under the
           order-header row so it never covers "the words" at the top of the page;
           position:fixed keeps it on screen while the form scrolls, and the header
           is a drag handle whose dropped spot persists. Collapsible. */
        .ds-viewport {
            /* PR-DS-PREVIEW-DOCK (2026-08-02): DOCKED right-hand column, sticky to
               the top so the preview stays put while the form/wizard on the left
               scrolls. Reverts the floating position:fixed PiP + drag. Same #ds-svg,
               so updateDoorPreview() drives it unchanged; the drag/position JS
               (_dsPipOnPointerDown / dsApplyPreviewPos) self-neutralizes because it
               guards on getComputedStyle().position === 'fixed', no longer true. */
            flex: 1 1 44%;
            min-width: 320px;
            max-width: 460px;
            align-self: flex-start;              /* top-align in the split row */
            position: sticky;
            top: var(--sp-4);                    /* stays at the top as the left scrolls */
            display: flex;
            flex-direction: column;
            gap: var(--sp-2);
            padding: 0;
        }
        /* PR-DS-PREVIEW-FULL (2026-08-02): show the ENTIRE door. The card gets a
           DEFINITE height so its flex:1 canvas resolves to a real height; the SVG
           then fills that canvas (width/height 100%, below) and its
           preserveAspectRatio="xMidYMid meet" fits the whole 240×320 drawing inside
           with letterboxing — no bottom-rail clipping at any door aspect. Was
           `max-height: min(52vh,460px)` on the card + an INDEPENDENT
           `max-height: min(44vh,420px)` on the SVG, which could exceed the canvas
           and get cropped by the card's overflow:hidden. Scoped to the split
           (≥768px) layout; the ≤767px stacked layout keeps its own sizing. */
        @media (min-width: 768px) {
            .ds-viewport .ds-viewport-preview {
                height: min(80vh, 700px);
                max-height: none;
                min-height: 400px;
                /* PR-PREVIEW-FOLLOWS-EDIT (2026-08-29, owner: "move it down so
                   it scrolls down with to where each card your editing is"):
                   ONE 3D view, and it rides with the work. Sticky rather than
                   fixed so it stays inside its column and cannot cover the
                   cards; the offset clears the page header. */
                position: sticky;
                top: var(--sp-3);
                align-self: flex-start;
            }
            .ds-viewport .ds-preview-canvas { min-height: 0; }
            .ds-viewport .ds-preview-canvas > svg {
                width: 100%;
                height: 100%;
                max-width: 100%;
                max-height: 100%;
            }
        }
        /* Collapsed state — tuck the whole thing down to a compact tab so it never
           blocks a card the operator is filling in. Toggled by dsTogglePreviewPip(). */
        .ds-viewport.ds-pip-collapsed .ds-preview-canvas,
        .ds-viewport.ds-pip-collapsed .ds-preview-footer,
        .ds-viewport.ds-pip-collapsed .ds-preview-meta,
        .ds-viewport.ds-pip-collapsed .ds-jump-style-btn {
            display: none;
        }
        .ds-viewport.ds-pip-collapsed {
            width: auto;
        }
        .ds-viewport.ds-pip-collapsed .ds-viewport-preview {
            max-height: none;
            min-height: 0;
        }
        /* Collapse/expand toggle button in the preview header. */
        .ds-pip-toggle {
            appearance: none;
            border: 1px solid var(--bdr);
            background: var(--inp);
            color: var(--txt);
            border-radius: var(--radius-sm);
            width: 28px;
            height: 28px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 15px;
            line-height: 1;
            flex-shrink: 0;
            transition: background .12s ease, border-color .12s ease;
        }
        .ds-pip-toggle:hover { border-color: var(--acc); color: var(--acc); }
        /* When collapsed the toggle shows the "expand" glyph via ::before swap in JS;
           the label reads "Preview" so a tucked-away PiP is still discoverable. */
        .ds-viewport.ds-pip-collapsed .ds-preview-header {
            border-bottom: none;
        }
        /* PR-DS-PREVIEW-DOCK (2026-08-02): docked column — no drag. Header is a
           plain title bar (no grab cursor, no grip glyph), and the collapse toggle
           (a floating-PiP affordance) is hidden. */
        .ds-viewport .ds-preview-header {
            cursor: default;
            user-select: auto;
            -webkit-user-select: auto;
        }
        .ds-pip-toggle { display: none; }
        .ds-viewport.ds-pip-dragging {
            transition: none;
            filter: drop-shadow(0 14px 36px rgba(0,0,0,.55));
        }
        .ds-viewport.ds-pip-dragging,
        .ds-viewport.ds-pip-dragging .ds-preview-header { cursor: grabbing; }
        /* Buttons in the header keep a normal pointer cursor and stay click-only —
           _dsPipOnPointerDown ignores pointerdowns that start on a button. */
        .ds-viewport .ds-preview-header button { cursor: pointer; }
        /* Bottom toolbar 56px */
        .ds-bottom-toolbar {
            display: flex;
            align-items: center;
            gap: var(--sp-3);
            padding: 0 var(--sp-6);
            height: 56px;
            min-height: 56px;
            background: var(--sf);
            border-top: 1px solid var(--bdr);
            flex-shrink: 0;
        }
        .ds-bottom-toolbar .ds-tb-spacer { flex: 1; }
        .ds-bottom-toolbar button {
            min-height: 48px;
            min-width: 56px;
            padding: 0 var(--sp-5);
            font-weight: var(--fw-semibold);
            font-size: var(--fs-base);
        }

        /* Bundle panel -- visible bundle animates in; hidden ones are display:none */
        .ds-bundle {
            display: flex;
            flex-direction: column;
            gap: var(--sp-4);
        }

        /* The open group keeps its title; a closed one is just its strip, and a
           title above a one-line summary would be two labels for one thing. */
        /* The check mark that used to sit on a step chip. On one screen it can
           sit on the group itself, so every group's state reads at a glance. */
        .ds-bundle[data-complete="true"] > .ds-bundle-header .ds-bundle-title::after {
            content: " ¹3";
            color: var(--ok);
            font-weight: 700;
        }
        .ds-bundle[hidden] { display: none; }
        /* PR-ONE-PANEL (2026-08-28): the slide-in is gone. It was written for
           ONE bundle appearing as you stepped forward; with all five on screen
           it fired five times at once on open, which reads as a stutter, not a
           transition. */
        /* Bundle panel header — subtle lead accent bar + eyebrow label
           + title + subtitle. The eyebrow ("STEP 3 OF 5 · FRAME") gives
           the operator orientation without needing to look up at the
           nav bar; the vertical accent bar on the left grounds the
           header visually against the rest of the bundle body.
           Populated by JS via a data-step attr on .ds-bundle header. */
        .ds-bundle-header {
            position: relative;
            padding: var(--sp-2) 0 var(--sp-4) var(--sp-4);
            margin-bottom: var(--sp-4);
            border-bottom: 1px solid var(--bdr);
        }
        .ds-bundle-header::before {
            content: "";
            position: absolute;
            left: 0;
            top: var(--sp-2);
            bottom: var(--sp-4);
            width: 3px;
            border-radius: 2px;
            background: var(--acc);
        }
        .ds-bundle-title {
            font-family: var(--ff-ui);
            font-size: var(--fs-xl);                 /* 20px */
            font-weight: var(--fw-semibold);
            color: var(--txt);
            margin: 0 0 var(--sp-1);
            line-height: 1.25;
            letter-spacing: -0.005em;
        }
        .ds-bundle-subtitle {
            font-family: var(--ff-ui);
            font-size: var(--fs-sm);
            color: var(--dim);
            margin: 0;
            line-height: 1.5;
            max-width: 52ch;
        }

        /* PR-CUST-LINKVIS (2026-06-12): saved-customer link chip under the
           Door Studio Customer field. States mirror the server's
           OrderResponse.customer_link_state: linked (ok) / ambiguous
           (warn — the V116 resolver refused to guess between duplicate
           names) / match_available + no_match (dim, informational). */
        .ds-custlink {
            display: inline-block;
            margin-top: 6px;
            padding: 3px 10px;
            border-radius: 999px;
            font-size: 11px;
            line-height: 1.4;
            border: 1px solid var(--bdr);
            color: var(--dim);
            background: transparent;
        }
        .ds-custlink-ok {
            color: var(--ok);
            border-color: color-mix(in srgb, var(--ok) 45%, var(--bdr));
            background: color-mix(in srgb, var(--ok) 12%, transparent);
        }
        .ds-custlink-warn {
            color: var(--warn);
            border-color: color-mix(in srgb, var(--warn) 50%, var(--bdr));
            background: color-mix(in srgb, var(--warn) 12%, transparent);
            font-weight: var(--fw-bold, 700);
        }
        .ds-custlink-dim { color: var(--dim); }

        /* Hidden backing select -- visually removed; JS reads/writes its value */
        .ds-hidden-select {
            position: absolute;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0,0,0,0);
            white-space: nowrap;
            border: 0;
        }

        /* Type selector -- 6-tile segmented row operating #sg-type underneath */
        .ds-type-tiles {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: var(--sp-2);
        }
        .ds-type-tile {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: var(--sp-1);
            min-height: 72px;                       /* larger tap zone */
            padding: var(--sp-3) var(--sp-2);
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            border: 1.5px solid var(--bdr);
            border-radius: var(--radius-sm);
            color: var(--dim);
            font-size: 11px;
            font-weight: var(--fw-semibold);
            text-align: center;
            cursor: pointer;
            /* Chrome transitions only — never on label text that will
               become a dimension or price (gate 12). Tiles are chrome. */
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        color var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out);
            letter-spacing: 0.2px;
            line-height: 1.2;
            box-shadow: 0 1px 0 color-mix(in srgb, var(--bg) 50%, transparent) inset;
        }
        .ds-type-tile:hover:not([aria-checked="true"]) {
            border-color: color-mix(in srgb, var(--acc) 70%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
            color: var(--txt);
            transform: translateY(-1px);
        }
        .ds-type-tile:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
        }
        .ds-type-tile[aria-checked="true"] {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 14%, var(--inp));
            color: var(--txt);
            box-shadow:
                0 0 0 1px var(--acc) inset,
                0 3px 10px color-mix(in srgb, var(--acc) 25%, transparent);
        }
        /* PR-DS-TYPE-LOCK (2026-05-14): tiles for the conflicting product family
           (doors vs drawer boxes) dim out once the order is locked to one kind.
           Operator must clear same-kind items first to switch. */
        .ds-type-tile.is-locked {
            opacity: 0.35;
            cursor: not-allowed;
            filter: grayscale(1);
        }
        .ds-type-tile.is-locked:hover:not([aria-checked="true"]) {
            border-color: var(--bdr);
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 92%, var(--bg)) 0%,
                var(--inp) 100%);
            color: var(--dim);
            transform: none;
        }
        .ds-type-tile[aria-checked="true"]::after {
            /* Small checkmark badge in the top-right corner of the selected tile. */
            content: "";
            position: absolute;
            top: 6px;
            right: 6px;
            width: 14px;
            height: 14px;
            border-radius: 50%;
            background: var(--acc);
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: center;
        }
        .ds-type-tile-icon {
            width: 22px;
            height: 22px;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-shrink: 0;
        }
        .ds-type-tile-icon svg {
            width: 20px;
            height: 20px;
            stroke: currentColor;
            stroke-width: 1.6;
            fill: none;
        }

        /* Bundle 4 hero — Shop Standard "Recommended" CTA. Theme-aware
           green via --ok so it stays distinct from the progress-bar's
           --acc (blue) active-step chip — they won't visually merge.
           Phase B (later PR) demotes the dropdowns into a collapsible
           Advanced section + adds Economy/Tandem/Pullout secondary cards. */
        /* PR-DBOX-HERO-FLAT (2026-08-25, owner: "the green button — take off
           the highlight, it should look like the others"): Shop Standard was
           a saturated green slab with a 6px left rail and an outer glow ring,
           shouting next to the neutral preset cards beside it. It now wears
           the SAME surface as .ds-shop-card and gets the same accent border +
           checkmark when it is the active preset — its hover / focus /
           selected states are the sibling rules themselves (this selector is
           appended to them below), so the two can never drift apart again.
           Only the layout differs, which is all a hero needs: taller, roomier
           gap, bigger title. */
        .ds-hero-shop-standard {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            justify-content: center;
            gap: var(--sp-1);
            width: 100%;
            min-height: 88px;
            padding: var(--card-pad-y) var(--card-pad-x);
            background: var(--pn);
            border: var(--card-bd) solid var(--bdr);
            border-left: var(--card-rail) solid transparent;
            border-radius: var(--card-radius);
            color: var(--txt);
            cursor: pointer;
            text-align: left;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
        }
        .ds-hero-shop-standard:active:not(:disabled) {
            transform: translateY(0);
            filter: brightness(0.96);
        }
        .ds-hero-title {
            font-family: var(--ff-ui);
            font-size: 18px;
            font-weight: 700;
            letter-spacing: 0.04em;
            color: var(--txt);
            line-height: 1.15;
        }
        .ds-hero-rec {
            font-size: 13px;
            font-weight: 600;
            letter-spacing: 0.02em;
            color: var(--dim);
            margin-left: 4px;
            text-transform: none;
        }
        .ds-hero-sub {
            font-family: var(--ff-ui);
            font-size: 13px;
            font-weight: 400;
            color: var(--dim);
            line-height: 1.35;
        }
        /* Hero block wraps the Shop Standard button + first-load onboard
           note so the note sits tight under the button (6px gap) instead
           of inheriting the bundle's full --sp-4 gap. The block itself is
           a single flex item in the bundle's column layout. */
        .ds-hero-block {
            display: flex;
            flex-direction: column;
            gap: 6px;
        }
        .ds-hero-onboard-note {
            margin: 0;
            padding: 0 var(--sp-1);
            font-family: var(--ff-ui);
            font-size: 11px;
            font-weight: 500;
            font-style: italic;
            color: var(--dim);
            line-height: 1.35;
        }
        .ds-hero-onboard-note[hidden] { display: none; }

        /* Bundle 4 secondary preset cards. Visually subordinate to the hero
           (smaller min-height, neutral background, --acc hover/focus — distinct
           from the hero's --ok green so the visual hierarchy stays "green = use
           the recommended; blue = consider this instead"). Phase B (later PR)
           layers on selected-state visuals + Tandem-no-slide advisory + keyboard
           shortcuts. */
        .ds-shop-cards {
            display: flex;
            flex-direction: column;
            gap: var(--sp-2);
        }
        /* PR-CARD-TOKENS (2026-08-25, owner: "match that with the door studio
           padding ... to really get close to the same home page look"): the
           preset cards now measure exactly what the Cabinet Studio metric
           cards measure — same padding, border, radius and reserved left
           rail, all from tokens.css. Only the INTERNAL layout still differs
           (these stack their lines vertically; a metric card sets its icon
           beside the value), which is content, not chrome. */
        .ds-shop-card {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            justify-content: center;
            gap: 4px;
            width: 100%;
            min-height: 80px;
            padding: var(--card-pad-y) var(--card-pad-x);
            background: var(--pn);
            border: var(--card-bd) solid var(--bdr);
            border-left: var(--card-rail) solid transparent;
            border-radius: var(--card-radius);
            color: var(--txt);
            cursor: pointer;
            text-align: left;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
        }
        .ds-shop-card:hover,
        .ds-hero-shop-standard:hover:not(:disabled) {
            border-color: color-mix(in srgb, var(--acc) 55%, var(--bdr));
            background: var(--pn-h);
            transform: translateY(-1px);
            box-shadow: 0 3px 10px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        .ds-shop-card:active {
            transform: translateY(0);
            filter: brightness(0.96);
        }
        .ds-shop-card:focus-visible,
        .ds-hero-shop-standard:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
        }
        .ds-shop-card-title {
            font-family: var(--ff-ui);
            font-size: 15px;
            font-weight: 700;
            letter-spacing: 0.03em;
            color: var(--txt);
            line-height: 1.15;
        }
        .ds-shop-card-summary {
            font-family: var(--ff-ui);
            font-size: 12px;
            font-weight: 500;
            color: var(--txt);
            line-height: 1.3;
        }
        .ds-shop-card-note {
            font-family: var(--ff-ui);
            font-size: 11px;
            font-weight: 400;
            font-style: italic;
            color: var(--dim);
            line-height: 1.3;
        }
        .ds-shop-card-badge {
            align-self: flex-start;
            padding: 3px 9px;
            margin-bottom: 4px;
            background: color-mix(in srgb, var(--acc) 28%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 65%, var(--bdr));
            border-radius: 999px;
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            white-space: nowrap;
            pointer-events: none;
        }
        .ds-shop-card-badge[hidden] { display: none; }

        /* PR-DBOX-PRESET-EDIT (2026-05-15): pencil-edit affordance in the
           top-right corner of each preset card.
           PR-0C-AUDIT-FIXUP (2026-05-15) — ux-specialist P0: 26×26 was
           below the 44×44 shop-floor min tap target. Bumped to 44×44 with
           negative margin to keep the visual icon small (~24px effective)
           but the hit area honors gloved-hand reality. event.stopPropagation
           in the onclick prevents the parent card's "apply preset" handler
           from firing when the operator wants to edit instead. */
        /* PR-DBOX-EDIT-OBVIOUS (2026-07-31): the edit affordance was a dim
           icon-only pencil in the top-right corner — easy to miss, and on the
           SELECTED card the ✓ badge sat right on top of it. It's now a clearly
           labeled "✎ Edit" pill in the BOTTOM-right, out of the way of the
           top-left number badge + top-right selection checkmark, and obviously
           a button. */
        .ds-card-edit {
            position: absolute;
            bottom: 8px;
            right: 8px;
            padding: 5px 10px 5px 8px;
            display: inline-flex;
            align-items: center;
            gap: 5px;
            background: color-mix(in srgb, var(--acc) 15%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 50%, var(--bdr));
            border-radius: 7px;
            color: var(--acc);
            font-size: 12px;
            font-weight: 600;
            line-height: 1;
            cursor: pointer;
            opacity: 0.95;
            transition: opacity var(--dur-chrome) var(--ease-out),
                        background var(--dur-chrome) var(--ease-out),
                        color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out);
        }
        .ds-card-edit-lbl { letter-spacing: 0.01em; }
        .ds-card-edit > svg { width: 13px; height: 13px; }
        .ds-card-edit:hover {
            opacity: 1;
            background: color-mix(in srgb, var(--acc) 24%, var(--inp));
            color: var(--acc);
            transform: translateY(-1px);
        }
        .ds-card-edit:focus-visible {
            opacity: 1;
            outline: 2px solid var(--acc);
            outline-offset: 1px;
        }
        /* Hero "Edit" pill sits over the bright-green hero — flip colors so
           it's still readable against the saturated background.
           PR-DBOX-AUDIT-FIXUP-2 (2026-05-15) — sentinel M-4 + ux P1:
           replaced 4 raw rgba()/#fff literals with cf-* tokens via
           color-mix so theme switches (and a future high-contrast mode)
           don't leave overlay+text mismatched on the saturated hero. */
        /* PR-DBOX-HERO-FLAT (2026-08-25): the hero's dark-overlay Edit pill
           was there to stay legible on the saturated green. On the neutral
           surface it just looked like a different button from the ones on the
           cards beside it, so it inherits the shared .ds-card-edit style. */

        /* 5th "Custom" card — visually distinct via dashed border so it
           reads as "different category" not "yet another preset." No edit
           button (Custom has no stored payload to edit). */
        .ds-shop-card-custom {
            border-style: dashed !important;
            border-color: color-mix(in srgb, var(--dim) 70%, var(--bdr)) !important;
            background: color-mix(in srgb, var(--inp) 60%, var(--bg));
        }
        .ds-shop-card-custom:hover {
            border-color: var(--acc) !important;
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
        }
        .ds-shop-card-custom .ds-shop-card-title {
            color: var(--dim);
        }
        .ds-shop-card-custom:hover .ds-shop-card-title {
            color: var(--txt);
        }

        /* PR-DBOX-PRESET-EDIT round 3 — inline editor panel. Lives under
           the card grid in Bundle 4. Hidden by default; pencil click flips
           hidden=false and adds .is-open which fades it in. Layout is a
           full-width card with a clear header, the form body, and a
           sticky-ish action bar at the bottom holding Save / Cancel /
           Reset. Reset on the left is muted (rare action); Save on the
           right is large + green so the operator's eye lands on it. */
        .dbox-inline-editor {
            margin-top: var(--sp-3);
            padding: var(--sp-3) var(--sp-4);
            background: color-mix(in srgb, var(--inp) 80%, var(--bg));
            border: 2px solid var(--acc);
            border-radius: var(--radius-sm);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 14%, transparent);
            animation: dbox-editor-fade-in var(--dur-chrome) var(--ease-out);
        }
        @keyframes dbox-editor-fade-in {
            from { opacity: 0; transform: translateY(-4px); }
            to   { opacity: 1; transform: translateY(0); }
        }
        /* PR-DBOX-AUDIT-FIXUP-2 (2026-05-15) — ux P1: honor the user's
           OS-level reduced-motion preference. WCAG 2.3.3 + the existing
           comment at line ~778 confirms the rest of AsCut zeros
           --dur-quick under reduced-motion via tokens.css; the editor's
           local @keyframes was missed in the prior pass. */
        @media (prefers-reduced-motion: reduce) {
            .dbox-inline-editor { animation: none; }
        }
        .dbox-inline-editor-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 10px;
            margin-bottom: 4px;
        }
        .dbox-inline-editor-head h3 {
            margin: 0;
            font-size: 15px;
            font-weight: 700;
            letter-spacing: 0.02em;
            color: var(--txt);
        }
        .dbox-inline-editor-head h3 #dbox-edit-target {
            color: var(--acc);
        }
        /* PR-0C-AUDIT-FIXUP (2026-05-15) — ux P0: 28×28 → 44×44 shop-floor min. */
        .dbox-inline-editor-close {
            background: transparent;
            border: 1px solid var(--bdr);
            color: var(--dim);
            width: 44px;
            height: 44px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            line-height: 1;
        }
        .dbox-inline-editor-close:hover {
            background: var(--bg);
            color: var(--txt);
        }
        .dbox-inline-editor-hint {
            margin: 0 0 var(--sp-3) 0;
            color: var(--dim);
            font-size: 11px;
            line-height: 1.4;
        }
        .dbox-inline-editor-body {
            display: flex;
            flex-direction: column;
            gap: var(--sp-2);
        }
        /* PR-0C-AUDIT-FIXUP (2026-05-15) — ux P0: 36→44px shop-floor min tap target. */
        .dbox-inline-editor select,
        .dbox-inline-editor input[type="text"] {
            width: 100%;
            min-height: 44px;
        }

        /* Round 4 — live preview block at the top of the inline editor.
           Shows the operator exactly what the card will look like after
           Save, so material picks (which auto-update the Summary line)
           give immediate visual feedback. Dashed accent border separates
           it from the editable form below. */
        .dbox-inline-preview {
            margin-bottom: var(--sp-3);
            padding: var(--sp-2) var(--sp-3);
            background: var(--bg);
            border: 1px dashed color-mix(in srgb, var(--acc) 50%, var(--bdr));
            border-radius: var(--radius-sm);
        }
        .dbox-preview-label {
            font-size: 10px;
            text-transform: uppercase;
            letter-spacing: 0.08em;
            color: var(--dim);
            margin-bottom: 4px;
        }
        .dbox-preview-card {
            display: flex;
            flex-direction: column;
            gap: 2px;
            padding: var(--sp-2);
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
        }
        .dbox-preview-badge {
            align-self: flex-start;
            padding: 2px 8px;
            background: color-mix(in srgb, var(--acc) 28%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 65%, var(--bdr));
            border-radius: 999px;
            font-size: 9px;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--txt);
        }
        .dbox-preview-badge[hidden] { display: none; }
        .dbox-preview-title {
            font-size: 14px;
            font-weight: 700;
            letter-spacing: 0.03em;
            color: var(--txt);
        }
        .dbox-preview-summary { font-size: 12px; color: var(--txt); }
        .dbox-preview-note    { font-size: 11px; color: var(--dim); font-style: italic; }

        /* PR-DBOX-PRESET-EDIT — action bar. Reset on the left, Cancel +
           the big primary Save on the right. Save is the only green button
           so the operator's eye lands on it. */
        .dbox-edit-actions {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 10px;
            margin-top: var(--sp-3);
            padding-top: var(--sp-3);
            border-top: 1px solid var(--bdr);
        }
        .dbox-edit-action-group {
            display: flex;
            gap: 10px;
            align-items: center;
        }
        /* PR-0C-AUDIT-FIXUP (2026-05-15) — ux P1: --ok is the cyan status
           token in AsCut (3 status colors only: cyan/amber/rose). Using
           a status color on a primary action button conflates "synced"
           with "tap to commit." Save is an action, not a status — use the
           indigo accent token. Per AsCut design language locked 2026-05-14. */
        .dbox-edit-save {
            min-height: 44px;
            padding: 0 24px;
            font-size: 14px;
            font-weight: 700;
            letter-spacing: 0.04em;
            background: var(--acc) !important;
            border-color: var(--acc) !important;
            /* PR-DBOX-AUDIT-FIXUP-2 (2026-05-15): #fff → var(--acc-ink)
               keeps the indigo accent button text correct under any
               theme switch (acc-ink is the cf-* paired text token). */
            color: var(--acc-ink) !important;
            box-shadow: 0 2px 8px color-mix(in srgb, var(--acc) 30%, transparent);
        }
        .dbox-edit-save:hover {
            filter: brightness(1.08);
            transform: translateY(-1px);
        }
        /* PR-DBOX-AUDIT-FIXUP-2 (2026-05-15) — ux P0: Cancel inherits the
           base .bg button (padding 9/18, ~32px tall) which is below the
           44px shop-floor tap-target floor. Bump the entire action group
           to 44px min-height so Cancel + the action-group cluster all
           clear WCAG 2.5.5 + Apple HIG. */
        .dbox-edit-actions .bg,
        .dbox-edit-action-group .bg {
            min-height: 44px;
        }
        .dbox-edit-reset {
            color: var(--dim);
            font-size: 12px;
        }

        /* ── PR-DBOX-PRESET-MODAL (2026-08-04) — full-spec preset editor as a
              blurred modal (replaces the round-3 inline panel). Backdrop dims +
              blurs the page; the panel is centered, scrolls on short screens,
              and reuses the .dbox-edit-actions / -save / -reset footer styles
              above so the buttons match the rest of Bundle 4. Modeled on the
              existing .sync-modal / .shortcuts-modal patterns. */
        .dbox-edit-modal {
            position: fixed;
            inset: 0;
            z-index: 320;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 24px;
            background: rgba(0, 0, 0, 0.6);
            backdrop-filter: blur(6px) saturate(140%);
            -webkit-backdrop-filter: blur(6px) saturate(140%);
            animation: dbox-modal-fade 0.16s ease-out;
        }
        .dbox-edit-modal[hidden] { display: none; }
        @keyframes dbox-modal-fade { from { opacity: 0; } to { opacity: 1; } }
        body.dbox-modal-open { overflow: hidden; }
        .dbox-edit-panel {
            width: 560px;
            max-width: 100%;
            max-height: 88vh;
            overflow-y: auto;
            background: color-mix(in srgb, var(--pn) 96%, #000);
            border: 1px solid var(--bdr);
            border-radius: 14px;
            padding: 20px 22px;
            box-shadow: 0 24px 70px rgb(var(--shadow-ink) / 0.7);
        }
        @media (prefers-reduced-motion: reduce) {
            .dbox-edit-modal { animation: none; }
        }
        .dbox-edit-panel-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            margin-bottom: 4px;
        }
        .dbox-edit-panel-head h3 {
            margin: 0;
            font-size: 16px;
            font-weight: 700;
        }
        .dbox-edit-panel-head h3 #dbox-m-target {
            color: var(--acc);
        }
        .dbox-edit-panel-close {
            background: none;
            border: none;
            color: var(--dim);
            font-size: 20px;
            line-height: 1;
            cursor: pointer;
            min-width: 44px;
            min-height: 44px;
        }
        .dbox-edit-panel-close:hover { color: var(--fg, #fff); }
        .dbox-edit-panel-hint {
            margin: 0 0 var(--sp-3);
            font-size: 12px;
            color: var(--dim);
        }
        .dbox-edit-panel-body .fg { margin-bottom: var(--sp-3); }
        .dbox-edit-panel-body select,
        .dbox-edit-panel-body input[type="text"] {
            width: 100%;
        }
        .dbox-m-thk {
            margin-bottom: var(--sp-3);
        }
        .dbox-m-thk-head {
            font-size: 12px;
            font-weight: 600;
            color: var(--dim);
            margin-bottom: 6px;
        }
        .dbox-m-verify {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 12px;
            margin-top: 4px;
        }

        /* Card highlight while its inline editor is open — makes it clear
           which card the form is editing. Subtle accent ring; doesn't
           interfere with the data-selected="true" active-preset chrome. */
        .ds-shop-card.is-being-edited,
        .ds-hero-shop-standard.is-being-edited {
            outline: 2px dashed var(--acc);
            outline-offset: 2px;
        }

        /* PR-BOX-SPEC-SELECTION-STATE — cards (hero included) gain an accent
           border + checkmark when they're the active preset; see the shared
           [data-selected="true"] rules above.
           PR-DBOX-HERO-FLAT (2026-08-25): the hero's separate deselected
           treatment is GONE. It existed only to tone the saturated green down
           when a sibling card was active — with the green removed, the plain
           base rule already IS the deselected look, identical to a sibling's. */
        .ds-shop-card {
            position: relative;
        }
        /* PR-CARD-TOKENS (2026-08-25): selected now reads the way it reads on
           the Cabinet Studio dashboard — accent tint plus a solid rail down
           the left edge, painted into the transparent border the base rule
           already reserves. The old treatment was an all-round accent border
           plus a 1px inset ring plus the ✓ badge below: three signals for one
           bit of state, and none of them the one the dashboard uses. */
        .ds-shop-card[data-selected="true"],
        .ds-hero-shop-standard[data-selected="true"] {
            background: color-mix(in srgb, var(--acc) 12%, var(--pn));
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            border-left-color: var(--acc);
            box-shadow: 0 2px 10px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        /* PR-CARD-TOKENS (2026-08-25): the ✓ badge is OFF — the dashboard has
           no such badge and "match the cabinet studio" was the call. Kept in
           the file (display:none) rather than deleted: it is the one signal
           the rail can't carry, namely WHICH of several peers is chosen when
           they're all on screen at once, so it may well earn its way back. */
        .ds-shop-card[data-selected="true"]::after,
        .ds-hero-shop-standard[data-selected="true"]::after {
            display: none;
            content: "";
            position: absolute;
            top: 8px;
            right: 8px;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background-color: var(--acc);
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%230a1012' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: center;
            pointer-events: none;
        }

        /* PR-BOX-SPEC-ADVANCED-COLLAPSE — collapsible "See all options"
           section that wraps the legacy Style/Side/Bottom dropdowns. Closed
           by default; opens on Bundle-4 entry only when reconciliation
           lands on null (custom-state saved order). Operator can toggle
           freely; runtime state changes don't auto-close to respect
           operator intent. */
        .ds-advanced {
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            background: color-mix(in srgb, var(--inp) 50%, transparent);
        }
        .ds-advanced > summary {
            list-style: none;
            cursor: pointer;
            padding: var(--sp-2) var(--sp-3);
            font-family: var(--ff-ui);
            font-size: 11px;
            font-weight: 700;
            color: var(--dim);
            letter-spacing: 0.06em;
            text-transform: uppercase;
            display: flex;
            align-items: center;
            gap: 8px;
            user-select: none;
        }
        .ds-advanced > summary::-webkit-details-marker { display: none; }
        .ds-advanced > summary::before {
            content: "";
            width: 7px;
            height: 7px;
            border-right: 1.5px solid currentColor;
            border-bottom: 1.5px solid currentColor;
            transform: rotate(-45deg);
            transition: transform var(--dur-chrome) var(--ease-out);
            flex-shrink: 0;
        }
        .ds-advanced[open] > summary::before {
            transform: rotate(45deg);
        }
        .ds-advanced > summary:hover {
            color: var(--txt);
        }
        .ds-advanced > summary:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
            border-radius: var(--radius-sm);
        }
        .ds-advanced-body {
            padding: var(--sp-3);
            border-top: 1px solid var(--bdr);
            display: flex;
            flex-direction: column;
            gap: var(--sp-3);
        }

        /* PR-LIBEDIT-SAVE-FLASH (2026-05-18) — visible-on-the-button
           feedback after a successful library-item save. The existing
           "✓ Saved" pill (top of drawer) + green "Saved!" message
           (bottom of form) live where the operator may not be looking
           when they click Save. This adds the third cue ON the button
           itself: text swaps to "✓ Saved!", background goes cyan
           success, a single subtle pulse fires, and after 1500 ms it
           reverts. Uses `var(--ok)` (cyan, the canonical AsCut
           success status color) so it skins correctly across all 7
           themes. */
        .ba.lib-save-flash {
            background: var(--ok) !important;
            color: var(--bg) !important;
            border-color: var(--ok) !important;
            animation: lib-save-pulse 600ms ease-out;
        }
        @keyframes lib-save-pulse {
            0%   { transform: scale(1);    box-shadow: 0 0 0 0   color-mix(in srgb, var(--ok) 60%, transparent); }
            40%  { transform: scale(1.04); box-shadow: 0 0 0 8px color-mix(in srgb, var(--ok) 0%,  transparent); }
            100% { transform: scale(1);    box-shadow: 0 0 0 0   transparent; }
        }
        /* Honor prefers-reduced-motion — keep the color swap, skip the
           pulse animation for operators with motion sensitivity. */
        @media (prefers-reduced-motion: reduce) {
            .ba.lib-save-flash { animation: none; }
        }

        /* PR-DOOR-DESIGN-APPLIES-TO (2026-05-18) — chip pills for the
           "Door Design" section's Applies-to multi-select.
           At rest: --inp / --bdr (neutral panel surface, theme-aware).
           Selected (`:has(input:checked)`): indigo (--acc) tint —
           matches the AsCut action-layer convention. "All" master
           uses font-weight:700 to distinguish; never amber (amber is
           reserved for warning status per the 3-status-color rule). */
        .ed-applies-chip {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 12px;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 6px;
            cursor: pointer;
            font-size: 12px;
            min-height: 44px;
            color: var(--txt);
            transition: background 120ms ease, border-color 120ms ease;
        }
        .ed-applies-chip:hover {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
        }
        .ed-applies-chip:has(input:checked) {
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            border-color: color-mix(in srgb, var(--acc) 50%, transparent);
            color: var(--txt);
        }
        /* PR-DOOR-DESIGN-UX-FIXUP (2026-05-18, ux-specialist audit P1
           #1+#3): hide the native 13px checkbox so the entire 44px label
           is the tap target — gloved-finger friendly. Selection state
           shown via the :has(input:checked) background tint above + a
           leading checkmark glyph rendered via ::before. "All" master
           gets a leading indigo bar so it reads as a master toggle
           rather than a fifth equal option. */
        .ed-applies-chip input[type="checkbox"] {
            position: absolute;
            opacity: 0;
            pointer-events: none;
        }
        .ed-applies-chip::before {
            content: "";
            display: inline-block;
            width: 14px;
            height: 14px;
            border: 1.5px solid var(--bdr);
            border-radius: 3px;
            background: var(--inp);
            flex-shrink: 0;
            transition: background 120ms ease, border-color 120ms ease;
        }
        .ed-applies-chip:has(input:checked)::before {
            background: var(--acc);
            border-color: var(--acc);
            box-shadow: inset 0 0 0 2px var(--inp), inset 0 0 0 14px var(--acc);
            /* Render an inline checkmark via SVG mask so it inherits --acc-ink
               contrast cleanly across all themes (vs ::after pseudo limits). */
            background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.5 8.5l3 3 6-6' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
            background-repeat: no-repeat;
            background-position: center;
            background-size: 14px 14px;
        }
        .ed-applies-chip-all {
            font-weight: 700;
            letter-spacing: 0.04em;
            border-left: 3px solid color-mix(in srgb, var(--acc) 60%, var(--bdr));
            padding-left: 14px;
        }
        /* PR-DOOR-DESIGN-UX-FIXUP (2026-05-18, ux-specialist audit P1
           #2): make the disclosure toggle's keyboard focus state
           visible. WCAG 2.4.11 (Focus Appearance) — without this, a
           keyboard operator tabbing into the section sees no ring on
           the summary element. */
        .ed-design-section > summary:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 3px;
            border-radius: 4px;
        }

        /* PR-BOX-SPEC-KEYBOARD-SHORTCUTS — small key-badge in the top-left of
           each Bundle-4 tap target. aria-hidden so screen readers don't
           announce the digit; the shortcut info is in the button's aria-label
           ("Keyboard shortcut N."). pointer-events: none so the badge never
           blocks the tap target. */
        .ds-kbd {
            position: absolute;
            top: 6px;
            left: 6px;
            min-width: 18px;
            height: 18px;
            padding: 0 5px;
            background: color-mix(in srgb, var(--bg) 70%, transparent);
            border: 1px solid color-mix(in srgb, var(--txt) 22%, transparent);
            border-radius: 4px;
            color: var(--txt);
            font-family: var(--ff-ui);
            font-size: 10px;
            font-weight: 700;
            line-height: 16px;
            text-align: center;
            pointer-events: none;
            user-select: none;
        }
        /* PR-DBOX-HERO-FLAT (2026-08-25): same story for the "1" shortcut
           chip — it now uses the shared .ds-kbd look the 2/3/4 chips use. */
        /* Bump in-flow content's left padding so the absolute kbd in the
           top-left doesn't overlap card text — especially the Economy
           "Most used for builder orders" pill that would otherwise land
           under the kbd. 36px clears the 6px-offset, 18px-min kbd, its
           1px border, plus a comfortable gap. */
        .ds-hero-shop-standard,
        .ds-shop-card {
            padding-left: 36px;
        }

        /* PR-BOX-SPEC-PRESS-ANIM — micro press animation. :active scales the
           tap target to 0.97 for a tactile press feel (mouse/touch); the
           .ds-keypress class fires a single-shot keyframe pulse so keyboard
           shortcut activations get matching visual feedback. Wrapped in
           prefers-reduced-motion so operators with motion-reduction settings
           opt out — :active states fall back to the existing brightness
           dim alone (already provided by the base :active rules). */
        @media (prefers-reduced-motion: no-preference) {
            .ds-hero-shop-standard:active:not(:disabled),
            .ds-shop-card:active {
                transform: scale(0.97);
                transition-duration: 80ms;
            }
            .ds-hero-shop-standard.ds-keypress,
            .ds-shop-card.ds-keypress {
                animation: ds-box-spec-keypress 180ms ease-out;
            }
        }
        @keyframes ds-box-spec-keypress {
            0%   { transform: scale(1); }
            35%  { transform: scale(0.97); }
            100% { transform: scale(1); }
        }

        /* Bundle Back / Next navigation row.
           Hit every time an operator advances or retreats through the
           5-bundle flow. Primary (Next) is accent-filled + confident;/* Primary — Next/Add advance. Accent-filled.
         * 2026-04-23: `color` was hardcoded `#0a1012` (near-black) —
         * a legacy from the pre-theme-swap cyan-only accent era. Now
         * routes through `--acc-ink` so every theme picks the right
         * ink: white on Studio Light's blue, dark on Onyx's green,
         * black on Dark's cyan, white on shop's blue. */
        /* Secondary — Back / cancel. Ghost style;/* Disabled Next (when required fields missing) */
        /* PR-DS-ORDER-TOTAL (2026-08-11): the order summary under the piece cards.
           Deliberately a SMALL step up from the old bare accent line — a tinted
           pill with a heavier left edge and the piece count one size larger. Enough
           to catch the eye when scanning for "how many did I enter", not enough to
           compete with the action buttons right below it. */
        .ds-order-total {
            display: inline-flex;
            align-items: baseline;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 14px;
            padding: 8px 14px;
            border: 1px solid color-mix(in srgb, var(--acc) 40%, var(--bdr));
            border-left: 4px solid var(--acc);
            border-radius: 8px;
            background: color-mix(in srgb, var(--acc) 9%, transparent);
            color: var(--acc);
            font-size: 13px;
            font-weight: 600;
        }
        .ds-order-total:empty { display: none; }
        .ds-order-total-n {
            font-size: 17px;
            font-weight: 800;
            font-variant-numeric: tabular-nums;
            color: var(--white, #fff);
            margin-right: 1px;
        }
        .ds-order-total-sep { color: var(--dim); font-weight: 400; }
        .ds-order-total-groups,
        .ds-order-total-brk { color: var(--dim); font-weight: 600; }

        /* PR-DS-CARD-COUNTS (2026-08-11): the piece-count badge at the top-right of
           every style-group card. Two numbers: this batch's piece count (big), and
           the whole order's count across every batch (small, underneath). Sized so
           an operator can read it from arm's length at the bench — the old count was
           a 11px dim chip at the tail of the settings row, which wrapped out of
           sight on a tablet exactly when the row got long enough to matter. */
        .ds-card-count {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            flex: none;
            min-width: 78px;
            padding: 6px 12px;
            border: 1px solid var(--bdr);
            border-radius: 10px;
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            line-height: 1.05;
            cursor: default;
        }
        .ds-card-count-n {
            font-size: 22px;
            font-weight: 800;
            color: var(--white, #fff);
            font-variant-numeric: tabular-nums;
        }
        .ds-card-count-lbl {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: .08em;
            text-transform: uppercase;
            color: var(--dim);
            margin-top: 1px;
        }
        .ds-card-count-of {
            font-size: 10px;
            color: var(--dim);
            margin-top: 4px;
            padding-top: 4px;
            border-top: 1px solid var(--bdr);
            width: 100%;
            text-align: center;
            white-space: nowrap;
            font-variant-numeric: tabular-nums;
        }/* PR-DS-TYPE-STEP-LOCK (2026-08-01): the ← Back buttons that lead to the
           Type step and the "Type" nav chip render disabled while the order's
           item type is locked. Kept pointer-clickable (no pointer-events:none)
           so a tap still fires the explanatory toast from dsBundleGoto rather
           than doing nothing silently.
           PR-DS-TYPE-STEP-OPEN (2026-08-11): .ds-type-locked is now set ONLY for
           DRAWER BOX orders. Door-family orders (doors / drawer fronts / slab
           fronts / end panels) keep the Type step live so the next piece can be
           a different door-family type. */
        /* Drawer-box orders never use the DOOR "Style & Materials" step (bundle 2),
           so dim its nav chip too. Click is still gated in dsBundleGoto. */
        #d-new[data-ds-lock-kind="drawer-box"] #ds-bnav-2 {
            opacity: 0.4;
            cursor: not-allowed;
        }/* "Skip for now" — tertiary text-link style. Reads as secondary
           to Back/Next so an operator pre-quoting a job without a customer
           name yet has an obvious escape hatch without it competing
           visually with the primary forward action. */

        /* Match cabinet body toggle */
        /* Wraps when the param panel narrows: the hint drops to its own line
           so the checkbox label always keeps full width (otherwise the long
           hint squeezed the label and a global break-word shattered
           "Match cabinet body material" into one letter per line). */
        .ds-match-body-row {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            gap: var(--sp-1) var(--sp-3);
            padding: var(--sp-3);
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
        }
        .ds-match-body-row label {
            display: flex;
            align-items: center;
            gap: var(--sp-2);
            color: var(--txt);
            font-size: var(--fs-base);
            font-weight: var(--fw-medium);
            cursor: pointer;
            flex: 1 1 auto;
            min-width: 0;
        }
        .ds-match-body-row input[type="checkbox"] {
            width: 18px; height: 18px; cursor: pointer; flex-shrink: 0;
        }
        /* flex-basis:100% forces the hint onto its own line under the label. */
        .ds-match-body-hint { font-size: var(--fs-sm); color: var(--dim); flex: 1 1 100%; }

        /* Confirm-before-commit panel -- absolute overlay inside .ds-param-panel */
        .ds-confirm-panel {
            position: absolute;
            inset: 0;
            background: var(--bg);
            z-index: var(--z-drawer);
            display: flex;
            flex-direction: column;
            overflow-y: auto;
            padding: var(--sp-6);
            gap: var(--sp-4);
        }
        .ds-confirm-panel[hidden] { display: none; }
        .ds-confirm-title {
            font-size: var(--fs-xl);
            font-weight: var(--fw-semibold);
            color: var(--txt);
            margin: 0;
            line-height: 1.3;
        }
        .ds-confirm-body-text {
            font-size: var(--fs-sm);
            color: var(--dim);
            margin: 0;
            line-height: 1.5;
        }
        .ds-confirm-summary {
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: var(--sp-4);
            display: flex;
            flex-direction: column;
            gap: var(--sp-2);
        }
        .ds-confirm-row {
            display: flex;
            justify-content: space-between;
            font-size: var(--fs-sm);
            line-height: 1.4;
        }
        .ds-confirm-row-label { color: var(--dim); font-weight: var(--fw-medium); }
        .ds-confirm-row-val {
            color: var(--txt);
            font-weight: var(--fw-semibold);
            font-family: var(--ff-mono);
            text-align: right;
        }
        .ds-confirm-cutlist-wrap {
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            padding: var(--sp-3) var(--sp-4);
            overflow-x: auto;
        }
        .ds-confirm-cutlist-title {
            font-size: var(--fs-sm);
            font-weight: var(--fw-semibold);
            color: var(--dim);
            margin: 0 0 var(--sp-2);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        /* cutlist table -- NO transition on any cell (math-guardian gate 12) */
        .ds-confirm-cl-table {
            width: 100%;
            border-collapse: collapse;
            font-size: var(--fs-sm);
            font-family: var(--ff-mono);
            line-height: 1.5;
        }
        .ds-confirm-cl-table th {
            text-align: left;
            color: var(--dim);
            font-weight: var(--fw-medium);
            padding: 2px var(--sp-2) var(--sp-1) 0;
            border-bottom: 1px solid var(--bdr);
        }
        .ds-confirm-cl-table th:last-child,
        .ds-confirm-cl-table td:last-child { text-align: right; }
        .ds-confirm-cl-table td { padding: 3px var(--sp-2) 3px 0; color: var(--txt); }
        .ds-confirm-cl-section-row td {
            font-size: 10px;
            font-weight: var(--fw-semibold);
            color: var(--acc2);
            text-transform: uppercase;
            letter-spacing: 0.8px;
            padding-top: var(--sp-3);
        }
        .ds-confirm-actions {
            display: flex;
            gap: var(--sp-3);
            padding-top: var(--sp-3);
            border-top: 1px solid var(--bdr);
            margin-top: auto;
        }
        .ds-confirm-go {
            flex: 1;
            min-height: 56px;
            background: var(--ok);
            border: none;
            border-radius: var(--radius-sm);
            color: var(--bg);
            font-weight: var(--fw-bold);
            font-size: var(--fs-base);
            cursor: pointer;
            transition: background-color var(--dur-chrome) var(--ease-out);
        }
        .ds-confirm-go:hover { filter: brightness(1.08); }
        .ds-confirm-go:disabled { opacity: 0.45; cursor: not-allowed; }
        .ds-confirm-back {
            min-height: 56px;
            min-width: 80px;
            padding: 0 var(--sp-5);
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-sm);
            color: var(--txt);
            font-weight: var(--fw-semibold);
            font-size: var(--fs-base);
            cursor: pointer;
        }

        /* Post-Add success state (2026-04-22).
           After the user confirms an Add, the same panel flips to a
           success view with 3 explicit next-action choices. No more
           "Where did the panel go?" confusion; no more hunting for the
           bundle-1 nav to start a new batch. */
        .ds-confirm-success-mark {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 72px;
            height: 72px;
            margin: var(--sp-4) auto var(--sp-2);
            border-radius: 50%;
            background: color-mix(in srgb, var(--ok) 16%, transparent);
            color: var(--ok);
            flex-shrink: 0;
        }
        .ds-confirm-success-actions {
            display: flex;
            flex-direction: column;
            gap: var(--sp-3);
            margin-top: var(--sp-5);
        }
        .ds-confirm-success-actions > button {
            width: 100%;
            min-height: var(--sp-10);            /* 56px */
            padding: var(--sp-3) var(--sp-5);
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            justify-content: center;
            gap: 2px;
            font-weight: var(--fw-semibold);
            font-size: var(--fs-base);
            text-align: left;
            cursor: pointer;
            border-radius: var(--radius-sm);
            border: 1px solid var(--bdr);
            transition: filter var(--dur-chrome) var(--ease-out),
                        background-color var(--dur-chrome) var(--ease-out);
        }
        .ds-confirm-success-actions > button:hover {
            filter: brightness(1.08);
        }
        .ds-confirm-success-actions > .sa-primary {
            background: var(--ok);
            color: var(--ok-ink);
            border-color: var(--ok);
        }
        .ds-confirm-success-actions > .sa-secondary {
            background: var(--acc);
            color: var(--acc-ink);
            border-color: var(--acc);
        }
        .ds-confirm-success-actions > .sa-tertiary {
            background: var(--inp);
            color: var(--txt);
        }
        .ds-confirm-btn-hint {
            font-size: var(--fs-xs, 11px);
            font-weight: var(--fw-regular);
            opacity: 0.78;
            letter-spacing: 0.01em;
        }

        /* ═══════════════════════════════════════════════════════════════
           Door preview — architectural drafting aesthetic (2026-04-22).
           Replaces the prior flat-accent SVG with a properly framed
           preview that reads like a shop drawing: title bar with species
           pill + metadata, centered SVG with weighted stroke hierarchy
           + proper dimension arrows + labeled members, footer title
           block with overall dimensions. Tokens-only; snaps on every
           value change (no transition on dimension text per gate 12). */
        .ds-viewport-preview {
            /* PR-DS-PREVIEW-STICKY-COL (2026-08-01): the STICKING now lives on
               the parent column (.ds-viewport), not here. This element had both
               `position: sticky` AND a later `position: relative` — relative won
               (last declaration), so the sticky never engaged and the preview
               scrolled away. It stays `position: relative` (its inner absolute
               bits depend on it); the pinned column keeps it on screen. */
            flex: 0 1 auto;
            max-height: min(64vh, 560px);
            display: flex;
            flex-direction: column;
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--inp) 88%, var(--bg)) 0%,
                var(--inp) 100%);
            border: 1px solid var(--bdr);
            border-radius: var(--radius-md);
            box-shadow: var(--shadow-card);
            min-height: 300px;
            overflow: hidden;
            position: relative;
        }
        /* Header scales down gracefully: title never wraps or shrinks, the
           pill row takes the middle and wraps its pills, and the "Change
           style" button drops to a second line before anything gets crushed.
           Fixes the mid-word "DOOR PREVI EW" wrap on narrow preview columns. */
        .ds-preview-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-wrap: wrap;
            padding: var(--sp-3) var(--sp-4);
            gap: var(--sp-2) var(--sp-3);
            background: color-mix(in srgb, var(--inp) 60%, var(--bg));
            border-bottom: 1px solid var(--bdr);
            flex-shrink: 0;
            min-height: 48px;
        }
        .ds-preview-title {
            font-family: var(--ff-ui);
            font-size: var(--fs-xs, 11px);
            font-weight: var(--fw-semibold);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
            margin: 0;
            white-space: nowrap;
            flex: 0 0 auto;
        }
        .ds-preview-meta {
            display: flex;
            align-items: center;
            justify-content: flex-start;
            gap: var(--sp-2);
            flex-wrap: wrap;
            flex: 1 1 auto;
            min-width: 0;
        }
        .ds-jump-style-btn { flex: 0 0 auto; }
        .ds-preview-pill {
            display: inline-flex;
            align-items: center;
            gap: var(--sp-1);
            padding: 3px var(--sp-2);
            border-radius: 999px;
            background: color-mix(in srgb, var(--bg) 75%, transparent);
            border: 1px solid var(--bdr);
            font-family: var(--ff-mono);
            font-size: 10px;
            font-weight: var(--fw-medium);
            color: var(--txt);
            letter-spacing: 0.02em;
            max-width: 100%;
            overflow: hidden;
        }
        .ds-preview-pill-swatch {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            border: 1px solid color-mix(in srgb, var(--txt) 25%, transparent);
            flex-shrink: 0;
        }
        .ds-preview-canvas {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: var(--sp-5) var(--sp-5) var(--sp-4);
            position: relative;
        }
        /* PR-NO-SQUARES (2026-08-28, owner: "i want the squares on the screen
           gone"). This was a graph-paper backdrop meant to read as drafting
           paper. It made sense behind a flat 2D elevation; behind a shaded 3D
           object it reads as a grid printed ON the wood. Gone. */
        .ds-preview-canvas > svg {
            position: relative;
            width: auto;
            height: auto;
            max-width: 100%;
            /* PR-DS-PREVIEW-CENTER (2026-08-01): scales with the screen but stays
               modest — the earlier min(62vh,720px) made the box far too tall.
               Sits centered in the (now shorter, vertically-centered) card. */
            max-height: min(44vh, 420px);
            display: block;
            cursor: zoom-in;
            filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.25));
        }
        .ds-preview-footer {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: var(--sp-3) var(--sp-4);
            gap: var(--sp-3);
            background: color-mix(in srgb, var(--inp) 60%, var(--bg));
            border-top: 1px solid var(--bdr);
            flex-shrink: 0;
            min-height: 44px;
            font-family: var(--ff-mono);
            font-size: var(--fs-sm);
            letter-spacing: 0.01em;
        }
        .ds-preview-overall {
            color: var(--txt);
            font-weight: var(--fw-semibold);
        }
        .ds-preview-overall-label {
            color: var(--dim);
            font-weight: var(--fw-regular);
            margin-right: 6px;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            font-size: 10px;
        }
        .ds-preview-zoom-hint {
            color: var(--dim);
            font-family: var(--ff-ui);
            font-size: 11px;
            display: flex;
            align-items: center;
            gap: 6px;
        }
        .ds-preview-zoom-hint svg { opacity: 0.7; }

        /* ─── SVG drafting styles ────────────────────────────────────
           Stroke hierarchy:
             frame (outer)        1.8px solid
             member (stile/rail)  1.2px solid
             panel cells          0.6px dashed
             dimensions           0.8px with arrow ticks
           Every stroke/fill routes through tokens so all 4 themes work. */
        .ds-preview-canvas .ds-prev-frame {
            fill: none;
            stroke: var(--txt);
            stroke-width: 1.8;
            stroke-linejoin: round;
        }
        .ds-preview-canvas .ds-prev-fill {
            fill: color-mix(in srgb, var(--acc2, var(--acc)) 18%, transparent);
            stroke: color-mix(in srgb, var(--acc2, var(--acc)) 55%, transparent);
            stroke-width: 0.8;
        }
        /* PR-PANEL-MISMATCH (2026-05-01) */
        /* Amber-flash for picker revert: when the operator clicks
           Cancel on the species-mismatch confirm, the picker's
           value rolls back. The flash makes the rollback observable
           in peripheral vision since the system dialog held focus.
           UX-audit follow-up 2026-05-01: also tint the <select>
           element's own background so the control itself signals
           it changed, not just an outline on the wrapper.
           Skipped under prefers-reduced-motion via the @media block. */
        .ds-revert-flash {
            outline: 3px solid var(--warn);
            outline-offset: 2px;
            transition: outline-color 380ms cubic-bezier(0.32, 0.72, 0.24, 1);
        }
        .ds-revert-flash select {
            background-color: color-mix(in srgb, var(--warn) 22%, var(--inp, var(--bg)));
            transition: background-color 380ms cubic-bezier(0.32, 0.72, 0.24, 1);
        }
        @media (prefers-reduced-motion: reduce) {
            .ds-revert-flash, .ds-revert-flash select {
                outline: none;
                background-color: var(--inp, var(--bg));
                transition: none;
            }
        }
        /* Non-blocking on-load badge: surfaces saved-order frame ↔
           panel mismatches without a modal dialog. role="status" so
           screen readers announce politely. Token-only colors. */
        .ds-mismatch-loaded-badge {
            margin: 8px 0 0 0;
            padding: 10px 14px;
            border: 1px solid color-mix(in srgb, var(--warn) 60%, transparent);
            border-left: 4px solid var(--warn);
            border-radius: 6px;
            background: color-mix(in srgb, var(--warn) 8%, transparent);
            color: var(--txt);
            font-size: 13px;
            line-height: 1.45;
            white-space: pre-wrap;
        }
        /* PR-MISMATCH-STRONG-WARN (2026-05-01) — persistent visible
           state for unresolved frame ↔ panel compatibility warnings.
           Replaces the easy-to-miss window.confirm() flow. The yellow
           border + tinted background stays on BOTH pickers until the
           operator either accepts the override or picks a compatible
           panel. Token-only colors. */
        .ds-mismatch-warn {
            outline: 3px solid var(--warn);
            outline-offset: 2px;
            background: color-mix(in srgb, var(--warn) 10%, transparent);
            border-radius: 6px;
            padding: 4px;
            transition: outline-color 220ms cubic-bezier(0.32, 0.72, 0.24, 1);
        }
        .ds-mismatch-warn select {
            background-color: color-mix(in srgb, var(--warn) 14%, var(--inp, var(--bg)));
        }
        .ds-mismatch-inline {
            margin: 12px 0 0 0;
            padding: 14px 16px;
            border: 1px solid color-mix(in srgb, var(--warn) 70%, transparent);
            border-left: 4px solid var(--warn);
            border-radius: 8px;
            background: color-mix(in srgb, var(--warn) 10%, transparent);
            color: var(--txt);
            font-size: 13px;
            line-height: 1.5;
        }
        .ds-mismatch-inline-actions {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
        .ds-mismatch-inline-btn {
            min-height: 56px;
            min-width: 160px;
            padding: 10px 18px;
            border: 1px solid var(--bdr);
            border-radius: 6px;
            background: var(--bg);
            color: var(--txt);
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: background-color 150ms ease, border-color 150ms ease;
        }
        .ds-mismatch-inline-btn:hover,
        .ds-mismatch-inline-btn:focus-visible {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 8%, var(--bg));
        }
        .ds-mismatch-inline-btn--accept {
            border-color: var(--warn);
            background: color-mix(in srgb, var(--warn) 18%, var(--bg));
        }
        .ds-mismatch-inline-btn--accept:hover,
        .ds-mismatch-inline-btn--accept:focus-visible {
            background: color-mix(in srgb, var(--warn) 28%, var(--bg));
        }/* PR-MISMATCH-STRONG-WARN Phase 3 (2026-05-01) — preemptive
           visual signal on Bundle 2's Next button while the panel-
           compat warning is unresolved. Token-only colors. UX pre-
           audit Rec 3: amber border + label hint, no pulse, no
           disable (operators must distinguish "warning" from "field
           empty"). */
        /* Blocking modal shown when operator clicks Next with
           unresolved warning. Custom modal (not window.confirm)
           because confirm() is too easy to muscle-memory dismiss
           on iOS Safari PWA. role="alertdialog", focus-trap,
           Escape closes — all in JS. */
        .ds-mismatch-block-modal {
            position: fixed;
            inset: 0;
            z-index: 9999;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 16px;
        }
        .ds-mismatch-block-backdrop {
            position: absolute;
            inset: 0;
            background: color-mix(in srgb, var(--bg) 85%, black);
            opacity: 0.85;
        }
        .ds-mismatch-block-card {
            position: relative;
            max-width: 520px;
            width: 100%;
            padding: 24px 26px;
            border: 1px solid color-mix(in srgb, var(--warn) 70%, transparent);
            border-left: 4px solid var(--warn);
            border-radius: 10px;
            background: var(--bg);
            color: var(--txt);
            box-shadow: 0 16px 48px color-mix(in srgb, black 45%, transparent);
        }
        .ds-mbm-title {
            font-size: 16px;
            font-weight: 700;
            margin-bottom: 8px;
            color: var(--txt);
        }
        .ds-mbm-body {
            font-size: 13px;
            line-height: 1.55;
            color: var(--txt);
            margin-bottom: 18px;
        }
        .ds-mbm-actions {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
        }
        .ds-mbm-btn {
            min-height: 56px;
            min-width: 180px;
            padding: 12px 22px;
            border: 1px solid var(--bdr);
            border-radius: 8px;
            background: var(--bg);
            color: var(--txt);
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: background-color 150ms ease, border-color 150ms ease;
        }
        .ds-mbm-btn:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        .ds-mbm-btn--primary {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 18%, var(--bg));
        }
        .ds-mbm-btn--primary:hover,
        .ds-mbm-btn--primary:focus-visible {
            background: color-mix(in srgb, var(--acc) 28%, var(--bg));
        }
        .ds-mbm-btn--override {
            border-color: var(--warn);
            color: color-mix(in srgb, var(--warn) 92%, var(--txt));
        }
        .ds-mbm-btn--override:hover,
        .ds-mbm-btn--override:focus-visible {
            background: color-mix(in srgb, var(--warn) 12%, var(--bg));
        }
        .ds-preview-canvas .ds-prev-stroke {
            fill: none;
            stroke: var(--txt);
            stroke-width: 1.2;
            stroke-linejoin: round;
        }
        .ds-preview-canvas .ds-prev-panel {
            fill: color-mix(in srgb, var(--bg) 60%, transparent);
            stroke: color-mix(in srgb, var(--dim) 70%, transparent);
            stroke-width: 0.6;
            stroke-dasharray: 2 2;
        }
        .ds-preview-canvas .ds-prev-dim {
            stroke: color-mix(in srgb, var(--dim) 80%, transparent);
            stroke-width: 0.8;
            fill: none;
        }
        .ds-preview-canvas .ds-prev-dim-arrow {
            fill: color-mix(in srgb, var(--dim) 80%, transparent);
            stroke: none;
        }
        .ds-preview-canvas .ds-prev-witness {
            stroke: color-mix(in srgb, var(--dim) 50%, transparent);
            stroke-width: 0.5;
            stroke-dasharray: 2 2;
            fill: none;
        }
        .ds-preview-canvas .ds-prev-text {
            fill: var(--txt);
            font-family: var(--ff-mono), monospace;
            font-size: 9.5px;
            font-weight: 500;
            letter-spacing: 0.02em;
            paint-order: stroke;
            stroke: var(--inp);
            stroke-width: 2.5;
            stroke-linejoin: round;
        }
        .ds-preview-canvas .ds-prev-text-dim {
            fill: color-mix(in srgb, var(--dim) 90%, var(--txt));
            font-size: 8.5px;
            font-weight: 500;
        }
        .ds-preview-canvas .ds-prev-text-overall {
            fill: var(--txt);
            font-size: 10.5px;
            font-weight: 600;
        }
        .ds-preview-canvas .ds-prev-corner {
            fill: none;
            stroke: color-mix(in srgb, var(--acc) 70%, transparent);
            stroke-width: 1.4;
            stroke-linecap: round;
        }

        /* Initial visibility for conditional form rows is handled entirely by
           toggleSGFields() at runtime — see web/door-studio.js. CSS defaults
           used to live here (#sg-dbstyle-wrap / #sg-wood-wrap / #sg-piece-d-wrap
           all had display:none) but they BLOCKED the JS toggle from showing
           the field: setDisp(id, true) clears the inline `style="display:..."`,
           and a CSS-applied `display: none` then wins. Result was a hidden D
           field for drawer-box pieces and a hidden drawer-box style picker.
           toggleSGFields() runs synchronously in initDoorStudio() so the
           initial frame is correct. The brief flash before init is not a
           real shop-floor concern. */

        /* ── Door Studio responsive layout ───────────────────────────
           Landscape tablet (≤1100px, ≥768px): narrow the param panel
           so the viewport preview still gets meaningful width. */
        @media (max-width: 1100px) and (min-width: 768px) {
            .ds-param-panel {
                flex: 0 0 52%;
                min-width: 360px;
                max-width: 520px;
            }
            .ds-viewport {
                min-width: 260px;
            }
        }

        /* Portrait tablet (≤767px): param panel stacks above the viewport.
           CRITICAL: override min-width so the panel never causes horizontal
           overflow on a 768px screen. */
        @media (max-width: 767px) {
            .ds-template-b {
                flex-direction: column;
                overflow-y: auto;
                overflow-x: hidden;
            }
            .ds-param-panel {
                flex: none;
                width: 100%;
                min-width: 0;   /* ← kills the 480px floor that caused overflow */
                max-width: 100%;
                border-right: none;
                border-bottom: 1px solid var(--bdr);
            }
            .ds-viewport {
                flex: none;
                min-width: 0;
                max-width: 100%;
                max-height: 200px;
                overflow: hidden;
                /* PR-DS-PREVIEW-PIP: on narrow screens the floating PiP would
                   crowd a small viewport — revert to a normal in-flow preview
                   below the form (un-fix all the pinning props). */
                position: static;
                right: auto;
                bottom: auto;
                width: auto;
                z-index: auto;
                filter: none;
                align-self: auto;
                top: auto;
            }
            /* PR-DS-PREVIEW-STICKY-COL: stacked layout, preview is a normal card. */
            .ds-viewport-preview {
                position: static;
                margin-block: 0;
                max-height: none;
            }
            /* PR-DS-PREVIEW-DRAG: not floating here — drop the drag affordances so
               the header scrolls normally and doesn't advertise a move it can't do.
               (dsApplyPreviewPos also strips any inline left/top on this layout.) */
            .ds-viewport .ds-preview-header {
                cursor: default;
                touch-action: auto;
                user-select: auto;
                -webkit-user-select: auto;
            }
            .ds-viewport .ds-preview-title::before { content: none; }
            .ds-type-tiles { grid-template-columns: repeat(3, 1fr); }/* Tighten the bundle bar step labels on narrow screens */
        }

        /* Small portrait (≤480px): hide viewport entirely to maximize form space */
        @media (max-width: 480px) {
            .ds-viewport { display: none; }
            .ds-bundle .fr {
                grid-template-columns: 1fr;
            }
        }

        /* ── PR-ASCUT-2B — cf-* utility scaffolding ─────────────────────
           All rules below are gated on body[data-ui-version="ascut"] so
           they activate ONLY for operators on the AsCut theme. Legacy
           themes are unaffected. cf- prefix prevents collisions with
           Rust-emitted CSS classes (dwg-*, no-print, plan-view-wrap) in
           cabinet-server's cutlist.rs and drawings modules.

           These are PRIMITIVES — buttons, cards, kpis, focus rings. The
           per-section reskins (PR-ASCUT-4-A..E) compose them. Apple HIG
           feel: generous whitespace, single accent (var(--acc)), restrained
           depth (1px borders + subtle shadow), spring physics in transitions.
        */

        /* Subtle shell polish — sidebar + topbar + statusbar gain a hint of
           depth without restructuring. body-gated so legacy themes don't
           inherit the shadow/border tweaks. */
        /* PR-HOME-REDESIGN (2026-06-13): dropped the topbar/statusbar
           backdrop-filter blur — FORGE bans backdrop-blur (reads as
           "loading"); the 1px borders carry the shell separation. */
        body[data-ui-version="ascut"] #app-topbar {
            border-bottom: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] #app-sidebar {
            border-right: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] #app-statusbar {
            border-top: 1px solid var(--bdr);
        }

        /* Card — the workhorse surface. Apple-style: pn background, 1px
           border, generous radius, subtle shadow on hover. No heavy
           elevation by default. */
        body[data-ui-version="ascut"] .cf-card {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 16px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cf-card:hover {
            border-color: var(--bdr-h, var(--bdr));
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 12%, transparent);
        }
        body[data-ui-version="ascut"] .cf-card[role="button"]:hover,
        body[data-ui-version="ascut"] .cf-card[tabindex]:hover {
            transform: translateY(-1px);
        }

        /* Buttons — primitive shapes. Three variants: primary (accent fill),
           secondary (ghost), tertiary (text-only). 48px min tap target. */
        body[data-ui-version="ascut"] .cf-btn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            min-height: 36px;
            min-width: 64px;
            padding: 0 14px;
            border-radius: 8px;
            font-family: var(--ff-ui, inherit);
            font-size: 13px;
            font-weight: 600;
            letter-spacing: 0.01em;
            border: 1px solid transparent;
            cursor: pointer;
            user-select: none;
            transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1),
                        filter 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cf-btn:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        body[data-ui-version="ascut"] .cf-btn:active:not(:disabled) {
            transform: scale(0.97);
        }
        body[data-ui-version="ascut"] .cf-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        body[data-ui-version="ascut"] .cf-btn-primary {
            background: var(--acc);
            color: var(--acc-ink);
            border-color: var(--acc);
        }
        body[data-ui-version="ascut"] .cf-btn-primary:hover:not(:disabled) {
            filter: brightness(1.08);
        }

        body[data-ui-version="ascut"] .cf-btn-secondary {
            background: var(--pn);
            color: var(--txt);
            border-color: var(--bdr);
        }
        body[data-ui-version="ascut"] .cf-btn-secondary:hover:not(:disabled) {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 8%, var(--pn));
        }

        body[data-ui-version="ascut"] .cf-btn-ghost {
            background: transparent;
            color: var(--txt);
            border-color: transparent;
        }
        body[data-ui-version="ascut"] .cf-btn-ghost:hover:not(:disabled) {
            background: color-mix(in srgb, var(--acc) 8%, transparent);
        }

        /* KPI tile — for the QBO-style homepage. Big number + label + delta. */
        body[data-ui-version="ascut"] .cf-kpi {
            display: flex;
            flex-direction: column;
            gap: 4px;
            padding: 16px;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            min-height: 88px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cf-kpi:hover {
            border-color: color-mix(in srgb, var(--acc) 40%, var(--bdr));
        }
        body[data-ui-version="ascut"] .cf-kpi-label {
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .cf-kpi-value {
            font-size: 28px;
            font-weight: 600;
            letter-spacing: -0.01em;
            color: var(--txt);
            font-variant-numeric: tabular-nums;
            line-height: 1.1;
        }
        body[data-ui-version="ascut"] .cf-kpi-delta {
            font-size: 12px;
            font-weight: 500;
            color: var(--dim);
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .cf-kpi-delta-up {
            color: var(--ok);
        }
        body[data-ui-version="ascut"] .cf-kpi-delta-down {
            color: var(--no);
        }

        /* Pill — small status indicators (online, sync, role badges, etc.) */
        body[data-ui-version="ascut"] .cf-pill {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 2px 8px;
            background: color-mix(in srgb, var(--acc) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 30%, transparent);
            border-radius: 999px;
            font-size: 11px;
            font-weight: 600;
            color: var(--txt);
        }
        body[data-ui-version="ascut"] .cf-pill-ok {
            background: color-mix(in srgb, var(--ok) 14%, transparent);
            border-color: color-mix(in srgb, var(--ok) 35%, transparent);
        }
        body[data-ui-version="ascut"] .cf-pill-warn {
            background: color-mix(in srgb, var(--warn) 14%, transparent);
            border-color: color-mix(in srgb, var(--warn) 35%, transparent);
        }
        body[data-ui-version="ascut"] .cf-pill-no {
            background: color-mix(in srgb, var(--no) 14%, transparent);
            border-color: color-mix(in srgb, var(--no) 35%, transparent);
        }

        /* Stack — vertical flex layout helper. */
        body[data-ui-version="ascut"] .cf-stack {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        body[data-ui-version="ascut"] .cf-stack-sm { gap: 6px; }
        body[data-ui-version="ascut"] .cf-stack-lg { gap: 24px; }

        /* Row — horizontal flex layout helper. */
        body[data-ui-version="ascut"] .cf-row {
            display: flex;
            align-items: center;
            gap: 12px;
        }
        body[data-ui-version="ascut"] .cf-row-sm { gap: 6px; }
        body[data-ui-version="ascut"] .cf-row-lg { gap: 24px; }

        /* Section header — for grouping cards on the homepage and beyond. */
        body[data-ui-version="ascut"] .cf-section-title {
            font-size: 12px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
            margin: 0 0 10px 0;
        }

        /* Refined typography scale (gated). Inter is already loaded;
           these classes just expose the scale as utilities. */
        body[data-ui-version="ascut"] .cf-text-display {
            font-size: 32px;
            font-weight: 600;
            letter-spacing: -0.02em;
            line-height: 1.15;
        }
        body[data-ui-version="ascut"] .cf-text-title {
            font-size: 24px;
            font-weight: 600;
            letter-spacing: -0.01em;
            line-height: 1.2;
        }
        body[data-ui-version="ascut"] .cf-text-headline {
            font-size: 17px;
            font-weight: 600;
            line-height: 1.3;
        }
        body[data-ui-version="ascut"] .cf-text-body {
            font-size: 14px;
            font-weight: 400;
            line-height: 1.45;
        }
        body[data-ui-version="ascut"] .cf-text-caption {
            font-size: 12px;
            font-weight: 400;
            color: var(--dim);
            line-height: 1.4;
        }
        body[data-ui-version="ascut"] .cf-text-mono {
            font-family: 'JetBrains Mono', 'SF Mono', 'Consolas', monospace;
            font-size: 13px;
            font-variant-numeric: tabular-nums;
        }

        /* Reduced-motion override — all cf-* transition durations collapse
           to 0ms when the operator prefers reduced motion. */
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .cf-btn,
            body[data-ui-version="ascut"] .cf-card,
            body[data-ui-version="ascut"] .cf-kpi {
                transition: none;
            }
            body[data-ui-version="ascut"] .cf-btn:active:not(:disabled) {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-2B ──────────────────────────────────────────────── */

        /* ── PR-ASCUT-3 — restyle existing primitives in ascut mode ──
           Overrides for the most-visible pre-existing UI patterns so the
           operator on the AsCut theme sees a coherent Apple-HIG-aligned
           visual language without any HTML changes. Every rule is gated on
           body[data-ui-version="ascut"]; legacy themes inherit the
           original styling untouched.

           Pattern: lift the corner radius, tighten the borders, refine
           hover transitions, swap shadow chrome for accent-tinted glows
           via color-mix(). The existing element IDs and class names stay
           intact — only the look changes. */

        /* KPI tile — restyle to Apple-card feel. Tabular-nums on the value
           span so currency / count columns align vertically. */
        body[data-ui-version="ascut"] .kpi-tile {
            border-radius: 10px;
            border: 1px solid var(--bdr);
            background: var(--pn);
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .kpi-tile:hover {
            /* PR-FORGE-CAB-CARDS (2026-06-13): flat pro-tool hover — border +
               bg tint, NO lift/glow. This block was overriding the global
               flat-hover fix in shell.css with higher specificity. */
            border-color: var(--bdr-h);
            background: var(--pn-h);
        }
        body[data-ui-version="ascut"] .kpi-tile-value {
            font-variant-numeric: tabular-nums;
            letter-spacing: -0.01em;
        }
        body[data-ui-version="ascut"] .kpi-tile-label {
            font-weight: 600;
            letter-spacing: 0.06em;
        }

        /* Report tile (Job Hub grid) — refine corners + ink, drop the
           orange-tinted hover for a theme-aware accent tint. */
        body[data-ui-version="ascut"] .rep-tile {
            border-radius: 12px;
            transition: background 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .rep-tile:hover {
            background: color-mix(in srgb, var(--acc) 8%, var(--pn));
            border-color: color-mix(in srgb, var(--acc) 55%, var(--bdr));
            box-shadow: 0 6px 18px rgb(var(--shadow-ink) / .14);
        }
        body[data-ui-version="ascut"] .rep-tile.on {
            background: color-mix(in srgb, var(--acc) 12%, var(--pn));
            border-color: var(--acc);
        }

        /* Feature tile (Door Studio home + similar) */
        body[data-ui-version="ascut"] .ft-tile {
            border-radius: 12px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .ft-tile:hover {
            /* PR-FORGE-CAB-CARDS: flat hover (no lift/glow). */
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            background: var(--pn-h);
        }

        /* Filter bar — soften corners + tighten the search field. */
        body[data-ui-version="ascut"] .filter-bar {
            border-radius: 12px;
        }
        body[data-ui-version="ascut"] .filter-bar .fb-search {
            border-radius: 8px;
            transition: border-color 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .filter-bar .fb-search:focus-within {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        body[data-ui-version="ascut"] .filter-bar select:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 1px;
        }

        /* Jobs table — tabular-figure alignment on numeric columns + 1px
           dividers instead of heavy borders. */
        body[data-ui-version="ascut"] .jobs-table {
            border-collapse: separate;
            border-spacing: 0;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .jobs-table thead th {
            font-weight: 600;
            font-size: 11px;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--dim);
            border-bottom: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] .jobs-table tbody tr {
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .jobs-table tbody tr:hover {
            background: color-mix(in srgb, var(--acc) 5%, transparent);
        }

        /* Type-picker tile (jd-tile) — used in cab-newjob wizard etc. */
        body[data-ui-version="ascut"] .jd-tile {
            border-radius: 10px;
            transition: border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .jd-tile:hover {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
        }
        body[data-ui-version="ascut"] .jd-tile.active {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 10%, var(--pn));
        }
        body[data-ui-version="ascut"] .jd-tile:active:not(.active) {
            transform: scale(0.98);
        }

        /* Library type-chip + shape-chip — pill polish */
        body[data-ui-version="ascut"] .lib-chip,
        body[data-ui-version="ascut"] .lib-shape-chip,
        body[data-ui-version="ascut"] .dview-pill {
            border-radius: 999px;
            transition: border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .lib-chip:hover:not(.active),
        body[data-ui-version="ascut"] .lib-shape-chip:hover:not(.active),
        body[data-ui-version="ascut"] .dview-pill:hover:not(.active) {
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            color: var(--txt);
        }

        /* Metrics ribbon (pg-cab dash) — match the Apple-card vocabulary */
        body[data-ui-version="ascut"] .metrics-ribbon {
            border-radius: 12px;
        }
        body[data-ui-version="ascut"] .metric-cell {
            transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .metric-cell:hover {
            background: color-mix(in srgb, var(--acc) 8%, transparent);
        }

        /* Drawer overlay — gentler backdrop with the same acrylic feel
           as the topbar. The drawer panel keeps its existing radius. */
        body[data-ui-version="ascut"] .drawer-overlay {
            backdrop-filter: blur(8px) saturate(140%);
            -webkit-backdrop-filter: blur(8px) saturate(140%);
            background: color-mix(in srgb, #000 55%, transparent);
        }
        body[data-ui-version="ascut"] .drawer {
            border-radius: 12px;
            border: 1px solid var(--bdr);
            box-shadow: 0 30px 60px rgb(var(--shadow-ink) / 0.4);
        }

        /* Reduced-motion override — all PR-ASCUT-3 transitions go to 0
           when the operator has prefers-reduced-motion set. */
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .kpi-tile,
            body[data-ui-version="ascut"] .rep-tile,
            body[data-ui-version="ascut"] .ft-tile,
            body[data-ui-version="ascut"] .filter-bar .fb-search,
            body[data-ui-version="ascut"] .jobs-table tbody tr,
            body[data-ui-version="ascut"] .jd-tile,
            body[data-ui-version="ascut"] .lib-chip,
            body[data-ui-version="ascut"] .lib-shape-chip,
            body[data-ui-version="ascut"] .dview-pill,
            body[data-ui-version="ascut"] .metric-cell {
                transition: none;
            }
            body[data-ui-version="ascut"] .kpi-tile:hover,
            body[data-ui-version="ascut"] .rep-tile:hover,
            body[data-ui-version="ascut"] .ft-tile:hover {
                transform: none;
            }
            body[data-ui-version="ascut"] .jd-tile:active:not(.active) {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-3 ──────────────────────────────────────────────── */

        /* ── PR-ASCUT-LOGIN — polish the first-impression screen ───────── */
        body[data-ui-version="ascut"] .login-wrap {
            background: radial-gradient(ellipse at 50% 30%,
                color-mix(in srgb, var(--acc) 18%, var(--bg)) 0%,
                var(--bg) 70%);
        }
        body[data-ui-version="ascut"] .login-box {
            background: color-mix(in srgb, var(--pn) 92%, transparent);
            backdrop-filter: blur(20px) saturate(140%);
            -webkit-backdrop-filter: blur(20px) saturate(140%);
            border: 1px solid color-mix(in srgb, var(--acc) 18%, var(--bdr));
            border-radius: 18px;
            padding: 56px 48px 44px;
            box-shadow:
                0 32px 80px rgb(var(--shadow-ink) / 0.55),
                0 0 0 1px color-mix(in srgb, var(--acc) 12%, transparent),
                inset 0 1px 0 color-mix(in srgb, var(--acc) 14%, transparent);
        }
        /* "CF" logomark above the wordmark — pure CSS, no SVG, no HTML
           changes. A 48px rounded square with a stacked gradient + the
           "CF" glyph rendered via text-shadow for crispness. */
        body[data-ui-version="ascut"] .login-box::before {
            content: "CF";
            display: block;
            width: 56px;
            height: 56px;
            margin: 0 auto 20px;
            background: linear-gradient(135deg, var(--acc) 0%, var(--acc2) 100%);
            border-radius: 14px;
            color: var(--acc-ink);
            font-family: var(--ff-ui, inherit);
            font-weight: 800;
            font-size: 22px;
            letter-spacing: 0.02em;
            text-align: center;
            line-height: 56px;
            box-shadow:
                0 8px 24px color-mix(in srgb, var(--acc) 40%, transparent),
                inset 0 1px 0 rgba(255,255,255,0.18);
        }
        body[data-ui-version="ascut"] .login-box h1 {
            color: var(--txt);
            font-weight: 700;
            font-size: 28px;
            letter-spacing: -0.01em;
            margin: 0 0 6px 0;
            text-align: center;
            text-transform: none;
        }
        body[data-ui-version="ascut"] .login-box .sub {
            color: var(--dim);
            font-size: 13px;
            font-weight: 500;
            margin: 0 0 32px 0;
            text-align: center;
            letter-spacing: 0;
        }
        body[data-ui-version="ascut"] .login-box .fg {
            margin-bottom: 14px;
        }
        body[data-ui-version="ascut"] .login-box .fg label {
            display: block;
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
            margin-bottom: 6px;
        }
        body[data-ui-version="ascut"] .login-box .fg input {
            width: 100%;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 12px 14px;
            color: var(--txt);
            font-family: var(--ff-ui, inherit);
            font-size: 14px;
            transition: border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 160ms cubic-bezier(0.22, 1, 0.36, 1);
            outline: none;
        }
        body[data-ui-version="ascut"] .login-box .fg input:hover {
            border-color: var(--bdr-h, var(--bdr));
        }
        body[data-ui-version="ascut"] .login-box .fg input:focus {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent);
        }
        body[data-ui-version="ascut"] .login-box button[type="submit"] {
            width: 100%;
            margin-top: 16px;
            padding: 13px 16px;
            background: linear-gradient(135deg,
                var(--acc) 0%,
                color-mix(in srgb, var(--acc) 80%, var(--acc2)) 100%);
            color: var(--acc-ink);
            border: 1px solid color-mix(in srgb, var(--acc) 60%, var(--acc2));
            border-radius: 10px;
            font-family: var(--ff-ui, inherit);
            font-size: 14px;
            font-weight: 600;
            letter-spacing: 0.02em;
            cursor: pointer;
            transition: filter 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 140ms cubic-bezier(0.22, 1, 0.36, 1);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 30%, transparent);
        }
        body[data-ui-version="ascut"] .login-box button[type="submit"]:hover {
            filter: brightness(1.08);
            box-shadow: 0 6px 18px color-mix(in srgb, var(--acc) 40%, transparent);
        }
        body[data-ui-version="ascut"] .login-box button[type="submit"]:active {
            transform: scale(0.98);
        }
        body[data-ui-version="ascut"] .login-box button[type="submit"]:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 3px;
        }
        body[data-ui-version="ascut"] .login-box .err {
            margin-top: 12px;
            padding: 10px 12px;
            background: color-mix(in srgb, var(--no) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--no) 35%, transparent);
            border-radius: 8px;
            color: var(--no);
            font-size: 12px;
            font-weight: 500;
            text-align: center;
        }
        body[data-ui-version="ascut"] .login-box .err:empty {
            display: none;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .login-box .fg input,
            body[data-ui-version="ascut"] .login-box button[type="submit"] {
                transition: none;
            }
            body[data-ui-version="ascut"] .login-box button[type="submit"]:active {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-LOGIN ───────────────────────────────────────────── */

        /* ── PR-ASCUT-SHELL — sidebar / topbar / statusbar polish ──────── */
        body[data-ui-version="ascut"] #app-sidebar .sidebar-nav-item {
            border-radius: 0 10px 10px 0;
            margin-right: 8px;
            position: relative;
        }
        body[data-ui-version="ascut"] #app-sidebar .sidebar-nav-item:hover {
            background: color-mix(in srgb, var(--acc) 8%, transparent);
        }
        body[data-ui-version="ascut"] #app-sidebar .sidebar-nav-item[aria-current="page"],
        body[data-ui-version="ascut"] #app-sidebar .sidebar-nav-item.active {
            background: color-mix(in srgb, var(--acc) 14%, transparent);
            border-left: 3px solid var(--acc);
            box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acc) 22%, transparent);
        }
        body[data-ui-version="ascut"] #app-sidebar .sidebar-section-label {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.1em;
            text-transform: uppercase;
            color: var(--dim);
            opacity: 0.65;
        }

        body[data-ui-version="ascut"] #app-topbar .util-btn,
        body[data-ui-version="ascut"] #app-topbar .topbar-menu-btn {
            border-radius: 8px;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #app-topbar .util-btn:hover,
        body[data-ui-version="ascut"] #app-topbar .topbar-menu-btn:hover {
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            color: var(--txt);
        }
        body[data-ui-version="ascut"] #app-topbar .util-btn:focus-visible,
        body[data-ui-version="ascut"] #app-topbar .topbar-menu-btn:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        body[data-ui-version="ascut"] #app-topbar .topbar-home-btn,
        body[data-ui-version="ascut"] #app-topbar .topbar-back-btn {
            border-radius: 8px;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #app-topbar .topbar-home-btn:hover:not(:disabled),
        body[data-ui-version="ascut"] #app-topbar .topbar-back-btn:hover:not(:disabled) {
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            color: var(--acc);
        }
        body[data-ui-version="ascut"] #app-topbar .topbar-page-title {
            font-weight: 600;
            font-size: 13px;
            color: var(--dim);
            letter-spacing: 0.02em;
        }

        body[data-ui-version="ascut"] #app-topbar #topbar-search-btn {
            background: color-mix(in srgb, var(--acc) 10%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 25%, var(--bdr));
        }
        body[data-ui-version="ascut"] #app-topbar #topbar-search-btn:hover {
            background: color-mix(in srgb, var(--acc) 18%, transparent);
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
        }

        body[data-ui-version="ascut"] #app-statusbar {
            font-size: 11px;
            color: var(--dim);
            letter-spacing: 0.02em;
        }

        body[data-ui-version="ascut"] #app-bottom-nav .bnav-item {
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #app-bottom-nav .bnav-item:hover {
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            color: var(--txt);
        }
        body[data-ui-version="ascut"] #app-bottom-nav .bnav-item[aria-current="page"],
        body[data-ui-version="ascut"] #app-bottom-nav .bnav-item.active {
            color: var(--acc);
        }

        body[data-ui-version="ascut"] .hub-page-header,
        body[data-ui-version="ascut"] .hub-primary-action {
            border-radius: 10px;
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] #app-sidebar .sidebar-nav-item,
            body[data-ui-version="ascut"] #app-topbar .util-btn,
            body[data-ui-version="ascut"] #app-topbar .topbar-menu-btn,
            body[data-ui-version="ascut"] #app-topbar .topbar-home-btn,
            body[data-ui-version="ascut"] #app-topbar .topbar-back-btn,
            body[data-ui-version="ascut"] #app-bottom-nav .bnav-item {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-SHELL ───────────────────────────────────────────── */

        /* ── PR-ASCUT-HUB — Hub home page polish ──────────────────────── */
        /* Bigger Apple-style headline + refined subtitle. */
        body[data-ui-version="ascut"] .hub-page-header {
            padding: 32px 24px 20px;
        }
        body[data-ui-version="ascut"] .hub-page-header h1 {
            font-size: 32px;
            font-weight: 700;
            letter-spacing: -0.02em;
            line-height: 1.15;
        }
        body[data-ui-version="ascut"] .hub-page-header .hub-subtitle {
            font-size: 14px;
            color: var(--dim);
            margin-top: 6px;
            line-height: 1.5;
            max-width: 60ch;
        }
        /* PR-HOME-REDESIGN (2026-06-13): flattened the primary action — was
           an accent gradient + drop-shadow GLOW + translateY lift (exactly
           the blue-glow chrome the operator flagged, and a FORGE no-no on
           data surfaces). Now a flat accent button; hover = brighten only. */
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action {
            border-radius: 10px;
            background: var(--acc);
            color: var(--acc-ink);
            border: 1px solid var(--acc);
            font-weight: 600;
            letter-spacing: 0.01em;
            transition: filter 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:hover,
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:focus-visible {
            filter: brightness(1.08);
            /* kill the base rule's translateY lift + drop-shadow (FORGE flat) */
            transform: none;
            box-shadow: none;
        }
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:active {
            filter: brightness(0.95);
            /* kill the base rule's press translateY + shadow flash (FORGE flat) */
            transform: none;
            box-shadow: none;
        }

        /* Studio shortcut pills (Cabinet / Door) — refined ghost-pill style. */
        body[data-ui-version="ascut"] .hub-studio-shortcut {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            color: var(--txt);
            font-weight: 600;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-studio-shortcut:hover {
            background: color-mix(in srgb, var(--acc) 8%, var(--pn));
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            color: var(--acc);
        }
        body[data-ui-version="ascut"] .hub-studio-shortcut:active {
            transform: scale(0.98);
        }
        body[data-ui-version="ascut"] .hub-studio-shortcut:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }

        /* KPI strip — tighten spacing + ensure grid feels intentional. */
        body[data-ui-version="ascut"] .kpi-strip {
            padding: 8px 24px 16px;
            gap: 12px;
        }

        /* ── PR-PROD-BAR (2026-05-23, v2) — In Production drawer ────────────
           Tile-click only — no inline strip. Clicking the H-3 "In
           production" KPI tile opens #ipd-drawer with every active job
           grouped by state (Active now / Idle / Not yet started) so the
           office can see exactly where every job sits in the shop.
           All text colors use --white (the home page's "honest" token —
           dark on light themes, light on dark themes) so job names are
           readable on every theme. */
        @keyframes prodPulse {
            0%, 100% { opacity: 1; }
            50%      { opacity: 0.55; }
        }

        /* Make the H-3 KPI tile look clickable since we wire a handler. */
        #kpi-h3 { cursor: pointer; transition: transform 120ms, border-color 120ms; }
        #kpi-h3:hover { border-color: var(--acc); transform: translateY(-1px); }
        #kpi-h3:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }

        /* ── PR-ADM-USERS-POLISH (2026-05-23) — admin Users page ─────────── */
        .adm-card {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            margin-bottom: 18px;
            overflow: hidden;
        }
        .adm-form-card { padding: 22px 24px 18px; }
        .adm-form-head { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 1px solid var(--bdr); }
        .adm-form-title { font-size: 1.25rem; font-weight: 700; color: var(--white); margin: 0 0 4px; }
        .adm-form-sub   { font-size: 0.8125rem; color: var(--dim); margin: 0; }
        .adm-form-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px 20px;
            margin-bottom: 8px;
        }
        .adm-field { display: flex; flex-direction: column; gap: 4px; }
        .adm-field-wide { grid-column: 1 / -1; }
        .adm-field label {
            font-size: 0.6875rem;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--dim);
        }
        .adm-field input,
        .adm-field select {
            background: var(--inp, var(--bg));
            color: var(--white);
            border: 1px solid var(--bdr);
            border-radius: 6px;
            padding: 9px 11px;
            font-size: 0.9375rem;
            font-family: inherit;
            transition: border-color 120ms, box-shadow 120ms;
        }
        .adm-field input:focus,
        .adm-field select:focus {
            outline: none;
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        .adm-field-hint {
            font-size: 0.75rem;
            color: var(--dim);
            line-height: 1.4;
            margin-top: 2px;
        }
        .adm-form-error {
            color: var(--risk-late);
            font-size: 0.8125rem;
            margin: 14px 0 0;
            min-height: 18px;
            font-weight: 500;
        }
        .adm-form-actions {
            display: flex;
            justify-content: flex-end;
            gap: 10px;
            margin-top: 18px;
            padding-top: 16px;
            border-top: 1px solid var(--bdr);
        }

        /* Table card */
        .adm-table-head { padding: 18px 24px 12px; border-bottom: 1px solid var(--bdr); }
        .adm-table-title { font-size: 1.0625rem; font-weight: 700; color: var(--white); margin: 0 0 2px; }
        .adm-table-count { color: var(--dim); font-family: var(--ff-mono, "JetBrains Mono", monospace); font-weight: 500; font-size: 0.875rem; margin-left: 4px; }
        .adm-table-sub   { font-size: 0.75rem; color: var(--dim); margin: 0; }

        .adm-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.875rem;
        }
        .adm-table thead th {
            text-align: left;
            font-size: 0.6875rem;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--dim);
            padding: 12px 16px;
            background: var(--bg);
            border-bottom: 1px solid var(--bdr);
        }
        .adm-table tbody td {
            padding: 12px 16px;
            border-bottom: 1px solid color-mix(in srgb, var(--bdr) 50%, transparent);
            vertical-align: middle;
        }
        .adm-table tbody tr.adm-row { transition: background 100ms; }
        .adm-table tbody tr.adm-row:hover { background: color-mix(in srgb, var(--acc) 4%, transparent); }
        .adm-table tbody tr.adm-row.inactive { opacity: 0.55; }
        .adm-table tbody tr.adm-row.is-me { background: color-mix(in srgb, var(--acc) 5%, transparent); }
        .adm-table tbody tr:last-child td { border-bottom: 0; }
        .adm-table-empty { padding: 36px !important; text-align: center; color: var(--dim); font-style: italic; }

        .adm-username { color: var(--white); font-weight: 600; }
        .adm-display  { color: var(--white); }
        .adm-me-badge {
            display: inline-block;
            margin-left: 8px;
            font-size: 0.5625rem;
            font-weight: 800;
            letter-spacing: 0.08em;
            color: var(--acc);
            background: color-mix(in srgb, var(--acc) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 40%, var(--bdr));
            padding: 1px 6px;
            border-radius: 3px;
            text-transform: uppercase;
            vertical-align: middle;
        }
        .adm-pill {
            display: inline-block;
            padding: 4px 11px;
            border-radius: 999px;
            font-size: 0.75rem;
            font-weight: 600;
            letter-spacing: 0.02em;
        }
        .adm-pill.ok { color: var(--risk-on-track); background: color-mix(in srgb, var(--risk-on-track) 14%, var(--bg)); border: 1px solid color-mix(in srgb, var(--risk-on-track) 40%, var(--bdr)); }
        .adm-pill.no { color: var(--risk-late); background: color-mix(in srgb, var(--risk-late) 14%, var(--bg)); border: 1px solid color-mix(in srgb, var(--risk-late) 40%, var(--bdr)); }

        /* Inline cell <select> styled as a chip */
        .adm-cell-select {
            background: var(--bg);
            color: var(--white);
            border: 1px solid var(--bdr);
            border-radius: 999px;
            padding: 5px 10px;
            font-size: 0.8125rem;
            font-family: inherit;
            cursor: pointer;
            min-width: 130px;
            transition: border-color 120ms, background 120ms;
        }
        .adm-cell-select:hover  { border-color: var(--acc); }
        .adm-cell-select:focus  { outline: none; border-color: var(--acc); box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent); }
        .adm-role-chip.role-admin       { color: #d946ef; }
        .adm-role-chip.role-designer    { color: var(--acc); }
        .adm-role-chip.role-shop_floor  { color: #f59e0b; }
        .adm-role-chip.role-viewer      { color: var(--dim); }
        .adm-role-chip.static, .adm-station-chip.static {
            display: inline-block;
            padding: 5px 11px;
            border-radius: 999px;
            font-size: 0.8125rem;
            background: var(--bg);
            border: 1px dashed var(--bdr);
        }
        .adm-station-chip.unset { color: var(--dim); font-style: italic; }
        .adm-station-chip.set   { color: var(--white); }

        /* Action buttons row */
        .adm-actions-cell { text-align: right; white-space: nowrap; }
        .adm-action {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            background: transparent;
            border: 1px solid transparent;
            color: var(--dim);
            padding: 5px 10px;
            border-radius: 6px;
            font-size: 0.75rem;
            font-weight: 500;
            font-family: inherit;
            cursor: pointer;
            transition: background 100ms, color 100ms, border-color 100ms;
            margin-left: 2px;
        }
        .adm-action:hover         { background: color-mix(in srgb, var(--acc) 8%, transparent); border-color: var(--acc); color: var(--white); }
        .adm-action.accent        { color: var(--acc); }
        .adm-action.accent:hover  { background: color-mix(in srgb, var(--acc) 14%, transparent); color: var(--white); }
        .adm-action.subtle        { color: color-mix(in srgb, var(--white) 55%, transparent); }
        .adm-action.subtle:hover  { color: var(--white); background: color-mix(in srgb, var(--white) 8%, transparent); }
        .adm-action.danger        { color: var(--risk-late); }
        .adm-action.danger:hover  { background: color-mix(in srgb, var(--risk-late) 12%, transparent); border-color: var(--risk-late); color: var(--risk-late); }
        .adm-action.ok            { color: var(--risk-on-track); }
        .adm-action.ok:hover      { background: color-mix(in srgb, var(--risk-on-track) 12%, transparent); border-color: var(--risk-on-track); color: var(--risk-on-track); }
        .adm-action-icon { display: inline-block; font-size: 0.8125rem; }
        .adm-row-self { color: var(--dim); font-size: 0.75rem; }
        /* ── /PR-ADM-USERS-POLISH ───────────────────────────────────────── */

        /* PR-IPD-4 (2026-05-23): the legacy drawer-specific CSS that lived
           here (.ipd-head, .ipd-title, .ipd-sub, .ipd-group*, .ipd-job*,
           .ipd-worker-chip, .ipd-job-noworker, #ipd-drawer .drawer) was
           removed with the drawer DOM. The `.ipd-prog*` block below stays
           because pg-production reuses _ipdRenderProgress for the 7-phase
           strip on every list card. */

        /* 7-phase progress bar (Cut → Mill → Build → Sand → Spray → Finish → Pack) */
        .ipd-prog {
            grid-column: 1 / -1;
            margin-top: 10px;
            display: flex;
            flex-direction: column;
            gap: 4px;
        }
        .ipd-prog-bar {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 3px;
            height: 8px;
        }
        .ipd-prog-seg {
            background: color-mix(in srgb, var(--dim) 22%, transparent);
            border-radius: 2px;
            transition: background 200ms;
        }
        .ipd-prog-seg.done {
            background: var(--risk-on-track);
        }
        .ipd-prog-seg.in_progress {
            background: var(--warn, #facc15);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--warn, #facc15) 30%, transparent);
            animation: prodPulse 1.6s ease-in-out infinite;
        }
        .ipd-prog-labels {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 3px;
            font-size: 0.6875rem;
            font-family: var(--ff-mono, "JetBrains Mono", monospace);
            color: var(--dim);
            margin-top: 2px;
        }
        .ipd-prog-lbl {
            text-align: center;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .ipd-prog-lbl.done       { color: var(--risk-on-track); font-weight: 600; }
        .ipd-prog-lbl.in_progress { color: var(--risk-at-risk); font-weight: 700; }
        .ipd-prog-summary {
            font-size: 0.75rem;
            margin-top: 2px;
        }
        .ipd-prog-now  { color: var(--risk-at-risk); font-weight: 700; }
        .ipd-prog-done { color: var(--risk-on-track); font-weight: 600; }
        .ipd-prog-idle { color: var(--dim); font-style: italic; }
        /* ── /PR-PROD-BAR (drawer-only CSS removed in PR-IPD-4) ─────────── */

        /* ── PR-SCHED2-1 (2026-05-24) — drawer purged ──────────────────────
           The PR-SCHED-5 #sched-drawer DOM + CSS + JS were deleted here
           (~240 LOC CSS, ~20 LOC DOM, ~350 LOC JS). Schedule lives on
           the full page #pg-schedule-v2 (see CSS block immediately below).
           These three #kpi-h5 tile styles stay — the H-5 KPI tile still
           clicks through to go('pg-schedule'). */
        #kpi-h5 { cursor: pointer; transition: transform 120ms, border-color 120ms; }
        #kpi-h5:hover { border-color: var(--acc); transform: translateY(-1px); }
        #kpi-h5:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
        /* ── /PR-SCHED2-1 ──────────────────────────────────────────────── */

        /* ── PR-SCHED-FULLPAGE (2026-05-23) — Schedule as full page ───── */
        #pg-schedule-v2 { padding: 0; }
        #pg-schedule-v2 .hub-header { padding: 18px 24px 0; }

        /* PR-SCHED-FULLPAGE-POLISH (2026-05-23): full redesign for
           breathing room + clear hierarchy + premium feel. Was: dense
           paper-binder look. Now: airy ServiceTitan-grade dispatch
           board. Every value below tuned for whitespace > density. */
        .schedp-shell {
            display: grid;
            grid-template-columns: 280px 1fr;
            gap: 24px;
            padding: 16px 28px 32px;
            min-height: calc(100vh - 160px);
        }

        /* ── Unscheduled tray (left rail) ──────────────────────────── */
        .schedp-tray {
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--warn, #facc15) 6%, var(--pn)) 0%,
                var(--pn) 80px);
            border: 1px solid var(--bdr);
            border-radius: 14px;
            padding: 18px 16px;
            position: sticky;
            top: 16px;
            max-height: calc(100vh - 180px);
            display: flex; flex-direction: column;
            align-self: start;
            box-shadow: 0 1px 0 rgb(var(--shadow-ink) / 0.04);
        }
        .schedp-tray-head {
            display: flex; justify-content: space-between; align-items: center;
            font-size: 0.75rem; font-weight: 700;
            text-transform: uppercase; letter-spacing: 0.08em;
            color: var(--white);
            padding-bottom: 12px; margin-bottom: 12px;
            border-bottom: 1px solid var(--bdr);
        }
        .schedp-tray-count {
            background: var(--bg);
            border: 1px solid var(--bdr);
            font-family: var(--ff-mono, monospace);
            padding: 3px 10px;
            border-radius: 999px;
            color: var(--white);
            font-size: 0.75rem;
            font-weight: 700;
            min-width: 28px;
            text-align: center;
        }
        .schedp-tray-list {
            display: flex; flex-direction: column; gap: 8px;
            overflow-y: auto; flex: 1;
            min-height: 100px;
            margin: 0 -4px;       /* let scrollbar live in padding gutter */
            padding: 0 4px;
        }
        .schedp-tray-empty {
            color: var(--dim); font-style: italic; font-size: 0.8125rem;
            padding: 16px 4px;
            text-align: center;
        }
        .schedp-tray-hint {
            color: var(--dim);
            font-size: 0.75rem;
            line-height: 1.5;
            margin-top: 14px; padding-top: 12px;
            border-top: 1px dashed var(--bdr);
            text-align: center;
        }

        /* ── Multi-week scrollable area ──────────────────────────── */
        .schedp-weeks {
            display: flex; flex-direction: column; gap: 28px;
            min-width: 0;
        }
        .schedp-loading {
            color: var(--dim); font-style: italic;
            padding: 60px 30px;
            text-align: center;
            font-size: 0.9375rem;
        }
        .schedp-week {
            background: transparent;
            border: none;
            padding: 0;
        }
        .schedp-week-head {
            display: flex; justify-content: space-between; align-items: baseline;
            margin-bottom: 12px;
            padding-bottom: 10px;
            border-bottom: 2px solid var(--bdr);
        }
        .schedp-week-title {
            font-size: 1rem; font-weight: 700; color: var(--white);
            display: flex; align-items: center; gap: 12px;
            letter-spacing: -0.01em;
        }
        .schedp-week-meta {
            font-size: 0.75rem;
            font-family: var(--ff-mono, monospace);
            color: var(--dim);
            font-weight: 600;
        }
        .schedp-week.current {
            position: relative;
        }
        .schedp-week.current .schedp-week-head {
            border-bottom-color: var(--acc);
        }
        .schedp-week.current .schedp-week-title::before {
            content: "THIS WEEK";
            background: var(--acc); color: #fff;
            font-size: 0.625rem; font-weight: 700;
            letter-spacing: 0.08em;
            padding: 3px 9px; border-radius: 4px;
        }
        .schedp-days {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 10px;
        }

        /* ── Day cell ─────────────────────────────────────────────── */
        .schedp-day {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 14px 12px 12px;
            display: flex; flex-direction: column;
            min-height: 180px;
            transition: border-color 120ms, background 120ms;
        }
        .schedp-day.today {
            background: linear-gradient(180deg,
                color-mix(in srgb, var(--acc) 8%, var(--pn)) 0%,
                var(--pn) 60px);
            border-color: color-mix(in srgb, var(--acc) 40%, var(--bdr));
        }
        .schedp-day-head {
            display: flex; justify-content: space-between; align-items: flex-end;
            margin-bottom: 12px;
            padding-bottom: 10px;
            border-bottom: 1px solid color-mix(in srgb, var(--bdr) 60%, transparent);
        }
        .schedp-day-name {
            color: var(--white);
            font-size: 0.8125rem;
            font-weight: 800;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            line-height: 1;
        }
        .schedp-day-date {
            color: var(--dim);
            font-weight: 500;
            font-family: var(--ff-mono, monospace);
            font-size: 0.75rem;
            margin-top: 4px;
            display: block;
            margin-left: 0;
        }
        .schedp-day.today .schedp-day-name { color: var(--acc); }
        .schedp-day-count {
            background: var(--bg);
            border: 1px solid var(--bdr);
            padding: 3px 9px;
            border-radius: 999px;
            font-family: var(--ff-mono, monospace);
            color: var(--white);
            font-weight: 600;
            font-size: 0.75rem;
            min-width: 26px;
            text-align: center;
        }
        .schedp-day-count.over-cap {
            background: color-mix(in srgb, var(--warn, #facc15) 22%, var(--pn));
            color: var(--risk-at-risk);
            border-color: var(--warn, #facc15);
        }
        .schedp-day-list {
            display: flex; flex-direction: column; gap: 8px;
            flex: 1; overflow-y: auto;
            margin: 0 -4px;
            padding: 0 4px;
        }

        /* ── Job card — premium feel ─────────────────────────────── */
        .schedp-card {
            background: var(--bg);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 11px 12px 10px;
            cursor: pointer;
            font-size: 0.8125rem;
            position: relative;
            transition: border-color 140ms, transform 140ms, box-shadow 140ms;
            display: flex; flex-direction: column; gap: 6px;
        }
        .schedp-card:hover {
            border-color: var(--acc);
            transform: translateY(-1px);
            box-shadow: 0 2px 8px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        .schedp-card:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
        .schedp-card[draggable="true"] { cursor: grab; }
        .schedp-card.schedp-dragging { opacity: 0.45; cursor: grabbing; transform: rotate(1deg); }
        .schedp-card.rush {
            border-left: 4px solid var(--risk-late);
            padding-left: 11px;
            background: linear-gradient(90deg,
                color-mix(in srgb, var(--risk-late) 5%, var(--bg)) 0%,
                var(--bg) 40%);
        }

        /* PR-SCHED-URGENCY (2026-08-09): 🔴/🟡/🟢 Kanban deadline band.
           This card shows risk as a TEXT badge (not a left stripe), so
           the whole border is free — urgency frames all four sides.
           Colour is never the sole carrier: the aria-label spells the
           tier out and flagged rushes keep the RUSH badge.
             🔴 rush · 🟡 standard (resting) · 🟢 flexible filler */
        .schedp-card.urgency-rush     { border: 2px solid var(--risk-late); }
        .schedp-card.urgency-standard { border: 2px solid var(--warn); }
        .schedp-card.urgency-flexible { border: 2px solid var(--ok); }

        /* Job# headline */
        .schedp-card-jobnum {
            display: flex; align-items: center; gap: 6px;
            line-height: 1.2;
            white-space: nowrap;
            min-height: 18px;
        }
        .schedp-card-jobnum .schedp-jn {
            font-family: var(--ff-mono, "JetBrains Mono", monospace);
            font-weight: 700;
            font-size: 1rem;
            color: var(--white);
            letter-spacing: -0.01em;
        }
        /* Customer secondary */
        .schedp-card-customer {
            font-weight: 500;
            color: var(--white);
            opacity: 0.78;
            font-size: 0.8125rem;
            line-height: 1.25;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .schedp-card-meta {
            display: flex; justify-content: space-between; align-items: baseline;
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem;
            color: var(--dim);
            margin-top: 2px;
            padding-top: 6px;
            border-top: 1px dashed color-mix(in srgb, var(--bdr) 60%, transparent);
        }
        /* PR-UNIFY-D1 (2026-05-24): compact pipeline strip per card.
           7 phases (Cut Mill Build Sand Spray Finish Pack). One char
           per phase: ✓ done, ● in progress (amber pulse), · gray. */
        .schedp-card-pipeline {
            display: flex; gap: 4px; justify-content: space-between;
            margin-top: 4px; padding-top: 4px;
            border-top: 1px dashed color-mix(in srgb, var(--bdr) 40%, transparent);
            font-family: var(--ff-mono, monospace);
            font-size: 11px; line-height: 1; letter-spacing: 0.04em;
        }
        .schedp-phase { color: color-mix(in srgb, var(--dim) 70%, transparent); }
        .schedp-phase.done   { color: var(--risk-on-track); font-weight: 700; }
        .schedp-phase.active {
            color: var(--risk-at-risk); font-weight: 800;
            animation: schedp-phase-pulse 1.4s ease-in-out infinite;
        }
        @keyframes schedp-phase-pulse {
            0%, 100% { opacity: 1; }
            50%      { opacity: 0.45; }
        }
        @media (prefers-reduced-motion: reduce) {
            .schedp-phase.active { animation: none; }
        }

        .schedp-card-risk { font-weight: 700; letter-spacing: 0.04em; }
        .schedp-card-risk.on_track    { color: var(--ok); }
        .schedp-card-risk.at_risk     { color: var(--warn); }
        .schedp-card-risk.late        { color: var(--no); }
        .schedp-card-risk.already_late{ color: var(--no); }
        .schedp-card-risk.unknown     { color: var(--dim); font-weight: 500; }
        /* PR-AUDIT-27 (2026-05-26): Monte Carlo P(on_time) chip. Banded
           color: ≥80% = ok (relax), 50-79% = warn (watch), <50% = no
           (intervene). Uses the same status tokens as the risk badge
           so theme swaps stay coherent. */
        .schedp-card-pot {
            margin-left: 6px;
            padding: 1px 6px;
            border-radius: 4px;
            font-size: 0.6875rem;
            font-weight: 700;
            letter-spacing: 0.03em;
            font-variant-numeric: tabular-nums;
            background: color-mix(in srgb, var(--dim) 18%, transparent);
            color: var(--ink);
        }
        .schedp-card-pot.pot-good { color: var(--ok);   background: color-mix(in srgb, var(--ok)   18%, transparent); }
        .schedp-card-pot.pot-mid  { color: var(--warn); background: color-mix(in srgb, var(--warn) 18%, transparent); }
        .schedp-card-pot.pot-bad  { color: var(--no);   background: color-mix(in srgb, var(--no)   18%, transparent); }

        /* ── PR-AUDIT-28 (2026-05-26): What-If scenario drawer ──── */
        .schedp-whatif {
            position: fixed; inset: 0; z-index: 9000;
            display: flex; justify-content: flex-end;
        }
        .schedp-whatif[hidden] { display: none; }
        .schedp-whatif-shade {
            position: absolute; inset: 0;
            background: color-mix(in srgb, var(--bg) 70%, transparent);
        }
        .schedp-whatif-panel {
            position: relative; width: min(720px, 92vw); height: 100%;
            background: var(--pn);
            box-shadow: -8px 0 32px color-mix(in srgb, var(--bg) 60%, transparent);
            display: flex; flex-direction: column;
            color: var(--ink);
        }
        .schedp-whatif-head {
            padding: 16px 20px;
            border-bottom: 1px solid var(--bdr);
            display: flex; align-items: baseline; gap: 12px;
        }
        .schedp-whatif-head h3 {
            margin: 0; font-size: 1.05rem; letter-spacing: 0.03em; flex: 0 0 auto;
        }
        .schedp-whatif-sub { color: var(--dim); font-size: 0.85rem; flex: 1 1 auto; }
        .schedp-whatif-close {
            background: transparent; border: 0; color: var(--ink);
            font-size: 1.5rem; cursor: pointer; line-height: 1;
            padding: 0 8px;
        }
        .schedp-whatif-tabs {
            display: flex; gap: 6px;
            padding: 8px 20px 0;
            border-bottom: 1px solid var(--bdr);
        }
        .schedp-whatif-tabs button {
            background: transparent; border: 0;
            color: var(--dim); font-weight: 600;
            padding: 8px 14px; border-radius: 6px 6px 0 0;
            cursor: pointer;
        }
        .schedp-whatif-tabs button.active {
            color: var(--ink);
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            border-bottom: 2px solid var(--acc);
        }
        .schedp-whatif-body {
            flex: 1 1 auto; overflow-y: auto; padding: 16px 20px;
        }
        .schedp-whatif-pane[hidden] { display: none; }
        .schedp-whatif-help {
            color: var(--dim); font-size: 0.85rem; margin: 0 0 12px;
        }
        .schedp-whatif-chart {
            display: flex; flex-direction: column; gap: 6px;
        }
        .schedp-tornado-row {
            display: grid;
            grid-template-columns: 110px 1fr 60px;
            align-items: center;
            gap: 8px;
            font-size: 0.85rem;
        }
        .schedp-tornado-station {
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.04em;
        }
        .schedp-tornado-bar-track {
            position: relative;
            height: 18px;
            background: color-mix(in srgb, var(--dim) 12%, transparent);
            border-radius: 3px;
        }
        .schedp-tornado-bar-track::before {
            content: '';
            position: absolute; top: 0; bottom: 0; left: 50%;
            width: 1px; background: var(--dim);
        }
        .schedp-tornado-bar-neg, .schedp-tornado-bar-pos {
            position: absolute; top: 0; bottom: 0;
        }
        .schedp-tornado-bar-neg { right: 50%; background: var(--no); border-radius: 2px 0 0 2px; }
        .schedp-tornado-bar-pos { left: 50%;  background: var(--ok); border-radius: 0 2px 2px 0; }
        .schedp-tornado-swing {
            font-variant-numeric: tabular-nums;
            color: var(--dim); font-size: 0.75rem; text-align: right;
        }
        .schedp-whatif-results {
            margin-top: 12px;
            display: flex; flex-direction: column; gap: 4px;
            font-size: 0.85rem;
        }
        .schedp-whatif-result-row {
            display: grid;
            grid-template-columns: 90px 1fr 50px 50px 64px;
            align-items: center; gap: 8px;
            padding: 4px 6px;
            border-radius: 4px;
        }
        .schedp-whatif-result-row:nth-child(even) {
            background: color-mix(in srgb, var(--dim) 6%, transparent);
        }
        .schedp-whatif-result-job { font-weight: 700; font-variant-numeric: tabular-nums; }
        .schedp-whatif-result-delta { font-variant-numeric: tabular-nums; font-weight: 700; }
        .schedp-whatif-result-delta.good { color: var(--ok); }
        .schedp-whatif-result-delta.bad  { color: var(--no); }
        .schedp-whatif-result-delta.zero { color: var(--dim); }
        .schedp-whatif-summary {
            padding: 10px 12px;
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            border-radius: 4px;
            font-size: 0.85rem;
            margin-bottom: 8px;
        }
        .schedp-whatif-budget {
            display: flex; align-items: center; gap: 8px;
            font-size: 0.85rem; margin-bottom: 12px;
        }
        .schedp-whatif-budget input {
            width: 64px; padding: 4px 6px;
            background: var(--bg); color: var(--ink);
            border: 1px solid var(--bdr); border-radius: 4px;
        }
        #schedp-whatif-sim-controls {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 6px 12px;
            margin-bottom: 12px;
            font-size: 0.85rem;
            align-items: center;
        }
        #schedp-whatif-sim-controls input {
            width: 60px; padding: 4px 6px;
            background: var(--bg); color: var(--ink);
            border: 1px solid var(--bdr); border-radius: 4px;
        }
        #schedp-whatif-sim-run,
        #schedp-whatif-auto-run {
            background: var(--acc); color: var(--bg);
            border: 0; padding: 8px 16px; border-radius: 4px;
            font-weight: 700; cursor: pointer;
        }
        .schedp-rush-badge {
            background: var(--risk-late); color: #fff;
            font-size: 0.5625rem; font-weight: 700;
            letter-spacing: 0.08em; text-transform: uppercase;
            padding: 2px 6px; border-radius: 3px;
            flex: 0 0 auto;
        }
        .schedp-blocked-badge {
            background: var(--warn, #facc15); color: #422006;
            font-size: 0.5625rem; font-weight: 700;
            letter-spacing: 0.08em; text-transform: uppercase;
            padding: 2px 6px; border-radius: 3px;
            margin-left: auto;
        }

        /* ── Drop targets ─────────────────────────────────────────── */
        .schedp-day.schedp-drop-target,
        .schedp-tray.schedp-drop-target {
            background: color-mix(in srgb, var(--acc) 14%, var(--bg));
            border-color: var(--acc);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 30%, transparent);
        }

        /* Empty-day placeholder — minimal so the column doesn't look broken */
        .schedp-day-empty {
            color: var(--dim);
            font-size: 0.6875rem;
            font-style: italic;
            text-align: center;
            padding: 14px 0;
            opacity: 0.5;
        }

        /* ── PR-SCHED2-2 (2026-05-24) — enlarged single-week canvas ──
           One Mon-Fri week now dominates the screen instead of four
           stacked. Day cells grow taller (320 min) so 5+ cards per
           day breathe. Three additions: 4px capacity bar (cyan/amber/
           rose), .past dim, .over-cap dashed border. UX research:
           drum-buffer-rope + Asana/ShopMonkey/Procore conventions.
           --------------------------------------------------------- */
        .schedp-days { gap: 14px; }
        .schedp-day {
            min-height: 320px;
            padding: 16px 14px 14px;
        }
        /* 4px capacity bar — sits between day-head and the card list,
           full column width. The single most-glanceable signal: is
           this day full? Drum-buffer-rope visual management. */
        .schedp-day-cap {
            height: 4px;
            border-radius: 2px;
            margin: 0 -2px 12px;
            background: color-mix(in srgb, var(--acc) 35%, transparent);
            transition: background 140ms;
        }
        .schedp-day-cap.near {
            background: color-mix(in srgb, var(--warn, #facc15) 55%, transparent);
        }
        .schedp-day-cap.over {
            background: var(--risk-late);
        }
        /* Today: amber 2px left border + already has ivory→slate
           gradient on .today (rule above). Add the border without
           clobbering the gradient. */
        .schedp-day.today {
            border-left: 2px solid var(--warn, #facc15);
            padding-left: 13px;
        }
        /* Past days: dim card opacity + muted header. Eye lands on
           the live half of the week. Day header text stays readable. */
        .schedp-day.past .schedp-day-list { opacity: 0.55; }
        .schedp-day.past .schedp-day-name { color: var(--dim); }
        .schedp-day.past .schedp-day-cap  { opacity: 0.5; }
        /* Over-capacity day: dashed rose border on the column (not
           the cards) is the loudest possible signal that the office
           is over-committing this day's shipping capacity. */
        .schedp-day.over-cap {
            border: 1.5px dashed var(--risk-late);
        }
        .schedp-week-current-tag {
            color: var(--acc);
            font-size: 0.625rem;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            margin-left: 10px;
            vertical-align: middle;
        }
        /* ── /PR-SCHED2-2 ───────────────────────────────────────── */

        /* ── PR-SCHED2-5 (2026-05-24) — View toggle + unscheduled ──
           3-segment pill: Week | Month | Unscheduled. Replaces the
           old "◀ 4 weeks / Today / 4 weeks ▶" buttons. Adapts step
           buttons' semantics per active view. ----------------------- */
        .schedp-viewseg {
            display: inline-flex;
            background: var(--bg);
            border: 1px solid var(--bdr);
            border-radius: 999px;
            padding: 3px;
            font-family: var(--ff-mono, "JetBrains Mono", monospace);
        }
        .schedp-viewseg-btn {
            background: transparent;
            border: none;
            color: var(--dim);
            font: inherit;
            font-size: 0.75rem;
            font-weight: 700;
            letter-spacing: 0.04em;
            padding: 7px 16px;
            border-radius: 999px;
            cursor: pointer;
            transition: background 120ms, color 120ms;
        }
        .schedp-viewseg-btn:hover {
            color: var(--white);
        }
        .schedp-viewseg-btn[aria-selected="true"] {
            background: var(--pn);
            color: var(--white);
            box-shadow: 0 1px 0 rgb(var(--shadow-ink) / 0.05);
        }
        .schedp-viewseg-btn:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        /* Step buttons hide entirely in Unscheduled view (no
           pagination semantic). */
        #pg-schedule-v2[data-view="unscheduled"] .schedp-stepgrp {
            display: none;
        }
        /* Unscheduled view = full-page list mode. Hides the week grid +
           month grid + the left-rail tray (which becomes the list
           itself). When unscheduled is active, the page is just the
           tray expanded. */
        #pg-schedule-v2[data-view="unscheduled"] .schedp-shell {
            grid-template-columns: 1fr;
        }
        #pg-schedule-v2[data-view="unscheduled"] .schedp-weeks,
        #pg-schedule-v2[data-view="unscheduled"] .schedp-month {
            display: none !important;
        }
        #pg-schedule-v2[data-view="unscheduled"] .schedp-tray {
            max-height: none;
            position: static;
        }
        #pg-schedule-v2[data-view="unscheduled"] .schedp-tray-list {
            max-height: none;
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
            gap: 10px;
        }
        /* Month view hides the unscheduled tray (it's already a
           planning lens — too much density to compete). */
        #pg-schedule-v2[data-view="month"] .schedp-tray {
            display: none;
        }
        #pg-schedule-v2[data-view="month"] .schedp-shell {
            grid-template-columns: 1fr;
        }
        #pg-schedule-v2[data-view="month"] .schedp-weeks {
            display: none !important;
        }
        /* Week view hides the month grid (default) — same pattern. */
        #pg-schedule-v2[data-view="week"] .schedp-month {
            display: none !important;
        }
        /* ── /PR-SCHED2-5 ───────────────────────────────────────── */

        /* ── PR-SCHED2-6 (2026-05-24) — Right-rail detail panel ────
           Replaces the floating popover. Sticky 340px column to the
           right of the week grid (week view only). Mobile (<980px)
           collapses to a fixed bottom drawer.
           --------------------------------------------------------- */
        #pg-schedule-v2[data-view="week"] .schedp-shell {
            grid-template-columns: 260px minmax(0, 1fr) 340px;
        }
        .schedp-detail {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 14px;
            padding: 18px 18px 14px;
            position: sticky;
            top: 16px;
            max-height: calc(100vh - 180px);
            overflow-y: auto;
            align-self: start;
            display: flex;
            flex-direction: column;
            box-shadow: 0 1px 0 rgb(var(--shadow-ink) / 0.04);
        }
        /* Hide rail in month + unscheduled (already-cramped views). */
        #pg-schedule-v2[data-view="month"] .schedp-detail,
        #pg-schedule-v2[data-view="unscheduled"] .schedp-detail {
            display: none;
        }
        .schedp-detail-empty {
            text-align: center;
            color: var(--dim);
            padding: 40px 12px;
        }
        .schedp-detail-empty-icon {
            font-size: 2rem;
            color: var(--bdr);
            margin-bottom: 10px;
        }
        .schedp-detail-empty-title {
            color: var(--white);
            font-weight: 700;
            font-size: 0.9375rem;
            margin-bottom: 8px;
        }
        .schedp-detail-empty-hint {
            font-size: 0.8125rem;
            line-height: 1.5;
        }
        .schedp-detail-body.hidden { display: none; }
        .schedp-detail-head {
            padding-bottom: 14px;
            margin-bottom: 14px;
            border-bottom: 1px solid var(--bdr);
        }
        .schedp-detail-jobnum {
            font-family: var(--ff-mono, "JetBrains Mono", monospace);
            font-weight: 700;
            font-size: 1.25rem;
            color: var(--white);
            letter-spacing: -0.01em;
        }
        .schedp-detail-customer {
            color: var(--white);
            opacity: 0.75;
            font-size: 0.875rem;
            margin-top: 2px;
            line-height: 1.3;
        }
        .schedp-detail-pills {
            display: flex; flex-wrap: wrap; gap: 6px;
            margin-top: 10px;
        }
        .schedp-detail-pills .schedp-rush-badge,
        .schedp-detail-pills .schedp-blocked-badge {
            margin: 0;
        }
        .schedp-detail-risk-row {
            font-family: var(--ff-mono, monospace);
            font-size: 0.75rem;
            color: var(--dim);
            padding: 10px 0 14px;
            margin-bottom: 12px;
            border-bottom: 1px dashed color-mix(in srgb, var(--bdr) 60%, transparent);
            display: flex; justify-content: space-between; align-items: baseline;
            gap: 10px;
        }
        .schedp-detail-field {
            margin-bottom: 14px;
        }
        .schedp-detail-label {
            display: block;
            font-size: 0.6875rem;
            color: var(--dim);
            text-transform: uppercase;
            letter-spacing: 0.06em;
            font-weight: 700;
            margin-bottom: 5px;
        }
        .schedp-detail-field input[type="date"],
        .schedp-detail-field input[type="number"] {
            width: 100%;
            background: var(--bg);
            border: 1px solid var(--bdr);
            border-radius: 6px;
            padding: 8px 10px;
            color: var(--white);
            font: inherit;
            font-size: 0.875rem;
            font-family: var(--ff-mono, monospace);
        }
        .schedp-detail-field input:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 1px;
            border-color: var(--acc);
        }
        .schedp-detail-clear {
            margin-top: 6px;
            background: transparent;
            border: none;
            color: var(--dim);
            font-size: 0.6875rem;
            cursor: pointer;
            padding: 4px 0;
            font-family: inherit;
        }
        .schedp-detail-clear:hover { color: var(--risk-late); text-decoration: underline; }
        .schedp-detail-toggle {
            display: flex;
            align-items: flex-start;
            gap: 8px;
            font-size: 0.8125rem;
            color: var(--white);
            cursor: pointer;
            line-height: 1.4;
        }
        .schedp-detail-toggle input[type="checkbox"] { margin-top: 2px; }
        .schedp-detail-actions {
            display: flex; flex-direction: column; gap: 8px;
            margin-top: 4px; padding-top: 14px;
            border-top: 1px solid var(--bdr);
        }
        .schedp-detail-actions .bg, .schedp-detail-actions .ba { width: 100%; }
        .schedp-detail-status {
            font-size: 0.75rem;
            min-height: 1em;
            margin-top: 10px;
            font-family: var(--ff-mono, monospace);
        }
        .schedp-detail-status.ok    { color: var(--risk-on-track); }
        .schedp-detail-status.error { color: var(--risk-late); }
        /* Mobile: collapse the rail into a fixed bottom drawer when
           narrower than 980px (tablet portrait, phone). The detail
           stays accessible without stealing screen real estate from
           the week grid. */
        @media (max-width: 980px) {
            #pg-schedule-v2[data-view="week"] .schedp-shell {
                grid-template-columns: 280px minmax(0, 1fr);
            }
            .schedp-detail {
                position: fixed;
                bottom: 0; left: 0; right: 0;
                top: auto;
                max-height: 60vh;
                border-radius: 14px 14px 0 0;
                box-shadow: 0 -8px 24px rgb(var(--shadow-ink) / 0.25);
                z-index: 50;
                margin: 0;
                /* Hide when no job is selected so the empty-state doesn't
                   occlude the week grid on mobile. JS toggles
                   .schedp-detail--has-selection when something is in. */
            }
            .schedp-detail:not(.schedp-detail--has-selection) {
                display: none;
            }
        }
        @media (max-width: 720px) {
            #pg-schedule-v2[data-view="week"] .schedp-shell {
                grid-template-columns: 1fr;
            }
        }
        /* ── /PR-SCHED2-6 ───────────────────────────────────────── */

        /* ── PR-SCHED2-4 (2026-05-24) — Month-view heat-map ───────
           Mon-Fri only (weekends omitted, matching shop policy).
           Each cell = capacity bar + count + one rush highlight.
           Drag intentionally disabled — this is a planning lens
           per cabinet-shop-ux research, not a scheduling surface.
           Click cell → jumps to week view at that week.
           --------------------------------------------------------- */
        .schedp-month.hidden { display: none; }
        .schedp-month {
            display: flex;
            flex-direction: column;
            gap: 18px;
            min-width: 0;
        }
        .schedp-month-head {
            display: flex; justify-content: space-between; align-items: baseline;
            padding-bottom: 10px;
            border-bottom: 2px solid var(--bdr);
        }
        .schedp-month-title {
            font-size: 1.0625rem;
            font-weight: 700;
            color: var(--white);
            letter-spacing: -0.01em;
        }
        .schedp-month-meta {
            font-size: 0.75rem;
            font-family: var(--ff-mono, monospace);
            color: var(--dim);
            font-weight: 600;
        }
        .schedp-month-weekrow-head {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 8px;
            font-size: 0.6875rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.08em;
            color: var(--dim);
            padding: 0 2px 6px;
            border-bottom: 1px solid color-mix(in srgb, var(--bdr) 60%, transparent);
        }
        .schedp-month-weekrow-head > span { text-align: left; }
        .schedp-month-grid {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            grid-auto-rows: minmax(110px, auto);
            gap: 8px;
        }
        /* Heat-map cell — paper card, ivory bg, restraint. Status
           lives in the capacity bar at the top + one optional
           highlight line. NEVER colors the whole cell (AsCut
           anti-flooding rule). */
        .schedp-month-cell {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 10px 12px;
            cursor: pointer;
            display: flex;
            flex-direction: column;
            gap: 6px;
            transition: border-color 140ms, transform 140ms;
            position: relative;
        }
        .schedp-month-cell:hover {
            border-color: var(--acc);
            transform: translateY(-1px);
        }
        .schedp-month-cell:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        /* Outside the displayed month (lead/trail Mon-Fri days of
           neighbor months) — render as flat dim placeholders so the
           5-col grid stays rectangular without implying they're
           clickable. */
        .schedp-month-cell.outside {
            background: transparent;
            border-color: transparent;
            cursor: default;
        }
        .schedp-month-cell.outside .schedp-month-celldate,
        .schedp-month-cell.outside .schedp-month-cellcount {
            opacity: 0.35;
        }
        .schedp-month-cell.outside:hover { transform: none; border-color: transparent; }
        /* Today gets the same 2px amber left border treatment as the
           week view's today column — visual continuity between views. */
        .schedp-month-cell.today {
            border-left: 2px solid var(--warn, #facc15);
            padding-left: 11px;
        }
        /* Past days dim down per UX research. */
        .schedp-month-cell.past { opacity: 0.62; }
        .schedp-month-cellrow1 {
            display: flex; justify-content: space-between; align-items: baseline;
            gap: 8px;
        }
        .schedp-month-celldate {
            font-family: var(--ff-mono, monospace);
            font-weight: 700;
            color: var(--white);
            font-size: 0.9375rem;
            letter-spacing: -0.01em;
        }
        .schedp-month-cellcount {
            font-size: 0.6875rem;
            color: var(--dim);
            font-family: var(--ff-mono, monospace);
            font-weight: 600;
        }
        .schedp-month-cellcount.has-jobs {
            color: var(--white);
        }
        /* 4px capacity bar — same color scheme as the week view's
           .schedp-day-cap so the visual language is identical across
           views. Cyan when under, amber at limit, rose when over. */
        .schedp-month-cellcap {
            height: 4px;
            border-radius: 2px;
            background: color-mix(in srgb, var(--acc) 35%, transparent);
        }
        .schedp-month-cellcap.near {
            background: color-mix(in srgb, var(--warn, #facc15) 55%, transparent);
        }
        .schedp-month-cellcap.over {
            background: var(--risk-late);
        }
        .schedp-month-cell.over-cap {
            border: 1.5px dashed var(--risk-late);
        }
        /* Single highlight line — top rush job for the day. Truncates
           hard at ~20 chars so cells stay uniform height. */
        .schedp-month-cellrush {
            font-size: 0.6875rem;
            color: var(--risk-late);
            font-weight: 600;
            line-height: 1.2;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            display: flex; align-items: center; gap: 4px;
            cursor: pointer;
        }
        .schedp-month-cellrush::before {
            content: "▲";
            font-size: 0.5rem;
            color: var(--risk-late);
        }
        .schedp-month-cellblocked {
            position: absolute;
            top: 8px; right: 10px;
            font-size: 0.6875rem;
            color: var(--risk-at-risk);
        }
        .schedp-month-empty {
            text-align: center;
            color: var(--dim);
            font-style: italic;
            padding: 60px 20px;
        }
        /* Read-only banner — month view never edits via drag. */
        .schedp-month-hint {
            color: var(--dim);
            font-size: 0.75rem;
            text-align: center;
            padding: 12px 0 0;
            font-style: italic;
        }
        /* ── /PR-SCHED2-4 ───────────────────────────────────────── */

        /* Inline edit popover — floats over the page, positioned relative
           to the clicked card by JS. */
        .schedp-edit-popover {
            position: fixed;
            z-index: 200;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            box-shadow: 0 10px 32px rgb(var(--shadow-ink) / 0.35);
            padding: 18px 20px 16px;
            width: 360px;
            max-width: calc(100vw - 24px);
        }
        .schedp-edit-popover.hidden { display: none; }
        .schedp-edit-close {
            position: absolute; top: 8px; right: 8px;
            background: transparent; border: none; color: var(--dim);
            font-size: 1rem; cursor: pointer; padding: 4px 8px;
        }
        .schedp-edit-close:hover { color: var(--white); }
        .schedp-edit-title {
            font-weight: 700; font-size: 1rem; color: var(--white);
            padding-right: 28px;
        }
        .schedp-edit-sub {
            color: var(--dim); font-size: 0.75rem;
            font-family: var(--ff-mono, monospace);
            margin-bottom: 14px; padding-bottom: 10px;
            border-bottom: 1px solid var(--bdr);
        }
        .schedp-edit-field {
            display: flex; flex-direction: column; gap: 4px;
            margin-bottom: 12px;
        }
        .schedp-edit-label {
            font-size: 0.6875rem; font-weight: 700;
            letter-spacing: 0.06em; text-transform: uppercase;
            color: var(--dim);
        }
        .schedp-edit-field input[type="date"],
        .schedp-edit-field input[type="number"] {
            background: var(--bg); color: var(--white);
            border: 1px solid var(--bdr);
            padding: 6px 9px; border-radius: 5px;
            font-size: 0.875rem; font-family: inherit;
        }
        .schedp-edit-field input:focus {
            outline: none; border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
        }
        .schedp-edit-toggle {
            display: flex; align-items: center; gap: 8px;
            color: var(--white); font-size: 0.8125rem;
            cursor: pointer;
        }
        .schedp-edit-clear {
            background: transparent; border: none; color: var(--acc);
            font-size: 0.6875rem; padding: 2px 0; cursor: pointer;
            text-align: left; font-family: inherit;
            text-decoration: underline;
        }
        .schedp-edit-actions {
            display: flex; gap: 8px; margin-top: 14px;
            padding-top: 12px;
            border-top: 1px solid var(--bdr);
        }
        .schedp-edit-status {
            font-size: 0.6875rem;
            color: var(--dim);
            margin-top: 6px;
            min-height: 14px;
            text-align: right;
        }
        .schedp-edit-status.ok { color: var(--risk-on-track); }
        .schedp-edit-status.error { color: var(--risk-late); }

        @media print {
            .schedp-no-print, .sidebar, .schedp-tray-hint,
            .schedp-edit-popover { display: none !important; }
            body * { visibility: hidden; }
            #pg-schedule-v2, #pg-schedule-v2 * { visibility: visible; }
            #pg-schedule-v2 { position: absolute; left: 0; top: 0; width: 100%; }
        }

        @media (max-width: 980px) {
            .schedp-shell {
                grid-template-columns: 1fr;
            }
            .schedp-tray { position: static; max-height: 240px; }
        }
        /* ── /PR-SCHED-FULLPAGE ─────────────────────────────────────────── */

        /* ── PR-DS-SCHED-PAPER (2026-05-25) — paper-ticket aesthetic ────────
           Operator: "make it look like the shop floor ui". Reuses the
           --st-* tokens that shop-floor-v2.html uses (ivory paper on
           slate canvas, JetBrains Mono dims, oxblood RUSH stamps).
           Scoped to #pg-schedule-v2 so the rest of the app keeps the
           AsCut dark-navy theme. */
        #pg-schedule-v2 {
            /* PR-FORGE-CAB-SCHEDULE (2026-06-13): the paper-ticket palette
               (ivory on slate) was the single most jarring off-home seam —
               Schedule is reached straight from the home KPI strip yet
               rendered as a beige page while everything else is dark forge.
               Remap the --st-* tokens onto forge tokens so it reads as a
               DARK ticket (same structure, forge colors). Status stamps map
               to forge semantic colors so RUSH/late stay legible on dark. */
            --st-canvas:        var(--bg);
            --st-paper:         var(--pn);
            --st-ink:           var(--txt);
            --st-ink-dim:       var(--dim);
            --st-rule:          var(--bdr);
            --st-oxblood:       var(--no);
            --st-amber:         var(--warn);
            --st-rose:          var(--no);
            --st-emerald:       var(--ok);
            --st-cyan:          var(--acc);
            /* PR-LIB-COLOR-TOKENS (2026-05-25): aliases for the names
               shop-floor.js references but that previously only existed in
               shop-floor-legacy.html — keeps every paper-aesthetic var()
               resolving cleanly on every surface that loads shop-floor.js. */
            --st-forest:        var(--st-emerald);
            --st-forest-d:      var(--st-emerald);
            --st-oxblood-d:     var(--st-oxblood);
            --st-paper-edge:    var(--st-rule);
            --st-ink-soft:      var(--st-ink-dim);
            background: var(--st-canvas);
        }
        /* Day column → ivory paper card on the slate canvas. */
        #pg-schedule-v2 .schedp-day {
            background: var(--st-paper);
            color: var(--st-ink);
            border: 1px solid var(--st-rule);
            border-radius: 4px;
            box-shadow: 0 2px 6px rgb(var(--shadow-ink) / 0.3);
            padding: 14px 12px;
        }
        #pg-schedule-v2 .schedp-day.today {
            border-left: 6px solid var(--st-oxblood);
        }
        #pg-schedule-v2 .schedp-day-name {
            color: var(--st-cyan);
            font-family: 'JetBrains Mono', monospace;
            font-weight: 700;
            text-transform: uppercase;
        }
        #pg-schedule-v2 .schedp-day-date {
            color: var(--st-ink-dim);
            font-family: 'JetBrains Mono', monospace;
            font-size: 12px;
        }
        #pg-schedule-v2 .schedp-day-count {
            background: var(--st-ink);
            color: var(--st-paper);
            font-family: 'JetBrains Mono', monospace;
            font-weight: 700;
            border-radius: 999px;
            padding: 2px 10px;
            font-size: 12px;
        }
        #pg-schedule-v2 .schedp-day-count.over-cap {
            background: var(--st-oxblood);
            color: #fff;
        }
        /* Job card within a day → small paper ticket with monospace job#. */
        #pg-schedule-v2 .schedp-card {
            background: #fff;
            color: var(--st-ink);
            border: 1px solid var(--st-rule);
            border-left: 3px solid var(--st-ink);
            border-radius: 3px;
            padding: 10px 12px;
            box-shadow: 0 1px 2px rgb(var(--shadow-ink) / 0.15);
        }
        #pg-schedule-v2 .schedp-card:hover {
            background: #fffdf6;
            border-left-color: var(--st-cyan);
        }
        #pg-schedule-v2 .schedp-card.rush {
            border-left: 4px solid var(--st-oxblood);
            background: #fff5f5;
        }
        /* PR-SCHED-URGENCY (2026-08-09): 🔴/🟡/🟢 band on the paper-ticket
           variant. The left stripe is the ticket's own mark here, so
           urgency frames the top/right/bottom in the station palette. */
        #pg-schedule-v2 .schedp-card.urgency-rush {
            border-top: 2px solid var(--st-oxblood);
            border-right: 2px solid var(--st-oxblood);
            border-bottom: 2px solid var(--st-oxblood);
        }
        #pg-schedule-v2 .schedp-card.urgency-standard {
            border-top: 2px solid var(--st-amber);
            border-right: 2px solid var(--st-amber);
            border-bottom: 2px solid var(--st-amber);
        }
        #pg-schedule-v2 .schedp-card.urgency-flexible {
            border-top: 2px solid var(--st-emerald);
            border-right: 2px solid var(--st-emerald);
            border-bottom: 2px solid var(--st-emerald);
        }
        #pg-schedule-v2 .schedp-card-jobnum {
            color: var(--st-ink);
            font-family: 'JetBrains Mono', monospace;
            font-weight: 700;
        }
        #pg-schedule-v2 .schedp-card-jobnum .schedp-jn {
            color: var(--st-ink);
        }
        #pg-schedule-v2 .schedp-card-customer {
            color: var(--st-ink-dim);
            font-weight: 600;
        }
        #pg-schedule-v2 .schedp-card-meta,
        #pg-schedule-v2 .schedp-card-pipeline {
            color: var(--st-ink-dim);
            font-family: 'JetBrains Mono', monospace;
            font-size: 11px;
        }
        /* RUSH stamp — oxblood rectangle in JetBrains Mono. */
        #pg-schedule-v2 .schedp-card .schedp-rush-tag,
        #pg-schedule-v2 .schedp-rush-tag {
            background: var(--st-oxblood);
            color: #fff;
            font-family: 'JetBrains Mono', monospace;
            font-weight: 700;
            font-size: 10px;
            letter-spacing: 0.08em;
            padding: 2px 6px;
            border-radius: 2px;
        }
        /* Risk badges → paper-ticket colors instead of dashboard accents. */
        #pg-schedule-v2 .schedp-card-risk.on_track  { color: var(--st-emerald); }
        #pg-schedule-v2 .schedp-card-risk.at_risk   { color: var(--st-amber); }
        #pg-schedule-v2 .schedp-card-risk.late      { color: var(--st-oxblood); }
        #pg-schedule-v2 .schedp-card-risk.past_due  { color: var(--st-rose); }
        /* Unscheduled tray → also a paper card with a yellow header strip
           (matches the "STOP — needs scheduling" feel). */
        #pg-schedule-v2 .schedp-tray {
            background: var(--st-paper);
            color: var(--st-ink);
            border: 1px solid var(--st-rule);
            border-radius: 4px;
            box-shadow: 0 2px 6px rgb(var(--shadow-ink) / 0.3);
        }
        #pg-schedule-v2 .schedp-tray-head {
            color: var(--st-ink);
            border-bottom: 1px solid var(--st-rule);
        }
        #pg-schedule-v2 .schedp-tray-count {
            background: var(--st-oxblood);
            color: #fff;
            border: 0;
            font-family: 'JetBrains Mono', monospace;
        }
        #pg-schedule-v2 .schedp-tray-empty,
        #pg-schedule-v2 .schedp-tray-hint {
            color: var(--st-ink-dim);
        }
        #pg-schedule-v2 .schedp-week-head {
            border-bottom: 2px solid var(--st-paper);
        }
        #pg-schedule-v2 .schedp-week-title {
            color: var(--st-paper);
            font-family: 'JetBrains Mono', monospace;
        }
        #pg-schedule-v2 .schedp-week-meta {
            color: var(--st-ink-dim);
            font-family: 'JetBrains Mono', monospace;
        }
        #pg-schedule-v2 .schedp-week.current .schedp-week-head {
            border-bottom-color: var(--st-cyan);
        }
        /* ── /PR-DS-SCHED-PAPER ──────────────────────────────────────────── */

        /* ── PR-IPD-2 (2026-05-23) — In Production full page ────────────── */
        /* Sibling visual language to pg-schedule (paper card + oxblood
           rush border + monospace dims + risk dot palette). Class prefix
           prodp-* mirrors the schedp-* convention. */
        #pg-production { padding: 0; }
        #pg-production .hub-header { padding: 18px 24px 0; }

        .prodp-toolbar {
            padding: 12px 24px 8px;
            display: flex; flex-direction: column; gap: 10px;
            border-bottom: 1px solid var(--bdr);
            background: var(--bg);
        }
        .prodp-search {
            position: relative;
            display: flex; align-items: center;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 0 12px;
            transition: border-color 120ms, box-shadow 120ms;
        }
        .prodp-search:focus-within {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        .prodp-search-icon {
            font-size: 0.9rem; color: var(--dim);
            flex: 0 0 auto; padding-right: 8px;
        }
        .prodp-search-input {
            flex: 1; min-width: 0;
            background: transparent; border: none; outline: none;
            color: var(--white);
            font-size: 0.9375rem;
            padding: 10px 0;
            font-family: inherit;
        }
        .prodp-search-input::placeholder { color: var(--dim); }
        .prodp-search-clear {
            background: transparent; border: none; color: var(--dim);
            font-size: 0.875rem; cursor: pointer; padding: 4px 6px;
            border-radius: 4px;
        }
        .prodp-search-clear:hover { color: var(--white); background: var(--bg); }
        .prodp-search-clear.hidden { display: none; }

        .prodp-chips {
            display: flex; flex-wrap: wrap; gap: 6px;
        }
        .prodp-chip {
            background: var(--pn);
            border: 1px solid var(--bdr);
            color: var(--white);
            font-size: 0.75rem; font-weight: 600;
            padding: 5px 11px; border-radius: 999px;
            cursor: pointer;
            display: inline-flex; align-items: center; gap: 6px;
            transition: border-color 120ms, background 120ms;
            font-family: inherit;
        }
        .prodp-chip:hover { border-color: var(--acc); }
        .prodp-chip.on {
            background: var(--acc); color: #fff;
            border-color: var(--acc);
        }
        .prodp-chip-n {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; font-weight: 700;
            opacity: 0.8;
        }
        .prodp-chip.on .prodp-chip-n { opacity: 1; }

        .prodp-shell {
            display: grid;
            grid-template-columns: minmax(320px, 1fr) minmax(380px, 1.4fr);
            gap: 16px;
            padding: 12px 24px 24px;
            min-height: calc(100vh - 260px);
        }
        .prodp-list {
            display: flex; flex-direction: column; gap: 6px;
            max-height: calc(100vh - 280px);
            overflow-y: auto;
            padding-right: 4px; /* breathing room next to scrollbar */
        }
        .prodp-loading,
        .prodp-detail-empty,
        .prodp-list-empty {
            color: var(--dim); font-style: italic;
            text-align: center; padding: 32px 16px;
            font-size: 0.875rem;
        }

        /* Job card — kinship with schedp-card but more vertical real
           estate because the office is reading not scheduling. */
        .prodp-card {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 10px 12px;
            cursor: pointer;
            transition: border-color 120ms, transform 120ms, box-shadow 120ms;
            position: relative;
        }
        .prodp-card:hover { border-color: var(--bdr-h); background: var(--pn-h); } /* PR-FORGE-CAB-PRODUCTION: flat hover */
        .prodp-card:focus-visible {
            outline: none; border-color: var(--acc);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 30%, transparent);
        }
        .prodp-card.selected {
            border-color: var(--acc);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 28%, transparent);
        }
        .prodp-card.rush { border-left: 3px solid var(--risk-late); padding-left: 11px; }

        .prodp-card-head {
            display: flex; align-items: baseline; justify-content: space-between;
            gap: 8px;
        }
        .prodp-card-customer {
            font-size: 0.9375rem; font-weight: 700; color: var(--white);
            line-height: 1.25;
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            flex: 1; min-width: 0;
        }
        .prodp-card-jobnum {
            font-family: var(--ff-mono, "JetBrains Mono", monospace);
            font-size: 0.8125rem; font-weight: 700; color: var(--dim);
            flex: 0 0 auto;
        }
        .prodp-card-meta {
            display: flex; flex-wrap: wrap; gap: 4px 10px;
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
            margin-top: 3px;
        }
        .prodp-card-mat {
            display: flex; flex-wrap: wrap; gap: 4px;
            margin-top: 6px;
        }
        .prodp-card-mat-pill {
            background: var(--bg); border: 1px solid var(--bdr);
            color: var(--white);
            font-size: 0.6875rem; font-weight: 500;
            padding: 2px 7px; border-radius: 999px;
            font-family: var(--ff-mono, monospace);
        }
        .prodp-card-badges {
            display: flex; flex-wrap: wrap; gap: 5px;
            margin-top: 6px;
            align-items: center;
            font-size: 0.625rem;
        }
        .prodp-card-tracked {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
            margin-left: auto;
        }

        /* Risk + status badges. Same palette as schedp- so the
           office's eye carries learned meaning across the two pages. */
        .prodp-risk-pill {
            font-family: var(--ff-mono, monospace);
            font-weight: 700; letter-spacing: 0.05em;
            font-size: 0.625rem; text-transform: uppercase;
            padding: 1px 5px; border-radius: 3px;
            border: 1px solid transparent;
        }
        .prodp-risk-pill.on_track     { color: var(--risk-on-track); border-color: var(--risk-on-track); }
        .prodp-risk-pill.at_risk      { color: var(--risk-at-risk); border-color: var(--risk-at-risk); }
        .prodp-risk-pill.late         { color: var(--risk-late); border-color: var(--risk-late); }
        .prodp-risk-pill.already_late { color: var(--risk-late); border-color: var(--risk-late);
                                         background: color-mix(in srgb, var(--risk-late) 14%, transparent); }
        .prodp-risk-pill.unknown      { color: var(--dim); border-color: var(--bdr); }

        .prodp-risk-dot { font-size: 1rem; line-height: 1; }
        .prodp-risk-dot.on_track     { color: var(--risk-on-track); }
        .prodp-risk-dot.at_risk      { color: var(--risk-at-risk); }
        .prodp-risk-dot.late         { color: var(--risk-late); }

        .prodp-rush-pill {
            background: var(--risk-late); color: #fff;
            font-weight: 700; letter-spacing: 0.05em;
            font-size: 0.625rem; text-transform: uppercase;
            padding: 1px 5px; border-radius: 3px;
        }
        .prodp-blocked-pill {
            background: var(--warn, #facc15); color: #422006;
            font-weight: 700; letter-spacing: 0.05em;
            font-size: 0.625rem; text-transform: uppercase;
            padding: 1px 5px; border-radius: 3px;
        }

        .prodp-card-workers {
            display: flex; flex-wrap: wrap; gap: 5px;
            margin-top: 6px;
        }
        .prodp-card-worker {
            background: color-mix(in srgb, #facc15 18%, var(--bg));
            border: 1px solid color-mix(in srgb, #facc15 35%, var(--bdr));
            color: var(--white);
            font-size: 0.6875rem;
            padding: 2px 8px; border-radius: 999px;
            font-family: var(--ff-mono, monospace);
            display: inline-flex; align-items: center; gap: 4px;
        }
        .prodp-card-worker-at  { color: var(--dim); font-weight: 500; }
        .prodp-card-worker-stn { color: var(--white); font-weight: 700; }
        .prodp-card-noworker {
            font-size: 0.6875rem; color: var(--dim); font-style: italic;
            margin-top: 6px;
        }

        /* The 7-phase progress strip is rendered by the existing
           _ipdRenderProgress helper (ipd-prog-* classes). We only need
           to size it down for the card density. */
        .prodp-card .ipd-prog { margin-top: 8px; }
        .prodp-card .ipd-prog-bar { height: 6px; }

        /* ── Detail pane ──────────────────────────────────────────── */
        .prodp-detail {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 18px 20px;
            position: sticky;
            top: 16px;
            max-height: calc(100vh - 280px);
            overflow-y: auto;
            align-self: start;
        }
        .prodp-d-head {
            display: flex; flex-direction: column; gap: 4px;
            padding-bottom: 12px;
            border-bottom: 1px solid var(--bdr);
            margin-bottom: 14px;
        }
        .prodp-d-customer-row {
            display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
        }
        .prodp-d-customer {
            font-size: 1.25rem; font-weight: 700; color: var(--white);
            line-height: 1.2;
        }
        .prodp-d-jobnum {
            font-family: var(--ff-mono, monospace);
            font-size: 0.875rem; color: var(--dim);
            font-weight: 700;
        }
        .prodp-d-sub {
            font-family: var(--ff-mono, monospace);
            font-size: 0.75rem; color: var(--dim);
        }
        .prodp-d-badges {
            display: flex; flex-wrap: wrap; gap: 6px;
            margin-top: 4px;
        }

        .prodp-d-section {
            padding-top: 12px;
            margin-top: 12px;
            border-top: 1px dashed var(--bdr);
        }
        .prodp-d-section:first-of-type {
            border-top: none; margin-top: 0; padding-top: 0;
        }
        .prodp-d-section-h {
            font-size: 0.6875rem; font-weight: 700;
            letter-spacing: 0.06em; text-transform: uppercase;
            color: var(--dim);
            margin-bottom: 8px;
        }
        .prodp-d-grid {
            display: grid;
            grid-template-columns: max-content 1fr;
            gap: 4px 14px;
            font-size: 0.8125rem;
            color: var(--white);
        }
        .prodp-d-grid dt {
            color: var(--dim);
            font-size: 0.6875rem;
            font-weight: 700;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            align-self: center;
        }
        .prodp-d-grid dd { margin: 0; }
        .prodp-d-grid dd.mono {
            font-family: var(--ff-mono, monospace);
            font-size: 0.8125rem;
        }
        .prodp-d-grid dd.muted { color: var(--dim); font-style: italic; }
        .prodp-d-grid dd a { color: var(--acc); text-decoration: none; }
        .prodp-d-grid dd a:hover { text-decoration: underline; }

        .prodp-d-workflow {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
            gap: 6px 12px;
            font-size: 0.75rem;
        }
        .prodp-d-wf-item {
            display: flex; align-items: baseline; gap: 6px;
            color: var(--white);
        }
        .prodp-d-wf-item.pending { color: var(--dim); }
        .prodp-d-wf-mark { font-family: var(--ff-mono, monospace); }
        .prodp-d-wf-mark.done    { color: var(--risk-on-track); }
        .prodp-d-wf-mark.pending { color: var(--dim); }
        .prodp-d-wf-date {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
            margin-left: auto;
        }

        .prodp-d-workers {
            display: flex; flex-direction: column; gap: 4px;
        }
        .prodp-d-worker {
            display: flex; align-items: baseline; gap: 8px;
            font-size: 0.8125rem; color: var(--white);
        }
        .prodp-d-worker-dot {
            width: 8px; height: 8px; border-radius: 50%;
            background: #facc15;
            display: inline-block;
            box-shadow: 0 0 0 0 color-mix(in srgb, #facc15 50%, transparent);
            animation: prodp-pulse 1.4s ease-in-out infinite;
            flex: 0 0 auto;
        }
        @keyframes prodp-pulse {
            0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #facc15 50%, transparent); }
            50%       { box-shadow: 0 0 0 5px color-mix(in srgb, #facc15 0%, transparent); }
        }
        .prodp-d-worker-name { font-weight: 700; }
        .prodp-d-worker-at   { color: var(--dim); font-family: var(--ff-mono, monospace); font-size: 0.6875rem; }
        .prodp-d-worker-stn  { font-family: var(--ff-mono, monospace); font-size: 0.75rem; color: var(--white); }
        .prodp-d-worker-dur  {
            margin-left: auto;
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
        }

        .prodp-d-actions {
            display: flex; gap: 8px; flex-wrap: wrap;
            margin-top: 14px;
            padding-top: 14px;
            border-top: 1px solid var(--bdr);
        }

        /* ── PR-IPD-3 (2026-05-23) — Inline per-station timeline + worker
              aggregate. Loads async after the row is selected. ──────── */
        .prodp-d-timeline-pending {
            color: var(--dim); font-style: italic; font-size: 0.75rem;
            padding: 6px 0;
        }
        .prodp-d-timeline-error {
            color: var(--risk-late); font-size: 0.75rem;
            padding: 6px 0;
        }
        .prodp-d-station {
            margin-bottom: 14px;
        }
        .prodp-d-station:last-child { margin-bottom: 0; }
        .prodp-d-station-head {
            display: flex; align-items: baseline; justify-content: space-between;
            padding: 4px 0;
            border-bottom: 1px solid var(--bdr);
            margin-bottom: 4px;
        }
        .prodp-d-station-name {
            font-size: 0.8125rem; font-weight: 700;
            color: var(--white);
            letter-spacing: 0.02em;
        }
        .prodp-d-station-tot {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
        }
        .prodp-d-station-open {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; font-weight: 700;
            color: var(--risk-at-risk);
        }
        .prodp-d-entries {
            display: flex; flex-direction: column; gap: 2px;
            padding-left: 2px;
        }
        .prodp-d-entry {
            display: grid;
            grid-template-columns: 14px max-content 1fr max-content;
            align-items: baseline; gap: 8px;
            font-size: 0.75rem;
            color: var(--white);
            padding: 2px 0;
        }
        .prodp-d-entry.open {
            background: color-mix(in srgb, #facc15 8%, transparent);
            margin: 0 -4px;
            padding-left: 4px; padding-right: 4px;
            border-radius: 4px;
        }
        .prodp-d-entry-mark { color: var(--dim); text-align: center; }
        .prodp-d-entry-worker { font-weight: 600; }
        .prodp-d-entry-when {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--dim);
        }
        .prodp-d-entry-dur {
            font-family: var(--ff-mono, monospace);
            font-size: 0.6875rem; color: var(--white);
            justify-self: end;
        }
        .prodp-d-redo {
            font-size: 0.6875rem;
            color: var(--risk-at-risk);
            margin: 2px 0 4px 22px;
            padding: 3px 8px;
            background: color-mix(in srgb, var(--risk-at-risk) 10%, var(--pn));
            border-left: 2px solid var(--risk-at-risk);
            border-radius: 0 4px 4px 0;
        }
        .prodp-d-redo-tag {
            font-weight: 700; letter-spacing: 0.06em;
            font-family: var(--ff-mono, monospace);
            font-size: 0.625rem;
            margin-right: 6px;
            text-transform: uppercase;
        }

        .prodp-d-bywkr-row {
            display: grid;
            grid-template-columns: 14px max-content 1fr max-content;
            align-items: center; gap: 8px;
            font-size: 0.8125rem;
            color: var(--white);
            padding: 4px 0;
        }
        .prodp-d-bywkr-row.active {
            background: color-mix(in srgb, #facc15 8%, transparent);
            margin: 0 -4px;
            padding-left: 4px; padding-right: 4px;
            border-radius: 4px;
        }
        .prodp-d-bywkr-spacer { width: 8px; height: 8px; }
        .prodp-d-bywkr-name { font-weight: 700; }
        .prodp-d-bywkr-stations {
            display: flex; flex-wrap: wrap; gap: 3px;
            min-width: 0;
        }
        .prodp-d-bywkr-stn {
            background: var(--bg);
            border: 1px solid var(--bdr);
            font-family: var(--ff-mono, monospace);
            font-size: 0.625rem;
            color: var(--dim);
            padding: 1px 6px;
            border-radius: 999px;
        }
        .prodp-d-bywkr-total {
            font-family: var(--ff-mono, monospace);
            font-size: 0.75rem;
            color: var(--white);
            font-weight: 700;
            justify-self: end;
        }

        /* Legend strip — bottom of page, mirrors schedp legend pattern. */
        .prodp-legend {
            display: flex; flex-wrap: wrap; gap: 14px;
            padding: 10px 24px 18px;
            border-top: 1px solid var(--bdr);
            font-size: 0.6875rem;
            color: var(--dim);
            background: var(--bg);
        }
        .prodp-legend-item {
            display: inline-flex; align-items: center; gap: 6px;
        }
        .prodp-legend-hint { margin-left: auto; font-style: italic; }

        @media print {
            .prodp-no-print, .sidebar, .prodp-toolbar,
            .prodp-legend { display: none !important; }
            body * { visibility: hidden; }
            #pg-production, #pg-production * { visibility: visible; }
            #pg-production { position: absolute; left: 0; top: 0; width: 100%; }
            .prodp-shell { grid-template-columns: 1fr 1fr; }
            .prodp-detail { position: static; max-height: none; }
            .prodp-list { max-height: none; }
        }

        @media (max-width: 980px) {
            .prodp-shell { grid-template-columns: 1fr; }
            .prodp-detail { position: static; max-height: none; }
            .prodp-list   { max-height: 50vh; }
        }
        /* ── /PR-IPD-2 ──────────────────────────────────────────────────── */

        /* Hub right-rail content — refined typography for theme + units cards. */
        body[data-ui-version="ascut"] #hub-rail-themes,
        body[data-ui-version="ascut"] #hub-rail-units {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            padding: 16px;
        }

        /* Tab content slot — gentle vertical rhythm. */
        body[data-ui-version="ascut"] #pg-hub .tc {
            padding-top: 4px;
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .hub-page-header .hub-primary-action,
            body[data-ui-version="ascut"] .hub-studio-shortcut {
                transition: none;
            }
            body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:hover,
            body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:focus-visible {
                transform: none;
            }
            body[data-ui-version="ascut"] .hub-page-header .hub-primary-action:active,
            body[data-ui-version="ascut"] .hub-studio-shortcut:active {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-HUB ─────────────────────────────────────────────── */

        /* ── PR-ASCUT-FORMS — buttons, inputs, toasts ─────────────────── */
        /* Primary button (.ba) — accent gradient + drop shadow + scale press */
        body[data-ui-version="ascut"] .ba {
            background: linear-gradient(135deg,
                var(--acc) 0%,
                color-mix(in srgb, var(--acc) 80%, var(--acc2)) 100%);
            color: var(--acc-ink);
            border: 1px solid color-mix(in srgb, var(--acc) 55%, var(--acc2));
            border-radius: 8px;
            font-weight: 600;
            letter-spacing: 0.01em;
            box-shadow: 0 2px 8px color-mix(in srgb, var(--acc) 22%, transparent);
            transition: filter 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .ba:hover:not(:disabled) {
            filter: brightness(1.08);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 35%, transparent);
        }
        body[data-ui-version="ascut"] .ba:active:not(:disabled) {
            transform: scale(0.98);
        }
        body[data-ui-version="ascut"] .ba:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        body[data-ui-version="ascut"] .ba:disabled {
            opacity: 0.5;
            cursor: not-allowed;
            box-shadow: none;
        }

        /* Ghost button (.bg) — neutral surface, accent hover */
        body[data-ui-version="ascut"] .bg {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            color: var(--txt);
            font-weight: 600;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .bg:hover:not(:disabled) {
            background: color-mix(in srgb, var(--acc) 8%, var(--pn));
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            color: var(--acc);
        }
        body[data-ui-version="ascut"] .bg:active:not(:disabled) {
            transform: scale(0.98);
        }
        body[data-ui-version="ascut"] .bg:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }

        /* Semantic buttons — ok/danger/warn — refined gradient + drop shadow */
        body[data-ui-version="ascut"] .bk {
            background: linear-gradient(135deg,
                var(--ok) 0%,
                color-mix(in srgb, var(--ok) 80%, #000) 100%);
            color: var(--ok-ink, #000);
            border: 1px solid color-mix(in srgb, var(--ok) 55%, var(--bdr));
            border-radius: 8px;
            font-weight: 600;
            box-shadow: 0 2px 8px color-mix(in srgb, var(--ok) 22%, transparent);
        }
        body[data-ui-version="ascut"] .bk:hover:not(:disabled) {
            filter: brightness(1.08);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--ok) 35%, transparent);
        }

        body[data-ui-version="ascut"] .br {
            background: linear-gradient(135deg,
                var(--no) 0%,
                color-mix(in srgb, var(--no) 80%, #000) 100%);
            color: #fff;
            border: 1px solid color-mix(in srgb, var(--no) 55%, var(--bdr));
            border-radius: 8px;
            font-weight: 600;
            box-shadow: 0 2px 8px color-mix(in srgb, var(--no) 22%, transparent);
        }
        body[data-ui-version="ascut"] .br:hover:not(:disabled) {
            filter: brightness(1.08);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--no) 35%, transparent);
        }

        body[data-ui-version="ascut"] .bw {
            background: linear-gradient(135deg,
                var(--warn) 0%,
                color-mix(in srgb, var(--warn) 80%, #000) 100%);
            color: #1a1410;
            border: 1px solid color-mix(in srgb, var(--warn) 55%, var(--bdr));
            border-radius: 8px;
            font-weight: 600;
            box-shadow: 0 2px 8px color-mix(in srgb, var(--warn) 22%, transparent);
        }

        /* Form inputs — refined focus rings; sets the input/select/textarea
           interaction language for the entire app. */
        body[data-ui-version="ascut"] input[type="text"],
        body[data-ui-version="ascut"] input[type="password"],
        body[data-ui-version="ascut"] input[type="email"],
        body[data-ui-version="ascut"] input[type="number"],
        body[data-ui-version="ascut"] input[type="search"],
        body[data-ui-version="ascut"] input[type="tel"],
        body[data-ui-version="ascut"] input[type="url"],
        body[data-ui-version="ascut"] input:not([type]),
        body[data-ui-version="ascut"] select,
        body[data-ui-version="ascut"] textarea {
            border-radius: 8px;
            transition: border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] input[type="text"]:focus,
        body[data-ui-version="ascut"] input[type="password"]:focus,
        body[data-ui-version="ascut"] input[type="email"]:focus,
        body[data-ui-version="ascut"] input[type="number"]:focus,
        body[data-ui-version="ascut"] input[type="search"]:focus,
        body[data-ui-version="ascut"] input[type="tel"]:focus,
        body[data-ui-version="ascut"] input[type="url"]:focus,
        body[data-ui-version="ascut"] input:not([type]):focus,
        body[data-ui-version="ascut"] select:focus,
        body[data-ui-version="ascut"] textarea:focus {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent);
            outline: none;
        }

        /* Toasts — refined depth, accent-tinted left edge by variant */
        body[data-ui-version="ascut"] .toast {
            border-radius: 12px;
            backdrop-filter: blur(12px) saturate(140%);
            -webkit-backdrop-filter: blur(12px) saturate(140%);
            background: color-mix(in srgb, var(--toast-bg, var(--pn)) 92%, transparent);
            box-shadow:
                0 16px 48px rgb(var(--shadow-ink) / 0.55),
                inset 0 1px 0 color-mix(in srgb, var(--acc) 18%, transparent);
            font-weight: 500;
            font-size: 13px;
        }
        body[data-ui-version="ascut"] .toast.toast-ok,
        body[data-ui-version="ascut"] .toast.ok {
            border-left-color: var(--ok);
        }
        body[data-ui-version="ascut"] .toast.toast-warn,
        body[data-ui-version="ascut"] .toast.warn {
            border-left-color: var(--warn);
        }
        body[data-ui-version="ascut"] .toast.toast-err,
        body[data-ui-version="ascut"] .toast.err {
            border-left-color: var(--no);
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .ba,
            body[data-ui-version="ascut"] .bg,
            body[data-ui-version="ascut"] input[type="text"],
            body[data-ui-version="ascut"] input[type="password"],
            body[data-ui-version="ascut"] input[type="email"],
            body[data-ui-version="ascut"] input[type="number"],
            body[data-ui-version="ascut"] input[type="search"],
            body[data-ui-version="ascut"] select,
            body[data-ui-version="ascut"] textarea {
                transition: none;
            }
            body[data-ui-version="ascut"] .ba:active:not(:disabled),
            body[data-ui-version="ascut"] .bg:active:not(:disabled) {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-FORMS ───────────────────────────────────────────── */

        /* ── PR-ASCUT-DRAWERS — drawer chrome + inspector badges ──────── */
        /* Drawer h2 / subtitle — refined typography */
        body[data-ui-version="ascut"] .drawer h2 {
            color: var(--txt);
            font-size: 22px;
            font-weight: 700;
            letter-spacing: -0.01em;
            line-height: 1.2;
            margin: 0 0 8px 0;
        }
        body[data-ui-version="ascut"] .drawer .sub {
            color: var(--dim);
            font-size: 13px;
            font-weight: 400;
            line-height: 1.5;
            margin: 0 0 24px 0;
            max-width: 60ch;
        }
        /* Drawer close button — refined hover with accent halo */
        body[data-ui-version="ascut"] .drawer-close {
            border-radius: 10px;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .drawer-close:hover {
            background: color-mix(in srgb, var(--no) 14%, transparent);
            color: var(--no);
        }
        body[data-ui-version="ascut"] .drawer-close:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }

        /* Inspector badges (.insp-inherits, .insp-overridden) — refined pills */
        body[data-ui-version="ascut"] .insp-badge {
            border-radius: 999px;
            font-weight: 600;
            font-size: 10px;
            letter-spacing: 0.04em;
            padding: 3px 9px;
        }
        body[data-ui-version="ascut"] .insp-badge.insp-inherits {
            background: color-mix(in srgb, var(--bdr) 60%, transparent);
            border: 1px solid var(--bdr);
            color: var(--dim);
        }
        /* PR-TIER-CHIPS: the AsCut skin keeps its refined pill shape but takes
           the same tier palette — these must out-rank the gray ascut rule
           above, hence they repeat the body[data-ui-version] prefix. */
        body[data-ui-version="ascut"] .insp-badge.insp-inherits.insp-tier-job {
            background: color-mix(in srgb, var(--tier-job) 10%, transparent);
            border-color: color-mix(in srgb, var(--tier-job) 45%, var(--bdr));
            color: var(--tier-job);
        }
        body[data-ui-version="ascut"] .insp-badge.insp-inherits.insp-tier-shop {
            background: color-mix(in srgb, var(--tier-shop) 10%, transparent);
            border-color: color-mix(in srgb, var(--tier-shop) 45%, var(--bdr));
            color: var(--tier-shop);
        }
        body[data-ui-version="ascut"] .insp-badge.insp-overridden {
            background: color-mix(in srgb, var(--tier-cab) 16%, transparent);
            border: 1px solid color-mix(in srgb, var(--tier-cab) 45%, var(--bdr));
            color: var(--tier-cab);
        }
        body[data-ui-version="ascut"] .insp-reset {
            border-radius: 6px;
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 120ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .insp-reset:hover {
            background: color-mix(in srgb, var(--acc) 14%, var(--inp));
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            color: var(--acc);
        }

        /* Tab strips (used across cab-editor, libraries, etc.) — refined
           active state. Most tab strips use .tab class. */
        body[data-ui-version="ascut"] .tab {
            border-radius: 8px 8px 0 0;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .tab:hover:not(.on) {
            background: color-mix(in srgb, var(--acc) 6%, transparent);
            color: var(--txt);
        }
        body[data-ui-version="ascut"] .tab.on {
            background: var(--pn);
            color: var(--acc);
            font-weight: 600;
            border-bottom: 2px solid var(--acc);
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .drawer-close,
            body[data-ui-version="ascut"] .insp-reset,
            body[data-ui-version="ascut"] .tab {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-DRAWERS ─────────────────────────────────────────── */

        /* ── PR-DOOR-REV-DRAWER (Wave 5.6, 2026-05-17) — revision-history drawer ── */
        /* Fixed-position panel that slides in from the right edge. Hidden by
           default (aria-hidden=true + transform off-screen). When .open is
           added, slides into view. Width matches the existing ds-param-panel
           (360px) for visual consistency with the rest of Door Studio. */
        .rev-drawer {
            position: fixed;
            top: 0;
            right: 0;
            width: 380px;
            max-width: 100vw;
            height: 100vh;
            background: var(--inp);
            border-left: 1px solid var(--bdr);
            box-shadow: -4px 0 16px rgb(var(--shadow-ink) / .25);
            transform: translateX(100%);
            transition: transform .22s ease;
            z-index: 1000;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        .rev-drawer.open {
            transform: translateX(0);
        }
        /* PR-OVERLAY-RULE (2026-08-28): scrim under the revision drawer. Sits
           one z-index below it, so it catches every click the drawer does not
           and nothing behind can be hit by accident. */
        .rev-scrim {
            position: fixed; inset: 0; z-index: 999;
            background: rgba(15,23,42,.38);
        }
        .rev-scrim[hidden] { display: none; }
        .rev-drawer-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 14px 18px;
            border-bottom: 1px solid var(--bdr);
            background: color-mix(in srgb, var(--inp) 90%, var(--bg));
        }
        .rev-list {
            flex: 0 0 auto;
            max-height: 45%;
            overflow-y: auto;
            padding: 8px 6px;
            border-bottom: 1px solid var(--bdr);
        }
        .rev-row {
            display: block;
            width: 100%;
            text-align: left;
            background: transparent;
            border: 1px solid transparent;
            border-radius: 6px;
            color: var(--txt);
            padding: 10px 12px;
            margin-bottom: 4px;
            cursor: pointer;
            min-height: 44px;
            font-size: 13px;
            line-height: 1.4;
        }
        .rev-row:hover {
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            border-color: color-mix(in srgb, var(--acc) 25%, transparent);
        }
        .rev-row.selected {
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            border-color: var(--acc);
        }
        .rev-row .rev-num { font-weight: 600; color: var(--txt); }
        .rev-row .rev-time { color: var(--dim); font-size: 11px; margin-left: 6px; }
        .rev-row .rev-note { color: var(--dim); font-size: 12px; margin-top: 3px; font-style: italic; }
        .rev-row .rev-note-empty { color: color-mix(in srgb, var(--dim) 70%, transparent); }
        .rev-diff {
            flex: 1 1 auto;
            overflow-y: auto;
            padding: 12px 16px;
            font-family: ui-monospace, "SF Mono", Consolas, monospace;
            font-size: 12px;
            line-height: 1.5;
            color: var(--txt);
        }
        .rev-diff h4 {
            margin: 0 0 6px;
            font-family: inherit;
            font-size: 12px;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: .04em;
            color: var(--dim);
        }
        .rev-diff .rev-group {
            margin-bottom: 14px;
            padding: 10px 12px;
            border-radius: 6px;
            background: color-mix(in srgb, var(--bg) 55%, transparent);
            border: 1px solid var(--bdr);
        }
        .rev-diff .rev-group-head {
            font-weight: 700;
            margin-bottom: 6px;
        }
        .rev-diff .rev-field { padding-left: 12px; margin: 2px 0; }
        .rev-diff .rev-piece { padding-left: 8px; margin: 4px 0; border-left: 2px solid var(--bdr); padding-left: 12px; }
        .rev-diff .rev-add    { color: #22c55e; }   /* AsCut green */
        .rev-diff .rev-remove { color: #f43f5e; }   /* AsCut rose  */
        .rev-diff .rev-mod    { color: #f59e0b; }   /* AsCut amber */
        .rev-diff .rev-arrow  { color: var(--dim); }
        .rev-diff .rev-empty  { color: var(--dim); font-style: italic; padding: 8px 0; }
        /* ── /PR-DOOR-REV-DRAWER ───────────────────────────────────────────── */

        /* ── PR-DOOR-LIVE-VALIDATION (E-1 close-out 2026-05-17) — piece-edit-time chip ── */
        /* Warning surface that sits below the piece-input row. Fires the
           moment an operator types a width/height that trips a validator
           (arch min-chord, warp envelope) — not at cutlist-download time.
           One <div> per warning, color-coded by severity. */
        .ds-piece-warn {
            margin: 8px 0 4px;
            display: flex;
            flex-direction: column;
            gap: 4px;
        }
        .ds-piece-warn .ds-piece-warn-row {
            display: flex;
            align-items: flex-start;
            gap: 8px;
            padding: 8px 10px;
            border-radius: 6px;
            font-size: 12px;
            line-height: 1.4;
            border: 1px solid transparent;
        }
        .ds-piece-warn .ds-piece-warn-row svg {
            flex: 0 0 16px;
            margin-top: 1px;
        }
        .ds-piece-warn .ds-piece-warn-row.ds-warn {
            background: color-mix(in srgb, #f59e0b 12%, transparent);
            border-color: color-mix(in srgb, #f59e0b 40%, transparent);
            color: #f59e0b;
        }
        .ds-piece-warn .ds-piece-warn-row.ds-error {
            background: color-mix(in srgb, #f43f5e 14%, transparent);
            border-color: color-mix(in srgb, #f43f5e 50%, transparent);
            color: #f43f5e;
        }
        .ds-piece-warn .ds-piece-warn-row.ds-info {
            background: color-mix(in srgb, #22c55e 10%, transparent);
            border-color: color-mix(in srgb, #22c55e 35%, transparent);
            color: #22c55e;
        }
        /* ── /PR-DOOR-LIVE-VALIDATION ──────────────────────────────────────── */

        /* ── PR-ASCUT-DOOR — Door Studio polish ──────────────────────── */
        /* Page header inside pg-door */
        body[data-ui-version="ascut"] #pg-door h2 {
            font-size: 22px;
            font-weight: 700;
            letter-spacing: -0.01em;
            color: var(--txt);
        }
        /* Door tab strip (dtab) */
        body[data-ui-version="ascut"] #pg-door .tab {
            border-radius: 8px 8px 0 0;
            padding: 10px 16px;
        }
        /* Doors table (orders list) */
        body[data-ui-version="ascut"] #pg-door table {
            border-collapse: separate;
            border-spacing: 0;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] #pg-door table th {
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--dim);
            border-bottom: 1px solid var(--bdr);
            padding: 10px 12px;
        }
        body[data-ui-version="ascut"] #pg-door table tbody tr {
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #pg-door table tbody tr:hover {
            background: color-mix(in srgb, var(--acc) 5%, transparent);
        }
        /* Door Studio wizard step container (Bundles 1-5) gets a ascut
           card-like background to differentiate from the page body */
        body[data-ui-version="ascut"] #pg-door .ds-bundle {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            padding: 24px;
        }
        /* PR-DS-WIZARD-ALIGN (2026-08-01): join the step bar to the panel below
           so they read as ONE card — the bar is the rounded-top header, the
           param-panel is the square-top body. Drops the panel's top radius +
           top border so there's a single seam (the bar's border-bottom), not a
           double hairline with a gap. */
        body[data-ui-version="ascut"] #pg-door .ds-param-panel {
            border-top-left-radius: 0;
            border-top-right-radius: 0;
            border-top: none;
        }
        /* Door library table */
        body[data-ui-version="ascut"] #dl-table table {
            border-collapse: separate;
            border-spacing: 0;
        }
        body[data-ui-version="ascut"] #dl-table table tr {
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #dl-table table tr:hover {
            background: color-mix(in srgb, var(--acc) 5%, transparent);
            cursor: pointer;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] #pg-door table tbody tr,
            body[data-ui-version="ascut"] #dl-table table tr {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-DOOR ────────────────────────────────────────────── */

        /* ── PR-ASCUT-CAB — Cabinet Studio dashboard polish ──────────── */
        body[data-ui-version="ascut"] #pg-cab h1,
        body[data-ui-version="ascut"] #pg-cab .cab-page-title {
            font-size: 28px;
            font-weight: 700;
            letter-spacing: -0.015em;
            color: var(--txt);
        }
        body[data-ui-version="ascut"] #pg-cab h2 {
            font-size: 20px;
            font-weight: 600;
            letter-spacing: -0.01em;
            color: var(--txt);
        }
        /* Metrics ribbon container */
        body[data-ui-version="ascut"] .metrics-ribbon {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            overflow: hidden;
        }
        body[data-ui-version="ascut"] .metric-cell {
            padding: 16px 20px;
        }
        body[data-ui-version="ascut"] .metric-cell .metric-value {
            font-variant-numeric: tabular-nums;
            letter-spacing: -0.01em;
        }
        body[data-ui-version="ascut"] .metric-cell .metric-label {
            font-weight: 600;
            letter-spacing: 0.06em;
            text-transform: uppercase;
        }
        /* Job hub report tile grid container */
        body[data-ui-version="ascut"] #cab-job-reports {
            gap: 12px;
        }
        /* Room card chips */
        body[data-ui-version="ascut"] .cab-room-card {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cab-room-card:hover {
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            box-shadow: 0 6px 18px color-mix(in srgb, var(--acc) 14%, transparent);
            transform: translateY(-2px);
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .cab-room-card {
                transition: none;
            }
            body[data-ui-version="ascut"] .cab-room-card:hover {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-CAB ─────────────────────────────────────────────── */

        /* ── PR-ASCUT-WIZARD — wizard step-dot polish ────────────────── */
        /* PR-WIZ-UX: the base rule moved the dot's visual circle to ::before so
           the clickable element can be a 44×44 tap target. The ascut polish
           (gradient active fill, done tint, thicker border, glow) therefore has
           to paint ::before too — otherwise it would re-fill the whole 44px box.
           color stays on the element (it's the number text). */
        body[data-ui-version="ascut"] .njwiz-stepdot::before {
            border-width: 2px;
            transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .njwiz-stepdot.active { color: var(--acc-ink); }
        body[data-ui-version="ascut"] .njwiz-stepdot.active::before {
            background: linear-gradient(135deg, var(--acc) 0%, var(--acc2) 100%);
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 25%, transparent);
        }
        body[data-ui-version="ascut"] .njwiz-stepdot.done { color: var(--acc); }
        body[data-ui-version="ascut"] .njwiz-stepdot.done::before {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 12%, transparent);
        }
        body[data-ui-version="ascut"] .njwiz-stepline {
            transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .njwiz-stepline.done {
            background: linear-gradient(90deg,
                var(--acc) 0%,
                color-mix(in srgb, var(--acc) 70%, var(--bdr)) 100%);
        }
        /* PR-WIZ-UX: the "Shop default" badge was accent-blue — visually
           identical to the "Modified" (.njwiz-overrided-badge) badge, so the
           operator couldn't tell an untouched field from an edited one. Make it
           NEUTRAL grey so blue == "you changed this" is an unambiguous signal. */
        body[data-ui-version="ascut"] .njwiz-shopdef-badge {
            background: rgba(127,127,127,0.15);
            border: 1px solid var(--bdr);
            color: var(--dim);
            border-radius: 999px;
            padding: 2px 8px;
            font-weight: 700;
            letter-spacing: 0.04em;
        }
        /* PR-WIZ-UX: sentence case at 12px scans faster than uppercase 11px. */
        body[data-ui-version="ascut"] .njwiz-pageinfo {
            font-weight: 600;
            letter-spacing: 0.04em;
            font-size: 12px;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .njwiz-stepdot,
            body[data-ui-version="ascut"] .njwiz-stepline {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-WIZARD ──────────────────────────────────────────── */

        /* ── PR-ASCUT-LIB — Library tables + thumbnails polish ────────── */
        body[data-ui-version="ascut"] .lib-table {
            border-radius: 12px;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .lib-table thead th {
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.08em;
            background: color-mix(in srgb, var(--pn) 92%, var(--acc));
            border-bottom: 1px solid var(--bdr);
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .lib-table tbody tr {
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .lib-table tbody tr:hover {
            background: color-mix(in srgb, var(--acc) 6%, transparent);
            cursor: pointer;
        }
        body[data-ui-version="ascut"] .lib-table .lib-thumb {
            border-radius: 8px;
            border: 1px solid color-mix(in srgb, var(--acc) 12%, var(--bdr));
            background: linear-gradient(135deg,
                var(--inp) 0%,
                color-mix(in srgb, var(--inp) 80%, var(--bg)) 100%);
        }
        body[data-ui-version="ascut"] .lib-chips {
            gap: 6px;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .lib-table tbody tr {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-LIB ─────────────────────────────────────────────── */

        /* ── PR-ASCUT-TOOLTIPS — jd-help popover polish ───────────────── */
        body[data-ui-version="ascut"] .jd-help {
            color: var(--acc);
            border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .jd-help:hover {
            background: color-mix(in srgb, var(--acc) 22%, transparent);
            border-color: var(--acc);
            color: var(--acc);
        }
        /* AsCut popover — themed surface, refined depth, accent-tinted
           border with inner highlight. Overrides hardcoded #111 + 0.5
           shadow. */
        body[data-ui-version="ascut"] .jd-help:hover::after {
            background: color-mix(in srgb, var(--pn) 96%, transparent);
            color: var(--txt);
            backdrop-filter: blur(12px) saturate(140%);
            -webkit-backdrop-filter: blur(12px) saturate(140%);
            border: 1px solid color-mix(in srgb, var(--acc) 28%, var(--bdr));
            border-radius: 10px;
            padding: 8px 12px;
            font-size: 12px;
            line-height: 1.5;
            box-shadow:
                0 12px 32px rgb(var(--shadow-ink) / 0.45),
                inset 0 1px 0 color-mix(in srgb, var(--acc) 18%, transparent);
            min-width: 220px;
            max-width: 340px;
        }
        body[data-ui-version="ascut"] .jd-help:hover::before {
            border-top-color: color-mix(in srgb, var(--pn) 96%, transparent);
        }
        /* ── /PR-ASCUT-TOOLTIPS ────────────────────────────────────────── */

        /* ── PR-ASCUT-EMPTY — Empty state polish ─────────────────────── */
        body[data-ui-version="ascut"] .empty-state {
            padding: 64px 24px;
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .empty-state .ei {
            font-size: 32px;
            opacity: 0.7;
            margin-bottom: 16px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 72px;
            height: 72px;
            border-radius: 18px;
            background: color-mix(in srgb, var(--acc) 10%, var(--pn));
            border: 1px solid color-mix(in srgb, var(--acc) 22%, var(--bdr));
            color: var(--acc);
        }
        body[data-ui-version="ascut"] .empty-state .et {
            color: var(--txt);
            font-size: 17px;
            font-weight: 600;
            letter-spacing: -0.01em;
            margin-bottom: 6px;
        }
        body[data-ui-version="ascut"] .empty-state .es {
            color: var(--dim);
            font-size: 13px;
            line-height: 1.55;
            max-width: 360px;
            margin: 0 auto 18px;
        }
        /* ── /PR-ASCUT-EMPTY ───────────────────────────────────────────── */

        /* ── PR-ASCUT-MSG — validation message polish ─────────────────── */
        body[data-ui-version="ascut"] .msg {
            border-radius: 10px;
            font-weight: 500;
            line-height: 1.5;
            padding: 12px 14px;
        }
        body[data-ui-version="ascut"] .msg-ok {
            background: color-mix(in srgb, var(--ok) 12%, transparent);
            border: 1px solid color-mix(in srgb, var(--ok) 35%, var(--bdr));
            color: var(--ok);
        }
        body[data-ui-version="ascut"] .msg-warn {
            background: color-mix(in srgb, var(--warn) 12%, transparent);
            border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--bdr));
            color: var(--warn);
        }
        body[data-ui-version="ascut"] .msg-err {
            background: color-mix(in srgb, var(--no) 12%, transparent);
            border: 1px solid color-mix(in srgb, var(--no) 35%, var(--bdr));
            color: var(--no);
        }
        /* Banner-style messages (used at the top of pages, e.g.
           print-warnings-banner) get a slightly bolder presentation. */
        body[data-ui-version="ascut"] .print-warnings-banner,
        body[data-ui-version="ascut"] .print-blocked-banner {
            border-radius: 12px;
            font-weight: 600;
            padding: 14px 18px;
        }
        /* ── /PR-ASCUT-MSG ─────────────────────────────────────────────── */

        /* ── PR-UI-CLEANUP-THEME (2026-06-30) — Theme picker: mini-window preview cards ──
           Operator: "make the themes look nicer to select." Each card is a small
           app-window mockup (titlebar + accent row + surface row + ok/warn/no
           chips) painted with the theme's REAL tokens (read at render time, see
           app-core.js _themeSwatches) so the preview is always accurate. Replaces
           the old flat 4-bar `.theme-picker-card` with the stale hardcoded hex. */
        #settings-theme-picker, #home-theme-picker {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
            gap: 12px;
        }
        .theme-card {
            border: 1.5px solid var(--bdr);
            border-radius: 12px;
            background: var(--pn);
            padding: 12px;
            cursor: pointer;
            outline: none;
            transition: transform .16s cubic-bezier(.22,1,.36,1),
                        box-shadow .16s cubic-bezier(.22,1,.36,1),
                        border-color .16s cubic-bezier(.22,1,.36,1);
        }
        .theme-card:hover, .theme-card:focus-visible {
            transform: translateY(-2px);
            border-color: color-mix(in srgb, var(--acc) 55%, var(--bdr));
            box-shadow: 0 8px 22px color-mix(in srgb, var(--acc) 16%, transparent);
        }
        .theme-card.active {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 30%, transparent),
                        0 8px 24px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        /* mini "app window" preview — bg/border/colors are inline (real tokens) */
        .theme-card__preview {
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 9px;
            display: flex;
            flex-direction: column;
            gap: 6px;
            margin-bottom: 10px;
            box-shadow: inset 0 1px 0 rgba(255,255,255,.04);
        }
        .theme-card__preview .tc-titlebar { height: 7px; border-radius: 3px; opacity: .9; }
        .theme-card__preview .tc-row { height: 11px; border-radius: 3px; border: 1px solid transparent; }
        .theme-card__preview .tc-row--accent { width: 58%; }
        .theme-card__preview .tc-chips { display: flex; gap: 5px; margin-top: 1px; }
        .theme-card__preview .tc-chip { width: 11px; height: 11px; border-radius: 50%; box-shadow: 0 0 0 1px rgb(var(--shadow-ink) / .18); }
        .theme-card__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
        .theme-card__name { color: var(--txt); font-weight: 600; font-size: 13px; }
        .theme-card__active { color: var(--acc); font-size: 11px; font-weight: 600; }
        .theme-card__desc { color: var(--dim); font-size: 11px; line-height: 1.4; flex-basis: 100%; }
        @media (prefers-reduced-motion: reduce) {
            .theme-card { transition: none; }
            .theme-card:hover, .theme-card:focus-visible { transform: none; }
        }
        /* ── /PR-UI-CLEANUP-THEME ──────────────────────────────────────────── */

        /* ── PR-ASCUT-RAIL — Hub right rail polish ───────────────────── */
        body[data-ui-version="ascut"] .hub-rail-section {
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 12px;
            padding: 16px;
            margin-bottom: 12px;
        }
        body[data-ui-version="ascut"] .hub-rail-label {
            display: block;
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.1em;
            text-transform: uppercase;
            color: var(--dim);
            margin-bottom: 10px;
        }
        body[data-ui-version="ascut"] .hub-rail-link {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 8px 10px;
            border-radius: 8px;
            color: var(--txt);
            font-size: 13px;
            font-weight: 500;
            text-decoration: none;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-rail-link svg {
            width: 16px;
            height: 16px;
            flex-shrink: 0;
            color: var(--dim);
            transition: color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-rail-link:hover {
            background: color-mix(in srgb, var(--acc) 10%, transparent);
            color: var(--acc);
        }
        body[data-ui-version="ascut"] .hub-rail-link:hover svg {
            color: var(--acc);
        }
        /* Units toggle — pill button group */
        body[data-ui-version="ascut"] .hub-rail-units {
            display: inline-flex;
            gap: 0;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 3px;
        }
        body[data-ui-version="ascut"] .hub-rail-units button {
            border-radius: 7px;
            padding: 6px 14px;
            font-size: 12px;
            font-weight: 600;
            background: transparent;
            color: var(--dim);
            border: none;
            cursor: pointer;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-rail-units button:hover {
            color: var(--txt);
        }
        body[data-ui-version="ascut"] .hub-rail-units button[data-active="true"] {
            background: var(--pn);
            color: var(--acc);
            box-shadow: 0 1px 3px rgb(var(--shadow-ink) / 0.18),
                        inset 0 0 0 1px color-mix(in srgb, var(--acc) 30%, var(--bdr));
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .hub-rail-link,
            body[data-ui-version="ascut"] .hub-rail-link svg,
            body[data-ui-version="ascut"] .hub-rail-units button {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-RAIL ────────────────────────────────────────────── */

        /* ── PR-ASCUT-PALETTE — Command palette polish ───────────────── */
        body[data-ui-version="ascut"] .gs-modal {
            backdrop-filter: blur(20px) saturate(140%);
            -webkit-backdrop-filter: blur(20px) saturate(140%);
            background: rgba(0,0,0,0.55);
        }
        body[data-ui-version="ascut"] .gs-modal .gs-panel {
            width: 640px;
            border-radius: 16px;
            background: color-mix(in srgb, var(--sf) 94%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 18%, var(--bdr));
            backdrop-filter: blur(20px) saturate(140%);
            -webkit-backdrop-filter: blur(20px) saturate(140%);
            box-shadow:
                0 32px 80px rgb(var(--shadow-ink) / 0.55),
                inset 0 1px 0 color-mix(in srgb, var(--acc) 16%, transparent);
        }
        body[data-ui-version="ascut"] .gs-modal .gs-input {
            font-size: 17px;
            font-weight: 500;
            padding: 20px 24px;
            background: transparent;
            letter-spacing: -0.01em;
        }
        body[data-ui-version="ascut"] .gs-modal .gs-results {
            padding: 6px;
        }
        body[data-ui-version="ascut"] .gs-modal .gs-result {
            padding: 10px 14px;
            border-radius: 10px;
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .gs-modal .gs-result:hover,
        body[data-ui-version="ascut"] .gs-modal .gs-result.sel {
            background: color-mix(in srgb, var(--acc) 14%, transparent);
        }
        body[data-ui-version="ascut"] .gs-modal .gs-result .gs-icon {
            border-radius: 8px;
            background: color-mix(in srgb, var(--acc) 12%, var(--pn));
            border: 1px solid color-mix(in srgb, var(--acc) 22%, var(--bdr));
        }
        body[data-ui-version="ascut"] .gs-modal .gs-result .gs-text .gs-title {
            color: var(--txt);
            font-weight: 600;
            font-size: 14px;
        }
        body[data-ui-version="ascut"] .gs-modal .gs-result .gs-text .gs-sub {
            font-size: 12px;
        }
        body[data-ui-version="ascut"] .gs-modal .gs-foot {
            background: color-mix(in srgb, var(--pn) 50%, transparent);
            padding: 12px 18px;
        }
        body[data-ui-version="ascut"] .gs-modal .gs-foot .kbd {
            background: var(--inp);
            color: var(--txt);
            border-radius: 6px;
            padding: 3px 7px;
            font-family: 'JetBrains Mono', ui-monospace, monospace;
            font-size: 10px;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .gs-modal .gs-result {
                transition: none;
            }
        }/* ── /PR-ASCUT-PALETTE ─────────────────────────────────────────── *//* ── PR-ASCUT-BUNDLE — Door Studio wizard step-bar polish ───── *//* PR-DS-WIZARD-ALIGN (2026-08-01): three-state legibility per operator —
           a step you CAN reach reads white, a locked one you can't reads grayed,
           and the one you're on reads blue. Previously every step defaulted to
           --dim (gray) and only lifted to white on hover, so the whole bar looked
           disabled and you couldn't tell available from locked at a glance. *//* Navigable (not current / complete / locked) number circle reads white
           on a brighter ring so it's clearly enabled, not grayed. */
        @media (prefers-reduced-motion: reduce) {
        }
        /* ── /PR-ASCUT-BUNDLE ──────────────────────────────────────────── */

        /* ── PR-ASCUT-VGATE — validation gate polish ─────────────────── */
        body[data-ui-version="ascut"] .vgate-panel {
            border: 1px solid color-mix(in srgb, var(--no) 50%, var(--bdr));
            border-left: 4px solid var(--no);
            border-radius: 12px;
            padding: 16px 20px;
            background: color-mix(in srgb, var(--no) 6%, var(--pn));
            box-shadow:
                0 12px 32px rgb(var(--shadow-ink) / 0.35),
                inset 0 1px 0 color-mix(in srgb, var(--no) 18%, transparent);
        }
        body[data-ui-version="ascut"] .vgate-panel.vgate-soft {
            border-color: color-mix(in srgb, var(--warn) 50%, var(--bdr));
            border-left-color: var(--warn);
            background: color-mix(in srgb, var(--warn) 6%, var(--pn));
            box-shadow:
                0 12px 32px rgb(var(--shadow-ink) / 0.35),
                inset 0 1px 0 color-mix(in srgb, var(--warn) 18%, transparent);
        }
        body[data-ui-version="ascut"] .vgate-modal .vgate-panel {
            border-radius: 14px;
            width: 600px;
        }
        /* ── /PR-ASCUT-VGATE ───────────────────────────────────────────── */

        /* ── PR-ASCUT-MISC — order table + miscellaneous polish ──────── */
        body[data-ui-version="ascut"] .order-table {
            border-collapse: separate;
            border-spacing: 0;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .order-table thead th {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
            background: color-mix(in srgb, var(--pn) 95%, var(--acc));
            border-bottom: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] .order-table tbody tr {
            transition: background 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .order-table tbody tr:hover {
            background: color-mix(in srgb, var(--acc) 4%, transparent);
        }
        body[data-ui-version="ascut"] .order-table input,
        body[data-ui-version="ascut"] .order-table select {
            border-radius: 6px;
            border: 1px solid var(--bdr);
            background: var(--inp);
            padding: 6px 8px;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .order-table input:focus,
        body[data-ui-version="ascut"] .order-table select:focus {
            border-color: var(--acc);
            box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 20%, transparent);
            outline: none;
        }

        /* Inspector labels (used in cabinet inspector and standards panels) */
        body[data-ui-version="ascut"] .insp-label {
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.04em;
            color: var(--dim);
        }

        /* Hub jobs add row (the "+ Add" trigger) — refined ghost */
        body[data-ui-version="ascut"] .hub-jobs-add-row {
            border: 1px dashed var(--bdr);
            border-radius: 10px;
            color: var(--dim);
            transition: border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .hub-jobs-add-row:hover {
            border-color: var(--acc);
            background: color-mix(in srgb, var(--acc) 8%, transparent);
            color: var(--acc);
        }

        /* New-order CTA (Door studio "New Order" button) */
        body[data-ui-version="ascut"] .new-order-cta {
            border-radius: 10px;
            font-weight: 600;
            background: linear-gradient(135deg,
                var(--acc) 0%,
                color-mix(in srgb, var(--acc) 80%, var(--acc2)) 100%);
            color: var(--acc-ink);
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 28%, transparent);
            transition: filter 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .new-order-cta:hover {
            filter: brightness(1.08);
            box-shadow: 0 8px 22px color-mix(in srgb, var(--acc) 40%, transparent);
        }
        body[data-ui-version="ascut"] .new-order-cta:active {
            transform: scale(0.98);
        }

        /* AWS-2 badge (used in topbar showing standards mode) */
        body[data-ui-version="ascut"] .aws-badge {
            background: color-mix(in srgb, var(--ok) 16%, transparent);
            border: 1px solid color-mix(in srgb, var(--ok) 35%, var(--bdr));
            color: var(--ok);
            border-radius: 999px;
            font-weight: 700;
            letter-spacing: 0.06em;
            text-transform: uppercase;
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .order-table tbody tr,
            body[data-ui-version="ascut"] .hub-jobs-add-row,
            body[data-ui-version="ascut"] .new-order-cta {
                transition: none;
            }
            body[data-ui-version="ascut"] .new-order-cta:active {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-MISC ────────────────────────────────────────────── */

        /* ── PR-ASCUT-ADMIN-REPORTS — admin Shop Reports KPI cards ───── */
        body[data-ui-version="ascut"] .rep-kpi-card {
            border-radius: 12px !important;
            padding: 20px !important;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1) !important;
        }
        body[data-ui-version="ascut"] .rep-kpi-card:hover {
            border-color: color-mix(in srgb, var(--acc) 40%, var(--bdr)) !important;
            box-shadow: 0 4px 14px color-mix(in srgb, var(--acc) 12%, transparent) !important;
            transform: translateY(-1px);
        }
        body[data-ui-version="ascut"] .rep-kpi-card .rep-kpi-value {
            font-size: 32px !important;
            font-weight: 700 !important;
            letter-spacing: -0.02em;
        }
        body[data-ui-version="ascut"] .rep-kpi-card > div:first-child {
            font-size: 11px !important;
            letter-spacing: 0.08em !important;
            font-weight: 700 !important;
            color: var(--dim) !important;
            margin-bottom: 8px !important;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .rep-kpi-card {
                transition: none !important;
            }
            body[data-ui-version="ascut"] .rep-kpi-card:hover {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-ADMIN-REPORTS ──────────────────────────────────── */

        /* ── PR-ASCUT-HUB-TABS — Standards / Cutlists / Projects / Customers ── */
        /* Generic tab content polish — applies to every .tc inside #pg-hub */
        body[data-ui-version="ascut"] #pg-hub .tc h2 {
            font-size: 22px;
            font-weight: 700;
            letter-spacing: -0.01em;
            color: var(--txt);
            margin-bottom: 8px;
        }
        body[data-ui-version="ascut"] #pg-hub .tc h3 {
            font-size: 17px;
            font-weight: 600;
            letter-spacing: -0.005em;
            color: var(--txt);
        }
        /* Standards header bar — the one place the eye lands first. Flat, wide
           gutter, title ladder, and the single accent affordance (Save). */
        body[data-ui-version="ascut"] .std-header {
            display: flex; align-items: flex-start; justify-content: space-between;
            gap: 20px; flex-wrap: wrap;
            background: transparent;
            border: none;
            border-bottom: 1px solid color-mix(in srgb, var(--txt) 9%, transparent);
            border-radius: 0;
            padding: 4px 2px 18px;
            margin-bottom: 20px;
        }
        body[data-ui-version="ascut"] .std-header h2 {
            font-size: 22px; font-weight: 600; letter-spacing: -0.02em;
            color: var(--txt); margin: 0 0 4px 0; line-height: 1.15;
        }
        body[data-ui-version="ascut"] .std-subtitle {
            color: var(--dim);
            font-size: 13px;
            line-height: 1.5;
            letter-spacing: -0.003em;
            max-width: 64ch;
            margin: 0;
        }
        body[data-ui-version="ascut"] .std-header-status {
            display: flex; align-items: center; gap: 8px; flex-shrink: 0;
        }
        /* Expand / Collapse all — quiet ghost pair. */
        body[data-ui-version="ascut"] .std-header-status .bg {
            font-size: 12px !important; font-weight: 500;
            letter-spacing: -0.004em;
            padding: 7px 12px !important; height: 34px;
            border-radius: 9px !important;
            color: var(--dim) !important;
            background: transparent !important;
            border: 1px solid color-mix(in srgb, var(--txt) 12%, transparent) !important;
            transition: color 120ms ease, border-color 120ms ease;
        }
        body[data-ui-version="ascut"] .std-header-status .bg:hover {
            color: var(--txt) !important;
            border-color: color-mix(in srgb, var(--txt) 24%, transparent) !important;
        }
        /* Save — the ONLY accent surface on the page. Machined, deliberate. */
        body[data-ui-version="ascut"] .std-save-btn {
            height: 34px;
            padding: 7px 18px !important;
            border-radius: 9px !important;
            font-size: 13px !important; font-weight: 600;
            letter-spacing: -0.006em;
            background: var(--acc) !important;
            border: 1px solid var(--acc) !important;
            color: #fff !important;
            transition: filter 120ms ease, transform 60ms ease;
        }
        body[data-ui-version="ascut"] .std-save-btn:hover { filter: brightness(1.06); }
        body[data-ui-version="ascut"] .std-save-btn:active { transform: translateY(0.5px); }
        body[data-ui-version="ascut"] .std-status-msg {
            color: var(--dim);
            font-size: 12px;
            font-weight: 500;
        }
        /* Cutlist viewer (.cutlist-out monospace text) */
        body[data-ui-version="ascut"] .cutlist-out {
            font-family: 'JetBrains Mono', 'SF Mono', 'Consolas', ui-monospace, monospace !important;
            font-size: 12px !important;
            line-height: 1.55 !important;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            padding: 14px 16px;
        }
        /* PR-DBOX-CUTLIST-SCROLL (2026-08-04): the Door Cutlist report box
           (#do-cutlist) inherits the shared .cutlist-out cap (max-height 720px
           + overflow:auto). On the door Reports view that nests a SECOND
           scrollbar inside the page scroll — scroll the wrong one and the whole
           cutlist slides out of view, leaving an empty panel (operator-reported
           "long scrollbar, content gone"). Let this one report flow with the
           page so there's a single, predictable scroll. Other .cutlist-out
           users (cutlist studio, confirm panel) keep the capped box. */
        /* PR-CUTLIST-CONTAIN (2026-08-10): `overflow: visible` above fixed the
           VERTICAL double-scrollbar, but it also removed HORIZONTAL
           containment — and these rows are a fixed 105 monospace characters
           wide. On the split Reports view (.do-reports.split is a 50/50 grid)
           the card is roughly half the screen, so the text ran straight out
           past the card's right edge and under the neighbouring panel.
           Operator: "the words go outside the cutlist".

           The cap removal is the part worth keeping, so keep it and scroll the
           overflow instead of letting it escape:
             overflow-x: auto   → long rows scroll INSIDE the card
             overflow-y: hidden → with max-height:none the box always grows to
                                  fit its content, so nothing can overflow
                                  vertically; this only stops the browser from
                                  pairing an unwanted y-scrollbar with the
                                  x one (CSS forces a non-visible y when x is
                                  non-visible, so `visible` here is not an
                                  option).
           Monospace column alignment is why this cannot simply wrap — a
           wrapped cutlist row is unreadable at the saw. Printing is
           unaffected: printDoorCutlist opens its own window and never renders
           this element.

           PR-CUTLIST-FIT (2026-08-10): scrolling contained the text but the
           operator did not want the scrollbar ("I don't like the long scroll
           bar at all"). `_dsFitCutlistToCard()` in door-studio.js now scales
           the font so the widest row fits the card exactly, which means the
           overflow it was scrolling no longer exists.

           NO SCROLLBAR (operator, 2026-08-10: "can you get rid of the bar").
           `overflow-x: hidden` is only safe because the fitter's floor was
           dropped to a last-resort 4px, so the fit succeeds at ANY card width
           and there is nothing left to clip. The two must stay in sync: raise
           _DS_CUTLIST_MIN_PX back to a readability floor and this line starts
           silently cutting off the right-hand columns — a cutlist reaching the
           saw with no Length column, with nothing on screen to show it. If a
           floor is ever needed again, change this back to `auto` in the same
           edit. */
        #do-cutlist {
            max-height: none;
/* PR-REPORTS-ONE-AT-A-TIME (2026-08-15): overflow-x restored —
               the 2026-08-04 `overflow: visible` fix for the nested VERTICAL
               scrollbar also killed the horizontal escape valve, so wide
               drawer-box tables hard-clipped at the panel edge. With no
               height cap, the computed overflow-y:auto never shows a bar
               (the box just grows), while a too-narrow window now scrolls
               sideways instead of cutting columns off. (2026-08-15 merge:
               HEAD kept — this is the deliberate newer fix; origin's hidden/
               hidden was the pre-fix state. 2026-08-21 merge: HEAD kept again
               vs the sheet-grain lineage's same pre-fix state.) */
            overflow-x: auto;
            overflow-y: visible;
        }
        /* Cutlist sub-headers (when JS injects section dividers) */
        body[data-ui-version="ascut"] .cutlist-out h4 {
            color: var(--acc);
            font-family: var(--ff-ui, inherit);
            font-size: 13px;
            font-weight: 700;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            margin: 12px 0 8px 0;
        }
        /* Stale cutlist banner */
        body[data-ui-version="ascut"] .cutlist-stale {
            background: color-mix(in srgb, var(--warn) 12%, transparent);
            border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--bdr));
            border-left: 4px solid var(--warn);
            border-radius: 10px;
            padding: 12px 16px;
            color: var(--warn);
            font-weight: 600;
        }
        /* Project + customer list rows (rendered by JS, typically with .pn class) */
        body[data-ui-version="ascut"] #h-proj .pn,
        body[data-ui-version="ascut"] #h-cust .pn {
            border-radius: 12px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #h-proj .pn:hover,
        body[data-ui-version="ascut"] #h-cust .pn:hover {
            border-color: color-mix(in srgb, var(--acc) 35%, var(--bdr));
            box-shadow: 0 3px 12px color-mix(in srgb, var(--acc) 10%, transparent);
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] #h-proj .pn,
            body[data-ui-version="ascut"] #h-cust .pn {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-HUB-TABS ───────────────────────────────────────── */

        /* ── PR-ASCUT-SETTINGS — settings drawer content polish ───────
           Strictly scoped to #settings-panel so it can't leak elsewhere.
           Pure CSS — no JS changes, radio inputs untouched. */
        body[data-ui-version="ascut"] #settings-panel h3 {
            font-size: 14px;
            font-weight: 700;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            color: var(--dim);
            margin-top: 28px;
            margin-bottom: 10px;
            padding-bottom: 8px;
            border-bottom: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] #settings-panel h3:first-of-type {
            margin-top: 16px;
        }
        body[data-ui-version="ascut"] #settings-panel p {
            color: var(--dim);
            font-size: 12px;
            line-height: 1.55;
            max-width: 60ch;
        }
        body[data-ui-version="ascut"] #settings-panel .dt-quick-btn {
            border-radius: 10px;
            font-weight: 600;
            transition: border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #settings-panel .dt-quick-btn:hover {
            background: color-mix(in srgb, var(--acc) 8%, var(--inp));
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            color: var(--acc);
        }
        body[data-ui-version="ascut"] #settings-panel .dt-quick-btn:active {
            transform: scale(0.98);
        }
        body[data-ui-version="ascut"] #settings-panel .dt-grid {
            gap: 12px;
        }
        body[data-ui-version="ascut"] #settings-panel #settings-unit-pref label {
            padding: 8px 12px;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            transition: border-color 140ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] #settings-panel #settings-unit-pref label:hover {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 6%, var(--inp));
        }
        body[data-ui-version="ascut"] #settings-panel #set-profile-msg {
            font-weight: 500;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] #settings-panel .dt-quick-btn,
            body[data-ui-version="ascut"] #settings-panel #settings-unit-pref label {
                transition: none;
            }
            body[data-ui-version="ascut"] #settings-panel .dt-quick-btn:active {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-SETTINGS ────────────────────────────────────────── */

        /* ── PR-ASCUT-STD-2 (2026-08-04) — Shop Standards, machined ──────
           One spacing cell (4px). One radius language: section 16 / control 10
           (1.6 ratio = same billet, two scales). SF-Pro type ladder. Accent is
           WITHHELD to exactly two places: the focus ring and Save. The tinted
           icon badges, the 3:1 radius mismatch, and the hover-border flicker are
           gone. `!important` only where the JS render hardcodes inline styles that
           must be unified into one physical object. */

        /* Section = a single quiet billet. No badge, no flicker. */
        body[data-ui-version="ascut"] #shop-standards-body .std-section {
            background: var(--sf, var(--pn));
            border: 1px solid color-mix(in srgb, var(--txt) 9%, transparent);
            border-radius: 16px;
            padding: 24px 24px 22px;
            margin-bottom: 12px;
            transition: none;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section:hover {
            border-color: color-mix(in srgb, var(--txt) 9%, transparent);
        }
        /* Header: icon becomes a monochrome mark; title/subtitle a clean ladder. */
        body[data-ui-version="ascut"] #shop-standards-body .std-section-header {
            align-items: center !important;
            gap: 12px !important;
            padding-bottom: 0;
            border-bottom: none;
            margin-bottom: 20px;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section[data-collapsed="1"] .std-section-header {
            margin-bottom: 0;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section-icon {
            width: 22px; height: 22px;
            border-radius: 0; background: none !important; border: none !important;
            color: var(--dim);
            display: inline-flex; align-items: center; justify-content: center;
            flex-shrink: 0;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section-icon svg {
            width: 18px; height: 18px; stroke-width: 1.6;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section-title {
            font-size: 15px; font-weight: 600; letter-spacing: -0.012em;
            color: var(--txt); margin: 0 0 2px 0; line-height: 1.25;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-section-subtitle {
            font-size: 12.5px; font-weight: 400; line-height: 1.5;
            color: var(--dim); margin: 0; max-width: 62ch;
            letter-spacing: -0.003em;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-collapse-btn {
            color: var(--dim) !important; font-size: 12px !important;
            font-weight: 500; letter-spacing: -0.004em;
            padding: 6px 10px !important; border-radius: 8px !important;
            transition: color 120ms ease, background 120ms ease;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-collapse-btn:hover {
            color: var(--txt) !important;
            background: color-mix(in srgb, var(--txt) 6%, transparent) !important;
        }

        /* Field grid: 4px-grid gutters, auto-fill so columns breathe. */
        body[data-ui-version="ascut"] #shop-standards-body .std-field-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
            gap: 16px 20px;
            align-items: start;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-field {
            display: flex; flex-direction: column; gap: 6px; min-width: 0;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-field-label {
            display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
            font-size: 12px; font-weight: 500; letter-spacing: -0.004em;
            color: var(--txt); margin: 0;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-field-unit {
            font-size: 11.5px; font-weight: 400; color: var(--dim);
            font-feature-settings: "tnum"; letter-spacing: 0.005em;
            text-transform: none; flex-shrink: 0;
        }
        /* Controls machined as ONE part — number, text, select share every value.
           tabular numerals so decimal columns align like a micrometer readout. */
        body[data-ui-version="ascut"] #shop-standards-body .std-field input,
        body[data-ui-version="ascut"] #shop-standards-body .std-field select {
            width: 100% !important;
            height: 34px;
            padding: 7px 12px !important;
            background: var(--bg) !important;
            border: 1px solid color-mix(in srgb, var(--txt) 14%, transparent) !important;
            border-radius: 10px !important;
            color: var(--txt) !important;
            font-size: 14px !important; font-family: inherit;
            font-feature-settings: "tnum";
            letter-spacing: -0.006em;
            transition: border-color 120ms ease, box-shadow 120ms ease;
            appearance: none; -webkit-appearance: none;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-field input:hover,
        body[data-ui-version="ascut"] #shop-standards-body .std-field select:hover {
            border-color: color-mix(in srgb, var(--txt) 26%, transparent) !important;
        }
        body[data-ui-version="ascut"] #shop-standards-body .std-field input:focus,
        body[data-ui-version="ascut"] #shop-standards-body .std-field select:focus {
            outline: none;
            border-color: var(--acc) !important;
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent) !important;
        }
        /* Select chevron — one glyph, optically centered at the 12px inset. */
        body[data-ui-version="ascut"] #shop-standards-body .std-field select {
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23888888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E") !important;
            background-repeat: no-repeat !important;
            background-position: right 12px center !important;
            padding-right: 32px !important;
        }
        /* Help badge: a quiet dot, never a shout. */
        body[data-ui-version="ascut"] #shop-standards-body .std-field .jd-help {
            color: var(--dim); font-size: 11px; cursor: help;
            border: none !important; background: none !important;
            padding: 0 !important; margin-left: 2px; min-width: 0 !important;
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] #shop-standards-body .std-field input,
            body[data-ui-version="ascut"] #shop-standards-body .std-field select { transition: none; }
        }

        /* PR-STD-BUILDWITH — "What you build with" library chapter. Same billet
           language: chapter rules as hairlines, pickers as the machined control,
           a smart annotation line under each. */
        body[data-ui-version="ascut"] #shop-standards-body .bw-loading {
            color: var(--dim); font-size: 13px; padding: 8px 2px;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-chapter { margin-bottom: 22px; }
        body[data-ui-version="ascut"] #shop-standards-body .bw-chapter:last-child { margin-bottom: 0; }
        body[data-ui-version="ascut"] #shop-standards-body .bw-chapter-head {
            display: flex; align-items: baseline; gap: 10px;
            margin-bottom: 12px; padding-bottom: 8px;
            border-bottom: 1px solid color-mix(in srgb, var(--txt) 8%, transparent);
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-chapter-title {
            font-size: 11px; font-weight: 600; text-transform: uppercase;
            letter-spacing: 0.07em; color: var(--txt);
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-chapter-hint {
            font-size: 11.5px; font-weight: 400; color: var(--dim); letter-spacing: -0.002em;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-grid {
            display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
            gap: 16px 20px; align-items: start;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-field {
            display: flex; flex-direction: column; gap: 6px; min-width: 0;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-label {
            font-size: 12px; font-weight: 500; letter-spacing: -0.004em; color: var(--txt);
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-select {
            width: 100%; height: 34px; padding: 7px 32px 7px 12px;
            background: var(--bg); color: var(--txt);
            border: 1px solid color-mix(in srgb, var(--txt) 14%, transparent);
            border-radius: 10px;
            font-size: 14px; font-family: inherit; letter-spacing: -0.006em;
            appearance: none; -webkit-appearance: none;
            background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23888888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
            background-repeat: no-repeat; background-position: right 12px center;
            transition: border-color 120ms ease, box-shadow 120ms ease;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-select:hover {
            border-color: color-mix(in srgb, var(--txt) 26%, transparent);
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-select:focus {
            outline: none; border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent);
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-select:disabled {
            opacity: 0.55; cursor: not-allowed;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-ann {
            font-size: 11px; line-height: 1.4; color: var(--dim);
            min-height: 15px; letter-spacing: -0.002em;
        }
        body[data-ui-version="ascut"] #shop-standards-body .bw-ann .bw-empty {
            color: var(--no, #c76b6b); font-style: italic;
        }
        /* ── /PR-ASCUT-STD-2 ───────────────────────────────────────────── */

        /* ── PR-ASCUT-STATUS — status pills + shop-intel ─────────────── */
        /* Status pills (project status across hub + cab + door studios).
           Existing rules use var(--status-* fallback) — ascut override
           uses color-mix() for richer tinted bg + 1px border for definition. */
        body[data-ui-version="ascut"] .status-draft,
        body[data-ui-version="ascut"] .status-design,
        body[data-ui-version="ascut"] .status-nesting,
        body[data-ui-version="ascut"] .status-completed,
        body[data-ui-version="ascut"] .status-cancelled,
        body[data-ui-version="ascut"] .hub-job-status-pill {
            border-radius: 999px;
            font-weight: 600;
            font-size: 11px;
            letter-spacing: 0.04em;
            padding: 3px 9px;
            text-transform: uppercase;
        }
        body[data-ui-version="ascut"] .status-draft {
            background: color-mix(in srgb, var(--dim) 18%, transparent);
            border: 1px solid color-mix(in srgb, var(--dim) 35%, var(--bdr));
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .status-design {
            background: color-mix(in srgb, var(--acc) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 35%, var(--bdr));
            color: var(--acc);
        }
        body[data-ui-version="ascut"] .status-nesting {
            background: color-mix(in srgb, var(--warn) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--bdr));
            color: var(--warn);
        }
        body[data-ui-version="ascut"] .status-completed {
            background: color-mix(in srgb, var(--ok) 16%, transparent);
            border: 1px solid color-mix(in srgb, var(--ok) 35%, var(--bdr));
            color: var(--ok);
        }
        body[data-ui-version="ascut"] .status-cancelled {
            background: color-mix(in srgb, var(--no) 14%, transparent);
            border: 1px solid color-mix(in srgb, var(--no) 35%, var(--bdr));
            color: var(--no);
        }
        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): ascut .shop-intel
           overrides deleted (parent rule already gone). AsCut .widget-empty
           KEPT — filterGlobalSearch still uses .widget-empty. */
        body[data-ui-version="ascut"] .widget-empty {
            font-style: normal;
            font-size: 13px;
            color: var(--dim);
            padding: 16px 0;
            text-align: center;
        }
        /* ── /PR-ASCUT-STATUS ──────────────────────────────────────────── */

        /* ── PR-ASCUT-BROW — button row + small variants ─────────────── */
        body[data-ui-version="ascut"] .brow {
            gap: 8px;
            margin-top: 12px;
        }
        body[data-ui-version="ascut"] .bs {
            border-radius: 7px;
            padding: 6px 12px;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.02em;
        }
        body[data-ui-version="ascut"] .bs:focus-visible {
            outline: 2px solid var(--acc);
            outline-offset: 2px;
        }
        /* The .ba.bs combo needs the gradient + shadow to scale down
           gracefully when small. */
        body[data-ui-version="ascut"] .ba.bs {
            box-shadow: 0 2px 6px color-mix(in srgb, var(--acc) 18%, transparent);
        }
        body[data-ui-version="ascut"] .ba.bs:hover:not(:disabled) {
            box-shadow: 0 3px 10px color-mix(in srgb, var(--acc) 30%, transparent);
        }
        /* ── /PR-ASCUT-BROW ────────────────────────────────────────────── */

        /* ── PR-ASCUT-BULK — bulk-action bar polish ─────────────────── */
        body[data-ui-version="ascut"] .bulk-bar {
            background: color-mix(in srgb, var(--acc) 12%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 35%, var(--bdr));
            border-radius: 10px;
            padding: 10px 14px;
            color: var(--txt);
            font-weight: 500;
            backdrop-filter: blur(6px);
            -webkit-backdrop-filter: blur(6px);
        }
        body[data-ui-version="ascut"] .bulk-bar .bulk-count {
            color: var(--acc);
            font-weight: 700;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] .bulk-bar button {
            font-size: 11px;
            font-weight: 600;
        }
        /* ── /PR-ASCUT-BULK ────────────────────────────────────────────── */

        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): ascut widget-polish
           overrides (.widget, .widget-hdr, .widget-body, .add-widget-card)
           deleted — parent rules retired with WIDGET_DEFS. */

        /* ── PR-ASCUT-SEARCH — global search field polish ────────────── */
        body[data-ui-version="ascut"] .global-search {
            border-radius: 10px;
            background: color-mix(in srgb, var(--acc) 5%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 18%, var(--bdr));
        }
        body[data-ui-version="ascut"] .global-search:focus-within {
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 20%, transparent);
            background: var(--inp);
        }
        body[data-ui-version="ascut"] .global-search input::placeholder {
            color: color-mix(in srgb, var(--dim) 75%, var(--acc));
        }
        body[data-ui-version="ascut"] .global-search .kbd {
            background: var(--bg);
            border: 1px solid color-mix(in srgb, var(--acc) 25%, var(--bdr));
            color: var(--txt);
            font-family: 'JetBrains Mono', ui-monospace, monospace;
            font-size: 10px;
            font-weight: 500;
            border-radius: 5px;
            padding: 2px 6px;
        }
        /* ── /PR-ASCUT-SEARCH ──────────────────────────────────────────── */

        /* ── PR-ASCUT-USER-CHIP — topbar user chip + admin badge ─────── */
        body[data-ui-version="ascut"] .topbar-user-name {
            font-weight: 600;
            letter-spacing: -0.005em;
        }
        body[data-ui-version="ascut"] .topbar-user-role {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .admin-only-badge {
            background: color-mix(in srgb, var(--acc) 18%, transparent);
            border: 1px solid color-mix(in srgb, var(--acc) 38%, var(--bdr));
            color: var(--acc);
            font-weight: 700;
            letter-spacing: 0.06em;
            border-radius: 999px;
        }
        /* AWS-2 badge (compliance mode indicator in topbar) — semantic
           green pill for the active-standards state. */
        body[data-ui-version="ascut"] #aws-badge,
        body[data-ui-version="ascut"] .aws-active-pill {
            border-radius: 999px;
            font-weight: 700;
            letter-spacing: 0.06em;
        }
        /* ── /PR-ASCUT-USER-CHIP ───────────────────────────────────────── */

        /* PR-CLEAN-2 / WIDGET-PURGE (2026-05-31): ascut .dash-bar +
           .widget-grid overrides deleted — parent rules retired. */

        /* ── PR-ASCUT-STUDIO-CARD — studio entry cards polish ────────── */
        body[data-ui-version="ascut"] .studio-card {
            border-radius: 14px;
            padding: 28px 26px;
            transition: border-color 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .studio-card::before {
            height: 4px;
            background: linear-gradient(90deg,
                var(--studio-color, var(--acc)) 0%,
                color-mix(in srgb, var(--studio-color, var(--acc)) 70%, var(--acc2, var(--acc))) 100%);
            opacity: 1;
            border-radius: 14px 14px 0 0;
        }
        body[data-ui-version="ascut"] .studio-card:hover {
            border-color: var(--studio-color, var(--acc));
            background: color-mix(in srgb, var(--studio-color, var(--acc)) 6%, var(--pn));
            box-shadow: 0 12px 32px color-mix(in srgb, var(--studio-color, var(--acc)) 22%, transparent);
            transform: translateY(-2px);
        }
        body[data-ui-version="ascut"] .studio-card h4 {
            font-size: 19px;
            font-weight: 700;
            letter-spacing: -0.01em;
            color: var(--txt);
            margin-bottom: 6px;
        }
        body[data-ui-version="ascut"] .studio-card .studio-img {
            filter: drop-shadow(0 4px 12px color-mix(in srgb, var(--studio-color, var(--acc)) 30%, transparent));
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .studio-card {
                transition: none;
            }
            body[data-ui-version="ascut"] .studio-card:hover {
                transform: none;
            }
        }
        /* ── /PR-ASCUT-STUDIO-CARD ────────────────────────────────────── */

        /* ── PR-ASCUT-CAB-LIB — room editor library items polish ─────── */
        body[data-ui-version="ascut"] .cab-lib-section-header {
            /* PR-CAB-LIB-COMPACT (2026-07-29): was var(--dim) 10px — too faint to
               read ("BASE · 11"). Bumped to max-contrast --txt, bigger + heavier. */
            font-size: 12px;
            font-weight: 800;
            letter-spacing: 0.06em;
            color: var(--txt);
            background: color-mix(in srgb, var(--sf) 92%, var(--bg));
            border-bottom: 1px solid var(--bdr);
        }
        body[data-ui-version="ascut"] .cab-lib-item {
            border-radius: 8px;
            transition: border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 80ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cab-lib-item:hover {
            border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
            background: color-mix(in srgb, var(--acc) 8%, var(--pn));
            box-shadow: 0 3px 10px color-mix(in srgb, var(--acc) 14%, transparent);
        }
        body[data-ui-version="ascut"] .cab-lib-item .icn {
            background: color-mix(in srgb, var(--acc) 8%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 16%, var(--bdr));
            border-radius: 6px;
        }
        body[data-ui-version="ascut"] .cab-lib-item:hover .icn {
            background: color-mix(in srgb, var(--acc) 14%, var(--inp));
            border-color: color-mix(in srgb, var(--acc) 30%, var(--bdr));
        }
        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .cab-lib-item,
            body[data-ui-version="ascut"] .cab-lib-item .icn {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-CAB-LIB ─────────────────────────────────────────── */

        /* ── PR-ASCUT-GLOBAL — code/pre/kbd/hr/scrollbar polish ──────── */
        /* Code + pre — JetBrains Mono + accent-tinted bg */
        body[data-ui-version="ascut"] code {
            font-family: 'JetBrains Mono', ui-monospace, 'SF Mono', 'Consolas', monospace;
            font-size: 0.92em;
            padding: 1px 6px;
            background: color-mix(in srgb, var(--acc) 10%, var(--inp));
            border: 1px solid color-mix(in srgb, var(--acc) 16%, var(--bdr));
            border-radius: 4px;
            color: var(--txt);
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] pre {
            font-family: 'JetBrains Mono', ui-monospace, monospace;
            background: var(--inp);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            padding: 12px 14px;
            font-size: 12px;
            line-height: 1.55;
            font-variant-numeric: tabular-nums;
        }
        body[data-ui-version="ascut"] pre code {
            background: none;
            border: none;
            padding: 0;
            font-size: 1em;
        }
        /* kbd — keyboard shortcut hints */
        body[data-ui-version="ascut"] kbd {
            font-family: 'JetBrains Mono', ui-monospace, monospace;
            font-size: 10px;
            font-weight: 600;
            padding: 2px 7px;
            background: var(--bg);
            border: 1px solid color-mix(in srgb, var(--acc) 22%, var(--bdr));
            border-bottom-width: 2px;
            border-radius: 5px;
            color: var(--txt);
            letter-spacing: 0.04em;
            box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--acc) 14%, transparent);
        }
        /* hr — subtle accent gradient divider */
        body[data-ui-version="ascut"] hr {
            border: none;
            height: 1px;
            background: linear-gradient(90deg,
                transparent 0%,
                color-mix(in srgb, var(--acc) 25%, var(--bdr)) 50%,
                transparent 100%);
            margin: 24px 0;
        }
        /* Custom scrollbar (WebKit) — subtle Apple-feel. Firefox uses
           scrollbar-color which is a separate property. */
        body[data-ui-version="ascut"] ::-webkit-scrollbar {
            width: 12px;
            height: 12px;
        }
        body[data-ui-version="ascut"] ::-webkit-scrollbar-track {
            background: transparent;
        }
        body[data-ui-version="ascut"] ::-webkit-scrollbar-thumb {
            background: color-mix(in srgb, var(--dim) 35%, transparent);
            border: 3px solid transparent;
            border-radius: 999px;
            background-clip: padding-box;
            transition: background 140ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] ::-webkit-scrollbar-thumb:hover {
            background: color-mix(in srgb, var(--acc) 50%, var(--dim));
            background-clip: padding-box;
            border: 3px solid transparent;
        }
        body[data-ui-version="ascut"] ::-webkit-scrollbar-corner {
            background: transparent;
        }
        body[data-ui-version="ascut"] {
            scrollbar-color: color-mix(in srgb, var(--dim) 35%, transparent) transparent;
            scrollbar-width: thin;
        }
        /* Text selection — themed highlight */
        body[data-ui-version="ascut"] ::selection {
            background: color-mix(in srgb, var(--acc) 32%, transparent);
            color: var(--txt);
        }
        /* ── /PR-ASCUT-GLOBAL ──────────────────────────────────────────── */

        /* ── PR-ASCUT-OVERHAUL-1 — workspace bg + home hero strip ─────
           Structural change, not polish. Inject a brand-presence hero
           ABOVE the dash-bar on the home tab. Subtle gradient on the
           workspace bg. Gated to ascut mode so legacy themes
           inherit nothing. */

        /* PR-HOME-REDESIGN (2026-06-13): the workspace "radial accent" was
           the blue glow the operator flagged — a 1200x800 brand-accent
           ellipse anchored 200px ABOVE the viewport, bleeding a haze down
           behind the topbar on every page. Replaced with the flat base. */
        body[data-ui-version="ascut"] #app-workspace {
            background: var(--bg);
        }

        /* PR-THEME-LAYOUT-PARITY (2026-05-18): the AsCut brand
           additions below (home-hero banner, dashboard eyebrow,
           topbar/sidebar/statusbar wordmarks, per-page hero strips,
           admin pill, "Welcome to AsCut" banner, CF logomark)
           previously injected content via ::before / ::after that
           changed the page layout. These were AsCut-only — the
           other 6 themes didn't render them, so themes drifted in
           layout, not just color. Per user direction 2026-05-18:
           "they all should be the same in everyway other then the
           color change". Removed wholesale. KPI tile size bumps +
           OVERHAUL-4 CTA size bumps (next block) kept — they're
           tablet-ergonomic, not brand-specific, and now apply to
           every theme through the un-gated data-ui-version attr.
        */

        /* KPI strip — bigger numbers (tablet ergonomic, kept). */
        body[data-ui-version="ascut"] .kpi-tile {
            min-height: 112px;
            padding: 18px 20px;
        }
        body[data-ui-version="ascut"] .kpi-tile-value {
            font-size: 30px !important;
            font-weight: 700 !important;
            letter-spacing: -0.02em;
            line-height: 1.1;
        }

        /* ── PR-ASCUT-OVERHAUL-4 — bigger primary CTAs ────────────────
           Stop hiding the primary action behind subtle styling. AsCut
           CTAs are bigger, bolder, more confident — shop-floor reality
           (operator wants the gate to "create" clearly visible). */

        /* Hub page primary action (+ New job) — 52px min-height, bigger
           font, larger icon. Already gradient + shadow from PR-ASCUT-
           HUB; this PR bumps the size and presence. */
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action {
            min-height: 52px;
            padding: 0 24px;
            font-size: 15px;
            gap: 10px;
        }
        body[data-ui-version="ascut"] .hub-page-header .hub-primary-action svg {
            width: 20px;
            height: 20px;
        }

        /* Studio shortcuts — bump to 52px to match the primary */
        body[data-ui-version="ascut"] .hub-studio-shortcut {
            min-height: 52px;
            padding: 0 22px;
            font-size: 14px;
            gap: 10px;
        }
        body[data-ui-version="ascut"] .hub-studio-shortcut svg {
            width: 18px;
            height: 18px;
        }

        /* Big primary buttons globally (.ba but NOT .bs small variant) */
        body[data-ui-version="ascut"] .ba:not(.bs) {
            min-height: 44px;
            padding: 10px 22px;
            font-size: 13px;
        }

        /* Door studio Add Door CTA — already 56px; refine border-radius */
        body[data-ui-version="ascut"] .add-door-bar .ad-go {
            border-radius: 12px;
        }

        /* "+ New" / "+ Add" patterns — boost the icon + spacing */
        body[data-ui-version="ascut"] button > svg + *,
        body[data-ui-version="ascut"] button > * + svg {
            /* No-op — placeholder for icon-text spacing if needed */
        }

        /* Sidebar primary item (Hub) gets a slightly bolder treatment */
        body[data-ui-version="ascut"] #snav-hub {
            font-weight: 600;
        }
        /* ── /PR-ASCUT-OVERHAUL-4 ──────────────────────────────────────── */

        /* PR-THEME-LAYOUT-PARITY (2026-05-18): the "Welcome to AsCut"
           home hero (140px banner + 64px CF logomark badge) was the most
           visually disruptive theme-specific layout add. Operator
           feedback 2026-05-18: "espically cab fordge color scheme adds
           a big welcome thing on the top". Removed wholesale.
           The legacy "Jobs" dash-bar h1 is now the single visible page
           heading for the home tab on every theme — no longer
           hidden by an overriding ascut-only rule. */

        /* PR-ONE-BUILDER Phase 1 (2026-08-26): the entire Cutlist Studio
           style block (PR-STUDIO-1 .cs-* rules, all scoped to
           #pg-cutlist-studio) is gone with the page. */

        /* PR-THEME-LAYOUT-PARITY (2026-05-18): the OVERHAUL-6 Door
           Studio + Cabinet Studio page-hero strips with 🚪 / 🪚
           emoji badges injected content via ::before / ::after that
           every page rendered as an extra 110px chrome band. These
           added layout (not color) — removed wholesale so every theme
           shows the same page chrome. The OVERHAUL-3 base hero rules
           these scaled up are also removed above. */

        /* ── PR-ASCUT-MAT-SHEET — pull-up material picker sheet ─────────
           Replaces the drawer-box Side / Bottom <select> dropdowns with a
           modal sheet that slides up from the bottom. Subtle 4px blur on
           the content above (heavier scrim fallback for browsers that lack
           backdrop-filter). Cards show texture thumb + name + spec row +
           AWI Compliant pill. Advanced expander reveals search + category
           chips + recently-used (7-day window via localStorage).

           2026-05-14 UX direction: user explicitly asked for the blur. The
           "blur reads as loading" caveat from the 2026-04-23 page-scrim
           audit doesn't apply here — this sheet is unambiguously modal
           (grab handle + slide-up animation + dark scrim + headline). */
        body[data-ui-version="ascut"] .cf-mat-trigger {
            width: 100%;
            min-height: 44px;
            padding: 10px 14px;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            color: var(--txt);
            text-align: left;
            display: flex;
            align-items: center;
            justify-content: space-between;
            cursor: pointer;
            font-family: inherit;
            font-size: 14px;
            transition: border-color 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        /* PR-DBOX-TH-5 (cleanup 2026-05-15) — single-purpose card for the
           thickness override. Card frames the chip rail so the surface
           reads as one unit, not two random rows of inputs below the
           material pickers. Card title shows the override title +
           resolved value as a live-updating chip; the chip rail itself
           is the only required interaction; the Custom… escape hatch
           reveals the typed-input flow inline. 56px chips per WCAG +
           glove-allowance research. */
        .cf-thk-card {
            margin-top: 14px;
            padding: 14px 16px;
            border: 1px solid var(--bdr, #94A3B8);
            border-radius: 12px;
            background: var(--pn-h, var(--pn, #ffffff));
        }
        .cf-thk-card-head {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 12px;
        }
        .cf-thk-card-title {
            font-size: 13px;
            font-weight: 600;
            letter-spacing: 0.02em;
            text-transform: uppercase;
            color: var(--dim, #64748B);
        }
        .cf-thk-card-resolved {
            flex: 1;
            font-size: 14px;
            font-weight: 600;
            color: var(--acc, #60A5FA);
            min-height: 18px;
        }
        .cf-thk-chip-rail {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            align-items: center;
        }
        /* PR-PICK-ONLY-SHEETS (2026-08-26): a thickness an order asks for
           that its material no longer stocks. Never silently re-pointed —
           that would change what an existing order cuts — so it is shown
           as its own chip, flagged, and left selected. */
        .cf-thk-chip-unstocked {
            border-style: dashed !important;
            border-color: var(--warn, #B45309) !important;
            color: var(--warn, #B45309) !important;
        }
        .cf-thk-chip-unstocked .cf-thk-meas {
            color: var(--warn, #B45309);
            text-transform: uppercase;
            letter-spacing: .04em;
            font-size: 9px;
        }

        /* PR-PICK-THE-SHEET (2026-08-26): the measured size, under the
           nominal on a thickness chip. Quiet on purpose — the nominal is
           what the operator scans for; the measurement is the reassurance
           that the saw is getting the real number. */
        .cf-thk-meas {
            display: block;
            font-size: 10px;
            font-weight: 400;
            line-height: 1.1;
            margin-top: 2px;
            color: var(--dim, #64748B);
            font-variant-numeric: tabular-nums;
        }
        .cf-thk-chip-active .cf-thk-meas { color: inherit; opacity: .8; }

        .cf-thk-chip {
            min-height: 56px;
            min-width: 66px;
            padding: 10px 16px;
            background: var(--inp, var(--pn, #ffffff));
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 10px;
            color: var(--txt, #1E293B);
            font-family: inherit;
            font-size: 15px;
            font-weight: 600;
            letter-spacing: 0.01em;
            cursor: pointer;
            transition: border-color 120ms ease-out, background 120ms ease-out, color 120ms ease-out, transform 120ms ease-out;
        }
        .cf-thk-chip:hover,
        .cf-thk-chip:focus-visible {
            border-color: var(--acc, #60A5FA);
            outline: none;
            transform: translateY(-1px);
        }
        .cf-thk-chip.cf-thk-chip-active {
            background: var(--acc, #60A5FA);
            border-color: var(--acc, #60A5FA);
            color: var(--acc-ink, #ffffff);
            box-shadow: 0 1px 3px 0 rgb(var(--shadow-ink) / 0.08);
        }
        /* ── PR-DBOX-LUMBER-SOURCE (2026-08-24) — Sheets | Lumber rail ─────
           Sits in front of a drawer-box material dropdown and says which
           library that dropdown is listing. Same visual language as the
           thickness chips above (token colors, active = accent fill) but
           sized down: this rail rides INSIDE a form row and on a card
           header, next to 11px text, so a 56px chip would tower over it.
           Still 30px tall / 8px padding — a shop thumb has to hit it. */
        .cf-src-rail {
            display: inline-flex;
            gap: 4px;
            align-items: center;
            vertical-align: middle;
            margin-right: 6px;
        }
        .cf-src-chip {
            min-height: 30px;
            padding: 4px 10px;
            background: var(--inp, var(--pn, #ffffff));
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 999px;
            color: var(--txt, #1E293B);
            font-family: inherit;
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.02em;
            text-transform: uppercase;
            cursor: pointer;
            transition: border-color 120ms ease-out, background 120ms ease-out, color 120ms ease-out;
        }
        .cf-src-chip:hover,
        .cf-src-chip:focus-visible {
            border-color: var(--acc, #60A5FA);
            outline: none;
        }
        .cf-src-chip.cf-src-chip-active {
            background: var(--acc, #60A5FA);
            border-color: var(--acc, #60A5FA);
            color: var(--acc-ink, #ffffff);
        }
        /* In the wizard's full-screen picker sheet the rail is the primary
           control on the row, so it gets the roomier touch target. */
        .cf-src-rail-sheet {
            display: flex;
            gap: 8px;
            margin: 0 0 10px 0;
        }
        .cf-src-rail-sheet .cf-src-chip {
            min-height: 40px;
            padding: 8px 18px;
            font-size: 13px;
        }
        /* ── PR-DBOX-THK-FROM-LIBRARY (2026-08-24) — thickness picker ─────
           Replaces the chip rails beside a drawer-box material. Compact
           because it now rides in three places, including an 11px card
           header row. A blocking note (unstocked sheet size, lumber too
           thin to yield the part) turns the control's edge red and the
           surface refuses to save. */
        /* ── PR-DBOX-THK-FROM-LIBRARY (2026-08-24) — mm / inch rail in the
           Sheet Goods editor. Same pill language as the Sheets|Lumber rail so
           the two read as one family of "which mode am I in" controls. */
        /* 2026-08-25 owner correction: the unit pills sit INSIDE the thickness
           field, tucked against its right edge — "make the mm and inches button
           inside the thickness button like at the end of it". The field is a
           flex group; the input takes the slack and the rail rides at the end. */
        .mat-thk-inputgroup {
            display: flex;
            align-items: center;
            gap: 6px;
            width: 100%;
        }
        .mat-thk-inputgroup > input {
            flex: 1 1 auto;
            min-width: 0;
        }
        .mat-unit-rail {
            display: inline-flex;
            gap: 3px;
            align-items: center;
            flex: 0 0 auto;
        }
        .mat-unit-chip {
            min-height: 28px;
            padding: 4px 10px;
            background: var(--inp, var(--pn, #ffffff));
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 999px;
            color: var(--txt, #1E293B);
            font-family: inherit;
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.02em;
            cursor: pointer;
        }
        .mat-unit-chip.mat-unit-chip-active {
            background: var(--acc, #60A5FA);
            border-color: var(--acc, #60A5FA);
            color: var(--acc-ink, #ffffff);
        }
        .mat-unit-echo {
            font-size: 11px;
            color: var(--dim, #5A6573);
            font-variant-numeric: tabular-nums;
        }
        .cf-thk-pick {
            display: inline-flex;
            flex-direction: column;
            gap: 4px;
            vertical-align: middle;
            min-width: 0;
        }
        .cf-thk-pick > select {
            min-height: 30px;
            max-width: 100%;
            padding: 4px 8px;
            background: var(--inp, var(--pn, #ffffff));
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 6px;
            color: var(--txt, #1E293B);
            font-family: inherit;
            font-size: 12px;
        }
        .cf-thk-pick-blocked > select {
            border-color: var(--no, #D33A4B);
        }
        .cf-thk-pick-empty > select {
            font-style: italic;
            opacity: 0.75;
        }
        .cf-thk-custom-in {
            min-height: 30px;
            padding: 4px 8px;
            background: var(--inp, var(--pn, #ffffff));
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 6px;
            color: var(--txt, #1E293B);
            font-family: inherit;
            font-size: 12px;
        }
        .cf-thk-note {
            font-size: 11px;
            line-height: 1.35;
            max-width: 34ch;
        }
        .cf-thk-note-warn {
            color: var(--warn, #B45309);
        }
        .cf-thk-note-blocking {
            color: var(--no, #D33A4B);
            font-weight: 600;
        }
        .cf-thk-chip-custom {
            font-style: italic;
            font-weight: 500;
            opacity: 0.92;
        }
        .cf-thk-custom-wrap {
            margin-top: 12px;
            padding-top: 12px;
            border-top: 1px dashed var(--bdr, #94A3B8);
        }
        .cf-thk-custom-wrap input[type="text"] {
            width: 100%;
            min-height: 48px;
            padding: 10px 14px;
            font-size: 16px;
            font-family: inherit;
            border: 1.5px solid var(--bdr, #94A3B8);
            border-radius: 8px;
            background: var(--inp, var(--pn, #ffffff));
            color: var(--txt, #1E293B);
        }
        .cf-thk-custom-wrap input[type="text"]:focus {
            border-color: var(--acc, #60A5FA);
            outline: none;
        }
        .cf-thk-hint {
            font-size: 12px;
            color: var(--dim, #64748B);
            line-height: 1.4;
            margin-top: 8px;
            min-height: 18px;
        }
        .cf-thk-hint.cf-thk-hint-ok    { color: var(--ok, #06B6D4); }
        .cf-thk-hint.cf-thk-hint-warn  { color: var(--warn, #F59E0B); }
        .cf-thk-hint.cf-thk-hint-block { color: var(--no, #F43F5E); font-weight: 600; }
        .cf-thk-verify {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-top: 12px;
            min-height: 44px;
            font-size: 13px;
            color: var(--txt, #1E293B);
            cursor: pointer;
        }
        .cf-thk-verify input[type="checkbox"] {
            width: 20px;
            height: 20px;
            cursor: pointer;
            accent-color: var(--acc, #60A5FA);
        }
        body[data-ui-version="ascut"] .cf-mat-trigger:hover,
        body[data-ui-version="ascut"] .cf-mat-trigger:focus-visible {
            border-color: var(--acc);
            outline: none;
        }
        body[data-ui-version="ascut"] .cf-mat-trigger-label {
            flex: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        body[data-ui-version="ascut"] .cf-mat-trigger-label[data-empty="true"] {
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .cf-mat-trigger-chevron {
            margin-left: 8px;
            color: var(--dim);
            font-size: 11px;
        }

        body[data-ui-version="ascut"] .cf-mat-scrim {
            position: fixed;
            inset: 0;
            z-index: 60;
            background: rgba(0, 0, 0, 0.30);
            backdrop-filter: blur(4px);
            -webkit-backdrop-filter: blur(4px);
            opacity: 0;
            pointer-events: none;
            transition: opacity 150ms cubic-bezier(0.32, 0.72, 0.24, 1);
        }
        body[data-ui-version="ascut"] .cf-mat-scrim[data-open="true"] {
            opacity: 1;
            pointer-events: auto;
        }
        @supports not (backdrop-filter: blur(4px)) {
            body[data-ui-version="ascut"] .cf-mat-scrim {
                background: rgba(0, 0, 0, 0.45);
            }
        }

        body[data-ui-version="ascut"] .cf-mat-sheet {
            position: fixed;
            left: 0;
            right: 0;
            bottom: 0;
            z-index: 61;
            max-height: 85vh;
            background: var(--pn);
            border-top-left-radius: 16px;
            border-top-right-radius: 16px;
            box-shadow: 0 -8px 28px rgb(var(--shadow-ink) / 0.18);
            transform: translateY(100%);
            transition: transform 320ms cubic-bezier(0.25, 0.1, 0.25, 1.0);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet[data-open="true"] {
            transform: translateY(0);
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-grab {
            width: 40px;
            height: 4px;
            background: var(--dim);
            border: none;
            border-radius: 2px;
            margin: 10px auto 0;
            opacity: 0.4;
            cursor: pointer;
            flex-shrink: 0;
            padding: 0;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-header {
            padding: 12px 20px 14px;
            border-bottom: 1px solid var(--bdr);
            display: flex;
            align-items: flex-start;
            gap: 12px;
            flex-shrink: 0;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-header-text {
            flex: 1;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-header h2 {
            margin: 0;
            font-size: 18px;
            font-weight: 600;
            color: var(--txt);
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-header p {
            margin: 2px 0 0;
            font-size: 13px;
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-close {
            background: none;
            border: none;
            color: var(--dim);
            font-size: 24px;
            cursor: pointer;
            padding: 0;
            width: 32px;
            height: 32px;
            line-height: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-close:hover { background: var(--bg); }

        body[data-ui-version="ascut"] .cf-mat-sheet-body {
            overflow-y: auto;
            padding: 16px 20px 24px;
            flex: 1;
            -webkit-overflow-scrolling: touch;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-section-head {
            display: flex;
            align-items: center;
            gap: 8px;
            margin: 16px 0 10px;
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 0.08em;
            color: var(--dim);
            text-transform: uppercase;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-section-head:first-child { margin-top: 0; }
        body[data-ui-version="ascut"] .cf-mat-sheet-section-head .cf-mat-star {
            color: #f5b400;
            font-size: 14px;
        }

        body[data-ui-version="ascut"] .cf-mat-card {
            display: grid;
            grid-template-columns: 60px 1fr auto;
            gap: 12px;
            align-items: center;
            width: 100%;
            min-height: 72px;
            padding: 10px 12px;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 10px;
            margin-bottom: 8px;
            cursor: pointer;
            text-align: left;
            font-family: inherit;
            color: var(--txt);
            transition: border-color 120ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 120ms cubic-bezier(0.22, 1, 0.36, 1),
                        transform 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cf-mat-card:hover,
        body[data-ui-version="ascut"] .cf-mat-card:focus-visible {
            border-color: var(--acc);
            box-shadow: 0 2px 10px color-mix(in srgb, var(--acc) 14%, transparent);
            transform: translateY(-1px);
            outline: none;
        }
        body[data-ui-version="ascut"] .cf-mat-card[data-selected="true"] {
            border-color: var(--acc);
            box-shadow: 0 0 0 1px var(--acc) inset;
        }
        body[data-ui-version="ascut"] .cf-mat-card-thumb {
            width: 60px;
            height: 60px;
            border-radius: 6px;
            background: linear-gradient(135deg, #d6c4a8, #b5996d);
            background-size: cover;
            background-position: center;
            border: 1px solid var(--bdr);
            flex-shrink: 0;
        }
        body[data-ui-version="ascut"] .cf-mat-card-body {
            display: flex;
            flex-direction: column;
            gap: 3px;
            min-width: 0;
        }
        body[data-ui-version="ascut"] .cf-mat-card-name {
            font-size: 14px;
            font-weight: 600;
            color: var(--txt);
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        body[data-ui-version="ascut"] .cf-mat-card-spec {
            font-size: 12px;
            color: var(--dim);
        }
        body[data-ui-version="ascut"] .cf-mat-card-meta {
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            gap: 4px;
        }
        body[data-ui-version="ascut"] .cf-mat-pill {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.04em;
            padding: 3px 7px;
            border-radius: 4px;
            background: color-mix(in srgb, #f5b400 24%, transparent);
            color: #8a6a00;
            text-transform: uppercase;
            white-space: nowrap;
        }
        /* PR-MAT-CARD-TRUTH (2026-08-30): the flag that replaced the AWI pill.
           That one rendered on every row and so said nothing; this one fires
           only when the sheet is not stocked in a thickness the part can be
           made from. Muted rather than alarming — it is a fact about the stock,
           not an error the operator made, and the row stays pickable because a
           shop may well order it in. */
        body[data-ui-version="ascut"] .cf-mat-pill-no {
            background: color-mix(in srgb, var(--dim) 16%, transparent);
            color: var(--dim);
            text-transform: none;
            letter-spacing: 0;
            font-weight: 600;
        }
        body[data-ui-version="ascut"] .cf-mat-card-code {
            font-size: 10px;
            color: var(--dim);
            font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        }

        body[data-ui-version="ascut"] .cf-mat-sheet-advanced > summary {
            list-style: none;
            cursor: pointer;
            padding: 12px 0;
            border-top: 1px solid var(--bdr);
            margin-top: 12px;
            font-size: 12px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--txt);
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-advanced > summary::-webkit-details-marker { display: none; }
        body[data-ui-version="ascut"] .cf-mat-sheet-advanced > summary::after {
            content: '▾';
            color: var(--dim);
            font-size: 11px;
            transition: transform 120ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-advanced[open] > summary::after {
            transform: rotate(-180deg);
        }

        body[data-ui-version="ascut"] .cf-mat-sheet-search { margin: 10px 0 12px; }
        body[data-ui-version="ascut"] .cf-mat-sheet-search input {
            width: 100%;
            min-height: 40px;
            padding: 8px 12px;
            background: var(--bg);
            border: 1px solid var(--bdr);
            border-radius: 8px;
            color: var(--txt);
            font-family: inherit;
            font-size: 14px;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-search input:focus {
            outline: none;
            border-color: var(--acc);
        }

        body[data-ui-version="ascut"] .cf-mat-sheet-chips {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-bottom: 14px;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-chips button {
            padding: 6px 12px;
            background: var(--pn);
            border: 1px solid var(--bdr);
            border-radius: 999px;
            color: var(--txt);
            font-size: 12px;
            cursor: pointer;
            min-height: 32px;
            font-family: inherit;
        }
        body[data-ui-version="ascut"] .cf-mat-sheet-chips button[aria-selected="true"] {
            background: var(--acc);
            color: #fff;
            border-color: var(--acc);
        }

        body[data-ui-version="ascut"] .cf-mat-sheet-empty {
            padding: 24px 0;
            text-align: center;
            color: var(--dim);
            font-size: 13px;
        }

        @media (prefers-reduced-motion: reduce) {
            body[data-ui-version="ascut"] .cf-mat-sheet,
            body[data-ui-version="ascut"] .cf-mat-scrim {
                transition: none;
            }
        }
        /* ── /PR-ASCUT-MAT-SHEET ───────────────────────────────────────── */
    
/* ================================================================
   FORGE TERMINAL — component primitives (PR-UI-FORGE 2026-06-13)
   Zero new hex; everything references the active theme tokens.
   ================================================================ */

/* Command/search bar in the topbar (.topbar-search slot) */
.forge-cmd-bar {
    display: flex; align-items: center; gap: 8px;
    height: 34px; max-width: 560px; width: 100%;
    padding: 0 10px; cursor: pointer;
    background: var(--inp);
    border: 1px solid var(--bdr);
    border-radius: 6px;
    color: var(--dim);
}
.forge-cmd-bar:hover,
.forge-cmd-bar:focus-within,
.forge-cmd-bar:focus-visible {
    border-color: var(--acc);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--acc) 22%, transparent);
    outline: none;
}
.forge-cmd-bar svg { flex: 0 0 16px; color: var(--dim); }
.forge-cmd-ph { flex: 1; font: 13px var(--ff-ui); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.forge-kbd {
    font: 11px var(--ff-mono); color: var(--dim);
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 3px;
    padding: 1px 6px; margin-left: auto; flex: 0 0 auto;
}

/* "New ▾" split button (.topbar-actions) */
.btn-new-wrap { position: relative; display: inline-flex; border-radius: 6px; overflow: visible; }
.btn-new-main, .btn-new-caret {
    background: var(--acc); color: var(--acc-ink);
    border: none; font: 600 13px/1 var(--ff-ui); cursor: pointer;
    height: 32px; display: inline-flex; align-items: center; justify-content: center;
}
.btn-new-main { padding: 0 12px; border-radius: 6px 0 0 6px; gap: 6px; }
.btn-new-caret { padding: 0 8px; border-radius: 0 6px 6px 0; border-left: 1px solid color-mix(in srgb, var(--acc-ink) 28%, transparent); }
.btn-new-main:hover, .btn-new-caret:hover { background: var(--acc2); }
.btn-new-menu {
    position: absolute; top: calc(100% + 6px); right: 0; min-width: 220px;
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 8px;
    box-shadow: 0 10px 30px var(--scrim, rgb(var(--shadow-ink) / .5));
    padding: 5px; z-index: 1200;
}
.btn-new-menu[hidden] { display: none; }
.btn-new-menu button {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    width: 100%; padding: 9px 11px; border: none; background: transparent;
    color: var(--txt); font: 13px var(--ff-ui); cursor: pointer; border-radius: 5px; text-align: left;
}
.btn-new-menu button:hover { background: var(--pn-h); }
.btn-new-menu kbd {
    font: 11px var(--ff-mono); color: var(--dim);
    background: var(--inp); border: 1px solid var(--bdr); border-radius: 3px; padding: 1px 5px;
}
@media (pointer: coarse) and (hover: none) { .btn-new-main, .btn-new-caret { height: 44px; } }

/* ================================================================
   FORGE JOBS GRID + PILLS (PR-UI-FORGE-2 2026-06-13)
   ================================================================ */
.forge-jobs-grid { width: 100%; border-collapse: collapse; }
.forge-jobs-grid thead th {
    font: 600 11px/1 var(--ff-ui); letter-spacing: .06em; text-transform: uppercase;
    color: var(--dim); text-align: left; padding: 9px 12px;
    border-bottom: 1px solid var(--bdr); background: var(--pn); white-space: nowrap;
}
.forge-jobs-grid tbody td {
    font: 13px/1.35 var(--ff-ui); color: var(--txt);
    padding: 9px 12px; border-bottom: 1px solid var(--bdr); vertical-align: middle;
}
.forge-jobs-grid tbody tr { cursor: pointer; transition: none; }
.forge-jobs-grid tbody tr:hover { background: var(--pn-h); }
.forge-jobs-grid .fjg-job { font-weight: 600; }
.forge-jobs-grid .fjg-type { width: 64px; }
.forge-jobs-grid .fjg-scope, .forge-jobs-grid .fjg-upd { width: 92px; color: var(--dim); }
.forge-jobs-grid td:last-child { width: 28px; text-align: right; color: var(--muted); }
.forge-mono { font-family: var(--ff-mono); font-variant-numeric: tabular-nums; }
.forge-dim, .forge-jobs-grid .forge-dim { color: var(--muted); }

/* Status / type pills — color-mix tints over theme tokens (no new hex) */
.forge-pill {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px; border-radius: 4px;
    font: 600 11px/1.6 var(--ff-ui); letter-spacing: .02em; white-space: nowrap;
}
.pill-draft    { background: color-mix(in srgb, var(--dim)  16%, transparent); color: var(--dim);  }
.pill-quoted   { background: color-mix(in srgb, var(--acc)  16%, transparent); color: var(--acc);  }
.pill-inprod   { background: color-mix(in srgb, var(--acc)  26%, transparent); color: var(--acc2); }
.pill-qc       { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.pill-shipped  { background: color-mix(in srgb, var(--ok)   18%, transparent); color: var(--ok);   }
.pill-blocked  { background: color-mix(in srgb, var(--no)   16%, transparent); color: var(--no);
                 border: 1px solid color-mix(in srgb, var(--no) 32%, transparent); }
.pill-cabinet  { background: color-mix(in srgb, var(--acc-cabinet) 18%, transparent); color: var(--acc-cabinet);
                 border: 1px solid color-mix(in srgb, var(--acc-cabinet) 32%, transparent); }
.pill-door     { background: color-mix(in srgb, var(--acc-door) 22%, transparent); color: color-mix(in srgb, var(--acc-door) 70%, var(--txt));
                 border: 1px solid color-mix(in srgb, var(--acc-door) 40%, transparent); }
/* PR-FORGE-DOORS: rush marker on door-order rows. */
.pill-rush     { margin-left: 4px; background: color-mix(in srgb, var(--no) 18%, transparent); color: var(--no);
                 border: 1px solid color-mix(in srgb, var(--no) 38%, transparent); letter-spacing: .04em; }

/* PR-UI-FORGE-3: jobs header controls (type segment + status filter) */
.forge-jobs-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.forge-seg { position: relative; }
.forge-seg::after {
    content: ""; position: absolute; right: -7px; top: 50%; transform: translateY(-50%);
    height: 18px; width: 1px; background: var(--bdr);
}

/* ================================================================
   PR-UI-FORGE-4 — KPI strip → flat data ribbon (mono values)
   ================================================================ */
.kpi-strip .kpi-tile-value {
    font-family: var(--ff-mono, monospace) !important;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
    transition: none;              /* snap, never animate a number */
}
.kpi-strip .kpi-tile-label {
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 11px;
    color: var(--dim);
}
/* Denser ribbon: tighter tiles, hairline separators, no rounded float */
.kpi-strip .kpi-tile { padding-top: 12px; padding-bottom: 12px; }

/* ================================================================
   PR-UI-FORGE-6 — KPI strip → one flush data-ribbon (not floating
   cards). Continuous panel, hairline cell separators, no per-tile
   rounding/lift. The pro-console "telemetry strip" look.
   ================================================================ */
.kpi-strip {
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-radius: 8px;
    overflow: hidden;
    gap: 0 !important;
}
.kpi-strip .kpi-tile {
    border: none !important;
    border-radius: 0 !important;
    border-left: 1px solid var(--bdr) !important;
    background: transparent !important;
    box-shadow: none !important;
}
.kpi-strip .kpi-tile:first-child { border-left: none !important; }
.kpi-strip .kpi-tile:hover { background: var(--pn-h) !important; }

/* ================================================================
   PR-UI-FORGE-7 — COMMAND RAIL (data panels beside the jobs grid)
   ================================================================ */
.forge-side {
    flex: 0 0 340px; min-width: 0;
    display: flex; flex-direction: column; gap: var(--sp-4, 14px);
}
.forge-panel {
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 8px;
    padding: 12px 14px;
}
.forge-panel-actions { padding-bottom: 14px; }
.forge-panel-h {
    margin: 0 0 10px; font: 600 11px/1 var(--ff-ui); letter-spacing: .07em;
    text-transform: uppercase; color: var(--dim);
    display: flex; align-items: center;
}
.forge-panel-count { color: var(--acc); font-family: var(--ff-mono); letter-spacing: 0; }
.forge-panel-body { display: flex; flex-direction: column; gap: 2px; }
.forge-panel-empty { font: 12px var(--ff-ui); color: var(--muted); padding: 4px 0; }
/* PR-HOME-REDESIGN (2026-06-13): right-aligned "Floor ↗" jump link in a panel
   header + the Needs-Attention severity tags (late/rush/qc), matching the
   forge-pill color budget. */
.forge-panel-link {
    margin-left: auto; text-transform: none; letter-spacing: 0;
    font: 600 11px var(--ff-ui); color: var(--acc); text-decoration: none;
}
.forge-panel-link:hover { text-decoration: underline; }
.forge-att-tag {
    flex: 0 0 auto; font: 600 10px/1 var(--ff-mono); letter-spacing: .04em;
    padding: 3px 6px; border-radius: 4px; white-space: nowrap;
}
.forge-att-tag.is-late { background: color-mix(in srgb, var(--no) 16%, transparent);   color: var(--no); }
.forge-att-tag.is-rush { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.forge-att-tag.is-qc   { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }

/* Pipeline rows */
.forge-pipe-row { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.forge-pipe-label { flex: 0 0 96px; font: 12px var(--ff-ui); color: var(--txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.forge-pipe-bar { flex: 1; height: 6px; background: var(--inp); border-radius: 3px; overflow: hidden; }
.forge-pipe-fill { display: block; height: 6px; border-radius: 3px; background: var(--acc); }
.forge-pipe-fill.stage-pending    { background: var(--muted); }
.forge-pipe-fill.stage-scheduled  { background: var(--acc); }
.forge-pipe-fill.stage-production  { background: var(--acc2); }
.forge-pipe-fill.stage-qc          { background: var(--warn); }
.forge-pipe-fill.stage-shipped     { background: var(--ok); }
.forge-pipe-n { flex: 0 0 24px; text-align: right; font: 12px var(--ff-mono); color: var(--dim); }

/* Panel list rows (in-production, recent) */
.forge-prow {
    display: flex; align-items: center; gap: 8px; padding: 6px 4px;
    border-radius: 5px; cursor: pointer;
}
.forge-prow:hover { background: var(--pn-h); }
.forge-prow:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
.forge-prow-dot { flex: 0 0 7px; width: 7px; height: 7px; border-radius: 50%; }
.forge-prow-dot.is-cab  { background: var(--acc-cabinet); }
.forge-prow-dot.is-door { background: var(--acc-door); }
.forge-prow-name { flex: 1; font: 13px var(--ff-ui); color: var(--txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.forge-prow-meta { flex: 0 0 auto; font: 11px var(--ff-mono); color: var(--dim); }

/* Quick actions */
.forge-quick { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.forge-quick button {
    font: 600 12px/1 var(--ff-ui); color: var(--txt);
    background: var(--inp); border: 1px solid var(--bdr); border-radius: 6px;
    padding: 9px 8px; cursor: pointer; text-align: center;
}
.forge-quick button:hover { border-color: var(--acc); color: var(--acc); }

/* Responsive: command rail drops below the grid on narrow screens */
@media (max-width: 1100px) {
    .forge-side { flex-basis: auto; width: 100%; }
}

/* ================================================================
   PR-UI-FORGE-8 — twin studio launchers below the KPI ribbon.
   Cabinet Studio is admin-only; the auto-FIT grid collapses its
   track + gap when display:none'd (shell.css:2235) so Door Studio
   fills the row cleanly (auto-FILL would leave a dead column).
   ================================================================ */
.forge-studio-launch {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
    margin: 14px 0 18px;
}
.forge-studio-btn {
    display: flex; align-items: center; gap: 14px;
    min-height: 76px; padding: 0 22px; text-align: left; cursor: pointer;
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-left-width: 3px; border-left-color: var(--bdr);
    border-radius: 8px;
    font-family: var(--ff-ui); color: var(--txt);
    transition: border-color 140ms cubic-bezier(.22,1,.36,1),
                background   140ms cubic-bezier(.22,1,.36,1);
}
.forge-studio-btn.is-cabinet { border-left-color: color-mix(in srgb, var(--acc-cabinet) 60%, var(--bdr)); }
.forge-studio-btn.is-door    { border-left-color: color-mix(in srgb, var(--acc-door) 65%, var(--bdr)); }
.fsb-ico { flex: 0 0 38px; width: 38px; height: 38px; display: grid; place-items: center; border-radius: 6px; }
.fsb-ico svg { width: 20px; height: 20px; }
.forge-studio-btn.is-cabinet .fsb-ico { color: var(--acc-cabinet); background: color-mix(in srgb, var(--acc-cabinet) 14%, transparent); }
.forge-studio-btn.is-door    .fsb-ico { color: color-mix(in srgb, var(--acc-door) 80%, var(--txt)); background: color-mix(in srgb, var(--acc-door) 18%, transparent); }
.fsb-txt { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.fsb-title { font-size: 15px; font-weight: 600; line-height: 1.2; letter-spacing: -.01em; color: var(--txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fsb-sub { font-size: 11px; font-weight: 400; font-family: var(--ff-mono); line-height: 1; letter-spacing: .06em; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.forge-studio-btn:hover { border-color: var(--bdr-h); }
.forge-studio-btn.is-cabinet:hover { background: color-mix(in srgb, var(--acc-cabinet) 7%, var(--pn)); border-left-color: var(--acc-cabinet); }
.forge-studio-btn.is-door:hover    { background: color-mix(in srgb, var(--acc-door) 8%, var(--pn)); border-left-color: var(--acc-door); }
.forge-studio-btn:active { background: color-mix(in srgb, var(--acc) 5%, var(--pn)); }
.forge-studio-btn:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
/* Lone Door button (non-admin): a touch taller so it reads as a deliberate anchor */
body:not([data-role="admin"]) .forge-studio-btn { min-height: 64px; }
@media (prefers-reduced-motion: reduce) { .forge-studio-btn { transition: none; } }

/* ==================================================================
   PR-FORGE-DENSITY (2026-06-13) — operator: "scrolls down too much /
   takes up too much space." Tighten the top chrome (header → KPI →
   studio launchers) so the jobs grid is in view with minimal scroll.
   app.css loads after shell.css, so these win for equal specificity.
   ================================================================== */
/* Header: drop the consumer-app subtitle + slim the padding. The h1
   (shop name) carries identity; the generic tagline was a "website"
   signal + a redundant second line above the jobs heading. */
#hub-template-body .hub-page-header { padding: 16px var(--sp-6) 6px; }
#hub-template-body .hub-page-header .hub-subtitle { display: none; }
#hub-template-body .hub-page-header h1 { font-size: 20px; line-height: 1.15; }

/* KPI ribbon: shorter tiles, tighter pad + top gap. The number stays
   mono + prominent for shop-floor scan, just a touch smaller. */
#hub-template-body .kpi-strip { padding: 12px var(--sp-6) 0; gap: 10px; }
#hub-template-body .kpi-tile { min-height: 56px; padding: 10px 14px; gap: 2px; }
#hub-template-body .kpi-tile-icon { width: 26px; height: 26px; margin-bottom: 4px; }
#hub-template-body .kpi-tile-value { font-size: 23px; }
#hub-template-body .kpi-tile-label { font-size: 10px; }

/* Studio launchers: shorter, tighter margins. */
.forge-studio-launch { margin: 10px 0 12px; gap: 10px; }
.forge-studio-btn { min-height: 58px; padding: 0 18px; gap: 12px; }
.forge-studio-btn .fsb-ico { flex-basis: 32px; width: 32px; height: 32px; }
.forge-studio-btn .fsb-ico svg { width: 17px; height: 17px; }
.forge-studio-btn .fsb-title { font-size: 14px; }

/* Command-center area: pull it up under the launchers. */
#hub-template-body .hub-content-area { padding-top: 0; }

/* PR-FORGE-NAV (2026-06-13): small "opens in new tab" glyph on nav items
   that leave the SPA (Shop Floor). Pushed to the right, dimmed. */
.sidebar-nav-item .nav-ext { margin-left: auto; padding-left: 6px; font-size: 12px; line-height: 1; color: var(--muted, var(--dim)); opacity: .75; }
.sidebar-nav-item:hover .nav-ext { opacity: 1; }

/* PR-FORGE-HUB-STATES (2026-06-13): jobs-grid empty state — icon + message. */
.hub-jobs-empty .forge-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px 16px; color: var(--dim); }
.hub-jobs-empty .forge-empty-ico { color: var(--muted, var(--dim)); opacity: .7; }
.hub-jobs-empty .forge-empty-msg { font-size: 13px; }
/* PR-FORGE-HUB-PROGRESS (2026-06-13): coarse stage-ordinal progress under
   the Stage pill. Display-only; fill colors mirror the pipeline stages. */
.forge-jobs-grid .fjg-stage { white-space: nowrap; }
/* PR-HOME-REDESIGN BF-3 (2026-06-13): .fjg-prog (per-row mini stage bar)
   removed — it added vertical bulk; the stage pill carries the stage. */

/* PR-FORGE-HUB-DUEDATE (2026-06-13): due-date urgency coloring. */
.forge-jobs-grid .fjg-due { white-space: nowrap; }
.forge-jobs-grid .fjg-due[data-urgency="soon"] { color: var(--warn); }
.forge-jobs-grid .fjg-due[data-urgency="now"]  { color: var(--no); font-weight: 600; }

/* PR-FORGE-CAB-CUSTOMERS (2026-06-13): forge hairline data-grid + summary
   strip (replaces the sparse .li card list on the Customers tab). */
.cust-strip{display:flex;flex-wrap:wrap;gap:18px;align-items:baseline;padding:10px 14px;margin-bottom:10px;background:var(--pn);border:1px solid var(--bdr);border-radius:8px;font-size:12px;color:var(--dim);}
.cust-strip b{color:var(--txt);font-weight:600;font-variant-numeric:tabular-nums;}
.cust-strip .cust-strip-sep{width:1px;align-self:stretch;background:var(--bdr);}
.cust-grid{width:100%;border-collapse:collapse;font-size:12.5px;background:var(--sf);border:1px solid var(--bdr);border-radius:8px;overflow:hidden;}
.cust-grid thead th{background:var(--pn);color:var(--dim);text-align:left;font-weight:700;font-size:10px;letter-spacing:.8px;text-transform:uppercase;padding:9px 12px;border-bottom:1px solid var(--bdr);white-space:nowrap;}
.cust-grid thead th.num{text-align:right;}
.cust-grid thead th.cg-actions{text-align:right;}
.cust-grid tbody td{padding:9px 12px;border-bottom:1px solid var(--bdr);color:var(--txt);vertical-align:middle;}
.cust-grid tbody td.num{text-align:right;font-variant-numeric:tabular-nums;}
.cust-grid tbody td.dim{color:var(--dim);}
.cust-grid tbody tr:hover{background:var(--pn-h);}
.cust-grid .cg-name{color:var(--txt);font-weight:600;}
.cust-grid .cg-actions{text-align:right;white-space:nowrap;}
.cust-grid .cg-detail td{background:var(--bg);padding:0 12px;border-bottom:1px solid var(--bdr);}
.cust-grid .cg-detail .cg-detail-inner{padding:6px 0 10px;}
@media(max-width:760px){.cust-grid thead{display:none;}.cust-grid,.cust-grid tbody,.cust-grid tr,.cust-grid td{display:block;width:100%;}.cust-grid tbody tr{border-bottom:1px solid var(--bdr);padding:6px 0;}.cust-grid tbody td{border:none;padding:3px 12px;}.cust-grid tbody td.num{text-align:left;}}
/* PR-CUST-MASTER-DETAIL (2026-08-03): Customers as a searchable full-width ROW
   list; clicking a row swaps #cust-list in place for that customer's full detail.
   App theme tokens only (QuickBooks-flavored, not a copy). Legacy .cust-grid /
   .cust-card rules above are unused now (kept, harmless). */
.cust-avatar{flex:none;width:42px;height:42px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px;color:#fff;letter-spacing:.5px;text-transform:uppercase;user-select:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.14);}
.cust-avatar-lg{width:54px;height:54px;font-size:19px;}
.cust-rows{display:flex;flex-direction:column;gap:8px;}
.cust-row{display:flex;align-items:center;gap:12px;width:100%;padding:11px 14px;background:var(--pn);border:1px solid var(--bdr);border-radius:10px;cursor:pointer;transition:border-color .12s ease,background .12s ease;}
.cust-row:hover{border-color:var(--acc);background:var(--pn-h,var(--pn));}
.cust-row:focus-visible{outline:2px solid var(--acc);outline-offset:1px;}
.cust-row-av{flex:none;width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12.5px;color:#fff;text-transform:uppercase;user-select:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.14);}
.cust-row-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px;}
.cust-row-name{font-size:14.5px;font-weight:700;color:var(--txt);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px;}
.cust-row-qb{font-size:9px;font-weight:700;letter-spacing:.4px;color:var(--ok);background:rgba(46,160,67,.16);border-radius:4px;padding:1px 5px;flex:none;}
.cust-row-sub{font-size:12px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.cust-row-meta{flex:none;display:flex;align-items:center;gap:14px;}
.cust-row-jobs{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap;}
.cust-row-bal{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;}
.cust-row-bal.owed{color:var(--no);}
.cust-row-bal.credit{color:var(--acc);}
.cust-row-bal.paid{color:var(--ok);}
.cust-row-chev{flex:none;color:var(--dim);font-size:20px;line-height:1;}
@media(max-width:600px){.cust-row-sub{white-space:normal;}.cust-row-meta{gap:8px;}}
.cust-detail{background:var(--pn);border:1px solid var(--bdr);border-radius:12px;padding:16px 18px;box-shadow:var(--shadow-card);}
.cust-detail-bar{display:flex;align-items:center;gap:8px;margin-bottom:14px;flex-wrap:wrap;}
.cust-detail-spacer{flex:1 1 auto;}
.cust-detail-head{display:flex;align-items:center;gap:14px;padding-bottom:14px;border-bottom:1px solid var(--bdr);margin-bottom:14px;}
.cust-detail-hid{min-width:0;}
.cust-detail-name{font-size:20px;font-weight:800;color:var(--txt);display:flex;align-items:center;flex-wrap:wrap;gap:6px;word-break:break-word;}
.cust-detail-sub{font-size:13px;color:var(--dim);margin-top:2px;}
.cust-detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;}
.cust-detail-sec{margin-top:16px;}
.cust-detail-grid .cust-detail-sec{margin-top:0;}
.cust-detail-sec h4{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--dim);}
.cust-dl{display:flex;justify-content:space-between;gap:12px;font-size:13px;padding:3px 0;border-bottom:1px dashed color-mix(in srgb,var(--bdr) 70%,transparent);}
.cust-dl:last-child{border-bottom:none;}
.cust-dl span{color:var(--dim);flex:none;}
.cust-dl b{color:var(--txt);font-weight:600;text-align:right;word-break:break-word;}
.cust-dl-empty{font-size:13px;color:var(--dim);}
.cust-detail-jobs>div{display:flex;flex-wrap:wrap;}
.cust-detail-notes{font-size:13px;color:var(--txt);white-space:pre-wrap;word-break:break-word;background:var(--pn);border:1px solid var(--bdr);border-radius:8px;padding:8px 10px;}

/* ═══════════════════════════════════════════════════════════════════
   PR-EDIT-CABINETS PORT (2026-07-14): styles for the grafted editor UI.
   Fullscreen viewport, the ⓘ microcopy tooltip, the resolved-slide
   readout, topbar cabinet nav, bottom-bar type/section meta, and the
   narrow-panel Faces two-column→stack fix. The codespace's parallel
   pane-resize vars (--ec-left-w/.cab-resizing/.cab-resize-handle) and
   category-rail modal restructure were intentionally NOT ported — HEAD
   already has --cab-lw/--cab-rw pane resize and the V2 tab-rail nav.
   Fullscreen rules therefore key off HEAD's --cab-lw/--cab-rw.
   ═══════════════════════════════════════════════════════════════════ */
/* PR-VIEW-FULLSCREEN (2026-07-14): the ⤢ button is TRUE full screen —
   the grid becomes a fixed overlay covering every piece of app chrome
   (topbar, page header, statusbar), paired with a browser
   requestFullscreen. The in-viewer toolbar stays visible so the operator
   can toggle back (Esc also exits). */
.cab-editor-grid.cab-fullscreen{
    position:fixed;
    inset:0;
    z-index:9998;
    background:var(--bg);
    height:100vh;
    width:100vw;
}
body[data-view-fullscreen="true"]{overflow:hidden;}
body[data-view-fullscreen="true"] #forge-cmd-bar,
body[data-view-fullscreen="true"] #app-topbar,
body[data-view-fullscreen="true"] #app-statusbar,
body[data-view-fullscreen="true"] #app-bottom-nav{display:none !important;}
/* PR-VIEW-FS-LEFTNAV (2026-07-14): the LEFT rail (Library + Placed) stays
   usable in fullscreen. Its ‹ collapse toggle still works. The right
   panel appears only while Edit Cabinet mode is on (ec-editing). */
.cab-editor-grid.cab-fullscreen,
.cab-editor-grid.cab-fullscreen.cab-right-collapsed{grid-template-columns:var(--cab-lw,248px) 1fr 0;}
.cab-editor-grid.cab-fullscreen.cab-left-collapsed{grid-template-columns:0 1fr 0;}
.cab-editor-grid.cab-fullscreen.ec-editing{grid-template-columns:var(--cab-lw,248px) 1fr var(--cab-rw,336px) !important;}
.cab-editor-grid.cab-fullscreen.ec-editing.cab-left-collapsed{grid-template-columns:0 1fr var(--cab-rw,336px) !important;}
.cab-editor-grid.cab-fullscreen .cab-pane{opacity:0;pointer-events:none;overflow:hidden;border:none;}
#cv-left-pill{position:absolute;left:14px;top:14px;z-index:8;display:none;padding:9px 14px;font-size:12px;font-weight:600;border-radius:9px;border:1px solid var(--bdr);background:var(--pn);color:var(--white);cursor:pointer;box-shadow:0 8px 24px rgb(var(--shadow-ink) / .4);}
#cv-left-pill:hover{border-color:var(--acc2);color:var(--acc2);}
.cab-editor-grid.cab-fullscreen .cab-pane-left{opacity:1;pointer-events:auto;}
.cab-editor-grid.cab-fullscreen.ec-editing .cab-pane-right{opacity:1;pointer-events:auto;}

/* PR-MICROCOPY: ⓘ tooltip — parameter depth lives on hover. */
.ced-help-ic{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:6px;border-radius:50%;font-size:11px;font-style:normal;color:var(--dim);cursor:help;user-select:none;vertical-align:-2px;}
.ced-help-ic:hover,.ced-help-ic:focus-visible{color:var(--acc2);}
/* PR-SLIDE-REAR-CLEARANCE: resolved-slide readout (engine's actual pick). */
.ced-slide-resolved{margin-top:8px;font-size:12px;}
.ced-slide-resolved .ced-slide-pick{font-weight:700;color:var(--acc2);font-variant-numeric:tabular-nums;}
.ced-slide-resolved .ced-slide-warn{margin-top:4px;color:var(--no);font-size:11.5px;line-height:1.4;}

/* PR-CED-CATS: topbar cabinet ◀/▶ nav. */
#cab-editor-modal[data-layout="v2"] .ced-cabnav{display:flex;gap:4px;}
#cab-editor-modal[data-layout="v2"] .ced-cabnav-btn{min-width:34px;min-height:34px;border:1px solid var(--bdr);border-radius:6px;background:var(--inp,#1c2029);color:var(--white);font-size:13px;cursor:pointer;line-height:1;}
#cab-editor-modal[data-layout="v2"] .ced-cabnav-btn:hover{border-color:var(--acc2);color:var(--acc2);}
/* PR-CED-CATS: bottom-bar meta — type chip + section selector. */
#cab-editor-modal[data-layout="v2"] .ced-bottombar-meta{margin-right:auto;display:flex;align-items:center;gap:14px;}
#cab-editor-modal[data-layout="v2"] .ced-type-chip{font-size:12px;font-weight:600;color:var(--dim);border:1px solid var(--bdr);border-radius:6px;padding:6px 10px;text-transform:capitalize;}
#cab-editor-modal[data-layout="v2"] .ced-section-sel-wrap{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--dim);}
#cab-editor-modal[data-layout="v2"] .ced-section-sel-wrap[hidden]{display:none;}
#cab-editor-modal[data-layout="v2"] #ced-section-sel{background:var(--inp,#1c2029);color:var(--white);border:1px solid var(--bdr);border-radius:6px;padding:6px 8px;font-size:12px;min-height:34px;}

/* PR-CED-FACES-NARROW (2026-07-10): the Faces pane renders inside the
   ~300-380px V2 right panel. STACK the canvas column and the Custom
   Openings column — the old fixed-340px side column crushed the canvas
   to a sliver. Container queries pick two columns back up with room. */
#cab-editor-pane-faces{container-type:inline-size;}
.ced-faces-cols{display:flex;flex-direction:column;gap:14px;align-items:stretch;}
.ced-faces-main{flex:1 1 auto;min-width:0;width:100%;}
.ced-faces-side{display:flex;flex-direction:column;gap:10px;width:100%;min-width:0;}
@container (min-width: 640px){
    .ced-faces-cols{flex-direction:row;align-items:flex-start;}
    .ced-faces-side{width:340px;flex:0 0 340px;}
}

/* ── PR-JOBLINE-1 (2026-07-16): "move a job down the line" control ──────────
   Shared stage badge + accept/advance buttons + ⋯ menu (step-back / cancel /
   mark-paid) + Paid pill, plus a floating batch action bar. Rendered on the
   home All-Jobs list, the Projects tab, and the Cabinet + Door studio headers. */
.jl{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;vertical-align:middle;}
.jl-badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;letter-spacing:.02em;text-transform:uppercase;background:var(--pn-h);color:var(--ink,#334155);border:1px solid var(--bdr,#cbd5e1);}
.jl-badge[data-stage="Pending"]{background:color-mix(in srgb,var(--dim) 15%,transparent);color:var(--dim);border-color:color-mix(in srgb,var(--dim) 40%,transparent);}
.jl-badge[data-stage="Scheduled"]{background:color-mix(in srgb,var(--warn) 15%,transparent);color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent);}
.jl-badge[data-stage="InProduction"]{background:color-mix(in srgb,var(--acc) 15%,transparent);color:var(--acc);border-color:color-mix(in srgb,var(--acc) 40%,transparent);}
.jl-badge[data-stage="QC"]{background:color-mix(in srgb,var(--acc2) 15%,transparent);color:var(--acc2);border-color:color-mix(in srgb,var(--acc2) 40%,transparent);}
.jl-badge[data-stage="Shipped"]{background:color-mix(in srgb,var(--ok) 15%,transparent);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);}
.jl-badge[data-stage="Cancelled"]{background:color-mix(in srgb,var(--no) 15%,transparent);color:var(--no);border-color:color-mix(in srgb,var(--no) 40%,transparent);}
.jl-step{display:inline-flex;align-items:center;gap:4px;padding:5px 12px;min-height:32px;border-radius:6px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;border:1px solid var(--bdr,#cbd5e1);background:var(--pn);color:var(--ink,#222);}
.jl-step.jl-primary{background:var(--acc,#2563eb);border-color:var(--acc,#2563eb);color:#fff;}
.jl-step:hover{filter:brightness(1.06);}
.jl-step:disabled{opacity:.5;cursor:default;}
.jl-more{position:relative;display:inline-flex;}
.jl-more-btn{min-height:32px;min-width:32px;padding:2px 8px;border-radius:6px;cursor:pointer;border:1px solid var(--bdr,#cbd5e1);background:var(--pn);color:var(--ink,#222);font-weight:800;line-height:1;}
.jl-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:60;min-width:170px;display:flex;flex-direction:column;gap:2px;padding:6px;border-radius:8px;background:var(--pn);border:1px solid var(--bdr,#cbd5e1);box-shadow:0 8px 24px rgb(var(--shadow-ink) / .18);}
.jl-menu[hidden]{display:none;}
.jl-mi{text-align:left;padding:7px 10px;min-height:36px;border:none;border-radius:5px;background:transparent;color:var(--txt);font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap;}
.jl-mi:hover{background:var(--pn-h);}
.jl-mi.jl-danger{color:var(--no);}
/* PR-ONE-ROW-MENU (2026-08-28): divides caller-contributed items (Open,
   Cutlist) from the lifecycle moves below them in the same single menu. */
.jl-mi-sep{display:block;height:1px;margin:4px 6px;background:var(--bdr);}
.jl-mi.jl-danger:hover{background:color-mix(in srgb,var(--no) 15%,transparent);}
/* PR-JOBLINE-SCHED-DATE (2026-08-22): the "Push to schedule" ship-date popover. */
#jl-sched-pop{position:fixed;z-index:61;display:flex;flex-direction:column;gap:8px;min-width:240px;padding:12px;border-radius:8px;background:var(--pn);border:1px solid var(--bdr,#cbd5e1);box-shadow:0 8px 24px rgb(var(--shadow-ink) / .18);}
#jl-sched-pop .jl-sched-title{font-weight:700;font-size:13px;color:var(--white,#fff);}
#jl-sched-pop .jl-sched-lbl{font-size:11px;color:var(--dim,#64748b);}
#jl-sched-pop input[type=date]{padding:8px 10px;min-height:40px;border-radius:6px;border:1px solid var(--bdr,#cbd5e1);background:transparent;color:inherit;font-size:14px;color-scheme:dark light;}
#jl-sched-pop .jl-sched-hint{font-size:11px;color:var(--dim,#64748b);max-width:240px;}
#jl-sched-pop .jl-sched-row{display:flex;gap:8px;justify-content:flex-end;}
.jl-paid{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;background:color-mix(in srgb,var(--ok) 15%,transparent);color:var(--ok);border:1px solid color-mix(in srgb,var(--ok) 40%,transparent);}
.jl-done{font-size:12px;color:var(--dim,#94a3b8);font-weight:600;}
.jl-wait{display:inline-block;width:12px;height:12px;}
.forge-jobs-grid .fjg-act{white-space:nowrap;text-align:right;}
input.jl-cb{width:18px;height:18px;cursor:pointer;vertical-align:middle;accent-color:var(--acc,#2563eb);}
.jl-batchbar{position:fixed;left:50%;bottom:20px;transform:translateX(-50%);z-index:1200;display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:12px;background:var(--pn-h);color:var(--txt);box-shadow:0 12px 40px rgb(var(--shadow-ink) / .35);}
.jl-batchbar[hidden]{display:none;}
.jl-bb-count{font-weight:700;font-size:13px;}
.jl-bb-btn{padding:7px 14px;min-height:38px;border-radius:8px;font-size:13px;font-weight:700;cursor:pointer;border:1px solid color-mix(in srgb,var(--bdr) 70%,transparent);background:color-mix(in srgb,var(--bdr) 30%,transparent);color:var(--txt);}
.jl-bb-btn.jl-primary{background:var(--acc,#2563eb);border-color:var(--acc,#2563eb);}
.jl-bb-btn:hover{filter:brightness(1.1);}
.jl-bb-x{background:transparent;border:none;color:var(--dim);font-size:13px;font-weight:600;cursor:pointer;padding:6px 8px;}
.jl-bb-x:hover{color:var(--txt);}

/* ── PR-JOBS-HUB (2026-07-17): the Jobs tab hub ─────────────────────────────── */
.jh-filters{margin-bottom:14px;}
.jh-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px;}
.jh-tab{padding:6px 12px;border-radius:999px;border:1px solid var(--bdr,#e2e8f0);background:var(--pn);color:var(--ink,#334155);font-size:13px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:6px;}
.jh-tab[data-active="true"]{background:var(--acc,#2563eb);border-color:var(--acc,#2563eb);color:#fff;}
.jh-n{font-size:11px;font-weight:700;opacity:.8;background:rgba(127,127,127,.15);border-radius:999px;padding:0 6px;min-width:16px;text-align:center;}
.jh-tab[data-active="true"] .jh-n{background:rgba(255,255,255,.25);opacity:1;}
.jh-row2{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
.jh-segs{display:inline-flex;border:1px solid var(--bdr,#e2e8f0);border-radius:8px;overflow:hidden;}
.jh-seg{padding:6px 12px;border:none;background:var(--pn);color:var(--dim,#64748b);font-size:12.5px;font-weight:600;cursor:pointer;border-right:1px solid var(--bdr,#e2e8f0);}
.jh-seg:last-child{border-right:none;}
.jh-seg[data-active="true"]{background:var(--pn-h);color:var(--ink,#1e293b);}
.jh-search{flex:1;min-width:160px;padding:7px 12px;border-radius:8px;border:1px solid var(--bdr,#e2e8f0);background:var(--inp);color:var(--ink,#1e293b);font-size:13px;}
.jh-sort{padding:7px 10px;border-radius:8px;border:1px solid var(--bdr,#e2e8f0);background:var(--inp);color:var(--ink,#1e293b);font-size:13px;cursor:pointer;}
.jh-summary{display:flex;gap:20px;flex-wrap:wrap;padding:10px 14px;margin-bottom:12px;background:var(--bg);border:1px solid var(--bdr,#e2e8f0);border-radius:10px;font-size:13px;color:var(--dim,#64748b);}
.jh-summary strong{color:var(--ink,#1e293b);font-size:15px;}
.jh-summary em{font-style:normal;opacity:.75;}
.jh-tablewrap{overflow-x:auto;border:1px solid var(--bdr,#e2e8f0);border-radius:10px;background:var(--pn);}
.jh-table{width:100%;border-collapse:collapse;font-size:13.5px;}
.jh-table thead th{text-align:left;padding:10px 12px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--dim,#64748b);border-bottom:1px solid var(--bdr,#e2e8f0);white-space:nowrap;}
.jh-table td{padding:10px 12px;border-bottom:1px solid var(--bdr,#eef2f7);color:var(--ink,#1e293b);vertical-align:middle;}
.jh-tr{cursor:pointer;}
.jh-tr:hover{background:var(--pn-h);}
.jh-tr:last-child td{border-bottom:none;}
.jh-name{font-weight:600;}
.jh-mono{font-variant-numeric:tabular-nums;white-space:nowrap;}
.jh-price{font-weight:600;}
.jh-dim{color:var(--dim,#94a3b8);}
/* Audit R7 (2026-08-21): frozen-quote drift marker on the jobs hub. */
.jh-drift{color:var(--warn);font-size:11px;font-weight:700;white-space:nowrap;}
.jh-type{display:inline-block;padding:1px 7px;border-radius:6px;font-size:10px;font-weight:700;letter-spacing:.03em;}
.jh-cab{background:color-mix(in srgb,var(--acc) 15%,transparent);color:var(--acc);}
.jh-door{background:color-mix(in srgb,var(--acc2) 15%,transparent);color:var(--acc2);}
.jh-rush{display:inline-block;padding:1px 6px;border-radius:6px;font-size:10px;font-weight:700;background:color-mix(in srgb,var(--no) 15%,transparent);color:var(--no);}
.jh-paid{display:inline-flex;align-items:center;padding:1px 8px;border-radius:999px;font-size:11px;font-weight:700;background:color-mix(in srgb,var(--ok) 15%,transparent);color:var(--ok);}
.jh-stage{display:inline-block;padding:2px 9px;border-radius:999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.02em;background:var(--pn-h);color:var(--dim);}
.jh-stage[data-stage="Pending"]{background:color-mix(in srgb,var(--dim) 15%,transparent);color:var(--dim);}
.jh-stage[data-stage="Scheduled"]{background:color-mix(in srgb,var(--warn) 15%,transparent);color:var(--warn);}
.jh-stage[data-stage="InProduction"]{background:color-mix(in srgb,var(--acc) 15%,transparent);color:var(--acc);}
.jh-stage[data-stage="QC"]{background:color-mix(in srgb,var(--acc2) 15%,transparent);color:var(--acc2);}
.jh-stage[data-stage="Shipped"]{background:color-mix(in srgb,var(--ok) 15%,transparent);color:var(--ok);}
.jh-stage[data-stage="Cancelled"]{background:color-mix(in srgb,var(--no) 15%,transparent);color:var(--no);}
.jh-empty{padding:40px;text-align:center;color:var(--dim,#94a3b8);border:1px dashed var(--bdr,#e2e8f0);border-radius:10px;}
.jh-drawer{position:fixed;inset:0;z-index:1300;}
.jh-drawer[hidden]{display:none;}
.jh-drawer-bg{position:absolute;inset:0;background:rgba(15,23,42,.4);}
.jh-drawer-panel{position:absolute;top:0;right:0;height:100%;width:min(440px,92vw);background:var(--pn);border-left:1px solid var(--bdr,#e2e8f0);box-shadow:-8px 0 32px rgb(var(--shadow-ink) / .18);padding:22px 24px;overflow-y:auto;}
.jh-d-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:14px;}
.jh-d-title{font-size:20px;font-weight:700;color:var(--ink,#1e293b);line-height:1.2;}
.jh-d-sub{font-size:12.5px;color:var(--dim,#64748b);margin-top:3px;}
.jh-d-x{border:none;background:transparent;font-size:18px;color:var(--dim,#64748b);cursor:pointer;line-height:1;}
.jh-d-ctrl{margin-bottom:16px;}
.jh-d-dl{display:grid;grid-template-columns:110px 1fr;gap:9px 14px;font-size:13.5px;margin:0 0 18px;}
.jh-d-dl dt{color:var(--dim,#64748b);}
.jh-d-dl dd{margin:0;color:var(--ink,#1e293b);}
.jh-d-actions{display:flex;gap:8px;flex-wrap:wrap;}
.jh-btn{padding:9px 15px;border-radius:8px;border:1px solid var(--bdr,#e2e8f0);background:var(--pn);color:var(--ink,#1e293b);font-weight:600;font-size:13.5px;cursor:pointer;}
.jh-btn.jh-primary{background:var(--acc,#2563eb);border-color:var(--acc,#2563eb);color:#fff;}
.jh-btn:hover{filter:brightness(1.05);}
.jh-d-msg{margin-top:12px;font-size:13px;color:var(--dim,#64748b);}

/* ======================================================================
   PR-UI-MODE (2026-07-30) — MOBILE studio layout (mode-gated, not width)
   ======================================================================
   Operator: "you can click mobile and that ui you just built pops up. and if
   you're on desktop it's exactly the same as before you changed it."

   So the touch studio is an EXPLICIT MODE: every rule below is gated on
   html[data-ui-mode="mobile"] (set by ui-mode.js — auto on real phones/iPads,
   manual toggle, remembered per device). In DESKTOP mode NONE of this applies,
   so the studio is byte-identical to before, even when the window is narrow.
   In MOBILE mode the 3-pane editor becomes OVERLAY DRAWERS over a full-bleed
   3D viewport (LEFT reuses toggleCabLeft(); RIGHT auto-opens on select via
   :has(#cab-params-form); scrim-tap closes — wired in _wireCabDrawerScrim).
   The html[...] prefix also raises specificity above the pre-existing
   <=900px stack rule (app.css:1974), which still governs DESKTOP narrow. */

/* NOTE (2026-07-30 audit): a `touch-action: none` on #bevy-canvas / #plan-poster
   was REMOVED here. It was unnecessary (the studio is fixed-height, nothing to
   scroll) and it suppressed the browser's native long-press→context-menu on the
   canvas + risked disturbing Bevy/winit's own touch handling. The pre-existing
   touch gestures (tap-select, double-tap-part, long-press menu, orbit,
   palette-drag) were all built and working WITHOUT it — keep it that way. */

/* Mobile mode: bump the floating studio controls to the 44px tap floor. */
html[data-ui-mode="mobile"] .cab-view-bar .cv-btn,
html[data-ui-mode="mobile"] .cab-act-cluster .cv-btn,
html[data-ui-mode="mobile"] .cab-view-bar #vc-view,
html[data-ui-mode="mobile"] .cab-placed-panel .cpp-foot button { min-height: var(--touch-min, 44px); }
/* PR-MOBILE-DEAD-RAILS (2026-08-22, operator: "i have blue bars on the sides
   i think from cabinet add and params from desktop are stuck there"):

   Those bars are the DESKTOP column resizers. They are `position:absolute` at
   `left:var(--cab-lw)` / `right:var(--cab-rw)` — 212px and 252px in from the
   edges — and the mobile layout collapses the grid to a single `1fr` column
   without ever removing them, so both handles end up floating IN FRONT OF the
   3D view over nothing they can resize.

   They were invisible until touched because the background only paints on
   `:hover`. A finger has no un-hover: the first tap that lands on one leaves
   `color-mix(var(--acc) 45%)` — accent BLUE — stuck there for the rest of the
   session. Widening them to 16px "for touch" made two dead 16px blue rails
   over the operator's model.

   There is nothing to salvage: a drawer has no column to drag. Remove them
   from the layout entirely (display:none, not visibility) so they also stop
   swallowing touches meant for the canvas behind them. */
html[data-ui-mode="mobile"] .cab-resizer { display: none !important; }

/* PR-MOBILE-TAP-PLACE (2026-08-22): the "now tap where it goes" chip.
   Sits at the TOP of the viewport, not the bottom — the params sheet owns the
   bottom edge, and the operator's own hand covers the lower third of a phone
   while they aim. Pointer-events only on the Cancel button so the rest of the
   chip can never eat a placement tap. */
#mobile-place-chip {
    position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
    z-index: calc(var(--z-drawer, 50) + 2);
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px 8px 14px; border-radius: 999px;
    background: var(--pn); border: 1px solid var(--bdr);
    box-shadow: var(--shadow-5, 0 12px 40px rgb(var(--shadow-ink) / .36));
    font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--txt);
    pointer-events: none; max-width: calc(100vw - 24px);
}
/* PR-MOBILE-ACTIONS (2026-08-22): the touch action bar. Top-LEFT, because the
   act-cluster (Save / Cutlist / Edit Cabinet) already owns top-right and the
   params sheet owns the bottom. A thin strip, not a slab: on a phone the model
   is the point and chrome is rent. Buttons are full 44px targets but visually
   compact, and the strip wraps rather than overflowing on a narrow phone. */
#mobile-act-bar {
    position: absolute; top: 10px; left: 10px;
    z-index: calc(var(--z-drawer, 50) + 1);
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
    padding: 4px; border-radius: 12px;
    background: color-mix(in srgb, var(--pn) 88%, transparent);
    border: 1px solid var(--bdr);
    box-shadow: var(--shadow-3, 0 6px 18px rgb(var(--shadow-ink) / .28));
    max-width: calc(100% - 20px);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
#mobile-act-bar button {
    min-width: var(--touch-min, 44px); min-height: var(--touch-min, 44px);
    padding: 0 8px; border: none; border-radius: 9px;
    background: transparent; color: var(--txt);
    font: 700 14px/1 system-ui, sans-serif;
}
#mobile-act-bar button:active { background: color-mix(in srgb, var(--acc) 20%, transparent); }
#mobile-act-bar .mab-sep {
    width: 1px; align-self: stretch; margin: 4px 2px;
    background: var(--bdr);
}
/* The action bar hides while placing (the next tap belongs to the canvas, and
   the two would otherwise stack in the same corner). Driven by a CLASS from
   JS rather than a sibling selector: both elements are appended to the same
   host in whichever order the operator happened to trigger them, so `~` only
   matches one of the two possible orders, and the `:has()` form that covers
   the other is not universally supported on the phones this ships to. A class
   is deterministic on every browser and in both orders. */
#mobile-act-bar.mab-placing { display: none; }

#mobile-place-chip .mpc-cancel {
    pointer-events: auto;
    min-height: var(--touch-min, 44px); padding: 0 14px;
    border: 1px solid var(--bdr); border-radius: 999px;
    background: transparent; color: var(--dim);
    font: 600 12.5px/1 system-ui, sans-serif;
}

/* Same root cause, smaller blast radius: the studio's toolbar buttons paint a
   hover background that likewise sticks after a tap, so the last button you
   pressed reads as permanently active and competes with the REAL `.active`
   state that marks the current view mode. Suppress hover painting on devices
   that cannot hover; `.active` is unaffected and still shows true state.
   Scoped to these controls deliberately — the stylesheet carries 333 :hover
   rules and a blanket sweep would be a far bigger change than this bug
   justifies. */
@media (hover: none) {
    .cab-view-bar .cv-btn:hover,
    .cab-act-cluster .cv-btn:hover {
        background: transparent;
        color: var(--dim);
    }
    .cab-view-bar .cv-btn.active:hover,
    .cab-act-cluster .cv-btn.active:hover {
        background: color-mix(in srgb, var(--acc) 16%, transparent);
        color: var(--acc);
    }
}

/* ── Overlay-drawer studio — MOBILE MODE *and* a narrow screen ──────────
   The slide-in-drawer layout is a PHONE / portrait pattern. On a large
   tablet (e.g. Galaxy Tab S9 Ultra, >900px CSS width) mobile mode KEEPS the
   docked 3-pane — there's room for it — so nothing collapses off-screen or
   gets trapped behind a scrim. Only <=900px turns the panes into drawers. */
@media (max-width: 900px) {
  /* Viewport fills the whole editor; panes float over it. */
  html[data-ui-mode="mobile"] .cab-editor-grid,
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-left-collapsed,
  html[data-ui-mode="mobile"] .cab-editor-grid.ec-editing,
  html[data-ui-mode="mobile"] .cab-editor-grid.ec-editing.cab-left-collapsed,
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-fullscreen.cab-left-collapsed,
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-fullscreen.ec-editing.cab-left-collapsed {
    grid-template-columns: 1fr !important;
    grid-template-rows: 1fr !important;
  }
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-viewer {
    grid-column: 1; grid-row: 1; min-height: 0;
  }
  /* Both side panes become fixed-height overlay drawers. */
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane {
    position: absolute; top: 0; bottom: 0;
    width: min(86vw, 360px);
    max-height: none !important;
    z-index: var(--z-drawer, 50);
    background: var(--bg);
    box-shadow: var(--shadow-5, 0 12px 40px rgb(var(--shadow-ink) / .36));
    transition: transform var(--dur-chrome, 150ms) var(--ease-out);
    will-change: transform;
  }
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane-left  {
    left: 0; border-right: 1px solid var(--bdr); border-bottom: none;
    transform: translateX(-102%);
  }
  /* PR-MOBILE-STUDIO (2026-08-03): the RIGHT (Parameters) pane is a BOTTOM
     SHEET on phones, not a full-height side drawer — so the 3D viewport stays
     visible above it instead of being covered by an 86vw slab. Slides up from
     the bottom; ~62vh tall; rounded top edge like a native sheet. Overrides the
     generic `.cab-pane` (absolute top:0/bottom:0/width) via source order; the
     max-height needs !important to beat `.cab-pane`'s `max-height:none`. */
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane-right {
    top: auto; left: 0; right: 0; bottom: 0;
    width: auto;
    height: 62vh;
    max-height: 62vh !important;
    border-left: none; border-top: 1px solid var(--bdr);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -12px 40px rgb(var(--shadow-ink) / .36);
    transform: translateY(102%);
  }
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane .cab-pane-body { overflow-y: auto; }
  /* OPEN states — reuse existing app state, no new JS. */
  html[data-ui-mode="mobile"] .cab-editor-grid:not(.cab-left-collapsed) > .cab-pane-left { transform: translateX(0); }
  /* PR-MOBILE-SHEET-PEEK (2026-08-21, operator: "parameters pop up and I
     can't see anything but that and can't drag"): the Parameters sheet is
     now NON-MODAL with a collapsed PEEK state — the Google-Maps place-sheet
     pattern, and Material Design's standard (non-modal) bottom sheet: a
     sheet paired with a primary surface the user keeps working in gets NO
     scrim and must not block that surface. Selecting a cabinet shows only
     the 54px peek bar (drag handle + cabinet name + Done) so touch-drag on
     the 3D canvas works immediately; swipe up / tap the bar to expand for
     editing (`.cab-sheet-open`, wired in app-face.js); swipe down to peek.
     The old behavior — full 62vh sheet + full-screen scrim on every tap —
     made selecting and dragging mutually exclusive on phones. */
  /* PR-MOBILE-SHEET-V2 (2026-08-22). Operator, on the v1 peek: "covers too
     much / hard to dismiss", "too many taps to edit W/H/D", "it fights the 3D
     view". Three fixes, one shape:

     1. The PEEK now stands 132px tall instead of 54px. That is not padding —
        it is exactly enough to clear the sheet header PLUS `.insp-dim-row`,
        the width/height/depth row that `_renderInspectorPrimary` already emits
        as the first thing in the body. So the three numbers an operator
        actually changes are editable with ZERO taps of expansion, and no DOM
        is duplicated into the header to drift out of sync with the form.
     2. The sheet is capped at 58vh open (was 62vh) and, more importantly, the
        viewport BELOW is no longer covered — see the grid-rows rule after this
        block, which shrinks the 3D pane to sit above the open sheet. You can
        see the cabinet you are editing while you edit it.
     3. Dismissal has three exits, not one: swipe the handle down, tap Done, or
        deselect. (Tap-outside deliberately does NOT dismiss — the whole point
        of the non-modal sheet is that a tap on the model is a MODEL tap.) */
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting > .cab-pane-right {
    height: 58vh;
    max-height: 58vh !important;
    transform: translateY(calc(100% - 152px));
  }
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting.cab-sheet-open > .cab-pane-right {
    transform: translateY(0);
  }
  /* The 3D pane yields room instead of being buried. The grid is a single
     column; give it two ROWS when the sheet is open and let the viewer take
     what is left. `min-height:0` is load-bearing — without it the canvas
     refuses to shrink below its content and the sheet covers it anyway. */
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting.cab-sheet-open {
    grid-template-rows: 1fr 58vh !important;
  }
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting.cab-sheet-open > .cab-viewer {
    grid-row: 1; min-height: 0;
  }
  /* No hiding rules in the peek state — the sheet already clips its own
     overflow, and `.insp-primary` (which leads with the dim row) is the first
     thing in the form, so the 132px window lands on it naturally. An earlier
     draft tried `.cab-pane-body > *:not(.insp-primary)`, which is wrong:
     `.insp-primary` is a GRANDCHILD (body > #cab-params-form > .insp-primary),
     so that selector matched the form itself and would have hidden the entire
     inspector. Left as a warning rather than deleted silently.
     Peek scrolling is disabled so the collapsed bar can't be scrolled into a
     confusing half-state; the operator expands to reach the rest. */
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting:not(.cab-sheet-open)
    > .cab-pane-right .cab-pane-body { overflow-y: hidden; }
  /* MEASURED FIX (headless Chrome at phone size, 2026-08-22). The 132px peek
     was sized by eye and it was WRONG: the sheet showed y614-746 while the
     W/H/D row rendered at y723-788, so the row every operator actually edits
     was below the fold and clipped — the entire point of the taller peek,
     silently not delivered.
     The gap is what sits ABOVE the row inside `.insp-primary`: the cabinet
     header strip (sub-library chip + "Save as Template"), ~55px. In the PEEK
     state that strip is pure overhead — the sheet's own header already names
     the cabinet — so it is hidden while collapsed, which lifts the dim row to
     the top of the body and lands it inside the 132px window. It returns the
     moment the sheet is expanded. `.insp-dim-hints` goes with it for the same
     reason: hint text under a row you cannot see is noise.
     Chosen over simply growing the peek to ~190px because a quarter-screen bar
     on a phone is rent; this keeps the bar compact AND shows the numbers. */
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting:not(.cab-sheet-open)
    > .cab-pane-right .insp-cab-header,
  html[data-ui-mode="mobile"] .cab-editor-grid.cab-inspecting:not(.cab-sheet-open)
    > .cab-pane-right .insp-dim-hints { display: none; }
  /* Touch-sized dimension inputs — these are now the primary editing surface
     on a phone, so they get the full 44px target rather than desktop's 28px. */
  html[data-ui-mode="mobile"] .cab-pane-right .insp-dim-row input {
    min-height: var(--touch-min, 44px); font-size: 16px; /* 16px = no iOS zoom */
  }
  /* Drag handle: the universal "this resizes" cue on the sheet header. */
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane-right > .cab-pane-hdr {
    position: relative; padding-top: 18px; cursor: grab; touch-action: none;
  }
  html[data-ui-mode="mobile"] .cab-editor-grid > .cab-pane-right > .cab-pane-hdr::before {
    content: ""; position: absolute; top: 7px; left: 50%;
    transform: translateX(-50%);
    width: 40px; height: 4px; border-radius: 2px; background: var(--bdr);
  }
  /* Scrim behind an open drawer (grid ::after; tap = close, wired in JS).
     PR-MOBILE-SHEET-PEEK: the LEFT drawer only — the Parameters sheet is
     non-modal now, so `.cab-inspecting` no longer raises the scrim. */
  html[data-ui-mode="mobile"] .cab-editor-grid::after {
    content: ""; position: absolute; inset: 0;
    z-index: calc(var(--z-drawer, 50) - 1);
    background: var(--scrim, rgba(0,0,0,.42));
    opacity: 0; pointer-events: none;
    transition: opacity var(--dur-chrome, 150ms) var(--ease-out);
  }
  html[data-ui-mode="mobile"] .cab-editor-grid:not(.cab-left-collapsed)::after {
    opacity: 1; pointer-events: auto;
  }
  html[data-ui-mode="mobile"] .cab-left-reopen { z-index: calc(var(--z-drawer, 50) + 1); }
  /* Inspector Done button — only when the drawer layout is active. */
  html[data-ui-mode="mobile"] .cab-pane-right .cab-inspector-done { display: inline-flex; }
}
/* Done button hidden by default (docked layouts don't need it). */
.cab-pane-right .cab-inspector-done { display: none; }

/* ── Door Studio (mobile mode): stack + give the preview real height. ── */
html[data-ui-mode="mobile"] .ds-param-panel { min-width: 0 !important; width: 100% !important; }
html[data-ui-mode="mobile"] .ds-viewport,
html[data-ui-mode="mobile"] .ds-viewport-preview { min-width: 0 !important; }
html[data-ui-mode="mobile"] .ds-preview-col,
html[data-ui-mode="mobile"] #ds-preview-col { min-height: 42vh; }

/* ── Studio dialogs never wider than the screen (mobile mode). ── */
html[data-ui-mode="mobile"] .vgate-modal .vgate-panel,
html[data-ui-mode="mobile"] .gs-modal .gs-panel,
html[data-ui-mode="mobile"] .shortcuts-modal .sc-panel,
html[data-ui-mode="mobile"] .sync-modal .sync-panel {
  width: min(94vw, 560px) !important;
  max-width: 94vw !important;
  max-height: 90svh;
  overflow: auto;
}
/* /PR-UI-MODE ────────────────────────────────────────────────────────── */

/* ── PR-DS-CUST-COMBOBOX (2026-08-22): shared typeahead-listbox styles ──────
   Used by the Door Studio customer picker (#do-cust-listbox) AND the
   Customers-tab merge picker (#cust-merge-listbox). SINGLE copy — the inline
   duplicate that briefly lived in pg-door.html was removed same day so the
   two surfaces can't drift. Anchors under its input (wrapper is
   position:relative), ≤8 rows, 44px touch targets, match highlight via
   <mark>. */
.ds-custbox { position:absolute; top:100%; left:0; right:0; z-index:80;
    background:var(--pn, #171b21); border:1px solid var(--bdr);
    border-radius:10px; margin-top:4px; overflow-y:auto;
    max-height:min(340px, 45vh); display:none;
    box-shadow:0 14px 36px rgb(var(--shadow-ink) / .5); }
.ds-custbox.open { display:block; }
.ds-custbox-cap { padding:8px 14px 4px; font-size:10px; font-weight:700;
    letter-spacing:.05em; text-transform:uppercase; color:var(--dim); }
.ds-custopt { padding:9px 14px; min-height:44px; display:flex;
    flex-direction:column; justify-content:center; gap:2px; cursor:pointer;
    border-top:1px solid color-mix(in srgb, var(--bdr) 45%, transparent); }
.ds-custopt:first-child, .ds-custbox-cap + .ds-custopt { border-top:none; }
.ds-custopt.hl, .ds-custopt:hover { background:color-mix(in srgb, var(--acc) 14%, transparent); }
.ds-custopt .nm { font-weight:600; font-size:13.5px; color:var(--white); }
.ds-custopt .nm mark, .ds-custopt .sub mark { background:none; color:var(--acc); font-weight:700; }
.ds-custopt .sub { font-size:11.5px; color:var(--dim); }
.ds-custbox-none { padding:12px 14px; font-size:12px; color:var(--dim); line-height:1.55; }

/* ── PR-TOOLBAR-TIERS (2026-08-22): grouped cutlist-toolbar menus ──────────
   "Machine files ▾" / "Station cards ▾" dropdown panels on the Door Cutlist
   button row (door-studio.js). Anchored under their trigger; items keep the
   standard button look at full width with 44px shop-tablet targets. */
.tbmenu-wrap { position: relative; display: inline-flex; }
.tbmenu-panel {
    position: absolute; top: 100%; left: 0; z-index: 70;
    display: none; flex-direction: column; gap: 4px;
    min-width: 250px; margin-top: 4px; padding: 6px;
    background: var(--pn, #171b21); border: 1px solid var(--bdr);
    border-radius: 10px; box-shadow: 0 14px 36px rgb(var(--shadow-ink) / .5);
}
.tbmenu-panel.open { display: flex; }
.tbmenu-item { display: block; width: 100%; text-align: left; min-height: 44px; white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════════════════
   PR-DOOR-ORDER-HEAD (2026-08-27) — Door Studio order screen: fit one step
   on one screen, and make the chrome that survives feel built rather than
   assembled.

   Owner: "see how i have to scroll to see everything — we need to make that
   not a thing … i do not want any scrolling until absolutely necessary",
   plus "can you make it all more premium feeling too".

   MEASURED before (headless Chrome, 1578x902, order screen, Step 1 open):
     .shdr 70 · progress strip 44+14 · headrow 26 · customer line 22 ·
     "Edit job info" 34 · blurb 19+18 · step rail 56 · step's own header 141
     → first form field at y=610 of 902 (68% down); step bottom at y=1108,
       i.e. 206px below the fold, so "Next" was unreachable without scrolling.
   Step heights measured individually: Customer 702 · Type 422 · Style 742 ·
   Frame & Add 736 — every one of them FITS in 902 once the ~350px of stacked
   chrome above and inside it is gone. That is the whole strategy here.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. The studio banner does not belong on a single order ────────────────
   "Door Design Studio / Build & price doors and fronts" is 70px of masthead
   that says nothing new once you are inside job #3191 — and the global
   topbar breadcrumb already reads "Door Studio • Order". Hidden ONLY on the
   order screen; the orders list keeps its banner and its "Start New Order"
   button. :has() is safe here — this ships in a Chromium WebView (Tauri) and
   every current browser supports it. Getting back to a new order: topbar
   Back → list, or the "New Order" button in the actions row at the bottom of
   this very screen. */
#pg-door:has(> .scroll > #d-new.on) > .shdr { display: none; }

/* ── 2. The order bar — identity left, lifecycle + actions right ───────────
   One row where six stacked things used to be. */
.do-headbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
    margin: 0 0 var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
    /* Premium: a card, not a bare row. The faint top-light + 1px hairline
       reads as a raised surface under shop lighting without shouting. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--pn) 96%, var(--acc)) 0%,
        var(--pn) 100%);
    border: 1px solid var(--bdr);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2), var(--shadow-inset);
}
.do-headbar-id { min-width: 0; flex: 1 1 auto; }
.do-headbar-acts {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex: 0 0 auto;
}

/* The title is a button because the whole line is the edit affordance the
   deleted "Edit job info" control used to be. It must not LOOK like a button
   at rest — chrome only appears on hover/focus. */
.do-headbar-title {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    min-width: 0;
    max-width: 100%;
    padding: var(--sp-1) var(--sp-2);
    margin: calc(-1 * var(--sp-1)) calc(-1 * var(--sp-2));
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: inherit;
    transition: background-color var(--dur-chrome) var(--ease-out);
}
.do-headbar-title:hover { background: color-mix(in srgb, var(--acc) 10%, transparent); }
.do-headbar-title:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: 2px;
}
.do-headbar-job {
    font-family: var(--ff-ui);
    font-size: 1.375rem;                 /* 22px — the page's one title */
    font-weight: var(--fw-semibold);
    line-height: 1.2;
    color: var(--txt);
    letter-spacing: -0.015em;
    white-space: nowrap;
}
/* The job number is the one string an operator reads across the shop, so it
   is the heaviest thing on the line — and tabular figures keep it from
   jittering as the digits change between orders. */
.do-headbar-job b {
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
}
/* "· for Jesse" — was its own 22px line reading "New order — add the customer
   in Step 1". Now it rides the title line and truncates instead of wrapping. */
.do-headbar-cust {
    font-family: var(--ff-ui);
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    color: var(--acc);
    line-height: 1.2;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.do-headbar-cust[data-empty="1"] { color: var(--dim); font-weight: var(--fw-medium); }
/* The pencil is the "this is editable" hint. Quiet until you approach it. */
.do-headbar-pencil {
    font-size: 12px;
    color: var(--dim);
    opacity: 0;
    transition: opacity var(--dur-chrome) var(--ease-out);
}
.do-headbar-title:hover .do-headbar-pencil,
.do-headbar-title:focus-visible .do-headbar-pencil { opacity: 1; }

/* ── 3. The stage pill — small, and unlike the strip it replaces, live ─────
   The old Draft→Quote→Production→Shipped strip was 58px of pure decoration:
   setOrderProgress() painted classes and nothing was ever clickable. This
   pill is painted by that same function (same call site, no rewiring) and
   sits directly beside #do-jobline — the real, server-validated Accept /
   Start build / Mark paid control from job-line.js. Small AND working. */
.do-stage-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    height: 30px;
    padding: 0 var(--sp-3);
    border-radius: 999px;
    font-family: var(--ff-ui);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    letter-spacing: 0.02em;
    white-space: nowrap;
    border: 1px solid var(--bdr);
    background: var(--inp);
    color: var(--dim);
}
/* The leading dot carries the stage colour, so the pill itself stays calm and
   the four stages stay distinguishable at a glance across a shop. */
.do-stage-chip::before {
    content: "";
    width: 7px; height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}
/* PR-ONE-STATUS-MODEL (2026-08-28): keyed on the LIFECYCLE name, the same
   vocabulary .jl-badge and .jh-stage already use above. The chip used to be
   keyed on the legacy free-text status (draft/quoted/in_production/completed),
   which is how the same order could read DRAFT here and ESTIMATE everywhere
   else. One order, one word. */
.do-stage-chip[data-stage="Pending"]      { color: var(--dim); }
.do-stage-chip[data-stage="Scheduled"]    { color: var(--acc); border-color: color-mix(in srgb, var(--acc) 40%, var(--bdr)); background: color-mix(in srgb, var(--acc) 8%, var(--inp)); }
.do-stage-chip[data-stage="InProduction"] { color: var(--warn, #B45309); border-color: color-mix(in srgb, var(--warn, #B45309) 40%, var(--bdr)); background: color-mix(in srgb, var(--warn, #B45309) 8%, var(--inp)); }
.do-stage-chip[data-stage="QC"]           { color: var(--acc2); border-color: color-mix(in srgb, var(--acc2) 40%, var(--bdr)); background: color-mix(in srgb, var(--acc2) 8%, var(--inp)); }
.do-stage-chip[data-stage="Shipped"]      { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--bdr)); background: color-mix(in srgb, var(--ok) 8%, var(--inp)); }
.do-stage-chip[data-stage="Cancelled"]    { color: var(--no); border-color: color-mix(in srgb, var(--no) 40%, var(--bdr)); background: color-mix(in srgb, var(--no) 8%, var(--inp)); }
/* Once the order is saved, #do-jobline renders the authoritative stage badge
   straight from the server-authored lifecycle map (job-line.js), in that map's
   own vocabulary. This pill would then be a SECOND, differently-worded stage
   display for the same order. So it yields — via :has(), not JS, because
   job-line re-renders asynchronously when /api/lifecycle/actions resolves and
   a one-shot JS check would race it. The pill therefore covers exactly the
   cases the jobline cannot: an unsaved draft (no id to move) and a shop-floor
   role that is not allowed to advance work. */
#do-order-headrow:has(#do-jobline .jl-badge) .do-stage-chip { display: none; }

/* ISO/TS 9241-411 also sets a floor on the GAP between adjacent targets:
   3mm minimum (11px), 5-8mm preferred. The head-bar's action cluster and the
   step rail both sit at var(--sp-2) = 8px, which is under it. */
.do-headbar-acts { gap: 12px; }

/* Revisions — a quiet secondary. Was a 44px bordered block competing with the
   job number; now it sits in the same rhythm as the pill. */
.do-headbar-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    /* Was 34px (9.0mm) in the first pass -- below the ISO bare-finger floor.
       48px = 12.7mm, matching every other tap target on this screen. */
    height: 48px;
    padding: 0 var(--sp-3);
    background: transparent;
    border: 1px solid var(--bdr);
    border-radius: var(--radius-sm);
    color: var(--dim);
    font-family: var(--ff-ui);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--dur-chrome) var(--ease-out),
                border-color var(--dur-chrome) var(--ease-out),
                background-color var(--dur-chrome) var(--ease-out);
}
.do-headbar-btn:hover {
    color: var(--txt);
    border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr));
    background: color-mix(in srgb, var(--acc) 7%, transparent);
}
.do-headbar-btn:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: 2px;
}
.do-headbar-btn #do-rev-count { opacity: .65; font-weight: var(--fw-medium); }

/* PR-REVIEW-GATE (2026-08-28) — the sign-off control.
   Two states, and the UNSIGNED one is the loud one: an order nobody has
   checked is the condition that costs money, so it carries the warning colour
   and a filled dot. Signed reads as done and quiet — the same weight as
   Revisions beside it. */
/* ── PR-CONTROL-ROOM (2026-08-28) ─────────────────────────────────────────
   Locked decision #1: "Hub layout = A 'Control Room' — six-metric band, three
   columns (needs-you / orders / line+money)."

   The page it replaces was a dashboard: two big feature tiles, a row of
   icon+number cards, a list, and a rail that restated the list. The difference
   is that a control room puts the things that are WRONG in the operator's eye
   line and lets them click straight through to the jobs. */

/* Occasional actions are links in a strip, not two cards costing a full row. */
.cr-utilrow {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0 0 var(--sp-3);
}
.cr-util {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--dim);
}
.cr-util:hover { color: var(--acc); }
.cr-util:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: 3px;
    border-radius: 3px;
}
.cr-util-sep { color: var(--bdr); }
.cr-utilrow-spacer { flex: 1; }
.cr-utilrow-note { font-size: var(--fs-sm); color: var(--dim); }

/* ── the band ──────────────────────────────────────────────────────────── */
.cr-metrics {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
}
/* Every card is a button: each one narrows the list to the orders it counted.
   A number you cannot click through to is a poster, not a control. */
.cr-card {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-radius: 10px;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    min-width: 0;
    box-shadow: var(--shadow-2);
    transition: border-color var(--dur-chrome) var(--ease-out),
                background var(--dur-chrome) var(--ease-out),
                box-shadow var(--dur-chrome) var(--ease-out),
                transform var(--dur-chrome) var(--ease-out);
}
.cr-card:hover {
    border-color: color-mix(in srgb, var(--acc) 55%, var(--bdr));
    background: var(--pn-h);
}
/* The lift is what says "this is a control, not a poster", and it is
   mouse-only: on the shop tablet a transform on :hover sticks after the tap
   and leaves a card floating with nothing under the finger. */
@media (hover: hover) and (pointer: fine) {
    .cr-card:hover { box-shadow: var(--shadow-3); transform: translateY(-1px); }
}
.cr-card:active { transform: translateY(0); box-shadow: var(--shadow-1); }
/* A card whose number is already bad or warning carries the tint too, so the
   one that needs a person is visible as a SHAPE in the band and does not
   depend on noticing a colour change inside 19px of digits. */
.cr-card:has(.cr-bad) {
    background: color-mix(in srgb, var(--no) 6%, var(--pn));
    border-color: color-mix(in srgb, var(--no) 26%, var(--bdr));
}
.cr-card:has(.cr-bad):hover { background: color-mix(in srgb, var(--no) 11%, var(--pn)); }
.cr-card:has(.cr-warn) {
    background: color-mix(in srgb, var(--warn) 6%, var(--pn));
    border-color: color-mix(in srgb, var(--warn) 26%, var(--bdr));
}
.cr-card:has(.cr-warn):hover { background: color-mix(in srgb, var(--warn) 11%, var(--pn)); }
.cr-card:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: 2px;
}
.cr-card-l {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* The number is the card. Tabular so six of them line up as a row of figures
   rather than six independently-drifting glyph runs. */
.cr-card-v {
    font-size: 19px;
    font-weight: 700;
    color: var(--txt);
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
}
.cr-card-s { font-size: 11px; color: var(--dim); }
.cr-warn { color: var(--warn); }
.cr-bad { color: var(--no); }

/* ── three columns ─────────────────────────────────────────────────────── */
/* Specificity note, learned the hard way: bench-studio.css:500 carries
   `#d-orders .dash-grid { grid-template-columns: minmax(0,1fr) 260px }` — the
   SAME specificity as `#pg-door .cr-grid`, and bench-studio.css loads AFTER
   app.css in index.html, so it won. Three children in a two-column grid put
   NEEDS YOU in the 1fr and the orders list in 260px, which is exactly what
   the shipped page looked like. Two belts: this element no longer claims the
   `dash-grid` class, and this selector now carries both ids so nothing
   generic can outrank it. */
#pg-door #d-orders .cr-grid {
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr) 420px;
    gap: var(--sp-4);
    align-items: start;
}
/* Below the artboard's width the side columns stop earning their space before
   the list does — the list is the thing you came for, so it keeps the width. */
@media (max-width: 1180px) {
    #pg-door #d-orders .cr-grid { grid-template-columns: minmax(0, 1fr) 420px; }
    #pg-door #d-orders .cr-needs { grid-column: 1 / -1; }
}
@media (max-width: 900px) {
    #pg-door #d-orders .cr-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── NEEDS YOU ─────────────────────────────────────────────────────────── */
.cr-needs-count {
    margin-left: auto;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}
.cr-needs-list { padding: var(--sp-2); display: flex; flex-direction: column; gap: 4px; }
/* Each item is a button with a severity rail. The rail is the encoding — the
   colour says how bad, the position says which group, and neither depends on
   reading the number. */
.cr-need {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 100%;
    /* CABINETWORKS_GUIDELINES.md:54 — large touch targets. 9px of
       padding around a 12px title and an 11px note clears the 44px
       floor on the tablet these are tapped on. */
    padding: 9px 10px;
    background: none;
    border: 0;
    border-left: 4px solid var(--bdr);
    border-radius: 0 6px 6px 0;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
}
.cr-need:hover { background: color-mix(in srgb, var(--acc) 8%, transparent); }
.cr-need:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: -2px;
}
.cr-need-t { font-size: 12px; font-weight: 600; color: var(--txt); }
.cr-need-n {
    font-size: 11px;
    color: var(--dim);
    overflow: hidden;
    text-overflow: ellipsis;
}
.cr-need-bad { border-left-color: var(--no); }
.cr-need-warn { border-left-color: var(--warn); }
.cr-need-info { border-left-color: var(--acc); }
.cr-need-ok { border-left-color: var(--ok); }
.cr-needs-clear {
    padding: var(--sp-3);
    font-size: var(--fs-sm);
    color: var(--ok);
    font-weight: var(--fw-semibold);
    line-height: 1.5;
}
.cr-needs-sub {
    display: block;
    margin-top: 3px;
    color: var(--dim);
    font-weight: 400;
}

/* ── the filter banner ─────────────────────────────────────────────────── */
.cr-filter-banner {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 7px 12px;
    margin-bottom: var(--sp-2);
    background: color-mix(in srgb, var(--acc) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--acc) 35%, var(--bdr));
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
}
.cr-filter-label { font-weight: var(--fw-semibold); color: var(--txt); flex: 1; }
.cr-filter-clear {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--acc);
}
.cr-filter-clear:hover { text-decoration: underline; }
/* PR-LIST-FILTERS (2026-09-09) — click-to-filter chips in the rows and in
   the banner. In a row a chip reads as plain text (the value IS the text)
   until hovered; when its value is the active filter it takes the accent so
   the operator can see which cell the list is narrowed on. In the banner
   every chip is "on" and carries its own ×. */
.ds-fchip {
    background: none;
    border: 0;
    padding: 0 2px;
    margin: 0 -2px;
    border-radius: 4px;
    cursor: pointer;
    font: inherit;
    color: inherit;
    text-align: left;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ds-fchip:hover { color: var(--acc); text-decoration: underline dotted; }
.ds-fchip.ds-fchip-on {
    color: var(--acc);
    background: color-mix(in srgb, var(--acc) 14%, transparent);
    padding: 1px 6px;
    margin: 0;
}
.cr-filter-label { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); }
.cr-filter-label .ds-fchip { font-weight: var(--fw-semibold); }
.cr-filter-label .ds-fchip-x { opacity: .7; margin-left: 2px; }
.cr-filter-label .ds-fchip:hover .ds-fchip-x { opacity: 1; }
.ds-fchip-count { color: var(--dim); font-weight: var(--fw-semibold); }
/* PR-HEADERS-SORT (2026-09-09) — the column headings are the sort control.
   A heading has to READ as a heading and behave as a button: no button
   chrome, the whole cell is the target (shop hands, gloves), and the one
   that is driving the order is lit with the accent and its arrow. */
#pg-door .jobs-table th .ds-th {
    display: flex;
    align-items: center;
    gap: 2px;
    width: 100%;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    text-align: left;
    cursor: pointer;
    user-select: none;
}
#pg-door .jobs-table th .ds-th:hover { color: var(--acc); }
#pg-door .jobs-table th .ds-th:focus-visible {
    outline: 2px solid var(--acc);
    outline-offset: 2px;
    border-radius: 3px;
}
#pg-door .jobs-table th.ds-th-on,
#pg-door .jobs-table th.ds-th-on .ds-th { color: var(--acc); }
#pg-door .jobs-table th .ds-th-ar { font-size: 9px; line-height: 1; }

/* ── THE LINE ──────────────────────────────────────────────────────────── */
.cr-line { padding: var(--sp-2); }
.cr-line-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 2px 8px;
    width: 100%;
    padding: 5px 6px;
    background: none;
    border: 0;
    border-radius: 4px;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
}
.cr-line-row:hover { background: color-mix(in srgb, var(--acc) 7%, transparent); }
.cr-line-row:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
    outline-offset: -2px;
}
.cr-line-l { font-size: 12px; color: var(--txt); }
.cr-line-n {
    font-size: 12px;
    font-weight: 700;
    color: var(--txt);
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.cr-line-pcs { display: block; font-size: 10px; font-weight: 400; color: var(--dim); }
.cr-line-bar {
    grid-column: 1 / -1;
    display: block;
    height: 4px;
    background: var(--bdr);
    border-radius: 2px;
    overflow: hidden;
}
.cr-line-bar > span { display: block; height: 100%; background: var(--acc); border-radius: 2px; }
.cr-line-note {
    padding: var(--sp-2) 6px 0;
    font-size: 11px;
    color: var(--dim);
    line-height: 1.5;
}

/* ── MONEY ─────────────────────────────────────────────────────────────── */
.cr-money { padding: var(--sp-3); }
.cr-money-row {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: 3px 0;
    font-size: 12px;
    color: var(--dim);
}
.cr-money-row b { color: var(--txt); font-variant-numeric: tabular-nums; }
.cr-money-sep { height: 1px; background: var(--bdr); margin: var(--sp-2) 0; }
.cr-money-note {
    margin-top: var(--sp-2);
    padding: 8px;
    background: color-mix(in srgb, var(--warn) 10%, transparent);
    border-radius: 6px;
    font-size: 11px;
    color: var(--dim);
    line-height: 1.5;
}
.cr-money-note b { color: var(--warn); }

/* ── PR-FIT-ONE-SCREEN (2026-08-28) ───────────────────────────────────────
   Owner: "see all that dead space at the top remove that deadspace. make it
   all fit no scrolling."

   Measured at 1440x900 before this block: 130px of chrome before the wizard
   even starts, and the tallest step (Style & Materials) at 811px — so the
   step that matters most was the one that scrolled.

   Where the height was going, and why each cut is safe:

     · 72px  the page's own 36px top+bottom padding
     · 104px the Back/Next footer, from two 52px buttons
     ·  48px each step's 24px card padding, top and bottom
     ·  48px every select and input, an ISO bare-finger tap target

   That last one is the big one and it does not belong here: Door Studio is an
   OFFICE screen — desk, mouse, bare hands — and the owner settled that
   explicitly when the ISO sizes went in. Glove-sized controls belong to
   pg-production and pg-admin-tv, which are the tablet surfaces. 38px is a
   comfortable mouse target and buys back ~10px on every row. */
/* `.scroll` is the PARENT of #d-new, not a child — an #d-new .scroll rule
   matches nothing. Measured: the padding stayed at 36px while I thought I had
   removed it. */
#pg-door > .scroll { padding-top: var(--sp-3); padding-bottom: var(--sp-3); }
#pg-door #do-order-headrow { margin-bottom: var(--sp-2); padding-top: 6px; padding-bottom: 6px; }
body[data-ui-version="ascut"] #pg-door .ds-bundle { padding: 14px 18px; }
#pg-door .ds-bundle .fr { gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); }
/* These are MIN-height, not height — setting `height` leaves the min in force
   and nothing moves. Measured: selects stayed at 48px through two attempts. */
#pg-door .ds-bundle .fg select,
#pg-door .ds-bundle .fg input[type="text"],
#pg-door .ds-bundle .fg input[type="number"],
#pg-door .ds-bundle .fg input:not([type]) {
    min-height: 38px;
    height: 38px;
    /* The base #pg-door .fg rule pads these 14px top and bottom — sized for
       its 48px box. Forced to 38px, that left 8px of inner box for 16px of
       text: "Cherry" rendered half-clipped, the owner could not read Style,
       Frame or Panel. At a fixed height the box centres its own line; the
       vertical padding has no job. */
    padding-top: 0;
    padding-bottom: 0;
}
#pg-door .ds-bundle-nav {
    padding-top: var(--sp-2);
    margin-top: var(--sp-2);
}
#pg-door .ds-bundle-nav button { min-height: 38px; height: 38px; padding: 0 var(--sp-4); }
/* The step bar was 56px of its own; it is the wizard's only persistent
   chrome, so it can be a header rather than a band. */
#pg-door #ds-bundle-nav-bar { height: 44px; }
/* PR-DEAD-GAP (2026-08-29, owner: "there is dead space in between home and
   door order"). The gap between the fixed topbar and the order header was
   stacked paddings doing nothing: 12px on the scroller plus 12px on the tab,
   over an empty studio-header band. 4px each is a seam, not a shelf. */
#pg-door > .scroll { padding-top: 4px; }
#pg-door > .scroll > #d-new { padding-top: 4px; padding-bottom: var(--sp-3); }
#pg-door > .shdr:empty { display: none; }

/* The structural fix, after padding cuts stopped paying.
   Measured: 811 -> 731 from trimming chrome, and a single field group is
   already only 63px (21px label + 4px gap + 38px control). The rest is real
   content, so the remaining height had to come out of the LAYOUT, not the
   spacing.
   The param panel is ~770px wide and short on height — exactly the shape that
   wants columns. Style & Materials has four groups stacked in a 770px-wide
   column, each using half its width for a label and leaving the rest empty.
   Two columns halve the step's height and use the width that was already
   there. */
/* PR-COLUMNS-WERE-THE-WRONG-LEVER (2026-08-29).
   Two-column STEPS are gone. They looked like the fix and measured like one at
   1440, but the gain came back as height everywhere else: halving a group's
   width made its field rows wrap, so 1280 stayed at 745px, and the fields that
   did stay side by side shrank to 138px — too narrow to tell "Soft Maple -
   Paint Grade" from "Soft Maple - Stain Grade".

   Measured across three widths, tallest step per configuration:

       config              1440   1280   1152   narrowest select
       two-column groups    630    745    762     138px
       one column, 170px    633    653    731     179px   <- this

   The lever was never the group layout. It was the FIELD track, plus giving the
   form back the width the preview was holding. Removing this also removes the
   specificity hazard it shipped: those #pg-door #ds-bundle-N rules outranked
   `.ds-bundle[hidden]` and un-hid three steps, which is what put the Next
   button 366px below the fold. */

/* ── PR-STEP-DENSITY (2026-08-29) ─────────────────────────────────────────
   Itemized where a 660px Order Info actually went: three helper PARAGRAPHS
   cost 150px (a 123px date field is 63px of control and 60px of prose), the
   Back/Next buttons were 52px because a ascut skin rule outranked the 38px
   office sizing, the step's child gap was 20px six times over, and the panel
   kept 24px under the last row. None of it was field content.

   The helper text is not deleted: it is clamped to one line with the full
   sentence on hover, and it still backs aria-describedby. */
#pg-door .fg-hint {
    display: block;
    font-size: 11px;
    color: var(--dim);
    margin-top: 3px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
/* The ascut skin sizes .ba/.bg at 52px; that class-level rule beat the
   id-level 38px office sizing only because the old rule did not name the
   classes. Name them. */
#pg-door .ds-bundle-nav button,
#pg-door .ds-bundle-nav button.ba,
#pg-door .ds-bundle-nav button.bg {
    min-height: 38px;
    height: 38px;
    padding: 0 var(--sp-4);
}
/* Rhythm: 20px between every child of a step is meeting-notes spacing, not
   form spacing. 10px keeps the groups distinct at half the cost. */
#pg-door #d-new .ds-bundle { gap: 10px; padding: 10px 16px; }
#pg-door #d-new .ds-bundle-nav { padding-top: var(--sp-2); margin-top: 2px; }
#pg-door #d-new .ds-param-panel { padding-bottom: var(--sp-2); }
#pg-door #ds-bundle-2 .dsg { margin-bottom: 2px; }
/* While the wizard is EXPANDED, the report buttons under the panel act on a
   committed order that is, at that moment, being composed — Cutlist and
   Bidding need pieces. They return with the piece list when the wizard
   collapses, which is the view they operate on. 44px + a gap back. */
#pg-door #d-new:not(.ds-wiz-collapsed) > .brow { display: none; }
/* PR-STYLESLIM (2026-08-29): the element the rule above was WRITTEN for is
   actually <header class="do-headbar" id="do-order-headrow"> — the "Door
   Order #3235 · for jesse · Estimate · Revisions" band. It was never a
   .brow, so it stayed visible through every "remove the dead space" round
   (the synthesized-node trap: I hid the class I assumed, not the node the
   page renders). While the wizard is composing, the chips already carry
   the context; the headbar returns when the wizard collapses to the
   order view. */
#pg-door #d-new:not(.ds-wiz-collapsed) .do-headbar { display: none; }

/* ═══ PR-EDD-READABLE + PR-CARD-MATCH (2026-08-29) ═══════════════════════
   Owner, from live screenshots of the piece Edit modal and the group card:
   "how did you miss this? and this — cleaning it up and making it match.
   also giving more info on what the center stile and rail are doing."
   The modal's explainers lived INSIDE <label> text, so the global
   uppercase-label style shouted whole sentences; they are normal-case
   hint lines now, and each section carries a diagram. The card pieces
   below share the wizard's tokens instead of their own inline styles. */

/* — the Edit Door modal — */
#edit-door-modal .edd-hint {
    color: var(--dim);
    font-size: 11.5px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.45;
    margin-top: 4px;
}
#edit-door-modal .edd-apply {
    margin-top: 14px;
    padding: 10px 12px;
    background: color-mix(in srgb, var(--acc) 7%, transparent);
    border: 1px solid color-mix(in srgb, var(--acc) 30%, var(--bdr));
    border-radius: var(--radius-md, 8px);
}
#edit-door-modal .edd-apply > label { display: inline-block; margin: 0 8px 0 0; }
#edit-door-modal .edd-apply input { max-width: 84px; display: inline-block; }
#edit-door-modal .edd-apply-of { color: var(--txt); font-size: 13px; margin-left: 8px; }
#edit-door-modal .edd-apply-of b { color: var(--acc); }
#edit-door-modal .edd-priority { margin-top: 14px; }
#edit-door-modal .edd-radio-row { display: flex; gap: 16px; margin-top: 6px; flex-wrap: wrap; }
#edit-door-modal .edd-radio-row label {
    display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
    font-weight: 400; font-size: 13px; letter-spacing: 0; text-transform: none;
    color: var(--txt); margin: 0;
}
#edit-door-modal .edd-sec {
    display: flex; align-items: flex-start; gap: 14px;
    margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--bdr);
}
#edit-door-modal .edd-sec-text { flex: 1; min-width: 0; }
#edit-door-modal .edd-sec h3 { margin: 0 0 4px; }
#edit-door-modal .edd-prose {
    color: var(--dim); font-size: 12px; line-height: 1.5; margin: 0;
}
#edit-door-modal .edd-diagram {
    flex: none; width: 58px; height: auto; color: var(--dim); margin-top: 2px;
}
#edit-door-modal .fr { margin-top: 10px; }
#edit-door-modal .edd-footer {
    position: sticky; bottom: 0; background: var(--sf);
    padding: 14px 0; margin-top: 18px;
}

/* — the group card, matched to the wizard's tokens — */
#style-groups .ops-rail { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--bdr); }
#style-groups .ops-rail-head {
    display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;
}
#style-groups .ops-rail-label {
    font-size: 10.5px; letter-spacing: 1.2px; color: var(--dim); font-weight: 600;
}
#style-groups .ops-rail-total {
    font-size: 12.5px; color: var(--dim); font-weight: 600;
    font-variant-numeric: tabular-nums;
}
#style-groups .ops-rail-total.has-ops { color: var(--ok); }
#style-groups .ops-rail-chips {
    display: flex; flex-wrap: wrap; gap: 8px; padding-bottom: 4px;
}
#style-groups .ops-chip {
    display: inline-flex; flex-direction: column; align-items: flex-start; justify-content: center;
    min-width: 122px; min-height: 50px; padding: 7px 13px; margin: 0;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: var(--radius-md, 8px);
    cursor: pointer; font-family: inherit; text-align: left;
    transition: background var(--dur-panel) var(--ease-out),
                border-color var(--dur-panel) var(--ease-out);
}
#style-groups .ops-chip:hover { border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr)); }
#style-groups .ops-chip.on { background: var(--acc); color: var(--bg); border-color: var(--acc); }
#style-groups .ops-chip-name { font-size: 12.5px; font-weight: 600; line-height: 1.2; }
#style-groups .ops-chip-price {
    font-size: 11px; opacity: 0.82; margin-top: 2px; font-variant-numeric: tabular-nums;
}
#style-groups .ops-rail-sum {
    margin-top: 8px; font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums;
}
/* piece-row actions: Edit is a quiet secondary, the remove ✕ stays small */
#style-groups td .ba.bs {
    background: transparent; color: var(--acc);
    border: 1px solid color-mix(in srgb, var(--acc) 40%, var(--bdr));
}
#style-groups td .ba.bs:hover { background: color-mix(in srgb, var(--acc) 12%, transparent); }
/* the Add Piece bar: same surface as the wizard forms, no gradient pill */
#style-groups .add-door-bar {
    background: var(--inp);
    border: 1px solid var(--bdr);
}
#style-groups .add-door-bar .ba { border-radius: var(--radius-md, 8px); }

/* ═══ PR-CARD-CALM (2026-08-29, owner picked Option C) ═══════════════════
   Progressive disclosure on the group card: header summary line, resting
   rows with a Details column, collapsed ops rail. Everything expands in
   place; nothing moved surfaces. */
#style-groups .grp-sum {
    display: flex; align-items: center; gap: 10px;
    margin-top: 6px; min-width: 0;
}
#style-groups .grp-sum-txt {
    color: var(--dim); font-size: 12px; line-height: 1.4;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    min-width: 0;
}
#style-groups .grp-sum-btn {
    flex: none; padding: 3px 12px; font-size: 11.5px; font-weight: 600;
    background: transparent; color: var(--acc);
    border: 1px solid color-mix(in srgb, var(--acc) 40%, var(--bdr));
    border-radius: var(--radius-md, 8px); cursor: pointer;
}
#style-groups .grp-sum-btn:hover { background: color-mix(in srgb, var(--acc) 12%, transparent); }
#style-groups .grp-sum-btn.on { color: var(--txt); border-color: var(--bdr); }

#style-groups .pc-flags { max-width: 340px; }
#style-groups .pcf {
    display: inline-block; padding: 1px 7px; margin: 1px 0;
    font-size: 11px; color: var(--dim);
    background: var(--inp); border: 1px solid var(--bdr);
    border-radius: 999px; white-space: nowrap;
}
#style-groups .pcf-acc { color: var(--acc); border-color: color-mix(in srgb, var(--acc) 40%, var(--bdr)); }
#style-groups .pcf-dim { background: transparent; border-color: transparent; }
#style-groups .pcf-btn { cursor: pointer; font-family: inherit; }
#style-groups .pcf-btn:hover { background: color-mix(in srgb, var(--acc) 12%, transparent); }

/* ═══ PR-MEMBER-INLINE (2026-08-29) ═════════════════════════════════════
   The center rail/stile editor, opened inside the card under its row —
   never over the door it is editing. Live drawing left, controls right,
   presets on top. Collapses on Apply. */
#style-groups .pc-member > td { padding: 0; background: var(--pn); }
#style-groups .dsme {
    border: 1px solid color-mix(in srgb, var(--acc) 35%, var(--bdr));
    border-radius: var(--radius-md, 8px);
    background: var(--pn);
    margin: 6px 0 10px;
    overflow: hidden;
}
#style-groups .dsme-head {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    padding: 10px 14px;
    background: color-mix(in srgb, var(--acc) 8%, transparent);
    border-bottom: 1px solid var(--bdr);
}
#style-groups .dsme-head b { font-size: 14px; color: var(--txt); }
#style-groups .dsme-sub { font-size: 12px; color: var(--dim); }
#style-groups .dsme-body { display: flex; gap: 18px; padding: 14px; align-items: flex-start; }
/* PR-PREVIEW-FOLLOWS-EDIT (2026-08-29): the editor no longer draws a second
   door — the one 3D view is the big preview, which follows the piece being
   edited. What is left here is the sentence that a picture at an angle does
   not replace, so it reads as a line under the controls, not a picture box. */
#style-groups .dsme-canvas {
    width: 100%;
    padding-top: 12px;
    border-top: 1px solid var(--bdr);
}
#style-groups .dsme-svg { width: 240px; height: 300px; display: block; }
/* PR-MEMBER-3D (2026-08-29): the editor canvas renders the real door via the
   same mesh + painter the big preview uses. The painter emits a 100%-sized
   SVG, so the box sets the size. */
#style-groups .dsme-solid { width: 240px; height: 300px; display: block; margin: 0 auto; }
#style-groups .dsme-solid svg { width: 100%; height: 100%; display: block; }
#style-groups .dsme-frame { fill: var(--inp); stroke: var(--txt); stroke-width: 2; }
#style-groups .dsme-panel { fill: color-mix(in srgb, var(--txt) 10%, transparent); }
#style-groups .dsme-member { fill: var(--acc); }
#style-groups .dsme-read { margin-top: 0; font-size: 13.5px; color: var(--txt); line-height: 1.45; }
#style-groups .dsme-read b { color: var(--acc); }
#style-groups .dsme-dims { font-size: 11.5px; color: var(--dim); margin-top: 2px; font-variant-numeric: tabular-nums; }
#style-groups .dsme-wait { padding: 40px 10px; color: var(--dim); font-size: 12px; }

#style-groups .dsme-controls { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 14px; }
#style-groups .dsme-presets { display: flex; flex-wrap: wrap; gap: 6px; }
#style-groups .dsme-preset {
    padding: 6px 13px; font-size: 12.5px; font-weight: 600; font-family: inherit;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 999px; cursor: pointer;
}
#style-groups .dsme-preset:hover { border-color: color-mix(in srgb, var(--acc) 50%, var(--bdr)); }
#style-groups .dsme-preset.on { background: var(--acc); color: var(--bg); border-color: var(--acc); }

#style-groups .dsme-grid { display: flex; flex-wrap: wrap; gap: 18px; }
#style-groups .dsme-col { flex: 1; min-width: 220px; display: flex; flex-direction: column; gap: 8px; }
#style-groups .dsme-lbl { margin: 0; font-size: 13px; font-weight: 600; color: var(--txt); }
#style-groups .dsme-lbl span { display: block; font-size: 11.5px; font-weight: 400; color: var(--dim); margin-top: 1px; }
/* PR-OPENING-PANELS (2026-09-01): the per-opening panel picks, laid out as the
   door is looked at — TOP row first, each row's openings side by side, so the
   control is a picture of the door rather than a list of coordinates. Scoped
   under #style-groups like every other .dsme rule, which keeps it above the
   generic control styling in app.css without needing !important. */
#style-groups .dsme-openings { display: flex; flex-direction: column; gap: 6px; }
/* PR-OPENING-PANELS-P-NUMBERS (2026-09-01): ONE strip, side by side. A row per
   door row grew the editor taller with every centre rail; this is one
   label-plus-select tall whatever the panel count, and the cells share the
   width. `min-width:0` on the cell is what lets a select actually shrink inside
   a flex item instead of forcing the strip wider than the card. */
#style-groups .dsme-op-row { display: flex; gap: 6px; align-items: flex-end; flex-wrap: nowrap; }
/* PR-GLASS-3D (2026-09-01): each opening says WHICH one it is — position in
   words over the clear-opening size — because a column of identical dropdowns
   cannot tell you which panel you are about to change. */
#style-groups .dsme-op-cell { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
#style-groups .dsme-op-cell > label {
    font-size: 9.5px; font-weight: 600; letter-spacing: .6px;
    text-transform: uppercase; color: var(--dim);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#style-groups .dsme-op-size { text-transform: none; letter-spacing: 0; font-weight: 400; opacity: .75; }
#style-groups select.dsme-op {
    flex: 1 1 0; min-width: 0; padding: 4px 6px; font-size: 11.5px;
    background: var(--inp); border: 1px solid var(--bdr); color: var(--white);
    border-radius: 4px;
}
/* An opening the operator has SET reads as set, the same accent tint the Panel
   Stock and Grain pins use — a door with one lite should be obvious without
   opening the dropdown. */
#style-groups select.dsme-op.dsme-op-on {
    background: color-mix(in srgb, var(--acc) 18%, var(--inp));
    border-color: var(--acc);
}
#style-groups .dsme-row { display: flex; gap: 10px; flex-wrap: wrap; }
#style-groups .dsme-f { display: flex; flex-direction: column; gap: 3px; }
#style-groups .dsme-f > label {
    font-size: 9.5px; font-weight: 600; letter-spacing: .6px;
    text-transform: uppercase; color: var(--dim);
}
#style-groups .dsme-opt { text-transform: none; letter-spacing: 0; font-weight: 400; opacity: .8; }
#style-groups .dsme-f input {
    padding: 6px 9px; font-size: 13px; width: 110px;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 6px;
}
#style-groups .dsme-f-wide input { width: 100%; }
#style-groups .dsme-f-wide { width: 100%; }

#style-groups .dsme-cross {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding-top: 12px; border-top: 1px solid var(--bdr);
}
#style-groups .dsme-cross-lbl { font-size: 13px; font-weight: 600; color: var(--txt); }
#style-groups .dsme-radios { display: flex; gap: 14px; flex-wrap: wrap; }
#style-groups .dsme-radios label {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12.5px; color: var(--txt); cursor: pointer;
}
#style-groups .dsme-cross-hint { font-size: 11.5px; color: var(--dim); }

#style-groups .dsme-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    flex-wrap: wrap; padding-top: 12px; border-top: 1px solid var(--bdr);
}
#style-groups .dsme-apply { font-size: 12.5px; color: var(--dim); }
#style-groups .dsme-apply input {
    width: 64px; padding: 5px 8px; margin: 0 4px; font-size: 13px;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 6px;
}
#style-groups .dsme-btns { display: flex; gap: 8px; }
#style-groups .dsme-clear { color: var(--no); }

/* PR-MEMBER-OPENINGS (2026-09-17) — the editor edits the PANELS.
   One height box per row and one width box per column; a typed size is a pin
   (lit), a blank box is fill and shows what it came to. The opening you are on
   is outlined here and tinted in the 3D preview. Touch targets are 40px+: this
   card is used on an iPad with dusty hands. Colours are tokens only. */
#style-groups .dsme-hint { flex-basis: 100%; font-size: 12px; color: var(--dim); line-height: 1.45; }
#style-groups .dsme-sizes-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; }
#style-groups .dsme-axis { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
#style-groups .dsme-axis-lbl { margin: 0 0 2px; font-size: 13px; font-weight: 600; color: var(--txt); }
#style-groups .dsme-axis-lbl span { display: block; font-size: 11.5px; font-weight: 400; color: var(--dim); margin-top: 1px; }
#style-groups .dsme-axis-empty { margin: 0; padding: 6px 8px; font-size: 12px; color: var(--dim); }
#style-groups .dsme-size-row {
    display: grid; grid-template-columns: 64px minmax(0, 1fr) minmax(64px, auto);
    align-items: center; gap: 10px; padding: 5px 8px;
    border: 1px solid transparent; border-radius: 8px; cursor: pointer;
}
#style-groups .dsme-size-row > label {
    font-size: 12.5px; font-weight: 700; color: var(--txt);
    font-variant-numeric: tabular-nums; cursor: pointer;
}
#style-groups .dsme-size-row.dsme-sel,
#style-groups .dsme-op-cell.dsme-sel {
    border-color: var(--acc);
    background: color-mix(in srgb, var(--acc) 10%, transparent);
}
#style-groups .dsme-op-cell { border: 1px solid transparent; border-radius: 8px; padding: 4px; cursor: pointer; }
#style-groups .dsme-pin {
    min-height: 40px; width: 100%; box-sizing: border-box; padding: 8px 10px; font-size: 14px;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 8px; font-variant-numeric: tabular-nums;
}
#style-groups .dsme-pin.dsme-pin-on {
    border-color: var(--acc); font-weight: 600;
    background: color-mix(in srgb, var(--acc) 14%, var(--inp));
}
#style-groups .dsme-pin.dsme-pin-err { border-color: var(--no); }
#style-groups .dsme-size-out { font-size: 12px; color: var(--dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
#style-groups .dsme-size-out.dsme-size-set { color: var(--acc); font-weight: 700; letter-spacing: .4px; text-transform: uppercase; font-size: 10.5px; }
#style-groups .dsme-mid {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-left: 14px; padding: 1px 8px 1px 12px; font-size: 11.5px; color: var(--dim);
    border-left: 2px solid color-mix(in srgb, var(--acc) 45%, var(--bdr));
}
#style-groups .dsme-mid-name { font-weight: 600; font-size: 10px; letter-spacing: .5px; text-transform: uppercase; }
#style-groups .dsme-mid label { display: inline-flex; align-items: center; gap: 6px; }
#style-groups .dsme-cl {
    width: 84px; padding: 4px 6px; font-size: 12px; font-variant-numeric: tabular-nums;
    background: transparent; color: var(--txt);
    border: 1px dashed var(--bdr); border-radius: 6px;
}
#style-groups .dsme-cl:focus { border-style: solid; border-color: var(--acc); }
#style-groups .dsme-axis-btns { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
#style-groups .dsme-mini { min-height: 36px; padding: 7px 12px; font-size: 12.5px; }
#style-groups .dsme-mini:disabled { opacity: .45; cursor: default; }
#style-groups .dsme-more { border-top: 1px solid var(--bdr); padding-top: 10px; }
#style-groups .dsme-more > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--txt); }
#style-groups .dsme-more-body { display: flex; flex-direction: column; gap: 12px; padding-top: 10px; }
#style-groups .dsme-more .dsme-cross { border-top: 0; padding-top: 0; }
#style-groups .dsme-more .dsme-f input { width: 170px; min-height: 36px; }
#style-groups .dsme-frame-note { font-size: 11.5px; color: var(--dim); align-self: flex-end; padding-bottom: 8px; }
#style-groups .dsme-err {
    font-size: 12.5px; color: var(--dim); padding: 8px 10px;
    border: 1px solid var(--bdr); border-radius: 8px;
}
#style-groups .dsme-err.dsme-err-block {
    color: var(--no); border-color: var(--no); font-weight: 600;
    background: color-mix(in srgb, var(--no) 8%, transparent);
}
#style-groups .dsme-err.dsme-err-flash { animation: dsme-err-flash .45s ease; }
@keyframes dsme-err-flash { 0%, 100% { transform: none; } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
#style-groups .dsme-btns .ba:disabled { opacity: .45; cursor: not-allowed; }
/* Measured 2026-09-17: a six-opening door's strip of dropdowns holds the card
   at 756px, wider than a narrow tablet column. It stays ONE strip on a desk
   (the editor must not grow a row per door row); on a narrow screen it wraps. */
@media (max-width: 900px) {
    #style-groups .dsme-op-row { flex-wrap: wrap; }
    #style-groups .dsme-op-cell { flex: 1 1 120px; }
}

/* The callouts the painter draws over the 3D door while the editor is open.
   Sizes (font, stroke) are set inline, scaled off the projection; only the
   colours live here, as tokens. */
.ds-preview-canvas .ds3d-ov-hit { fill: var(--acc); fill-opacity: 0; stroke: none; cursor: pointer; }
.ds-preview-canvas .ds3d-ov-hit:hover { fill-opacity: .10; }
.ds-preview-canvas .ds3d-ov-hit.ds3d-ov-sel { fill-opacity: .24; stroke: var(--acc); stroke-linejoin: round; }
.ds-preview-canvas .ds3d-ov-dim { stroke: var(--acc); fill: none; stroke-linecap: round; }
.ds-preview-canvas .ds3d-ov-wit { stroke: var(--acc); stroke-opacity: .55; fill: none; stroke-dasharray: 2 2; }
.ds-preview-canvas .ds3d-ov-txt,
.ds-preview-canvas .ds3d-ov-sub {
    fill: var(--txt); font-family: var(--ff-mono, ui-monospace, monospace);
    paint-order: stroke; stroke: var(--pn); stroke-linejoin: round;
}
.ds-preview-canvas .ds3d-ov-txt { font-weight: 700; }
.ds-preview-canvas .ds3d-ov-sub { font-weight: 500; fill-opacity: .85; }
.ds-preview-canvas .ds3d-ov-frame { fill-opacity: .55; }
#style-groups td .ba.bs.on {
    background: var(--acc); color: var(--bg); border-color: var(--acc);
}

/* Narrow screens: the drawing goes on top, full width, and stays visible
   while the fields are filled in — the whole point of the editor. */
@media (max-width: 900px) {
    #style-groups .dsme-body { flex-direction: column; }
    #style-groups .dsme-canvas { width: 100%; }
    #style-groups .dsme-svg { width: 100%; max-width: 240px; margin: 0 auto; }
}
#style-groups .pc-exp {
    width: 26px; height: 24px; padding: 0; line-height: 1;
    background: transparent; color: var(--dim);
    border: 1px solid var(--bdr); border-radius: 6px; cursor: pointer;
    font-size: 13px;
}
#style-groups .pc-exp:hover { color: var(--txt); border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr)); }
#style-groups .pc-exp.on { color: var(--acc); border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr)); }
#style-groups .pc-more td {
    background: color-mix(in srgb, var(--inp) 55%, transparent);
    border-bottom: 1px solid var(--bdr);
    padding: 8px 10px;
}
#style-groups .pc-more-grid {
    display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-end;
}
#style-groups .pcm { display: inline-flex; flex-direction: column; gap: 3px; }
#style-groups .pcm > label {
    font-size: 9.5px; font-weight: 600; letter-spacing: .6px;
    text-transform: uppercase; color: var(--dim);
}
#style-groups .pcm-notes input { width: 200px !important; }

#style-groups .ops-rail-head {
    display: flex; align-items: center; gap: 12px; width: 100%;
    background: transparent; border: none; padding: 0; margin: 0 0 2px;
    cursor: pointer; font-family: inherit; text-align: left;
}
#style-groups .ops-rail-head:hover .ops-rail-label { color: var(--txt); }
#style-groups .ops-rail-active {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; font-size: 11.5px; color: var(--dim);
}
#style-groups .ops-rail-chev { flex: none; color: var(--dim); font-size: 12px; }
#style-groups .ops-rail.open .ops-rail-head { margin-bottom: 10px; }
/* Same reasoning as .brow, found from the owner's screenshots: on a real page
   the JS renders "No style groups yet…" plus the 0-pieces total pill UNDER the
   expanded wizard — ~150px my JS-less probe never saw, and exactly what pushed
   Next below the fold at 2560px. They belong to the piece-list view and return
   with it when the wizard collapses. */
#pg-door #d-new:not(.ds-wiz-collapsed) #style-groups,
#pg-door #d-new:not(.ds-wiz-collapsed) #do-total { display: none; }

/* The Add-row's qty/W/D/H inputs kept their 52px shop-floor sizing — the same
   ISO-vs-office split settled for every other control on this page. 87px
   fields made the size row the tallest thing in Frame & Add. */
#pg-door .add-door-bar .ad-field input {
    min-height: 38px;
    font-size: 16px;
    width: 84px;
}

/* Box Spec stacked its four preset cards vertically — 369px of a 629px step
   for four choices that read fine side by side. One row of cards is how every
   other picker on this page already works (the Type tiles). */
#pg-door #ds-bundle-4 .ds-shop-cards {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: stretch;
}
#pg-door #ds-bundle-4 .ds-shop-cards > .ds-shop-card {
    flex: 1 1 160px;
    min-width: 150px;
}



/* One track for every step. 170px is the WRAP threshold, not the width — the
   1fr lets a column grow to fill — and it is what keeps a material name legible
   at the narrowest measured layout (179px at 1440, 189px at 1280). */
#pg-door .ds-bundle .fr {
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}

/* PR-SLAB-SHEET-MATERIAL (2026-09-17): a slab answers "what is it made of" with
   ONE field where a framed door answers with Frame + Panel. Under `auto-fit`
   that lone field stretches the full width and the step stops matching its
   framed sibling, which is exactly what the owner flagged. Pin the slab row to
   the SAME two tracks so the Material picker keeps a Frame-sized column.
   `toggleSGFields` stamps data-slab. */
#pg-door .ds-bundle .fr#sg-materials-row[data-slab="1"] {
    grid-template-columns: repeat(2, minmax(170px, 1fr));
}

/* AFTER the base rule on purpose: same specificity, so source order
   decides. The first landing of this block sat above the base and lost
   silently at every laptop width. */
/* Laptops: below ~1380px the form column is too narrow for four 170px fields
   abreast, so the details row wraps and costs a line. 150px keeps them abreast
   — a compromise accepted ONLY at laptop widths; desktop keeps 170. */
@media (max-width: 1380px) {
    #pg-door .ds-bundle .fr {
        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    }
    #pg-door .ds-bundle .fr#sg-materials-row[data-slab="1"] {
        grid-template-columns: repeat(2, minmax(150px, 1fr));
    }
}
/* The 💡 guide banners narrate a wizard the operator is already standing in,
   and each one costs a paragraph of a step's budget. The subtitle already
   says what the step decides. */
#pg-door .ds-bundle .ds-step-guide { display: none; }

/* ── PR-NEXT-ALWAYS-REACHABLE (2026-08-29) ────────────────────────────────
   Owner: "i do not want to have to scroll to click next when setting up a new
   door order. you keep fucking this up."

   Measured across four window sizes, one step at a time, exactly as the wizard
   shows them. Trimming padding got 1440x900 to fit — with ONE pixel of
   headroom on Box Spec — and still left four of five steps scrolling at
   1280x800. Budget-chasing was never going to hold: step height depends on
   content, panel width and how the field grid wraps, and a laptop is not a
   1440 desktop.

   So the Back/Next bar sticks to the bottom of the viewport instead. If a step
   is short, the bar sits under it as it always did; if a step is tall, the step
   scrolls under a bar that never leaves the screen. Next is reachable at every
   step and every window size, by construction rather than by arithmetic.

   The bar keeps an opaque background and a top hairline so content scrolling
   beneath it reads as passing behind, not colliding with it. */
#pg-door #d-new .ds-bundle-nav {
    /* PR-NAV-IS-A-ROW (2026-08-29): stated here, at the same specificity as
       the sticky that measurably applies, because the restored base
       `.ds-bundle-nav { display:flex }` does NOT reach the page — computed
       display was `block`, so <nav> stacked its buttons and every footer with
       three children measured 84px (two 38px rows) instead of 46. */
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    position: sticky;
    /* Not 0: sticky bottom resolves against the SCROLLPORT'''s padding box, and
       this page'''s scroller carries a bottom padding (set just above). Measured
       with bottom:0 the bar landed a consistent 10px below the visible edge at
       every step and every window size — exactly that padding. Matching it puts
       the bar on the edge instead of just past it. */
    bottom: var(--sp-3);
    z-index: 2;
    background: var(--pn);
    /* Cancel the card's own left/right padding so the bar spans the step and
       nothing shows through at its edges while content scrolls under. */
    /* PR-H-SCROLLBAR (2026-08-29, owner: "a left and right scroll bar?").
       These bled -18px across what used to be 24px of card padding; the card
       is 16px now, so the bar poked 2px past each side — and .scroll's auto
       overflow renders that as a horizontal scrollbar. Match the padding. */
    margin-left: -16px;
    margin-right: -16px;
    padding-left: 16px;
    padding-right: 16px;
}

/* The preview held a fixed 460px at every window size, so at 1152 the form was
   down to 552px while the drawing kept 433. The form is the working surface;
   the preview yields first. Measured: form 813 -> 853 at 1440, 653 -> 693 at
   1280. */
#pg-door #d-new .ds-viewport {
    flex: 0 1 420px;
    min-width: 280px;
}

/* ── PR-MOBILE (2026-08-29) — owner: "optimize it for mobile aswell" ──────
   Measured at 512 (headless Chrome's narrowest window — phone proxy), 746 and
   1002 before touching anything. Tablets were already sound; three real
   defects, one per block below. */

/* 1. The sticky Next died on phones. The ≤767px rule gives .ds-template-b
   `overflow-y:auto`, which makes it a NEW scroll container — the nav then
   sticks to template-b's scrollport, which never clips, while the page
   scroller does the actual scrolling. Measured: Next at y=910 in a 682px
   viewport at scroll-top. Same trap PR-DS-PREVIEW-STICKY documents for the
   preview: an intermediate overflow context eats position:sticky. The column
   stack needs no scroll context of its own — and note `overflow-x:hidden`
   alone is enough to recreate the trap, because a hidden/visible pair
   computes visible to auto. Both axes must be visible. */
@media (max-width: 767px) {
    #pg-door #d-new .ds-template-b { overflow: visible; }
}

/* 2. The six hub metric cards stayed 6-abreast at phone width — 67px a card,
   with "Accepted, not released" ellipsised to nothing. Two rows of three,
   then three rows of two. */
@media (max-width: 700px) {
    #pg-door #d-orders .cr-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 460px) {
    #pg-door #d-orders .cr-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 3. Single-column hub order. NEEDS YOU stacked ABOVE the orders list
   (measured @348 vs @432), so a phone opened onto ~300px of problems before
   the first order. The list is what you came for; problems second, the
   line/money rail last. */
@media (max-width: 900px) {
    #pg-door #d-orders .cr-grid > .bench-main { order: 1; }
    #pg-door #d-orders .cr-grid > .cr-needs { order: 2; }
    #pg-door #d-orders .cr-grid > .bench-rail.dash-rail:not(.cr-needs) { order: 3; }
}

/* The constant scrollbar, found from the owner's 2560px screenshots: the
   preview column sizes itself `min(80vh, 700px)` against the WINDOW, but the
   page area is the window minus the fixed topbar and the status bar — so the
   preview always overshot the page and forced a scrollbar even when the form
   fit with room to spare. Measured: content bottom 798 in a 772 budget at
   1876px, identical on every step, because the preview was the deepest thing
   on the page. 250px covers topbar + order header + the preview card's own
   header and footer. */
@media (min-width: 768px) {
    #pg-door .ds-viewport .ds-viewport-preview {
        height: min(calc(100vh - 250px), 640px);
        min-height: 300px;
    }
}

/* Fold cover screens (measured at 344 and 376 via the iframe probe — the
   owner's actual device caught this below my 512px window floor). The
   stile/rail cards are a hard `repeat(4, 1fr)` grid, and a grid track cannot
   shrink below its content's intrinsic width — so at 344px the four cards ran
   to x=375 and the page CLIPPED them: the bottom-rail field was partially
   unreachable, with no scrollbar to even reveal it. Two-up fits with ~160px
   cards. */
@media (max-width: 480px) {
    #pg-door .sr-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    #pg-door .sr-card { min-width: 0; }
    /* Fold cover screens: "USE SHOP STANDARD (Recommended)" is one
       non-wrapping 325px line — 9px past a 329px content box. Let the
       chip drop to its own line instead of forcing a sideways scroll. */
    #pg-door .ds-hero-title { white-space: normal; }
    #pg-door .ds-hero-title .ds-hero-rec { display: block; margin-left: 0; }
    #pg-door .sr-card svg, #pg-door .sr-card img { max-width: 100%; height: auto; }
}

/* Touch, keyed to the POINTER and not the width: an iPad at 1024px gets the
   38px mouse sizing under any width rule, and a mouse-driven small window
   does not need thumb targets. 44px is the bare-finger floor (the ISO glove
   sizes stay where the owner put them — the shop-floor pages). */
@media (pointer: coarse) {
    #pg-door .ds-bundle .fg select,
    #pg-door .ds-bundle .fg input[type="text"],
    #pg-door .ds-bundle .fg input[type="number"],
    #pg-door .ds-bundle .fg input:not([type]),
    #pg-door .ds-bundle-nav button,
    #pg-door .add-door-bar .ad-field input,
    #pg-door .ds-bundle-bar-btn {
        min-height: 44px;
        height: 44px;
    }
    #pg-door #ds-bundle-nav-bar { height: 52px; }
}

/* PR-KILL-H-SCROLL (2026-08-29, owner: "left and right scroll bar still
   there we can get rid of that"). The 2px footer-bleed fix was one source;
   their hard-refreshed screenshot proves at least one more exists in a DOM
   state I cannot reproduce here. So this stops being a hunt: the studio's
   page scroller never scrolls sideways, categorically. Anything that pokes
   past the edge is clipped by a pixel or two instead of renting a scrollbar
   for the whole page — and wide content that NEEDS to pan (tables, reports)
   already carries its own overflow-x:auto container per house style. */
/* PR-PAGE-FITS-ITS-FRAME (2026-08-29, the actual root, one layer under
   yesterday's one-scroller patch — found by reading the shell contract
   end-to-end instead of patching what the probe showed):

       #app-workspace   position:fixed; top:56px; bottom:0|32px; overflow-y:auto
       .pg              height: 100vh                      (app.css:416)

   Every page is 100vh tall inside a container that is 100vh minus 56 to 88px.
   The page overshoots its frame BY DEFINITION. Consequences, all reported by
   the owner across three days of screenshots:

     · the workspace always scrolls ~56-88px even when content fits — the
       phantom "still have to scroll";
     · the inner .scroll scrolls too — TWO live scrollbars, the "left and
       right scroll bar";
     · the sticky footer anchored to the inner scrollport, whose bottom sits
       ~88px BELOW the window — pinning the Next button half-cut behind the
       status bar, exactly every screenshot.

   .pg predates the shell; the shell added the topbar/statusbar offsets and
   the page height was never updated. Scoped to this page rather than fixing
   .pg app-wide — the other studios have their own compensations built on the
   phantom scroll (both-scroller zeroing in dtab, REPORT-BTN-SCROLL) and get
   their own change.

   Second half, found the same day: this page is a lazy PARTIAL, so it
   mounts inside #pg-host — and shell.css styled the host's :not(:empty)
   state with padding:24px + min-height:60vh + auto height. That styling
   was written for the loading shimmer but kept applying after the page
   mounted (the host stays non-empty forever): a permanent 24px dead band
   around the whole studio, and an auto-height parent that turned any
   height:100% on the page into a no-op. Fixed at the host in shell.css
   (PR-HOST-IS-NOT-A-FRAME): the host is height:100% and geometry-free;
   placeholder styling applies only while a placeholder is the content.

   With the page exactly filling its frame, the ORIGINAL design works as
   intended: .shdr pins, .scroll is the single live scroller whose bottom
   edge IS the workspace's — above the status bar — so the sticky footer
   pins visibly. Yesterday's overflow:visible flattening is superseded. */
/* PR-PREVIEW-FOLLOWS-SCROLL (2026-09-05, owner: "have it actually follow down
   the page so i can see what im doing when going though doors"): the design
   above was right and never took effect.

   `.scroll` carries `flex:1 1 0%`, but #pg-door was `display:block` — so that
   flex was INERT. Measured: #pg-door 912px tall, `.scroll` 6491px tall inside
   it, `scrollHeight/clientHeight = 6491/6491`. It never scrolled a pixel. The
   real scrolling had fallen through to #app-workspace.

   That is why the preview would not follow. `position:sticky` binds to the
   nearest ancestor with a non-visible overflow — `.scroll` (overflow-y:auto) —
   and an element cannot stick against a box that never scrolls internally. So
   the whole `.scroll` box slid up as #app-workspace scrolled, carrying the
   preview off screen with it. PR-DS-PREVIEW-STICKY untrapped `.ds-template-b`
   one level down and stopped one ancestor too early.

   Making #pg-door the flex column it always meant to be lets `flex:1` resolve:
   `.shdr` keeps its natural height, `.scroll` is bounded to the frame and
   becomes the single live scroller the comment above describes — and the
   preview sticks against it, which is the whole point.

   `min-height:0` on both: a flex item's default `min-height:auto` refuses to
   shrink below its content, which would silently restore the old behaviour and
   leave this looking like it did nothing. */
/* PR-DOOR-PAGE-LEAK (2026-09-09, owner: "i get this bug if i go from door
   studio directly to libraries"): the display MUST be paired with `.on`.
   A bare `#pg-door.pg` rule has specificity (1,1,0) and clobbers
   `.pg{display:none}` (0,1,0) from line 416 — so once the Door Studio
   partial had mounted, its page box stayed laid out on EVERY other page
   and the control room bled through Home / Libraries / everything under
   it. go() removing `.on` did nothing; only the CSS ever decided.
   Same defect, same fix as PR-OPC-2-FIX for #pg-schedule-v2
   (schedule-opcenter.css:29). Layout props stay on the unconditional rule
   so nothing about the ACTIVE page's geometry changes. */
#pg-door.pg { height: 100%; flex-direction: column; min-height: 0; }
#pg-door.pg.on { display: flex; }
#pg-door > .scroll { overflow-y: auto; overflow-x: hidden; min-height: 0; }

/* PR-NO-STRETCH (2026-08-29): template-b's default align-items:stretch sized
   the form card to the PREVIEW column's height on tall windows — measured
   panelBottom == previewBottom with 47px of slack under the step, and on the
   owner's 980px-tall window the footer rode that stretched bottom instead of
   hugging the fields. Columns size themselves now. */
@media (min-width: 768px) {
    #pg-door #d-new .ds-param-panel { align-self: flex-start; }
}

/* The backstop: a hidden step is hidden, whatever else claims otherwise.
   Two ids plus the attribute, so it beats any #pg-door #ds-bundle-N rule. */
#pg-door #d-new .ds-bundle[hidden] { display: none; }

/* ── PR-WIZARD-BACK (2026-08-28) ───────────────────────────────────────────
   Owner: "just do the wizzard that was there."

   PR-ONE-PANEL removed the step bar and the Back/Next footers and pruned the
   59 rules that styled them. PR-NO-SCROLL then tried an accordion instead.
   Both were worse than what was there. This restores the original chrome
   verbatim from 5ae06d30^ — the step bar, the connector rail, the step
   circles, the Back/Next buttons and the warned state — rather than a
   re-implementation, so it behaves exactly as it did before I touched it. */
/* ============================================================
           Phase I.5 -- Door Studio Template B + 5-Bundle layout
           All values use design tokens -- no magic px, no hex colors.
           ============================================================ */

        /* Bundle progress bar -- sticky at top of #d-new param panel */
        /* Bundle nav bar — proper progress stepper.
           (1) horizontal connector line under the step circles, so the
               5 steps read as a progression rather than disconnected tabs.
           (2) step circles with ring-style active state (filled for
               completed, outlined for current, muted for upcoming).
           (3) subtle hover on upcoming steps so operators see they're
               tappable (even though bundle-goto will gate on completion). */
        .ds-bundle-bar {
            display: flex;
            align-items: center;
            gap: var(--sp-1);
            background: color-mix(in srgb, var(--sf) 88%, var(--bg));
            border-bottom: 1px solid var(--bdr);
            overflow-x: auto;
            padding: 0 var(--sp-3);
            height: 56px;
            flex-shrink: 0;
            position: relative;
            /* PR-DS-WIZARD-ALIGN (2026-08-01): the step bar is a sibling of the
               param-panel card (max-width:1180px), so left un-capped it ran the
               full page width — dragging the far-right ✕ way past the card edge.
               Cap it to the same 1180px so the bar reads as the card's header and
               the close button lands at the card's right edge, not the page's. */
            max-width: 1180px;
        }
/* Connector line — single horizontal rule behind the buttons
           that the step circles sit on top of. `::before` (NOT
           `::after`) because a pseudo-element's stacking position
           depends on DOM insertion order among its siblings: `::before`
           paints as the FIRST child (below later flex children), while
           `::after` paints as the LAST child (above them). The pre-2026-
           04-23 version used `::after` and the rail painted ON TOP of
           the step labels, making them look like they had a strike-
           through. Fix: swap to `::before`. Keeps the "one continuous
           rail" behaviour without per-button segmentation. */
        .ds-bundle-bar::before {
            content: "";
            position: absolute;
            left: var(--sp-6);
            right: var(--sp-6);
            top: 50%;
            transform: translateY(-50%);
            height: 2px;
            background: var(--bdr);
            pointer-events: none;
        }
.ds-bundle-bar-btn {
            position: relative;
            display: flex;
            align-items: center;
            gap: var(--sp-2);
            padding: 0 var(--sp-3);
            background: var(--sf);
            border: none;
            color: var(--dim);
            font-size: var(--fs-sm);
            font-weight: var(--fw-semibold);
            cursor: pointer;
            white-space: nowrap;
            min-height: 48px;
            min-width: 56px;
            transition: color var(--dur-chrome) var(--ease-out);
            letter-spacing: 0.3px;
            border-radius: var(--radius-sm);
        }
.ds-bundle-bar-btn:hover:not([aria-disabled="true"]):not([aria-current="step"]) {
            color: var(--txt);
        }
.ds-bundle-bar-btn:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 2px;
        }
.ds-bundle-bar-btn[aria-current="step"] {
            color: var(--txt);
        }
.ds-bundle-bar-btn[data-complete="true"] { color: color-mix(in srgb, var(--ok) 85%, var(--txt)); }
.ds-bundle-bar-btn[aria-disabled="true"] { opacity: 0.55; cursor: default; }
/* Step circle — 28px so it dominates the connector line underneath */
        .ds-bundle-step-num {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 28px;
            height: 28px;
            border-radius: 50%;
            background: var(--sf);
            border: 2px solid var(--bdr);
            color: var(--dim);
            font-size: 12px;
            font-weight: var(--fw-bold);
            flex-shrink: 0;
            transition: background-color var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        color var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
        }
.ds-bundle-bar-btn[aria-current="step"] .ds-bundle-step-num {
            background: var(--acc);
            color: var(--bg);
            border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 25%, transparent);
        }
.ds-bundle-bar-btn[data-complete="true"] .ds-bundle-step-num {
            background: var(--ok);
            color: var(--bg);
            border-color: var(--ok);
        }
/* Swap number for checkmark on completed steps */
        .ds-bundle-bar-btn[data-complete="true"] .ds-bundle-step-num {
            font-size: 0;                                  /* hide the "1" */
        }
.ds-bundle-bar-btn[data-complete="true"] .ds-bundle-step-num::before {
            content: "✓";
            font-size: 14px;
            font-weight: var(--fw-bold);
        }
secondary (Back) is ghosted so it doesn't compete. Both
           inherit base .ba/.bg classes but override styling here so
           the button family feels cohesive with Add Door + CTA rows. */
        .ds-bundle-nav {
            display: flex;
            align-items: center;
            gap: var(--sp-2);
            padding-top: var(--sp-4);
            margin-top: var(--sp-4);
            border-top: 1px solid var(--bdr);
        }
.ds-bundle-nav .ds-bn-spacer { flex: 1; }
.ds-bundle-nav button {
            min-height: 48px;
            min-width: 72px;
            padding: 0 var(--sp-5);
            font-family: var(--ff-ui);
            font-weight: var(--fw-semibold);
            font-size: var(--fs-base);
            letter-spacing: 0.02em;
            border-radius: var(--radius-sm);
            cursor: pointer;
            transition: background var(--dur-chrome) var(--ease-out),
                        border-color var(--dur-chrome) var(--ease-out),
                        color var(--dur-chrome) var(--ease-out),
                        transform var(--dur-chrome) var(--ease-out),
                        filter var(--dur-chrome) var(--ease-out),
                        box-shadow var(--dur-chrome) var(--ease-out);
        }
/* Primary — Next/Add advance. Accent-filled.
         * 2026-04-23: `color` was hardcoded `#0a1012` (near-black) —
         * a legacy from the pre-theme-swap cyan-only accent era. Now
         * routes through `--acc-ink` so every theme picks the right
         * ink: white on Studio Light's blue, dark on Onyx's green,
         * black on Dark's cyan, white on shop's blue. */
        .ds-bundle-nav button.ba {
            background: var(--acc);
            color: var(--acc-ink);
            border: 1.5px solid var(--acc);
            box-shadow: 0 3px 10px color-mix(in srgb, var(--acc) 28%, transparent);
        }
.ds-bundle-nav button.ba:hover:not(:disabled) {
            filter: brightness(1.06);
            transform: translateY(-1px);
            box-shadow: 0 5px 16px color-mix(in srgb, var(--acc) 38%, transparent);
        }
.ds-bundle-nav button.ba:active:not(:disabled) {
            transform: translateY(0);
            filter: brightness(0.95);
        }
doesn't compete. */
        .ds-bundle-nav button.bg {
            background: transparent;
            color: var(--txt);
            border: 1.5px solid var(--bdr);
        }
.ds-bundle-nav button.bg:hover:not(:disabled) {
            background: color-mix(in srgb, var(--acc) 6%, transparent);
            border-color: color-mix(in srgb, var(--acc) 60%, var(--bdr));
        }
.ds-bundle-nav button:focus-visible {
            outline: 2px solid color-mix(in srgb, var(--acc) 70%, transparent);
            outline-offset: 3px;
        }
/* Disabled Next (when required fields missing) */
        .ds-bundle-nav button.ba:disabled,
        .ds-bundle-nav button.ba[aria-disabled="true"] {
            opacity: 0.45;
            cursor: not-allowed;
            box-shadow: none;
            filter: none;
        }
/* PR-DS-TYPE-STEP-LOCK (2026-08-01): the ← Back buttons that lead to the
           Type step and the "Type" nav chip render disabled while the order's
           item type is locked. Kept pointer-clickable (no pointer-events:none)
           so a tap still fires the explanatory toast from dsBundleGoto rather
           than doing nothing silently.
           PR-DS-TYPE-STEP-OPEN (2026-08-11): .ds-type-locked is now set ONLY for
           DRAWER BOX orders. Door-family orders (doors / drawer fronts / slab
           fronts / end panels) keep the Type step live so the next piece can be
           a different door-family type. */
        #d-new.ds-type-locked .ds-bundle-nav button.bg.ds-back-to-type,
        #d-new.ds-type-locked #ds-bnav-1 {
            opacity: 0.4;
            cursor: not-allowed;
        }
#d-new.ds-type-locked .ds-bundle-nav button.bg.ds-back-to-type:hover {
            background: transparent;
            border-color: var(--bdr);
        }
/* "Skip for now" — tertiary text-link style. Reads as secondary
           to Back/Next so an operator pre-quoting a job without a customer
           name yet has an obvious escape hatch without it competing
           visually with the primary forward action. */
        .ds-bundle-nav button.ds-bundle-skip-link {
            background: transparent;
            color: color-mix(in srgb, var(--txt) 70%, var(--dim));
            border: 1px solid transparent;
            text-decoration: underline;
            text-underline-offset: 3px;
            text-decoration-color: color-mix(in srgb, var(--dim) 60%, transparent);
            min-width: auto;
            padding: 0 var(--sp-3);
            font-weight: var(--fw-medium);
        }
.ds-bundle-nav button.ds-bundle-skip-link:hover {
            color: var(--txt);
            text-decoration-color: var(--acc);
        }
/* PR-MISMATCH-STRONG-WARN Phase 3 (2026-05-01) — preemptive
           visual signal on Bundle 2's Next button while the panel-
           compat warning is unresolved. Token-only colors. UX pre-
           audit Rec 3: amber border + label hint, no pulse, no
           disable (operators must distinguish "warning" from "field
           empty"). */
        .ds-bundle-nav-warned button.ba {
            border-color: var(--warn);
            box-shadow: 0 3px 10px color-mix(in srgb, var(--warn) 28%, transparent);
        }
.ds-bundle-nav-warned button.ba::after {
            content: " ⚠";
        }
/* Tighten the bundle bar step labels on narrow screens */
            .ds-bundle-bar-btn { padding: 0 var(--sp-2); font-size: 11px; }
body[data-ui-version="ascut"] #pg-door .ds-bundle-bar {
            border-radius: 12px 12px 0 0;
        }
/* ── /PR-ASCUT-PALETTE ─────────────────────────────────────────── */

        /* ── PR-ASCUT-BUNDLE — Door Studio wizard step-bar polish ───── */
        body[data-ui-version="ascut"] .ds-bundle-bar {
            background: color-mix(in srgb, var(--sf) 92%, var(--bg));
            backdrop-filter: saturate(140%) blur(8px);
            -webkit-backdrop-filter: saturate(140%) blur(8px);
        }
body[data-ui-version="ascut"] .ds-bundle-bar::before {
            background: linear-gradient(90deg,
                var(--bdr) 0%,
                color-mix(in srgb, var(--acc) 25%, var(--bdr)) 50%,
                var(--bdr) 100%);
            height: 2px;
            border-radius: 1px;
        }
/* PR-DS-WIZARD-ALIGN (2026-08-01): three-state legibility per operator —
           a step you CAN reach reads white, a locked one you can't reads grayed,
           and the one you're on reads blue. Previously every step defaulted to
           --dim (gray) and only lifted to white on hover, so the whole bar looked
           disabled and you couldn't tell available from locked at a glance. */
        body[data-ui-version="ascut"] .ds-bundle-bar-btn {
            color: var(--txt);                 /* navigable / normal = white */
            transition: color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        background 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn[aria-disabled="true"] {
            color: var(--dim);                 /* can't go into = grayed off */
            opacity: 0.5;
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn:hover:not([aria-disabled="true"]):not([aria-current="step"]) {
            color: var(--txt);
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn[aria-current="step"] {
            color: var(--acc);                 /* clicked into = blue */
        }
body[data-ui-version="ascut"] .ds-bundle-step-num {
            border-radius: 50%;
            transition: background 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        border-color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        color 160ms cubic-bezier(0.22, 1, 0.36, 1),
                        box-shadow 160ms cubic-bezier(0.22, 1, 0.36, 1);
        }
/* Navigable (not current / complete / locked) number circle reads white
           on a brighter ring so it's clearly enabled, not grayed. */
        body[data-ui-version="ascut"] .ds-bundle-bar-btn:not([aria-disabled="true"]):not([aria-current="step"]):not([data-complete="true"]) .ds-bundle-step-num {
            color: var(--txt);
            border-color: color-mix(in srgb, var(--txt) 45%, var(--bdr));
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn[aria-current="step"] .ds-bundle-step-num {
            background: linear-gradient(135deg, var(--acc) 0%, var(--acc2) 100%);
            border-color: var(--acc);
            color: var(--acc-ink);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent);
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn[data-complete="true"] .ds-bundle-step-num {
            background: color-mix(in srgb, var(--acc) 14%, transparent);
            border-color: var(--acc);
            color: var(--acc);
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn[data-skipped="true"] {
            opacity: 0.4;
        }
body[data-ui-version="ascut"] .ds-bundle-bar-btn,
            body[data-ui-version="ascut"] .ds-bundle-step-num {
                transition: none;
            }
/* Bundle nav Next button (ds-bundle-nav .ba) — bigger */
        body[data-ui-version="ascut"] .ds-bundle-nav button.ba {
            min-height: 52px;
            min-width: 140px;
            padding: 0 26px;
            font-size: 14px;
            font-weight: 700;
            letter-spacing: 0.02em;
            border-radius: 10px;
        }
body[data-ui-version="ascut"] .ds-bundle-nav button.bg {
            min-height: 52px;
            padding: 0 22px;
            font-size: 13px;
            border-radius: 10px;
        }
.ds-param-panel > .ds-bundle-bar { gap: 12px; }
/* ── 4. The step rail, welded to the form card it drives ───────────────────
   It used to be a SIBLING of .ds-template-b: a 1180px-wide bar hovering over
   a ~56% card, belonging to neither column. It now lives inside
   #ds-param-panel as its first child, full-bleed to the card's edges and
   sticky, so the steps stay reachable while a long step scrolls. */
.ds-param-panel > .ds-bundle-bar {
    /* Full-bleed to the card's side edges. Only the SIDES get a negative
       margin: the panel's padding-top is already zeroed below, so a negative
       top margin here would drag the rail 24px ABOVE the card's own border
       (measured: rail top=145 against a card top of 168, overlapping the
       head-bar above it). */
    margin: 0 calc(-1 * var(--sp-6));
    max-width: none;
    /* 60px = a 48px pill + 6px top/bottom. See the tap-target note below. */
    height: 60px;
    padding: 0 var(--sp-4);
    position: sticky;
    top: 0;
    z-index: 4;
    border-bottom: 1px solid var(--bdr);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    /* Premium: the head is a distinctly lighter plane than the card body, so
       the card reads as ONE object with a header rather than two stacked
       boxes. Measured first pass used --sf as the base, which is the body's
       own colour in the light theme — the head vanished. --pn (the raised
       surface token) separates in both themes. */
    background: linear-gradient(180deg,
        var(--pn) 0%,
        color-mix(in srgb, var(--pn) 82%, var(--sf)) 100%);
    box-shadow: var(--shadow-inset), 0 1px 0 color-mix(in srgb, var(--bdr) 60%, transparent);
    backdrop-filter: blur(8px);
}
at this height it read as a strike-through. The pills carry the sequence. */
.ds-param-panel > .ds-bundle-bar::before { display: none; }
/* Step buttons as pills: the current step is the only filled thing on the
   row, so where you are is unmistakable from across the bench. */
/* TAP TARGETS (2026-08-27, corrected against ISO/TS 9241-411).
   A CSS px is 1/96in = 0.2646mm, so the numbers convert:
       ISO bare finger      12mm = 45px
       ISO light gloves     15mm = 57px   (also ISA-101 standard control)
       ISO heavy work glove 19mm = 72px
   My first pass shrank these pills to 36px to win vertical room. That is
   9.5mm -- below the BARE-FINGER floor, on a control a man in gloves taps to
   move between steps. Restored to 48px (12.7mm), which is what the codebase
   already used and what the sentinel audit blessed as "Rule C".
   Worth knowing separately: 48px only clears the bare-finger threshold. For
   gloves the standard wants 57px. That is an app-wide call, not one to make
   silently inside this PR. */
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn {
    min-height: 48px;
    min-width: 0;
    padding: 0 var(--sp-3);
    gap: var(--sp-2);
    background: transparent;
    border-radius: 999px;
    font-size: var(--fs-sm);
    letter-spacing: 0;
    transition: color var(--dur-chrome) var(--ease-out),
                background-color var(--dur-chrome) var(--ease-out);
}
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn[aria-current="step"] {
    background: color-mix(in srgb, var(--acc) 13%, transparent);
    color: var(--txt);
}
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn:hover:not([aria-disabled="true"]):not([aria-current="step"]) {
    background: color-mix(in srgb, var(--txt) 7%, transparent);
}
.ds-param-panel > .ds-bundle-bar .ds-bundle-step-num {
    width: 22px; height: 22px;
    border-width: 1.5px;
    font-size: 11px;
}
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn[aria-current="step"] .ds-bundle-step-num {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 22%, transparent);
}
/* Step NUMBERS come from a CSS counter, not from the markup.
   toggleSGFields() shows/hides the Box Spec chip depending on item type, so
   the hard-coded 1·2·3·5 in the HTML read "1 2 3 5" for a door and "1 2 3 4 5"
   for a drawer box. A counter skips display:none elements for free, so the row
   is always 1..n with no JS renumbering. */
.ds-param-panel > .ds-bundle-bar { counter-reset: ds-step; }
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn { counter-increment: ds-step; }
.ds-param-panel > .ds-bundle-bar .ds-bundle-step-num { font-size: 0; }
.ds-param-panel > .ds-bundle-bar .ds-bundle-bar-btn:not([data-complete="true"]) .ds-bundle-step-num::after {
    content: counter(ds-step);
    font-size: 11px;
    font-weight: var(--fw-bold);
}
sp-2 on top of that gives the
   head 24px of clearance from the first control without it drifting loose. */
.ds-param-panel > .ds-bundle-bar + * { margin-top: var(--sp-2); }
.ds-param-panel > .ds-bundle-bar { height: 48px; padding: 0 var(--sp-3); }

/* PR-SHRINK-THE-ROW (2026-08-28) — one line per order.
   The row was a three-line identity block measured at 117px; five orders
   filled the screen. Job number, customer and PO now sit on one baseline, and
   the row is what a list row should be: scannable, not a summary of an order
   you have not selected. */
#pg-door .jobs-table td { padding-top: 7px; padding-bottom: 7px; }
#pg-door .jobs-table .col-name {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    min-width: 0;
}
#pg-door .jobs-table .col-name .nm { font-weight: 600; flex: none; }
#pg-door .jobs-table .row-cust {
    color: var(--txt);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
#pg-door .jobs-table .row-po { flex: none; font-size: var(--fs-sm); }
/* The job-icon SVG went with the second and third lines — at one line it was
   22px of indent buying nothing. */
#pg-door .jobs-table .col-name > svg { display: none; }

/* ── PR-HUB-A3 (2026-08-28) — the pinned detail ────────────────────────────
   Locked decision #2: no overlay. The detail is always on screen, arrows walk
   the index, Enter opens. So the pane has to read as a READ-OUT of the row
   that is highlighted, not as a card competing with the table for attention:
   label/value rows, one accent (the row highlight), and the two actions at the
   bottom where a reader lands. */
.door-pin { padding: 0; }
.door-pin-head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-3) var(--sp-2);
    border-bottom: 1px solid var(--bdr);
}
.door-pin-job { font-weight: 700; color: var(--txt); font-size: var(--fs-base); }
.door-pin-body { padding: var(--sp-2) var(--sp-3); }
.door-pin-row {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    padding: 3px 0;
    font-size: var(--fs-sm);
    line-height: 1.5;
}
/* A fixed label column so the values line up as a scannable column rather than
   ragging in from wherever each label happens to end. */
.door-pin-k { flex: 0 0 62px; color: var(--dim); }
.door-pin-v { flex: 1; color: var(--txt); min-width: 0; overflow-wrap: anywhere; }
.door-pin-sub { color: var(--dim); }
.door-pin-ok { color: var(--ok); font-weight: var(--fw-semibold); }
.door-pin-warn { color: var(--warn); font-weight: var(--fw-semibold); }
.door-pin-acts {
    display: flex;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3) var(--sp-3);
    border-top: 1px solid var(--bdr);
}
.door-pin-acts .bg { flex: 1; min-width: 0; }
.door-pin-acts + .door-pin-acts { padding-top: 0; border-top: 0; }
/* PR-ACTIONS-TO-THE-PANE (2026-08-28): the stage move is the one button whose
   LABEL changes with the job ("Accept & Start", "Start build", "Mark
   delivered"), so it gets its own full-width row above the fixed verbs. */
.door-pin-stage {
    padding: var(--sp-2) var(--sp-3) 0;
    display: flex;
}
.door-pin-stage > * { flex: 1; }
.door-pin-stage .jl-btn,
.door-pin-stage button { width: 100%; }
/* Sign-off is the affirmative action when an order is blocked, so it reads as
   one — the same green the pane's Review line uses, not another grey button. */
.door-pin-go {
    background: var(--ok);
    border-color: var(--ok);
    color: var(--ok-ink);
}
.door-pin-go:hover { filter: brightness(1.06); }
/* PR-PIN-ACTIONS-FIRST (2026-09-17): Delete is one tap in the pane now, so it
   reads as destructive — red text and border, never a filled button that could
   be mistaken for the primary action. It still confirms before anything goes. */
.door-pin-del { color: var(--no); border-color: color-mix(in srgb, var(--no) 45%, var(--bdr)); }
.door-pin-del:hover { background: color-mix(in srgb, var(--no) 10%, transparent); color: var(--no); }
.door-pin-rush {
    margin-left: auto;
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--no);
    color: var(--no-ink);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
}
.door-pin-empty {
    padding: var(--sp-4) var(--sp-3);
    color: var(--dim);
    font-size: var(--fs-sm);
    text-align: center;
    line-height: 1.6;
}
.door-pin-hint { display: block; margin-top: 4px; font-size: 11px; opacity: 0.8; }

/* The highlighted row. A left rail plus a tint — the tint alone is too quiet
   against a striped table, and a border alone shifts the row by a pixel. */
.jobs-table tr.row-pinned > td {
    background: color-mix(in srgb, var(--acc) 10%, transparent);
}
.jobs-table tr.row-pinned > td:first-child {
    box-shadow: inset 3px 0 0 0 var(--acc);
}

/* PR-DBOX-3D-REAL-DIMS (2026-08-28) — the 3D object shares the preview canvas
   with #ds-svg, one visible at a time. The soft grey ground is the same one the
   library editor's viewer uses, so a box looks the same wherever it is shown. */
#ds-3d-host {
    width: 100%;
    /* PR-3D-FILLS-ITS-BOX (2026-09-05): was `aspect-ratio: 1 / 1`, which held
       the viewer to a 300x300 square inside a 340x541 canvas and left 241px of
       empty panel under the drawing. A square made sense while the painter
       emitted a fixed 240x240 viewBox; now that the viewBox crops to the drawn
       object, the host can take the whole canvas and the object grows into it.
       `min-height` keeps it sane if the canvas ever collapses. */
    height: 100%;
    min-height: 220px;
    touch-action: none;
    cursor: grab;
    user-select: none;
    overflow: hidden;
    border-radius: var(--radius-sm);
    background: var(--sf);
}
#ds-3d-host[hidden] { display: none; }
/* The label is a word, not a glyph — the collapse control beside it is the
   glyph, and two icons here would read as one control. */
.ds-3d-toggle {
    width: auto;
    padding: 0 var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
}

.do-review-btn { gap: var(--sp-2); }
.do-review-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
    background: var(--warn);
}
.do-review-btn[data-reviewed="false"] {
    color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 55%, var(--bdr));
    background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.do-review-btn[data-reviewed="false"]:hover {
    color: var(--warn);
    border-color: var(--warn);
    background: color-mix(in srgb, var(--warn) 16%, transparent);
}
.do-review-btn[data-reviewed="true"] .do-review-dot { background: var(--ok); }
.do-review-btn[data-reviewed="true"] { color: var(--ok); }
.do-review-btn[data-reviewed="true"]:hover {
    color: var(--ok);
    border-color: color-mix(in srgb, var(--ok) 50%, var(--bdr));
    background: color-mix(in srgb, var(--ok) 8%, transparent);
}/* ── 4. The step rail, welded to the form card it drives ───────────────────
   It used to be a SIBLING of .ds-template-b: a 1180px-wide bar hovering over
   a ~56% card, belonging to neither column. It now lives inside
   #ds-param-panel as its first child, full-bleed to the card's edges and
   sticky, so the steps stay reachable while a long step scrolls. */
/* The connector rail was drawn edge-to-edge under buttons that are now pills;/* Step buttons as pills: the current step is the only filled thing on the
   row, so where you are is unmistakable from across the bench. *//* TAP TARGETS (2026-08-27, corrected against ISO/TS 9241-411).
   A CSS px is 1/96in = 0.2646mm, so the numbers convert:
       ISO bare finger      12mm = 45px
       ISO light gloves     15mm = 57px   (also ISA-101 standard control)
       ISO heavy work glove 19mm = 72px
   My first pass shrank these pills to 36px to win vertical room. That is
   9.5mm -- below the BARE-FINGER floor, on a control a man in gloves taps to
   move between steps. Restored to 48px (12.7mm), which is what the codebase
   already used and what the sentinel audit blessed as "Rule C".
   Worth knowing separately: 48px only clears the bare-finger threshold. For
   gloves the standard wants 57px. That is an app-wide call, not one to make
   silently inside this PR. *//* Step NUMBERS come from a CSS counter, not from the markup.
   toggleSGFields() shows/hides the Box Spec chip depending on item type, so
   the hard-coded 1·2·3·5 in the HTML read "1 2 3 5" for a door and "1 2 3 4 5"
   for a drawer box. A counter skips display:none elements for free, so the row
   is always 1..n with no JS renumbering. */
/* ✓ on a finished step is already supplied by the base rule's ::before. */

/* ── 5. The card body ──────────────────────────────────────────────────────
   The bar is now the card's head, so the body owns the rest. */
.ds-param-panel {
    padding-top: 0;
    box-shadow: var(--shadow-2);
    overflow: visible;          /* let the head's sticky resolve to the page */
}
/* The panel is a flex column with gap:sp-4;
/* At rest the wizard is collapsed and #ds-bundle-nav-bar is display:none, so the
   card has no head to butt against — give the padding back or the "Add a door"
   CTA sits on the card's top edge. */
#d-new.ds-wiz-collapsed .ds-param-panel { padding-top: var(--sp-6); }

/* ── 6. Each step's own 118–158px header ───────────────────────────────────
   Every step opened with an accent bar, a 20px title, a subtitle, and a tinted
   💡 banner — measured at 141px on Step 1 before a single field. The pill row
   directly above already named the step, so the title restated it and the
   banner narrated a wizard the operator was already standing in.

   PR-ONE-PANEL (2026-08-28): the pill row is GONE. The title is now the ONLY
   thing naming the group, and with five groups stacked on one screen an
   unlabelled block of fields is unreadable — so the title comes back, at a
   size that reads as a section heading rather than a page heading.
   toggleSGFields writes this element ("Add Boxes" vs "Frame & Add Pieces"),
   which until now went nowhere. The 💡 banners stay hidden: five of them on
   one screen is narration, and the subtitle already says what the group
   decides. */
#pg-door .ds-bundle > .ds-bundle-header {
    padding: 0;
    margin: 0 0 var(--sp-4);
    border-bottom: none;
}
#pg-door .ds-bundle > .ds-bundle-header::before { display: none; }
#pg-door .ds-bundle > .ds-bundle-header > .ds-bundle-title {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--txt);
    margin: 0 0 2px;
}
#pg-door .ds-bundle > .ds-bundle-header > .ds-step-guide  { display: none; }
#pg-door .ds-bundle > .ds-bundle-header > .ds-bundle-subtitle {
    font-size: var(--fs-sm);
    color: var(--dim);
    line-height: 1.45;
    max-width: 64ch;
}
/* A header left with nothing in it must not still spend its margin.
   PR-ONE-PANEL (2026-08-28): this used to be `display: none`, which was safe
   only while the title was hidden too. It is not any more — Style & Materials
   lost its subtitle to PR-DS-B2-POLISH, so this rule was hiding that group's
   entire header, leaving the tallest group on the screen (645px) with no name
   on it. Measured in Chrome, not reasoned about. Now it only trims the margin:
   a header down to just its title still gets to show the title. */
#pg-door .ds-bundle > .ds-bundle-header:not(:has(> .ds-bundle-subtitle)) {
    margin-bottom: var(--sp-2);
}

/* ── 7. Breathing room, spent once ────────────────────────────────────────
   With ~350px reclaimed there is room to give a little back to the things an
   operator actually touches — that is what makes it read as considered rather
   than merely compressed. Tap targets are untouched (Rule C: 48px selects). */
#pg-door .ds-bundle .fg select,
#pg-door .ds-bundle .fg input[type="text"],
#pg-door .ds-bundle .fg input[type="number"],
#pg-door .ds-bundle .fg input:not([type]) {
    border-radius: var(--radius-sm);
    transition: border-color var(--dur-chrome) var(--ease-out),
                box-shadow var(--dur-chrome) var(--ease-out);
}
#pg-door .ds-bundle .fg select:focus,
#pg-door .ds-bundle .fg input:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 18%, transparent);
}

/* PR-DOOR-STEP3-ORDER (2026-08-27): #sg-details-row packs four fields across.
   Their labels carry a library chip and are not the same length, so a wrapped
   label used to push only ITS select down and break the row's shared baseline.

   Reserving two label lines fixed the alignment but cost 26px of height on
   every row whether it wrapped or not. `.fr` is a grid, so the cells in a row
   are already equal height -- pushing the select to the bottom of its cell
   aligns all of them for free, and the row is only ever as tall as its tallest
   label actually needs.

   PR-MATERIALS-BASELINE (2026-08-29, owner: screenshot of Frame and Panel not
   lining up). #sg-materials-row was REMOVED from this rule. The trick above is
   only sound when the select is the LAST child of its cell -- bottom-aligning
   the last child of equal-height grid cells lines them all up. In details-row
   that holds: every .fg is exactly label + select.

   Materials-row is not that shape. door-studio.js appends a stock note INTO
   each cell -- _dsRefreshFrameStockNote at :23060 and _dsPanelStockNote at
   :22634 -- so the select stops being last and `margin-top:auto` starts
   bottom-aligning it against whatever note sits below it. The two notes are
   deliberately different: the frame note is a bordered dashed box and the
   panel gate hint is plain text. Measured at 1440x900, headless: frame note
   32.2px, panel hint 16.8px, and the panel select inherited the entire 15.4px
   difference as a computed margin-top of 15.3906px. Frame select landed at
   221.5, panel at 236.9.

   Top-aligned is the correct behaviour here: both labels are two short words
   that cannot realistically wrap, so the selects sit under them and the height
   difference falls harmlessly to the bottom of the shorter cell. */
#sg-details-row > .fg > select {
    margin-top: auto;
}

/* ── 8. Narrow screens ────────────────────────────────────────────────────
   Under ~900px the split collapses and the head-bar's two halves stack. */
@media (max-width: 900px) {
    .do-headbar { gap: var(--sp-2); padding: var(--sp-3); }
    .do-headbar-acts { width: 100%; justify-content: flex-start; }
}

        /* ═══════════════════════════════════════════════════════════════
           PR-CED-ONE-SCREEN (2026-09-11): the Edit Cabinet screen in the
           standards page's grammar. Operator: the editor "looks off" next
           to Shop Standards, "like 2 different things to learn". One layout
           now — properties · 3D stage — and the properties
           column wears what Shop / Job Standards wear: a tier-colored title
           with a swatch (this is the CABINET tier, --tier-cab), a tab row
           with an underline, cards in columns, 15–17px type. These rules sit
           at the end of the file on purpose: they override the V2 grid and
           panel rules above at equal specificity by source order, and carry
           their own breakpoints because a later rule outside a media query
           would otherwise beat the earlier ones inside.
           ═══════════════════════════════════════════════════════════════ */
        /* Column 1 (the cabinet-tree rail) is gone by owner request
           (2026-09-11): the topbar ◀ ▶ cabnav is the way to switch
           cabinets. Hardcoded 0 (not the --ced-rail-w var) so a stale
           stored rail width can never reopen it; _cedSyncSashes sees
           0px and hides the rail's drag handle on its own. */
        #cab-editor-modal[data-layout="v2"] {
            grid-template-columns: minmax(0, 1fr) 0 var(--ced-side-w, 520px);
            grid-template-rows: 60px minmax(0, 1fr) auto;
        }
        @media (max-width: 1599px) and (min-width: 1280px) {
            #cab-editor-modal[data-layout="v2"] { grid-template-columns: minmax(0, 1fr) 0 var(--ced-side-w, 440px); }
        }
        @media (max-width: 1279px) and (min-width: 1024px) {
            #cab-editor-modal[data-layout="v2"] { grid-template-columns: minmax(0, 1fr) 0 var(--ced-side-w, 360px); }
        }
        @media (max-width: 1023px) and (min-width: 768px) {
            #cab-editor-modal[data-layout="v2"] { grid-template-columns: minmax(0, 1fr) 0 var(--ced-side-w, 300px); }
        }
        /* The properties column takes the wide middle; the stage the right. */
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel {
            grid-column: 1; border-left: none; border-right: 1px solid var(--bdr);
            display: flex; flex-direction: column; padding: 0 18px 28px; background: var(--bg);
        }
        #cab-editor-modal[data-layout="v2"] .ced-v2-stage { grid-column: 3; border-left: 1px solid var(--bdr); }
        #cab-editor-modal[data-layout="v2"] .ced-v2-tabrail { display: none; }

        /* ── Header: swatch + name in the cabinet tier's color, meta beside it ── */
        #cab-editor-modal[data-layout="v2"] .cab-editor-topbar { min-height: 60px; gap: 14px; background: var(--bg); padding: 0 18px; }
        #cab-editor-modal[data-layout="v2"] .cab-editor-topbar h2 {
            display: flex; align-items: baseline; gap: 10px; min-width: 0;
            font: 650 24px var(--ff-ui); color: var(--tier-cab, #7A3BAF); letter-spacing: -.02em;
        }
        .ced-tier-sw { width: 13px; height: 13px; border-radius: 3px; background: var(--tier-cab, #7A3BAF); display: inline-block; align-self: center; flex: 0 0 auto; }
        .ced-heading-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .ced-heading-meta { font: 500 15px var(--ff-ui); color: var(--dim); white-space: nowrap; }
        #cab-editor-modal[data-layout="v2"] .cab-editor-back-btn {
            min-height: 34px; padding: 5px 12px; border-radius: 8px; font: 600 13px var(--ff-ui);
            background: var(--pn); color: var(--dim); border: 1px solid var(--bdr);
        }
        #cab-editor-modal[data-layout="v2"] .cab-editor-back-btn:hover { border-color: var(--tier-cab, #7A3BAF); color: var(--tier-cab, #7A3BAF); }
        .ced-cabnav { display: flex; gap: 4px; }
        .ced-cabnav-btn { width: 30px; height: 30px; border-radius: 6px; border: 1px solid var(--bdr); background: var(--pn); color: var(--dim); font-size: 12px; cursor: pointer; }
        .ced-cabnav-btn:hover { border-color: var(--tier-cab, #7A3BAF); color: var(--tier-cab, #7A3BAF); }
        .ced-scope-pill {
            font: 600 14px var(--ff-ui); color: var(--tier-cab, #7A3BAF); white-space: nowrap;
            border: 1px solid color-mix(in srgb, var(--tier-cab, #7A3BAF) 45%, var(--bdr));
            background: color-mix(in srgb, var(--tier-cab, #7A3BAF) 10%, transparent);
            border-radius: 999px; padding: 5px 14px;
        }

        /* ── "Inherited" strip: value + rung, one line, wraps ── */
        .ced-method-strip {
            display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
            padding: 7px 12px; margin: 12px 0 0; border: 1px solid var(--bdr); border-radius: 8px;
            background: var(--pn); font: 500 13.5px var(--ff-ui); color: var(--dim);
        }
        .ced-method-strip[hidden] { display: none; }
        .ced-ms-k { font: 600 11px var(--ff-ui); letter-spacing: .08em; text-transform: uppercase; color: var(--tier-job, #1E7F4F); }
        .ced-ms-dim { color: var(--muted); }
        .ced-ms-i { white-space: nowrap; color: var(--txt); }
        .ced-ms-l { color: var(--dim); }
        .ced-ms-i em { font-style: normal; color: var(--muted); font-size: 12px; }
        .ced-ms-i.own em { color: var(--tier-cab, #7A3BAF); font-weight: 600; }
        .ced-ms-go { margin-left: auto; border: none; background: none; color: var(--tier-cab, #7A3BAF); font: 600 13px var(--ff-ui); cursor: pointer; padding: 2px 4px; }

        /* ── Tab row: the standards page's .ss-tabs, in purple ── */
        .ced-tabs {
            display: flex; gap: 2px; flex-wrap: wrap; border-bottom: 1px solid var(--bdr);
            margin: 10px 0 14px; position: sticky; top: 0; background: var(--bg); z-index: 5;
        }
        .ced-tab {
            border: none; background: none; color: var(--dim); border-bottom: 2px solid transparent; border-radius: 0;
            padding: 8px 14px; min-height: 42px; font: 600 16px var(--ff-ui); cursor: pointer; margin-bottom: -1px;
            transition: color .12s ease, border-color .12s ease;
        }
        .ced-tab:hover { color: var(--txt); }
        .ced-tab.is-on { color: var(--tier-cab, #7A3BAF); border-bottom-color: var(--tier-cab, #7A3BAF); }
        .ced-tab:focus-visible { outline: 2px solid var(--tier-cab, #7A3BAF); outline-offset: -3px; border-radius: 5px; }

        /* ── Cards: the body blocks, in columns ── */
        #ced-cat-pane { display: none; }
        #ced-cat-pane .ced-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: 14px; align-items: start; }
        #ced-cat-pane .ec-panel { background: transparent; }
        /* PR-UX-POLISH (2026-09-14): a card is a sheet ON the ground, not a
           rectangle drawn on it. One hairline plus a two-layer tinted shadow
           does that; the flat 1px border alone was why the pane read as a form
           printed on a page. */
        .ced-card, #ced-cat-pane .insp-prim-group {
            background: var(--pn); border: 1px solid var(--bdr); border-radius: var(--radius-lg);
            padding: 14px 16px 16px; margin: 0; min-width: 0; box-shadow: var(--shadow-1);
        }
        .ced-card-t, #ced-cat-pane .insp-prim-group-head {
            font: 600 16px var(--ff-ui); color: var(--txt); margin: 0 0 10px; padding-bottom: 9px;
            display: flex; align-items: center; gap: 9px; letter-spacing: -.005em;
            border-bottom: 1px solid var(--hairline);
        }
        /* The card's leading glyph. Sized and coloured as an ICON, so an inline
           SVG sits on the text baseline without inflating the line box. */
        #ced-cat-pane .insp-prim-group-icon, .ced-card-t .ced-card-ic {
            flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
            width: 19px; height: 19px; color: var(--acc);
        }
        #ced-cat-pane .insp-prim-group-icon svg, .ced-card-t .ced-card-ic svg { width: 19px; height: 19px; display: block; }
        #ced-cat-pane .insp-prim-group-icon:empty { display: none; }
        #ced-cat-pane label, #ced-cat-pane .ec-lbl, #ced-ec-host .ec-lbl { font-size: 15px; }
        #ced-cat-pane .ced-simple-hint { font-size: 13px; }

        /* ── The hosted inspector page: its own chrome hidden, sections as cards ── */
        #ced-ec-host .ec-head, #ced-ec-host .ec-tabs, #ced-ec-host .ec-actions { display: none; }
        #ced-ec-host .ec-panel { background: transparent; }
        #ced-ec-host .ec-scroll { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: 14px; align-items: start; }
        #ced-ec-host .ec-tabpage { display: none; }
        #ced-ec-host .ec-tabpage.active { display: contents; }
        #ced-ec-host .ec-sec { background: var(--pn); border: 1px solid var(--bdr); border-radius: 12px; min-width: 0; }
        #ced-ec-host .ec-sec-h { padding: 12px 16px 6px; cursor: default; }
        #ced-ec-host .ec-sec-t { font: 600 17px var(--ff-ui); letter-spacing: 0; text-transform: none; color: var(--txt); }
        #ced-ec-host .ec-chev { display: none; }
        #ced-ec-host .ec-sec-b { padding: 2px 16px 14px; }
        #ced-ec-host + .ced-v2-acc, #ced-cat-pane + .ced-v2-acc { margin-top: 14px; }

        /* ── The static panes (faces, construction, doors, appliance, shape) ── */
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-acc { padding: 0; gap: 14px; overflow: visible; flex: 0 0 auto; }
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-section[open] > .cab-editor-tabpane { border-top: none; padding: 0; }
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-section { margin: 0; }
        #cab-editor-modal[data-layout="v2"] .ced-v2-rightpanel .ced-v2-section:not([open]) { display: none; }
        #cab-editor-modal .cab-cnst-section { background: var(--pn); border: 1px solid var(--bdr); border-radius: 12px; margin-bottom: 14px; }
        #cab-editor-modal .cab-cnst-section-title { font: 600 17px var(--ff-ui); letter-spacing: 0; text-transform: none; }
        #cab-editor-modal .cab-app-banner { background: transparent; border: none; padding: 0 0 6px; font: 500 13.5px var(--ff-ui); color: var(--dim); }
        #cab-editor-modal .cab-app-field label, #cab-editor-modal .insp-prim-field label { font-size: 15px; }
        #cab-editor-modal .cab-app-section-title { font: 600 15px var(--ff-ui); letter-spacing: 0; text-transform: none; }
        #cab-editor-modal #ced-cat-pane, #cab-editor-modal #ced-ec-host { flex: 0 0 auto; }

        /* ── Bottom bar: the tools on the left, Cancel · Save on the right ── */
        .ced-bottombar-tools { display: flex; gap: 8px; margin-right: auto; align-items: center; }
        #cab-editor-modal[data-layout="v2"] .cab-editor-bottombar .ced-toolbar-snapshot-btn,
        #cab-editor-modal[data-layout="v2"] .cab-editor-bottombar .ced-toolbar-shop-drawing-btn { min-height: 40px; font: 600 13px var(--ff-ui); border-radius: 8px; }
        #cab-editor-modal[data-layout="v2"] .cab-editor-bottombar > .ba { background: var(--tier-cab, #7A3BAF); border-color: var(--tier-cab, #7A3BAF); color: #fff; }

        /* PR-CED-ONE-SCREEN, second pass from the first screenshots: the
           inspector's pages were drawn for a 340px side panel, so in a 1100px
           column their inputs stretched to a thousand pixels wide — the banned
           "oversized input floating in space" pattern. Rows become
           label-left / value-right like the standards tables, values are sized
           to the number, W/H/D sit in three columns, and cards fill by
           auto-fill so a lone card takes half the column, not all of it. */
        .ced-tab { font-size: 15px; padding: 8px 11px; }
        #ced-cat-pane .ced-cards, #ced-ec-host .ec-scroll { grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); }
        #ced-cat-pane .insp-prim-group-head { text-transform: none; letter-spacing: 0; color: var(--txt); }
        #ced-cat-pane .insp-prim-group-help { font-size: 13px; }
        /* Rows: label left, value right, wide controls wrap under the label. */
        #ced-cat-pane .ec-row, #ced-ec-host .ec-tabpage:not([data-tab="size"]) .ec-row {
            display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; margin-bottom: 6px;
            padding: 5px 0; border-bottom: 1px solid var(--hairline, var(--bdr));
        }
        #ced-cat-pane .ec-row:last-child, #ced-ec-host .ec-tabpage:not([data-tab="size"]) .ec-row:last-child { border-bottom: none; }
        #ced-cat-pane .ec-row > .ec-lbl, #ced-ec-host .ec-tabpage:not([data-tab="size"]) .ec-row > .ec-lbl { flex: 1 1 160px; margin: 0; color: var(--dim); font-weight: 500; }
        #ced-cat-pane .ec-row > .ec-seg, #ced-cat-pane .ec-row > .ec-tiles, #ced-cat-pane .ec-row > .ec-note,
        #ced-ec-host .ec-row > .ec-tiles, #ced-ec-host .ec-row > .ec-note, #ced-ec-host .ec-row > .ec-mini { flex: 1 1 100%; }
        /* PR-UX-POLISH (2026-09-14): section labels read as words, not shouted.
           The file already turned uppercase off for `.fg > label` for exactly
           this reason ("Labels read as words, not shouted") and then left
           .ec-sub SHOUTING two rules below it — so "BACKSPLASH" sat in caps
           with tracking over a card whose every other label was sentence case.
           Now it is a quiet small-caps-weight heading with a hairline rule,
           which is what actually separates two groups of fields. */
        #ced-cat-pane .ec-sub, #ced-ec-host .ec-sub {
            font: 600 12.5px var(--ff-ui); letter-spacing: 0; text-transform: none;
            color: var(--dim); margin: 14px 0 4px; padding-bottom: 5px;
            border-bottom: 1px solid var(--hairline);
        }
        /* Values sized to what they hold.
           PR-UX-POLISH (2026-09-14): NOT monospace. A measurement is a number,
           not code — 600-weight mono at 15px read like a terminal and, being far
           wider per character, clipped its own placeholder ("4" (standar"). The
           UI face with `tabular-nums` keeps the decimal points in a column,
           which is the only reason mono was reached for in the first place. */
        #ced-cat-pane .ec-inp, #ced-cat-pane .ec-row input[type="text"], #ced-cat-pane .insp-prim-field input[type="text"],
        #ced-ec-host .ec-inset-grid input, #ced-cat-pane .ec-grid2 input {
            min-height: var(--ctl-h-sm); padding: 4px 10px; border: 1px solid var(--bdr);
            border-radius: var(--radius-sm); background: var(--inp); color: var(--txt);
            font: 500 15px var(--ff-ui); font-variant-numeric: tabular-nums;
            width: 132px; max-width: 100%; text-align: right;
            transition: border-color var(--dur-quick, 120ms) var(--ease-out, ease),
                        box-shadow var(--dur-quick, 120ms) var(--ease-out, ease);
        }
        #ced-cat-pane .ec-inp:hover, #ced-cat-pane .ec-grid2 input:hover,
        #ced-cat-pane .insp-prim-field input[type="text"]:hover { border-color: var(--bdr-h); }
        #ced-cat-pane .ec-inp:focus, #ced-cat-pane .ec-row input[type="text"]:focus,
        #ced-cat-pane .insp-prim-field input[type="text"]:focus,
        #ced-cat-pane .ec-grid2 input:focus, #ced-cat-pane select:focus {
            outline: none; border-color: var(--acc);
            box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 16%, transparent);
        }
        /* A placeholder is a hint, not a value — it must never read as typed-in
           content the operator has to clear. */
        #ced-cat-pane input::placeholder { color: var(--muted); font-weight: 400; }
        /* PR-UX-POLISH (2026-09-14): one control height and one radius for every
           select in the pane, and the native arrow swapped for a drawn chevron —
           the OS default renders at three different sizes across Windows /
           macOS / Android and is the loudest un-designed pixel on the card. */
        /* `select.ec-inp` is listed EXPLICITLY. Several selects in this pane wear
           `.ec-inp` — the class the NUMBER-INPUT rule above is keyed on — and
           `#ced-cat-pane .ec-inp` (0,1,1,0) outranks a bare `#ced-cat-pane select`
           (0,1,0,1). Without naming the class here those selects took the number
           field's 132px width, right-alignment and 10px right padding, and then
           this rule's chevron painted straight over the option text. (It only
           SHOWED in dark: the number rule's `background:` SHORTHAND reset
           background-image to none, and only the dark override outranked it, so
           light silently lost the chevron and dark drew it on top of the words.
           Two different wrong results from one specificity bug.) */
        #ced-cat-pane select, #ced-cat-pane select.ec-inp,
        #ced-ec-host .ec-sec-b select, #ced-ec-host select.ec-inp {
            min-height: var(--ctl-h-sm); font: 500 15px var(--ff-ui);
            width: auto; max-width: 100%; text-align: left;
            padding: 4px 34px 4px 10px; border: 1px solid var(--bdr);
            border-radius: var(--radius-sm); background-color: var(--inp); color: var(--txt);
            -webkit-appearance: none; -moz-appearance: none; appearance: none;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235A6573' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
            background-repeat: no-repeat; background-position: right 9px center; background-size: 15px;
            transition: border-color var(--dur-quick, 120ms) var(--ease-out, ease),
                        box-shadow var(--dur-quick, 120ms) var(--ease-out, ease);
        }
        :root[data-theme="dark"] #ced-cat-pane select,
        :root[data-theme="dark"] #ced-cat-pane select.ec-inp,
        :root[data-theme="dark"] #ced-ec-host .ec-sec-b select,
        :root[data-theme="dark"] #ced-ec-host select.ec-inp {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%239AA3B0' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
        }
        #ced-cat-pane select:hover, #ced-ec-host .ec-sec-b select:hover { border-color: var(--bdr-h); }
        /* PR-UX-POLISH (2026-09-14): a field is a ROW — label left, value right —
           not a label stacked on top of a control.
           The stacked form left the entire right half of the card empty down its
           whole upper section while the sections below it (cutouts, scribe) were
           already two-column, so one card disagreed with itself about its own
           layout and ran twice as tall as it needed to. Wrapping is allowed so a
           field that carries an explanatory line underneath still gets one. */
        #ced-cat-pane .insp-prim-field {
            display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
            gap: 4px 12px; padding: 7px 0; margin: 0;
            border-bottom: 1px solid var(--hairline);
        }
        #ced-cat-pane .insp-prim-field:last-child { border-bottom: none; }
        #ced-cat-pane .insp-prim-field > label { flex: 1 1 140px; margin: 0; }
        #ced-cat-pane .insp-prim-field > .ced-simple-hint { flex: 1 1 100%; margin: 3px 0 0; }
        #ced-cat-pane .insp-prim-field select { flex: 0 1 auto; width: auto; min-width: 0; max-width: 100%; }
        /* PR-UX-POLISH: checkboxes take the accent instead of the browser's own
           blue, which fought the blueprint palette on every card. */
        #ced-cat-pane .ec-chk { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 15px; }
        #ced-cat-pane .ec-chk input,
        #ced-cat-pane .ced-ct-toggle input,
        #ced-cat-pane input[type="checkbox"] {
            width: 17px; height: 17px; margin: 0; accent-color: var(--acc);
            cursor: pointer; flex: 0 0 auto;
        }
        #ced-cat-pane .ec-chk, #ced-cat-pane .ced-ct-toggle { cursor: pointer; }
        #ced-cat-pane .ec-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; }
        /* PR-DBOX-DROPDOWNS (2026-09-11): Drawer boxes card — the five pick
           ladders sit in the 2-col grid; each select fills its column
           (the pane-wide 260px select floor would overflow a narrow column). */
        #ced-cat-pane .ced-dbox-grid { row-gap: 6px; }
        #ced-cat-pane .ced-dbox-grid .insp-prim-field select { width: 100%; min-width: 0; }
        /* Dimensions: three columns, no sliders (typing + steppers stay; the
           slider was the one control the standards ladder never uses). */
        #ced-ec-host .ec-tabpage[data-tab="size"] .ec-sec-b { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 18px; align-items: start; }
        #ced-ec-host .ec-tabpage[data-tab="size"] .ec-row { margin: 0; }
        #ced-ec-host .ec-tabpage[data-tab="size"] .ec-lbl { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; font-size: 15px; }
        #ced-ec-host .ec-slider, #ced-ec-host .ec-dim input[type="range"] { display: none; }
        #ced-ec-host .ec-dim { justify-content: flex-start; }
        #ced-ec-host .ec-dim input[type="text"] { flex: 0 1 120px; font: 600 17px var(--ff-mono); }
        #ced-ec-host .ec-step { min-width: 36px; min-height: 36px; }
        /* Reveals: five small numbers in a row, label beside each. */
        #ced-ec-host .ec-inset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 8px 16px; }
        #ced-ec-host .ec-inset-grid > * { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 15px; color: var(--dim); }
        #ced-ec-host .ec-inset-grid input { width: 96px; }
        /* A select is a word, not a number: keep it left-aligned, wide enough to read. */
        /* A select is a word, not a number: left-aligned and wide enough to read.
           PR-UX-POLISH (2026-09-14): `min-width: 220px` was a FLOOR the control
           could not go under, so in a narrow properties column a long option
           ("Follow Shop standard: down to the floor") overflowed its own box and
           ran under the drop-down chevron. Now it may shrink, and the text
           ellipsises instead of colliding. */
        #ced-cat-pane select.ec-inp, #ced-ec-host select.ec-inp {
            width: auto; min-width: 0; max-width: 100%; flex: 0 1 auto;
            text-align: left; font: 500 15px var(--ff-ui); text-overflow: ellipsis;
        }
        #ced-cat-pane .ec-row > select { flex: 0 1 auto; min-width: 0; }
        /* Labels read as words, not shouted: the .fg form-group uppercase is off inside the editor's cards. */
        #ced-cat-pane .fg > label, #ced-cat-pane .insp-override-row label, #ced-cat-pane .insp-prim-field > label { text-transform: none; letter-spacing: 0; font-size: 15px; color: var(--dim); font-weight: 500; }
/* ═══ Box Features editor — the parametric layout studio ══════════════════
   PR-DBOX-FEATURE-EDITOR (2026-09-05). Owner: "make sure the ui is very well
   designed and can do what advanced cad system can."

   The visual language is a shop drawing, not a form: a to-scale figure with
   real dimension lines, tabular numerals everywhere a size appears, and the
   selected compartment marked the way a CAD app marks a selected face.

   Every colour is a token. The canvas reads on both themes because it takes
   its ink from --txt/--dim and its fills from --pn/--sf, which both flip. */
.bf-studio { display: flex; flex-direction: column; gap: 12px; }

.bf-toolbar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px;
    padding: 10px 12px; background: var(--sf);
    border: 1px solid var(--bdr); border-radius: 6px;
}
.bf-tgroup { display: flex; align-items: center; gap: 6px; }
.bf-tlabel {
    font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
    color: var(--dim); white-space: nowrap;
}
.bf-toolbar select { height: 30px; padding: 0 6px; }
/* PR-DBOX-GUIDE-REFRESH (2026-09-09): the rule between what the row IS and what
   it is being TRIED in. Those two kinds of control had grown into one flat row
   of five. Collapses to a full-width hairline when the bar wraps, so the split
   survives a narrow column instead of stranding a vertical line mid-row. */
.bf-tsep { width: 1px; align-self: stretch; background: var(--bdr); margin: 0 2px; }
@media (max-width: 900px) {
    .bf-tsep { width: 100%; height: 1px; align-self: auto; margin: 2px 0; }
}
.bf-tgroup-size input[type="number"] {
    width: 68px; height: 30px; text-align: right;
    font-variant-numeric: tabular-nums;
}
.bf-x { color: var(--muted); font-size: 12px; }

/* Segmented control — the axis toggle */
.bf-seg {
    height: 30px; padding: 0 11px; font-size: 12px; cursor: pointer;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); margin: 0;
}
.bf-seg:first-of-type { border-radius: 5px 0 0 5px; }
.bf-seg:last-of-type { border-radius: 0 5px 5px 0; border-left: none; }
.bf-seg:hover { border-color: var(--bdr-h); }
.bf-seg-on { background: var(--acc); color: var(--acc-ink); border-color: var(--acc); }
.bf-seg-on:hover { background: var(--acc2); border-color: var(--acc2); }

.bf-btn {
    height: 28px; padding: 0 10px; font-size: 12px; cursor: pointer;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 5px;
}
.bf-btn:hover { border-color: var(--bdr-h); background: var(--pn-h); }
.bf-btn:disabled { opacity: .45; cursor: not-allowed; }
.bf-btn-danger { color: var(--no); border-color: var(--bdr); }
.bf-btn-danger:hover { border-color: var(--no); }
.bf-btn:focus-visible, .bf-seg:focus-visible, .bf-mode:focus-visible {
    outline: 2px solid var(--acc); outline-offset: 1px;
}

/* Canvas + inspector */
.bf-panes { display: grid; grid-template-columns: minmax(0, 1fr) 268px; gap: 12px; }
@media (max-width: 1080px) { .bf-panes { grid-template-columns: 1fr; } }

.bf-canvas-wrap {
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 6px;
    padding: 8px; min-height: 220px;
}
/* PR-DBOX-STUDIO-CALM (2026-09-05): cap the drawing so the whole live view
   fits one laptop screen.
   MEASURED at 1366x768 (1344x670 of actual viewport): the SVG scaled to full
   width against a fixed viewBox and took 467px, which put the status line and
   the live parts table below the fold — so the two things telling the operator
   what they had just built were the two things they could not see while
   building it. The viewBox keeps the aspect ratio, so capping the height only
   shrinks the drawing; nothing crops. */
.bf-svg { display: block; width: 100%; height: auto; max-height: 300px; margin: 0 auto; }
@media (max-height: 800px) { .bf-svg { max-height: 216px; } }

/* The selected sizing mode explains itself, in place — the studio's one
   load-bearing idea should not live in a hover tooltip. */
.bf-mode-doc {
    font-size: 11px; line-height: 1.55; color: var(--dim);
    background: var(--sf); border-left: 2px solid var(--acc);
    padding: 7px 9px; border-radius: 0 4px 4px 0; margin: 8px 0 10px;
}

/* The interior sections appear only when the type HAS an interior. A plain
   Tray is Identity / Purpose / Pricing, not eight sections it will never use. */
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-nav button[data-section="layout"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-nav button[data-section="construction"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-nav button[data-section="fittings"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-nav button[data-section="limits"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-nav button[data-section="bought"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-content > .lib-ed-section[data-section-id="layout"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-content > .lib-ed-section[data-section-id="construction"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-content > .lib-ed-section[data-section-id="fittings"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-content > .lib-ed-section[data-section-id="limits"],
.lib-ed-shell[data-dbt-interior="off"] .lib-ed-shell-content > .lib-ed-section[data-section-id="bought"] {
    display: none;
}
.dbt-int-toggle {
    display: flex; align-items: center; gap: 8px; cursor: pointer;
    font-size: 13px; color: var(--txt);
}
.bf-box { fill: none; stroke: var(--txt); stroke-width: 1.5; }
.bf-wall { fill: var(--sf); stroke: var(--txt); stroke-width: 1; }
.bf-bay { fill: var(--pn); stroke: var(--bdr-h); stroke-width: 1; cursor: pointer; }
.bf-bay:hover { fill: var(--pn-h); }
.bf-bay-sel { fill: var(--pn-h); stroke: var(--acc); stroke-width: 2; }
.bf-sep { fill: var(--txt); opacity: .82; }
.bf-dim {
    font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace;
    font-size: 10px; fill: var(--dim);
    text-anchor: middle; dominant-baseline: middle;
    pointer-events: none; font-variant-numeric: tabular-nums;
}
.bf-dim-major { fill: var(--txt); font-size: 10.5px; }
.bf-dimline { stroke: var(--muted); stroke-width: 1; }

.bf-inspector {
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 6px;
    padding: 12px; min-width: 0;
}
.bf-insp-head {
    font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
    color: var(--dim); margin-bottom: 10px;
}
.bf-modes { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.bf-mode {
    text-align: left; padding: 7px 10px; font-size: 12px; cursor: pointer;
    background: var(--inp); color: var(--txt);
    border: 1px solid var(--bdr); border-radius: 5px;
}
.bf-mode:hover { border-color: var(--bdr-h); background: var(--pn-h); }
.bf-mode-on { background: var(--acc); color: var(--acc-ink); border-color: var(--acc); }
.bf-insp-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 8px; }

/* Status strip — the live constraint report */
.bf-status {
    font-size: 12px; padding: 9px 12px; border-radius: 5px;
    border-left: 3px solid var(--bdr); background: var(--sf); color: var(--dim);
}
.bf-status-ok {
    border-left-color: var(--ok); color: var(--txt);
    font-variant-numeric: tabular-nums;
}
.bf-status-refused { border-left-color: var(--no); color: var(--txt); }
.bf-status-warn { border-left-color: var(--warn); color: var(--txt); }

/* PR-DBOX-INTERIOR-REGIONS (2026-09-06): the region/box-type mismatch line.
   Louder than the status strip on purpose — it is not reporting a result, it
   is saying this interior will not be built at all. */
.bf-region-warn {
    font-size: 12px; padding: 9px 12px; border-radius: 5px;
    border-left: 3px solid var(--warn); background: var(--sf); color: var(--txt);
}

/* PR-DBOX-ENVELOPE-TRUTH (2026-09-08): the box the numbers below were solved
   for, stated. Quieter than the status strip beside it — this is a standing
   condition, not an event, so it reads as a caption and never competes with a
   refusal. Tokens only, both themes. */
/* The trial envelope, stated. Sits directly under the toolbar as its caption —
   quieter than the status strip below it, because this is a standing condition
   and not an event. */
.bf-env {
    font-size: 11px; line-height: 1.5; color: var(--dim);
    padding: 7px 0 0;
}
.bf-env strong { color: var(--txt); font-weight: 600; }
/* The env caption and the scope path are one band when both are showing. */
.bf-env + .bf-crumb { margin-top: 6px; }

/* PR-DBOX-NESTED-SCOPE (2026-09-08): where in the tree the drawing is looking.
   Hidden at the root — a one-level layout should not have to read a path to
   know it is at the top. */
.bf-crumb {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
    font-size: 12px; margin-bottom: 8px;
}
.bf-crumb-step {
    background: none; border: none; padding: 2px 4px; cursor: pointer;
    font-size: 12px; color: var(--acc); border-radius: 4px;
}
.bf-crumb-step:hover { background: var(--pn-h); text-decoration: underline; }
.bf-crumb-step:focus-visible { outline: 2px solid var(--acc); outline-offset: 1px; }
.bf-crumb-sep { color: var(--dim); }
.bf-crumb-here { color: var(--txt); font-weight: 600; padding: 2px 4px; }
.bf-crumb-note { color: var(--dim); font-size: 11px; margin-left: 4px; }
.bf-btn-wide { width: 100%; text-align: center; }

/* Live parts list */
.bf-parts-wrap {
    background: var(--pn); border: 1px solid var(--bdr); border-radius: 6px;
    overflow: hidden;
}
/* PR-DBOX-STUDIO-TELLS-THE-TRUTH (2026-09-05): the parts list scrolls inside
   itself so the LIVE VIEW stays one screen.
   Capping the canvas alone was not enough — measured at 1366x768, a tower with
   three shelves, two sides and a bought line took the whole studio to 743px in
   a 670px viewport, and a 12-bay knife block would be far worse. The pane's own
   scroller keeps toolbar + verdict + drawing + parts together no matter how
   many parts a row makes; the header stays put so the columns never scroll
   away from their numbers. */
.bf-parts-scroll { max-height: 260px; overflow-y: auto; }
@media (max-height: 800px) { .bf-parts-scroll { max-height: 120px; } }
table.bf-parts thead th {
    position: sticky; top: 0; z-index: 1; background: var(--sf);
}
.bf-parts-head {
    font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
    color: var(--dim); padding: 9px 12px; border-bottom: 1px solid var(--bdr);
    background: var(--sf);
}
table.bf-parts { width: 100%; border-collapse: collapse; font-size: 12px; }
table.bf-parts th {
    text-align: left; font-size: 10px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--dim); font-weight: 600;
    padding: 7px 12px; border-bottom: 1px solid var(--bdr);
}
table.bf-parts td { padding: 6px 12px; border-bottom: 1px solid var(--bdr); color: var(--txt); }

/* PR-DBOX-STUDIO-TELLS-THE-TRUTH (2026-09-05): the stock and the machine.
   A chip rather than plain text for the machine, because it is the column the
   eye should be able to scan for the one row that differs — the tower side
   that just moved to the CNC because a line bore was added to it. */
.bf-stock { color: var(--dim); font-size: 11px; white-space: nowrap; }
.bf-mcell { white-space: nowrap; }
.bf-mc {
    display: inline-block; font-size: 10px; letter-spacing: .04em;
    padding: 2px 7px; border-radius: 999px;
    background: var(--sf); color: var(--dim); border: 1px solid var(--bdr);
}
.bf-mc-cnc { background: color-mix(in srgb, var(--acc) 16%, transparent);
             color: var(--acc); border-color: var(--acc); }
.bf-mc-ops { font-size: 10px; color: var(--dim); margin-left: 4px; }
/* An older engine blob does not report the machine. An em dash is honest; a
   confident "Chop saw" would not be. */
.bf-mc-unknown { color: var(--muted); border-style: dashed; }
.bf-unpriced { color: var(--warn); font-style: normal; letter-spacing: .02em; }

/* The compartment's number, tying the drawing to "Compartment 2 of 4". */
.bf-bay-no {
    fill: var(--dim); font-size: 9px; font-family: ui-monospace, monospace;
    text-anchor: middle; pointer-events: none;
}
/* The figure is one tab stop and takes arrow keys; it has to SHOW that it has
   focus, or a keyboard user is pressing arrows at nothing. */
.bf-svg:focus { outline: none; }
.bf-svg:focus-visible {
    outline: 2px solid var(--acc); outline-offset: 2px; border-radius: 4px;
}
table.bf-parts tr:last-child td { border-bottom: none; }
table.bf-parts .bf-n { text-align: right; font-variant-numeric: tabular-nums;
    font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace; }
table.bf-parts tr.bf-buy td { color: var(--dim); }
table.bf-parts .bf-buy-tag { font-size: 11px; font-style: italic; }

.bf-empty { padding: 14px 12px; font-size: 12px; color: var(--muted); }
.bf-help { font-size: 11px; color: var(--dim); line-height: 1.6; margin-top: 6px; }
.bf-help code {
    font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace;
    font-size: 10.5px; background: var(--sf); padding: 1px 4px; border-radius: 3px;
}
.bf-help-block {
    background: var(--sf); border: 1px solid var(--bdr); border-radius: 5px;
    padding: 10px 12px; margin-top: 0;
}
.bf-note-sm {
    font-size: 11px; color: var(--dim); background: var(--sf);
    border-left: 3px solid var(--warn); padding: 7px 10px;
    border-radius: 4px; margin-bottom: 10px; line-height: 1.5;
}

/* Bought lines */
.bf-brows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.bf-brow { display: grid; grid-template-columns: minmax(0,1fr) 110px 60px 150px 80px auto auto; gap: 6px; align-items: center; }
/* PR-DBOX-BOUGHT-PARTS-COST (2026-09-05): the header for those columns. */
.bf-bhead {
    display: grid; grid-template-columns: minmax(0,1fr) 110px 60px 150px 80px auto auto; gap: 6px;
    font-size: 10px; color: var(--dim); text-transform: uppercase;
    letter-spacing: 0.5px; margin-bottom: 4px;
}
.bf-b-cost { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 900px) { .bf-bhead { display: none; } }
.bf-brow input[type="text"], .bf-brow input:not([type]) { min-width: 0; }
.bf-b-qty { text-align: right; font-variant-numeric: tabular-nums; }
.bf-b-perlbl { font-size: 11px; color: var(--dim); white-space: nowrap; display: flex; align-items: center; gap: 4px; }
@media (max-width: 720px) { .bf-brow { grid-template-columns: 1fr; } }

/* ── PR-SLIDEPREP-UI (2026-09-05): the bore grid + the notch drawing ────── */
.sp-bhead {
    display: grid; grid-template-columns: repeat(4, minmax(0,1fr)) 34px; gap: 6px;
    font-size: 10px; color: var(--dim); text-transform: uppercase;
    letter-spacing: 0.5px; margin-bottom: 4px;
}
.sp-brow { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)) 34px; gap: 6px; align-items: center; }
.sp-brow input { min-width: 0; text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
    .sp-bhead { display: none; }
    .sp-brow { grid-template-columns: 1fr 1fr; }
}
/* The drawing. Stroke-only so it reads as a shop drawing, not a picture. */
.sp-svg { display: block; margin: 0 auto; }
.sp-part  { fill: var(--sf); stroke: var(--white); stroke-width: 1.25; }
.sp-cut   { fill: none; stroke: var(--warn); stroke-width: 1.75; }
.sp-ref   { stroke: var(--acc); stroke-width: 1.5; }
.sp-reftxt{ fill: var(--acc); font-size: 8px; letter-spacing: 0.6px; font-family: ui-monospace, monospace; }
.sp-dim   { stroke: var(--dim); stroke-width: 0.75; }
.sp-dtxt  { fill: var(--dim); font-size: 9px; font-family: ui-monospace, monospace; }
.sp-bore  { fill: none; stroke: var(--acc); stroke-width: 1.25; }
.sp-cross { stroke: var(--acc); stroke-width: 0.6; }
.sp-prev-note { margin-top: 8px; font-size: 10px; color: var(--dim); line-height: 1.5; text-align: center; }


/* ======================================================================
   PR-ONLINE-PAGE (2026-09-05) — Online orders (#pg-online)

   Every colour is a token so the page follows the app into dark mode with
   no second palette. The rail on each waiting row is NOT decoration: it is
   how long that customer has been waiting — accent today, amber after a
   day, red after three — so the row that needs an answer most is the one
   that catches the eye without adding a column. The list is sorted
   oldest-first, so colour and position agree.
   ====================================================================== */
#pg-online.pg { height: 100%; }
#pg-online .ol-wrap { max-width: 1080px; margin: 0 auto; padding: 24px 20px 80px; }

#pg-online .ol-head { margin-bottom: 28px; }
#pg-online .ol-title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
#pg-online .ol-title h1 { margin: 0; font-size: 26px; font-weight: 700; letter-spacing: -.02em; }
#pg-online .ol-sub { margin: 8px 0 0; color: var(--dim); font-size: 14px; line-height: 1.55; max-width: 72ch; }
#pg-online .ol-headacts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }

#pg-online .ol-pill { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 999px; background: var(--sf); color: var(--dim); }
#pg-online .ol-pill.ok { background: color-mix(in srgb, var(--ok) 15%, transparent); color: var(--ok); }
#pg-online .ol-pill.off { background: color-mix(in srgb, var(--no) 13%, transparent); color: var(--no); }

#pg-online .ol-btn { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600;
    padding: 7px 13px; border-radius: 7px; border: 1px solid var(--bdr); background: var(--pn);
    color: var(--txt); cursor: pointer; text-decoration: none; white-space: nowrap; }
#pg-online .ol-btn:hover { border-color: var(--acc); color: var(--acc); }
#pg-online .ol-btn.primary { background: var(--acc); border-color: var(--acc); color: #fff; }
#pg-online .ol-btn.primary:hover { background: var(--acc2); border-color: var(--acc2); color: #fff; }
#pg-online .ol-btn:disabled { opacity: .6; cursor: default; }
#pg-online .ol-btn.warn { border-color: color-mix(in srgb, var(--warn) 55%, var(--bdr)); color: var(--warn); }
#pg-online .ol-btn.warn:hover { background: color-mix(in srgb, var(--warn) 10%, transparent); color: var(--warn); }

#pg-online .ol-sec { margin-bottom: 30px; }
#pg-online .ol-sechead { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    padding-bottom: 8px; margin-bottom: 12px; border-bottom: 1px solid var(--bdr); }
#pg-online .ol-sechead h2 { margin: 0; font-size: 12px; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: var(--acc); }
#pg-online .ol-count { font-size: 11.5px; font-weight: 600; color: var(--dim);
    font-family: 'JetBrains Mono', ui-monospace, monospace; }
#pg-online .ol-hint { margin-left: auto; font-size: 12px; color: var(--dim); }

#pg-online .ol-row { display: grid; grid-template-columns: 3px 1fr auto auto; gap: 0 16px; align-items: center;
    padding: 13px 16px 13px 0; margin-bottom: 8px; background: var(--pn);
    border: 1px solid var(--bdr); border-radius: 9px;
    transition: border-color .12s ease, box-shadow .12s ease; }
#pg-online .ol-row:hover { border-color: color-mix(in srgb, var(--acc) 45%, var(--bdr));
    box-shadow: 0 2px 10px -6px color-mix(in srgb, var(--txt) 40%, transparent); }
#pg-online .ol-row .rail { align-self: stretch; min-height: 40px; border-radius: 3px 0 0 3px; background: var(--acc); }
#pg-online .ol-row.age-1 .rail { background: var(--warn); }
#pg-online .ol-row.age-2 .rail { background: var(--no); }
#pg-online .ol-row.age-1 .wait { color: var(--warn); font-weight: 600; }
#pg-online .ol-row.age-2 .wait { color: var(--no); font-weight: 600; }
#pg-online .ol-row.done { background: color-mix(in srgb, var(--acc) 4%, var(--pn)); }
#pg-online .ol-row.quiet { opacity: .75; }
#pg-online .ol-row.quiet .rail { background: var(--bdr); }
#pg-online .ol-row.failed { border-color: color-mix(in srgb, var(--no) 55%, var(--bdr)); }

#pg-online .ol-row .who { min-width: 0; }
#pg-online .ol-row .who b { font-size: 14.5px; font-weight: 650; letter-spacing: -.01em; }
#pg-online .ol-row .meta { margin-top: 3px; font-size: 12.5px; color: var(--dim);
    display: flex; flex-wrap: wrap; gap: 2px 7px; align-items: center; }
#pg-online .ol-row .meta .sep { opacity: .4; }
#pg-online .ol-row .jn { font-family: 'JetBrains Mono', ui-monospace, monospace; font-weight: 600;
    color: var(--txt); opacity: .75; }
#pg-online .ol-row .cash { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 15px;
    font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
#pg-online .ol-row .cash small { display: block; font-family: inherit; font-size: 10.5px; font-weight: 500;
    color: var(--dim); letter-spacing: .03em; text-transform: uppercase; margin-top: 1px;
    font-family: system-ui, sans-serif; }
#pg-online .ol-row .acts { display: flex; gap: 7px; align-items: center; }
#pg-online .ol-row .why { grid-column: 2 / -1; margin-top: 9px; padding: 9px 12px; font-size: 12.5px;
    line-height: 1.5; border-radius: 6px; color: var(--no);
    background: color-mix(in srgb, var(--no) 9%, transparent);
    border: 1px solid color-mix(in srgb, var(--no) 25%, transparent); }

/* Progress: the floor's own numbers, never recomputed here. */
#pg-online .ol-bar { margin-top: 8px; height: 5px; border-radius: 3px; background: var(--sf); overflow: hidden; max-width: 340px; }
#pg-online .ol-bar i { display: block; height: 100%; background: var(--acc); border-radius: 3px; }
#pg-online .ol-barlab { margin-top: 4px; font-size: 11.5px; color: var(--dim);
    font-family: 'JetBrains Mono', ui-monospace, monospace; }

#pg-online .ol-flag { display: inline-block; margin-left: 6px; font-size: 10.5px; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase; padding: 2px 7px; border-radius: 4px; vertical-align: 1px; }
#pg-online .ol-flag.warn { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
#pg-online .ol-flag.no { background: color-mix(in srgb, var(--no) 14%, transparent); color: var(--no); }
#pg-online .ol-flag.cab { margin-left: 0; margin-right: 4px; background: color-mix(in srgb, var(--acc-cabinet, #c89968) 20%, transparent); color: var(--acc-cabinet, #c89968); }

/* Files menu */
#pg-online .ol-menu { position: relative; }
#pg-online .ol-menu-p { display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
    min-width: 210px; padding: 5px; background: var(--pn); border: 1px solid var(--bdr);
    border-radius: 9px; box-shadow: 0 10px 30px -12px color-mix(in srgb, var(--txt) 55%, transparent); }
#pg-online .ol-menu.open .ol-menu-p { display: block; }
#pg-online .ol-mi { display: block; width: 100%; text-align: left; padding: 8px 10px; border: none;
    background: none; border-radius: 6px; cursor: pointer; color: var(--txt); }
#pg-online .ol-mi:hover { background: var(--sf); }
#pg-online .ol-mi b { display: block; font-size: 13px; font-weight: 600; }
#pg-online .ol-mi span { display: block; font-size: 11.5px; color: var(--dim); margin-top: 1px; }

#pg-online .ol-empty { padding: 26px 16px; text-align: center; color: var(--dim);
    border: 1px dashed var(--bdr); border-radius: 9px; }
#pg-online .ol-empty b { display: block; color: var(--txt); font-size: 15px; margin-bottom: 4px; }
#pg-online .ol-empty p { margin: 0 auto; max-width: 48ch; font-size: 13px; line-height: 1.55; }
#pg-online .ol-empty.quiet { border-style: solid; opacity: .7; }
#pg-online .ol-skel { height: 66px; margin-bottom: 8px; border-radius: 9px;
    background: linear-gradient(90deg, var(--sf), var(--pn), var(--sf));
    background-size: 200% 100%; animation: olsk 1.4s linear infinite; }
@keyframes olsk { to { background-position: -200% 0; } }
#pg-online .ol-spin { display: inline-block; width: 11px; height: 11px; margin-right: 6px;
    border: 2px solid color-mix(in srgb, currentColor 30%, transparent); border-top-color: currentColor;
    border-radius: 50%; animation: olspin .7s linear infinite; vertical-align: -1px; }
@keyframes olspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    #pg-online .ol-skel { animation: none; }
    #pg-online .ol-spin { animation-duration: 2.4s; }
    #pg-online .ol-row { transition: none; }
}

/* The sidebar count. This is the whole reason Online orders is a PAGE and not
   a tab on a settings screen: it says "somebody is waiting" from anywhere. */
.sidebar-nav-item .nav-badge { margin-left: auto; min-width: 18px; padding: 1px 6px; border-radius: 999px;
    background: var(--acc); color: #fff; font-size: 10.5px; font-weight: 700; line-height: 16px;
    text-align: center; font-family: 'JetBrains Mono', ui-monospace, monospace; }

@media (max-width: 720px) {
    #pg-online .ol-row { grid-template-columns: 3px 1fr; row-gap: 10px; }
    #pg-online .ol-row .cash, #pg-online .ol-row .acts { grid-column: 2; }
    #pg-online .ol-row .cash { text-align: left; }
}

/* ── PR-INVITE-LINKS-ONLY (2026-09-06): the invite link, made handable ──────
   This shop sends no email — `mailer::is_configured` is false, so
   `portal_invites` always returns `emailed: false` and the `accept_url` it
   ALWAYS returns is the entire delivery mechanism. The operator has to get
   that string onto a customer's phone, so the string gets a copy button
   instead of asking them to drag-select monospace text without touching the
   scrollbar. Same treatment on the admin page's own stylesheet. */
.invite-link {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 8px 9px 8px 11px;
    border: 1px solid var(--bdr);
    border-left: 3px solid var(--acc);
    border-radius: 7px;
    background: color-mix(in srgb, var(--acc) 6%, var(--sf));
}
.invite-link code {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    white-space: nowrap;
    font-family: monospace;
    font-size: 11.5px;
    color: var(--txt);
}
.invite-link .il-copy {
    flex: 0 0 auto;
    padding: 5px 11px;
    border: 1px solid var(--bdr);
    border-radius: 6px;
    background: var(--sf);
    color: var(--txt);
    font: 600 12px/1 inherit;
    cursor: pointer;
}
.invite-link .il-copy:hover { border-color: var(--acc); color: var(--acc); }
.invite-link .il-copy.ok { border-color: var(--ok); color: var(--ok); }
.invite-link .il-note {
    flex-basis: 100%;
    margin: 2px 0 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--muted);
}

/* ── PR-DBOX-BUILD-GUIDE (2026-09-08) — the recipes, in the Box Type editor ──
   Reference reading, not a form: wider measure, quieter rules, and every
   recipe collapsed so the section opens as a menu rather than a wall.
   Colours are tokens only — this reads in both themes. */

/* PR-DBOX-GUIDE-REFRESH (2026-09-09): the control reference and the sizing
   table. Both are READING surfaces — a quiet grid that scans top-to-bottom,
   not a form. Tokens only; verified in both themes. */
.dbt-doc-ctrls {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
    gap: 6px; margin-top: 10px;
}
.dbt-doc-ctrl {
    border: 1px solid var(--bdr); border-radius: 5px; padding: 8px 11px;
    font-size: 11.5px; line-height: 1.55; color: var(--dim);
}
.dbt-doc-ctrl b { display: block; color: var(--txt); font-weight: 600; margin-bottom: 2px; }
/* The two controls that arrived with nested authoring and the trial envelope.
   Marked so a shop that knows this screen can see what is new at a glance. */
.dbt-doc-ctrl-new { border-color: var(--acc); background: var(--sf); }

.dbt-doc-modes { margin-top: 10px; border: 1px solid var(--bdr); border-radius: 5px; overflow: hidden; }
.dbt-doc-mode, .dbt-doc-mode-hd {
    display: grid; grid-template-columns: 122px minmax(0, 1fr) minmax(0, 1.05fr);
    gap: 10px; padding: 7px 11px; font-size: 11.5px; line-height: 1.5;
    color: var(--dim); border-top: 1px solid var(--bdr);
}
.dbt-doc-mode-hd {
    border-top: none; background: var(--sf); color: var(--muted);
    font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
}
.dbt-doc-mode b { color: var(--txt); font-weight: 600; }
.dbt-doc-mode code {
    font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 10.5px;
    background: var(--sf); border-radius: 3px; padding: 0 3px;
}
@media (max-width: 760px) {
    .dbt-doc-mode, .dbt-doc-mode-hd { grid-template-columns: minmax(0, 1fr); gap: 2px; }
    .dbt-doc-mode-hd { display: none; }
}

.dbt-doc-axes { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.dbt-doc-axis {
    border: 1px solid var(--bdr); border-radius: 5px; padding: 8px 11px;
    font-size: 11.5px; color: var(--dim); line-height: 1.55;
}
.dbt-doc-axis-here { border-color: var(--acc); background: var(--sf); }
.dbt-doc-axis-name { font-weight: 600; color: var(--txt); margin-bottom: 2px; }
.dbt-doc-here {
    font-weight: 500; font-size: 10px; color: var(--acc);
    border: 1px solid var(--acc); border-radius: 3px; padding: 0 5px; margin-left: 7px;
    vertical-align: 1px;
}

/* The two rules — numbered, because the order matters. */
.dbt-doc-rule {
    display: flex; gap: 10px; align-items: flex-start;
    font-size: 12px; color: var(--txt); line-height: 1.6; margin-bottom: 12px;
}
.dbt-doc-rule-n {
    flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%;
    background: var(--acc); color: var(--acc-ink);
    font-size: 11px; font-weight: 700; line-height: 20px; text-align: center;
}
.dbt-doc-rule .bf-help { margin-top: 5px; }

/* Recipe cards. */
.dbt-doc-group {
    font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
    color: var(--dim); margin: 16px 0 7px;
}
.dbt-doc-group:first-of-type { margin-top: 12px; }
.dbt-doc-card {
    border: 1px solid var(--bdr); border-radius: 5px; margin-bottom: 5px;
    background: var(--bg);
}
.dbt-doc-card > summary {
    cursor: pointer; padding: 8px 11px; list-style: none;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
.dbt-doc-card > summary::-webkit-details-marker { display: none; }
.dbt-doc-card > summary::before {
    content: '+'; flex: 0 0 auto; color: var(--acc);
    font-weight: 700; font-size: 13px; width: 11px; line-height: 1.2;
}
.dbt-doc-card[open] > summary::before { content: '\2212'; }
.dbt-doc-card[open] > summary { border-bottom: 1px solid var(--bdr); }
.dbt-doc-card > summary:hover { background: var(--sf); }
.dbt-doc-title { font-size: 12.5px; font-weight: 600; color: var(--txt); }
.dbt-doc-blurb { font-size: 11px; color: var(--dim); }
.dbt-doc-body { padding: 9px 11px 10px; }

/* One setting: where it lives, then what to type. */
.dbt-doc-spec {
    display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 10px;
    font-size: 11.5px; color: var(--txt); line-height: 1.55; padding: 2.5px 0;
}
.dbt-doc-where {
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--dim); padding-top: 2px;
}
.dbt-doc-spec b { font-weight: 600; }
.dbt-doc-cuts {
    font-size: 11.5px; color: var(--txt); line-height: 1.55;
    border-top: 1px solid var(--bdr); margin-top: 8px; padding-top: 8px;
}
.dbt-doc-note {
    font-size: 11px; color: var(--dim); line-height: 1.6;
    border-left: 3px solid var(--acc); background: var(--sf);
    padding: 7px 10px; border-radius: 0 4px 4px 0; margin-top: 8px;
}

/* Refusals + the honest edges. */
.dbt-doc-refuse, .dbt-doc-gap {
    font-size: 11.5px; color: var(--txt); line-height: 1.6;
    border-left: 3px solid var(--warn); background: var(--sf);
    padding: 8px 11px; border-radius: 0 4px 4px 0; margin-top: 7px;
}
.dbt-doc-refuse > span {
    display: block; color: var(--dim); font-size: 11px; margin-top: 4px;
}
.dbt-doc-gap { border-left-color: var(--bdr); }
.dbt-doc-gap b { color: var(--txt); font-weight: 600; }
.dbt-doc-refuse code {
    font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace;
    font-size: 10.5px; background: var(--bg); padding: 1px 4px; border-radius: 3px;
}

/* Only stack the label above the value when the content column is genuinely
   narrow. At 1100px the two-column form still fits and reads tighter, so the
   breakpoint sits at the width where the value really would be squeezed into a
   two-word ribbon -- measured, not guessed. */
@media (max-width: 640px) {
    .dbt-doc-spec { grid-template-columns: minmax(0, 1fr); gap: 1px; }
    .dbt-doc-where { padding-top: 4px; }
}
