
    :root {
        /* §UI-DENSITY-SCALE — published from the ONE authority in uiScale.ts.
           Was previously hard-coded to 0.9 and referenced by nothing; the 0.9
           had instead been hand-multiplied into the literals below (10.8 = 12 ×
           0.9, 279 = 310 × 0.9 …).  Those residues are now scaled again by the
           injection-time transform like every other length, so this variable is
           reporting, not a second source of truth: read it, never re-derive it. */
        --app-ui-scale:       0.85;

        /* ═══════════════════════════════════════════════════════════════════
           §SHELL-FLOAT-BUDGET (L-4010..L-4016) — THE CANVAS REGION, PUBLISHED
           AS TWO NUMBERS. These are the only two facts every floating,
           canvas-anchored bar needs, and they are the ONE place a half-canvas
           mode is accounted for.
           ═══════════════════════════════════════════════════════════════════

           ⛔ THE DEFECT, MEASURED 2026-08-22. The shell floats a dozen bars at
           'position: fixed; left: 50%; translateX(-50%)'. In a HALF-canvas mode
           'left: 50%' is the right-hand PANEL's own left edge, so every one of
           them draws its right half onto the panel. The escape had been written
           by hand, once per (mode x bar):

             body.pryzm-mode-inspect  .wmb-toplevel-wrapper { left: 25% }
             body.pryzm-mode-inspect  .bam-container        { left: 25% }
             body.pryzm-mode-inspect  .ins-lens-bar         { left: 25% }
             body.pryzm-mode-inspect  .ins-explode-bar      { left: 25% }
             body.pryzm-mode-analysis .wmb-toplevel-wrapper { left: 25% }   (L-3601)

           FIVE rules for TWO modes and FOUR bars, i.e. three of the eight cells
           were simply missing — and the bar in the founder's screenshot,
           '.ceb-bar' (the editor toolbar: undo, redo, move, copy, delete and the
           drafting icons), was in NEITHER list. It is 'top: 56px' with 30px
           circular buttons, so it reaches y=86 — while the Analysis header
           reserve is 44px, derived from occluders that bottom out at y=36. That
           is the founder's still-sliced 'Every figure traceable to elements'.

           ⭐ ONE OWNED BUDGET, NOT A FIFTH PER-PANEL RESERVE. A reserve is the
           panel apologising for the shell; it has been added four times and the
           fifth would have been the hand-copy defect this repo keeps paying for.
           Instead the SHELL states where the canvas is and every bar centres on
           THAT. 'WorkspaceController._applyLayout()' writes both properties from
           the 'canvas' column of WORKSPACE_MODES — so a new half-canvas mode is a
           ROW (ADR-0343 §D.1), not a sixth CSS rule.

           ⚠ 'canvas: hidden' (Data mode) keeps 50%/100vw deliberately: there is
           no canvas to centre on, and the mode bar MUST stay reachable over the
           full-width workbench or the mode cannot be left. */
        --shell-canvas-cx:   50%;
        --shell-canvas-w:    100vw;

        /* §SHELL-TOPBAR-BAND (L-4030..L-4035) - THE VERTICAL HALF OF THE SAME
           BUDGET: how much of the top of the viewport the always-on fixed shell
           row occupies. Any OTHER bar that floats at the top of a canvas pane
           must start below it.

           ⛔ THE DEFECT, MEASURED 2026-08-22 from the founder's screenshot.
           '.wmb-toplevel-wrapper' is 'position: fixed; top: 6px' at z-index 200
           and is OPAQUE. '.svp-plan-view-header' - built by BOTH
           'PlanViewManager.ts:312' and 'SplitViewManager.ts:440' from one class -
           is 'position: absolute; top: 10px; left: 12px; right: 12px' at
           z-index 6, spanning its pane's FULL width with
           'justify-content: space-between'. The two bars occupy the SAME 30px
           band, and 200 paints over 6.

           So the shell row was covering the middle of the view header. What the
           founder photographed as 'an orphan chevron with no label' is the
           '.svp-view-select' with its LEFT half under the opaque bar: that
           select is 'appearance: none' with its chevron drawn as a
           'background-image' pinned to 'right 6px center', so the affordance
           survives at full opacity while the label does not. A control you can
           click and cannot read.

           ⭐ AND THIS IS WHY IT APPEARED TODAY. L-3500 added a THIRD child (the
           level pill) to the centred wrapper. The wrapper got wider, so it began
           covering ground it had not covered before. Nothing was duplicated -
           the '+Level:' select has always been the VIEW header's own control -
           but nothing anywhere said the two bars may not share a band.

           THE NUMBER, derived from the wrapper's own sheet, identically to the
           Analysis header reserve so there is ONE derivation and not two:
             6 (top) + 3 (wmb-bar padding) + 5 + 14 + 5 (wmb-btn) + 3 = 36 px
           Consumers add their own clearance on top. */
        --shell-topbar-h:    36px;
        --app-bg:            #e8edf6;
        --app-panel-bg:      #ffffff;
        /* §UX1-CONFIRM-GLASS — the translucent panel surface and its blur, declared
           ONCE here because C06 §6 puts every visual token in this layer and the
           onboarding confirm card is guarded token-only (see
           apps/editor/__tests__/typologyChoiceModel.test.ts). 0.92 is a SOLVED
           accessibility floor, not a taste value: over the worst-case backdrop
           (black) a white surface composites to alpha × 255, and 4.5:1 against
           the weakest foreground on that card (--app-text-2 / --vg-badge-warn-
           color, luminance ~0.1418) needs alpha ≥ 0.913. The derivation lives
           beside its one consumer, in onboardingStyles.ts §UX1-CONFIRM-GLASS. */
        --app-panel-glass:      rgba(255,255,255,0.92);
        --app-panel-glass-blur: blur(18px) saturate(1.35);
        --app-gradient:      linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%);
        --app-text:          #1a2035;
        --app-text-2:        #5a6a85;
        --app-text-muted:    #7a8aaa;
        --app-border:        #dde3f0;
        --app-border-light:  #eef1f8;
        --app-radius-lg:     16px;
        --app-radius-md:     12px;
        --app-radius-sm:     6px;
        --app-shadow-panel:  0 8px 32px rgba(30,50,120,0.13), 0 2px 8px rgba(30,50,120,0.07);
        --app-shadow-card:   0 2px 10px rgba(30,50,120,0.07), 0 1px 3px rgba(30,50,120,0.04);
        --app-shadow-header: 0 2px 12px rgba(102,0,255,0.35);
        --app-shadow-glow:   0 4px 16px rgba(102,0,255,0.40);
        --app-accent:        #6600FF;
        --app-violet-1:      #8B5CF6;
        --app-violet-2:      #7B3FF2;
        --app-violet-3:      #6600FF;
        --app-violet-soft:   rgba(102,0,255,0.08);
        --app-font:          'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        --app-font-size-body:  10.8px;
        --app-font-size-label: 9.9px;
        --app-font-size-h3:    11.7px;

        /* ── Status indicator tokens ─────────────────────────────────────── */
        --app-status-warning:   #f59e0b;
        --app-status-success:   #22c55e;
        --app-status-error:     #dc2626;
        --app-status-idle:      #7a8aaa;
        --app-status-violet:    #a855f7;
        --app-canvas-bg:        #0d1117;

        /* ── Furniture Carousel (fc-) design tokens ──────────────────────── */
        --fc-carousel-height:   279px;
        --fc-card-width:        153px;
        --fc-card-height:       189px;

        /* ── Tool HUD (th-) design tokens ────────────────────────────────── */
        --app-hud-bg:           rgba(232, 237, 246, 0.97);
        --app-hud-border:       1px solid rgba(139, 92, 246, 0.18);
        --app-success:          #16a34a;

        /* ── CDE state tokens (CDEVersionPanel) ─────────────────────────── */
        --cde-state-shared:     #3b82f6;
        --cde-state-published:  #16a34a;
        --cde-state-archived:   #6b7280;
        --cde-state-wip:        #ef4444;

        /* ── VG Governance badge / dot tokens ───────────────────────────── */
        --vg-badge-warn-bg:     #fff3cd;
        --vg-badge-warn-color:  #856404;
        --vg-badge-ok-bg:       #d1f5e0;
        --vg-badge-ok-color:    #1a6c3a;
        --vg-dot-view-override: #1a8fe0;
        --vg-dot-override:      #e6a817;
        --vg-dot-default:       #aaaaaa;

        /* ── Dark blue — option-background on dark property-panel header ── */
        --app-dark-blue:        #1e3a5f;

        /* ── Unified panel backdrop (§PANEL-BACKDROP-UNIFY) ───────────────
           ONE shared scrim behind EVERY floating panel/modal so they stop
           diverging (New-Project ~0.45 opaque · RAC/OS onboarding ~0.28–0.30
           · AI batch "Building N elements" ~0.58+heavy-blur). A single value
           that sits BETWEEN them: slightly more translucent than New-Project,
           not as see-through as the RAC panel. Brand: PRYZM purple-tinted dark
           (#6600FF family), NEVER pure black. Reference these two together. */
        --pryzm-panel-backdrop:      rgba(28, 12, 60, 0.26);
        --pryzm-panel-backdrop-blur: blur(2px);

        /* ── Loading ground (§UX2-LOADING-GROUND → §UX3-LOADING-GROUND-2 ·
           C06 §6, C84 EI-8/EI-9) ─────────────────────────────────────────
           Founder 2026-08-19 (twice): "make the background more grey, a bit
           darker, so we can see the PRYZM logo more" — and, looking at the still
           older deployed build, "darker grey than that, keep the prism rotating
           white, more white". These live HERE, not in 'LoadingOverlayView.ts',
           for the reason that file's own history shows: the tint has now been
           re-ruled FIVE times (§LOAD-MASK-OPAQUE-WHITE L-483 → §LOAD-MASK-TINT
           L-494 → §UX2's #C8C2DE → this), the first three as literals inside one
           screen, so every sibling loading surface drifted from it. A token is
           the difference between changing the vocabulary and adding a dialect.

           ⚠ THE GROUND ALONE DOES NOT MAKE THE MARK VISIBLE — MEASURED (§UX2).
           The prism's faces are white-at-alpha, so they COMPOSITE with the
           ground and darken in step with it. Both levers therefore move
           TOGETHER: the ground darker AND the face alphas up (front face now
           fully opaque, directly below).

           WHY EXACTLY hsl(259, 22%, 72%) AND NO DARKER — MEASURED 2026-08-19:
           the progress bar's light end is THE brand purple #6600FF, and
           SC 1.4.11 wants ≥3:1 for a meaningful graphic against this ground.
           On #B2A8C7 it reads 3.10:1; at 70% lightness it falls to 2.92:1. So
           this is the darkest purple-grey that keeps the brand-purple bar
           legal — going darker means giving up #6600FF in the gradient, which
           is a brand call, not a contrast call.

           NOT pure grey and NOT black: a purple-grey on the #6600FF hue
           (#B2A8C7 is hsl(259, 22%, 72%)), per the standing white+purple rule. */
        --pryzm-loading-ground:      #B2A8C7;

        /* Prism face alphas (§UX3-LOADING-GROUND-2 — "keep the prism rotating
           white, MORE WHITE"). The front face is now FULLY OPAQUE WHITE — the
           literal reading of the ask — and the other three keep §UX2's measured
           inter-face ratios exactly (0.88 : 0.66 : 0.50 : 0.38 scaled by 1/0.88),
           so the glass depth reading survives while the whole mark whitens.
           Measured against #B2A8C7: front 2.25:1 · left 1.88 · right 1.64 ·
           back 1.46 (§UX2's committed front was 1.62:1; the deployed build the
           founder is looking at is ~1.05:1). ⚠ Still below SC 1.4.11's 3:1 —
           white-on-light cannot reach 3:1 on any ground that carries this
           screen's dark text; §UX2's note stands: closing that gap needs a
           purple mark or stroke, which is a brand decision, not a value here.
           Consumed by 'PryzmLogoSpinner.ts' with the ORIGINAL values as CSS
           fallbacks, so any surface that does not load this sheet is unchanged
           rather than broken. The spinner is SHARED, so the sibling surfaces move
           with it — MEASURED on 'EngineLoadingOverlay''s pastel mesh (front face
           1.70→1.81 on #c8b6ff, 2.00→2.18 on #b8a2ff, 1.11→1.12 on the #f3f0ff
           base): whiter everywhere, worse nowhere. That boot screen keeps its own
           mesh-gradient ground on purpose — it is the landing-page palette, a
           different design decision from this overlay's masking ground. */
        --pryzm-prism-face-front:    1.00;
        --pryzm-prism-face-left:     0.75;
        --pryzm-prism-face-right:    0.57;
        --pryzm-prism-face-back:     0.43;

        /* Type on the loading ground. RE-MEASURED against #B2A8C7 (§UX3), because
           darkening a ground under text tuned for a lighter one is exactly where
           AA is lost — three of §UX2's five fell under 4.5:1 on the new ground
           and are darkened ONE step within their own hue; two held and are
           untouched:
             title  #1a1130  7.99:1  (unchanged — still has headroom)
             label  #3d3454  5.13:1  (unchanged — held above AA)
             note   #3f3757  4.92:1  (was #4e4468: 5.20 on #C8C2DE → 3.96 here)
             accent #4700b3  4.86:1  (was #5200cc: 5.48 → 4.18 here. Same hue and
                                      saturation as brand purple, hsl(264,100%,35%)
                                      — darkened only as far as AA required)
             error  #7d161c  4.65:1  (was #8f1a20: 5.23 → 3.99 here)
           All ≥ 4.5:1 for normal text (C43 §1.5 / SC 1.4.3). */
        --pryzm-loading-title:       #1a1130;
        --pryzm-loading-label:       #3d3454;
        --pryzm-loading-note:        #3f3757;
        --pryzm-loading-accent:      #4700b3;
        --pryzm-loading-error:       #7d161c;

        /* ── Floating panel chrome (§UX1-PANEL-CHROME, C06 §6) ────────────
           The FOUR floating site/GIS panels (Buildable envelope, Site
           analysis, Site plan overlay, and any that follow) were each styled
           with their own inline literals — 300 / 232 / 240 px wide, three
           different shadows, three different border colours, '13px' and
           '12px' body type. That is the same policy in several places,
           drifting: the §UI-DENSITY-SCALE lever in 'uiScale.ts' could not
           reach ANY of it, because the transform rewrites the injected
           stylesheet and those values live in 'Object.assign(el.style, …)'.
           Pointing the inline styles at these custom properties puts them
           back under the one density authority — the values below are the
           AUTHORED sizes; the effective size is value × --app-ui-scale.
           Smaller, quieter, and one place to change. */
        --pryzm-panel-surface:         #ffffff;
        --pryzm-panel-width:           272px;
        --pryzm-panel-width-wide:      288px;
        --pryzm-panel-min-width:       240px;
        --pryzm-panel-pad:             9px 11px;
        --pryzm-panel-radius:          10px;
        --pryzm-panel-gap:             7px;
        --pryzm-panel-border:          1px solid #ece7fb;
        /* Restrained: was '0 4px 18px rgba(20,10,60,0.18)' — a heavy drop that
           read as a modal, not a side card. Halved blur, ~55% of the alpha. */
        --pryzm-panel-shadow:          0 2px 10px rgba(20, 10, 60, 0.10);
        --pryzm-panel-ink:             #2a2340;
        --pryzm-panel-ink-muted:       #6b6480;
        --pryzm-panel-ink-faint:       #a49dbb;
        --pryzm-panel-rule:            #f1eefa;
        /* 'font-size' MUST appear in these names: 'uiScale.scaleDeclaration'
           keys the MIN_FONT_PX legibility floor off the property name, so a
           token called '--pryzm-panel-title-size' would scale straight through
           10px and land at 9.35px unfloored. Do not rename these. */
        --pryzm-panel-font-size-title: 12px;
        --pryzm-panel-font-size-body:  12px;
        --pryzm-panel-font-size-meta:  11px;

        /* ── The right-edge panel column (C06 §7.2, no-overlap policy) ────
           The founder's report included "one panel's close button sits on top
           of another panel's content". That was literally true: the Buildable
           envelope card is TOP-anchored with 'max-height: calc(100vh - 128px)'
           and the Site analysis panel is BOTTOM-anchored with
           'calc(100vh - 32px)' — on any viewport they claim the SAME pixels,
           and neither knew the other existed.

           The top offset is 148px, not the card's old 108px, because 108 is
           where the Forma view sub-bar already sits (GISAreaLayout
           mountFormaViewToggle) with the result-view toggle at 64 above it.
           148 clears both.

           These four values make the right edge a DECLARED, collision-free
           two-slot column: the top panel occupies [top, 50vh) and the bottom
           panel occupies [50vh, 100vh - bottom]. They tile exactly, and they
           only tile if the offsets and the max-heights use the SAME numbers —
           which is why all four are fenced out of the §UI-DENSITY-SCALE
           transform. Scaling the '148px' inside the calc() while the 'top: 148px'
           anchor stayed literal is precisely how a 13px overlap would creep
           back in. 'panelRegionTiling.spec.ts' pins the arithmetic. */
        /* @no-scale:start */
        --pryzm-panel-col-top:            148px;
        --pryzm-panel-col-max-height-top: calc(50vh - 148px);
        --pryzm-panel-col-bottom:            16px;
        --pryzm-panel-col-max-height-bottom: calc(50vh - 16px);
        /* @no-scale:end */

        /* ── Launcher-rail pills (the reopen affordances) ─────────────────
           C43 / WCAG 2.2 AA SC 2.5.8: these are the ONLY route back to the
           panels closed by default (C82 §1.1), so their hit target is fenced
           out of the density transform — 24px is a floor, not a preference.
           Only the target sizes are fenced; the pills' type is not. */
        /* @no-scale:start */
        --pryzm-pill-min-height:       26px;
        --pryzm-pill-pad:              5px 10px;
        /* @no-scale:end */
        --pryzm-pill-radius:           8px;
        --pryzm-pill-font-size:        11px;
        --pryzm-pill-shadow:           0 1px 6px rgba(20, 10, 60, 0.10);

        /* ── §PANEL-BRAND-STANDARD (L-1740..L-1744 · C06 §6) ──────────────
           The founder's report — "the inspect and data sections … don't follow
           the correct PRYZM ui standards … following the exactly colours" — is
           this block's whole reason to exist. Measured 2026-08-21, the two mode
           surfaces between them carried 226 hard-coded colour literals against
           this file's 60-odd tokens, including FOUR palettes that are not the
           product's: Tailwind slate (#e2e8f0 · #94a3b8 · #f8fafc · #0f172a), a
           rival blue accent (#3B8BD4), a rival purple (rgba(147,51,234,.90) —
           Tailwind purple-600, NOT #6600FF), and a cyan (rgb(0,180,220)).

           The literals were not merely duplicating tokens, they were CONTRADICTING
           them: 'var(--app-border, #e2e8f0)' appeared 18 times beside
           'var(--app-border, #dde3f0)' 10 times, for one token whose value is
           #dde3f0. Every one of those fallbacks is DEAD — AppTheme.injectAppTheme()
           concatenates DESIGN_TOKENS ahead of every panel sheet into ONE <style>
           element, so :root is always defined by the time a panel rule is read.
           A fallback that can never fire and disagrees with the token it backs is
           a second palette kept alive in comments-that-compile. They are removed,
           not corrected: correcting them would preserve the second source.

           The tokens below are the roles those literals were reaching for. They
           are named by ROLE, not by value, so the next panel asks "what is this
           FOR" instead of picking a hex.

           ⚠ Adding a value here is the ONLY sanctioned way to introduce a colour
           to a panel. If a role is missing, add the role — do not inline the hex. */

        /* Text/icon colour ON the brand accent, the brand gradient, or any other
           strong ground (a filled tooltip, an inverted chip). 6.98:1 against
           #6600FF — AA for normal text, AAA for large. */
        --app-on-accent:            #ffffff;
        /* Secondary ink and hairline veil on those same strong grounds — the
           count pill on a purple header bar, a ghost button inside it. Both
           panels had these as bare rgba(255,255,255,·) at five different alphas. */
        --app-on-accent-dim:        rgba(255,255,255,0.90);
        --app-on-accent-veil:       rgba(255,255,255,0.18);
        --app-on-accent-veil-hover: rgba(255,255,255,0.30);
        /* Accent under pointer/press. Same value as --app-violet-2; named
           separately because a hover is a ROLE and the ramp position is not. */
        --app-accent-hover:         #7B3FF2;

        /* Sunken surface — the recessed well behind a thumbnail, a code block,
           or an inset list. Replaces #fafafa / #f8fafc / #f8faff, which were
           three near-whites doing one job in one file. */
        --app-surface-sunken:       #f8faff;

        /* The FOUR interaction washes. The two panels used THIRTEEN distinct
           alphas of #6600FF (.04 .05 .06 .07 .08 .09 .10 .12 .15 .18 .2 .25 .30)
           for what is four states, so no two rows of the same kind matched.
           --app-violet-soft (0.08) already existed and is kept as the resting
           tint; these name the other three. */
        --app-wash-hover:           rgba(102,0,255,0.06);
        --app-wash-selected:        rgba(102,0,255,0.12);
        --app-wash-ring:            rgba(102,0,255,0.18);
        --app-focus-ring:           0 0 0 2px rgba(102,0,255,0.18);

        /* Elevation. The literals replaced here were rgba(0,0,0,·) — BLACK, which
           the standing white+purple rule forbids on product surfaces, and which
           is also why those panels read muddier than the rest of the app. These
           sit on the same blue-ink axis as --app-shadow-panel/-card. */
        --app-shadow-drawer:        -4px 0 24px rgba(30,50,120,0.10);
        --app-shadow-modal:         0 20px 60px rgba(30,50,120,0.18);
        --app-shadow-hud:           0 2px 12px rgba(30,50,120,0.14);

        /* Scrollbar thumb — the global rule at the foot of this file already
           paints every scrollbar #c4cde0; three panel rules re-declared it
           (twice as #c4cde0, once as #d0d4e3, which is a fourth grey). Named so
           a panel that genuinely needs its own thumb reads the same value. */
        --app-scrollbar-thumb:      #c4cde0;

        /* ── Status CARDS (soft ground + rule + ink) ──────────────────────
           --app-status-warning/-success/-error above are FILL colours: dots,
           bars, badges. They were also being used as TEXT, and as text on white
           they FAIL WCAG AA — measured on #ffffff: #d97706 is 3.19:1 and
           #16a34a is 3.30:1, both under the 4.5:1 floor for normal text
           (C43 §1.5 / SC 1.4.3). The -ink values below are the darkened
           members of the SAME hue that pass, measured on their own -bg:
             warn  #92400e on #fffbeb  6.84:1   (on white 7.09:1)
             ok    #15803d on #f0fdf4  4.79:1   (on white 5.02:1)
             err   #b91c1c on #fef2f2  5.91:1   (on white 6.47:1)
           So token-ising these is a contrast FIX, not a restyle. Use -ink for
           any status text; keep the plain token for fills. */
        --app-status-warning-bg:    #fffbeb;
        --app-status-warning-line:  #fde68a;
        --app-status-warning-ink:   #92400e;
        --app-status-success-bg:    #f0fdf4;
        --app-status-success-line:  #86efac;
        --app-status-success-ink:   #15803d;
        --app-status-error-bg:      #fef2f2;
        --app-status-error-line:    #fca5a5;
        --app-status-error-ink:     #b91c1c;

        /* ── §CHART-CATEGORICAL-SCALE (L-3001 · ADR-0343 §D.5 · SPEC §6) ──
           The categorical chart palette. Eight SERIES colours plus one named
           neutral. This is the scale ADR-0343 §U.2 deliberately left unnamed,
           because "asserting CVD-safety without simulating it is a hypothesis
           wearing the confidence of a measurement". It is named here because the
           simulation was RUN — see the numbers below, and the guard that re-runs
           them at __tests__/chartPalette.spec.ts.
           (⚠ No backticks anywhere in this block: DESIGN_TOKENS is itself a
           template literal, so one backtick in a comment ends the string and the
           whole theme fails to parse. Found the hard way, 2026-08-21.)

           ⚠ THIS IS A DELIBERATE, ARGUED EXCEPTION TO WHITE + PURPLE, NOT A
           RELAXATION OF IT. ADR-0343 §D.5.4: a purple-monochrome categorical
           scale CANNOT be colour-blind-safe. Separating eight series under
           deuteranopia, protanopia and tritanopia needs variation in lightness
           AND hue; a single-hue ramp gives lightness only. SC 1.4.1 wins over
           brand monochrome when colour is ENCODING rather than DECORATING —
           which is the distinction §D.5 draws between this block and
           §DATA-BUCKET-ACCENT-IS-ONE (which collapsed six DECORATIVE hues to
           one). Chrome on the Analysis surface is still --app-accent on white.

           PROVENANCE: series 1 is the brand accent. Series 2-8 are the
           Okabe-Ito palette (Wong, Nature Methods 8:441, 2011) with ONE
           substitution: its #0072B2 blue became #005F73, because under
           protanopia #0072B2 simulates to #5375b5 and #6600FF simulates to
           #005fff — ΔE00 10.20, the tightest pair in the set. The substitution
           lifted the global floor to 11.13. That was measured, not guessed.

           MEASURED 2026-08-21 — Machado/Oliveira/Fernandes (2009) severity-1.0
           simulation in linear sRGB, pairwise CIEDE2000 over CIE Lab (D65):
             normal   min ΔE00 21.72
             deuteran min ΔE00 11.52   (cat-2 vs cat-8)
             protan   min ΔE00 14.07   (cat-4 vs cat-6)
             tritan   min ΔE00 11.13   (cat-2 vs cat-6)  ← the global floor
           L* spread 36.8 … 89.1, so lightness is a real second channel.

           ⛔ COLOUR IS NEVER THE ONLY CHANNEL (SC 1.4.1). Measured contrast on
           white: cat-1 6.98:1 · cat-7 7.28:1 · cat-5 3.87:1 · cat-3 3.42:1 ·
           cat-6 3.06:1 · cat-4 2.31:1 · cat-2 2.25:1 · cat-8 1.32:1. Four of
           the eight are under 3:1 against white, and cat-8 is 1.32:1 — its
           lightness is exactly what makes the CVD floor work (every darker
           yellow tested collapsed into the orange family: #B8A400 dropped the
           floor to 1.01). So a fill in these colours MUST carry a 1px
           --app-panel-bg separator and a text label or legend entry. A slice
           identified by hue alone is a defect, not a style choice.

           ⛔ Do NOT extend this to --app-cat-9. Eight is where the floor stops
           clearing 10. A ninth series is a "+N others" bucket, not a colour.

           ⭐ §QTYHL132 (L-12120..L-12123) — THE NINE VALUES ARE NO LONGER WRITTEN
           HERE. They are AUTHORED IN TYPESCRIPT, in 'categoricalPalette.ts', and
           the nine declarations below are GENERATED from that array at module
           load. This is not tidying: a CSS custom property is a DOM concept, and
           the 3-D Analysis graph feeds these same colours to 'THREE.Color', which
           has NO CSS ENGINE. The founder's console printed hundreds of

               THREE.Color: Unknown color model var(--app-cat-1)

           because 'AnalysisTypes.CAT_TOKENS' handed the string 'var(--app-cat-N)'
           straight through to the renderer, where the parse failed and the mark
           was drawn in THREE's default WHITE — so "this category is white" and
           "this category's colour was lost" printed the same pixels.

           DOM/CSS consumers are UNCHANGED and keep using var(--app-cat-N). The
           non-CSS consumers call resolveCssColour(), which answers these nine
           from the same constant this block is printed from — so the two
           consumers cannot drift, and a resolved value cannot go stale.

           ⛔ Do NOT re-inline a hex here. A literal in this block would be a
           second authority for a value the TS array already owns (C84 EI-9), and
           'chartPalette.spec.ts' fails on exactly that.

           The neutral is emitted last and is NOT part of the rotation:
           "unassigned" / "untyped" is a real answer and must never be mistaken
           for a category — SPEC §4.1 W2/W3 require it as its own named slice,
           never folded into the largest. Same value as --app-scrollbar-thumb,
           declared separately because this is a ROLE. Verified NOT to degrade the
           scale: adding it keeps the global floor at 11.13 (the greys that read
           as "muted" — #9AA6BC, #7A8AAA — collapse into cat-6 under deuteranopia
           at ΔE00 5.34 and 0.73). */
        --app-cat-1:                #6600FF;
        --app-cat-2:                #E69F00;
        --app-cat-3:                #009E73;
        --app-cat-4:                #56B4E9;
        --app-cat-5:                #D55E00;
        --app-cat-6:                #CC79A7;
        --app-cat-7:                #005F73;
        --app-cat-8:                #F0E442;
        --app-cat-unassigned:       #C4CDE0;
    }

    /* ── Global typography baseline (§05 §2.3 Rule 6) ────────────────────── */
    body,
    button,
    input,
    select,
    textarea {
        font-family: var(--app-font);
    }

    :where(.plat-toolbar, .plat-hub-dropdown, .plat-modal, .tp-panel, .vb-panel, .plat-left-panel, .tpr-panel, .lnr-rail, #dw-workbench, .bam-container, .fc-container, .wmb-bar, .pi-section, .dw-section) {
        font-size: var(--app-font-size-body);
    }

    /* ── Global thin scrollbar (§05 §2.3 Rule 7) ─────────────────────────── */
    * {
        scrollbar-width: thin;
        scrollbar-color: #c4cde0 transparent;
    }
    *::-webkit-scrollbar { width: 4px; height: 4px; }
    *::-webkit-scrollbar-track { background: transparent; }
    *::-webkit-scrollbar-thumb { background: #c4cde0; border-radius: 2px; }
    *::-webkit-scrollbar-thumb:hover { background: #a8b4cc; }

    /* ── Shared utility keyframes ─────────────────────────────────── */
    @keyframes vtToastIn {
        from { opacity: 0; transform: translateY(10px); }
        to   { opacity: 1; transform: translateY(0); }
    }

    /* ─── PRYZM4 CSS @property — mesh gradient blob positions ─────────
       syntax:'<percentage>' lets the browser interpolate these numerically
       inside @keyframes. inherits:false is required for per-element
       transitions. Chrome 85+, Firefox 128+, Safari 16.4+.
    ─────────────────────────────────────────────────────────────────── */
    @property --lp4-b1x { syntax: '<percentage>'; initial-value: 5%;  inherits: false; }
    @property --lp4-b1y { syntax: '<percentage>'; initial-value: 55%; inherits: false; }
    @property --lp4-b2x { syntax: '<percentage>'; initial-value: 92%; inherits: false; }
    @property --lp4-b2y { syntax: '<percentage>'; initial-value: 18%; inherits: false; }
    @property --lp4-b3x { syntax: '<percentage>'; initial-value: 72%; inherits: false; }
    @property --lp4-b3y { syntax: '<percentage>'; initial-value: 90%; inherits: false; }
    @property --lp4-b4x { syntax: '<percentage>'; initial-value: 40%; inherits: false; }
    @property --lp4-b4y { syntax: '<percentage>'; initial-value: 5%;  inherits: false; }

    /* ─── PRYZM4 CSS @property — mesh gradient blob colours ───────────
       Palette: original 4 soft lavenders + 2 new deeper violet/indigo
       blobs (pastel-tinted from user refs: ~#8B4FCC medium-violet and
       ~#6600FF electric-indigo). The extra blobs add ~20% more colour
       presence while staying well within the light pastel range.
         --lp4-c1  left blob    #c8b6ff  pastel purple-violet
         --lp4-c2  right blob   #b8a2ff  richer pastel purple (brand purple lightened)
         --lp4-c3  base blob    #daceff  soft purple
         --lp4-c4  top blob     #ece7ff  near-white with purple cast
         --lp4-c5  accent blob  #b89dfa  medium violet-indigo (ref image 1 pastelised)
         --lp4-c6  accent blob  #c49bfb  violet-magenta (ref image 2 pastelised)
    ─────────────────────────────────────────────────────────────────── */
    @property --lp4-c1 { syntax: '<color>'; initial-value: #c8b6ff; inherits: false; }
    @property --lp4-c2 { syntax: '<color>'; initial-value: #b8a2ff; inherits: false; }
    @property --lp4-c3 { syntax: '<color>'; initial-value: #daceff; inherits: false; }
    @property --lp4-c4 { syntax: '<color>'; initial-value: #ece7ff; inherits: false; }
    @property --lp4-c5 { syntax: '<color>'; initial-value: #b89dfa; inherits: false; }
    @property --lp4-c6 { syntax: '<color>'; initial-value: #c49bfb; inherits: false; }

    /* ─── PRYZM4 CSS @property — extra blob positions (b5, b6) ─────── */
    @property --lp4-b5x { syntax: '<percentage>'; initial-value: 25%; inherits: false; }
    @property --lp4-b5y { syntax: '<percentage>'; initial-value: 30%; inherits: false; }
    @property --lp4-b6x { syntax: '<percentage>'; initial-value: 78%; inherits: false; }
    @property --lp4-b6y { syntax: '<percentage>'; initial-value: 65%; inherits: false; }

    /* ─── PRYZM4 mesh-flow keyframes — extended to 6 blobs ──────────── */
    @keyframes lp4-mesh-flow {
        0%   { --lp4-b1x:  5%; --lp4-b1y: 55%; --lp4-b2x: 92%; --lp4-b2y: 18%; --lp4-b3x: 72%; --lp4-b3y: 90%; --lp4-b4x: 40%; --lp4-b4y:  5%; --lp4-b5x: 25%; --lp4-b5y: 30%; --lp4-b6x: 78%; --lp4-b6y: 65%; }
        12%  { --lp4-b1x: 45%; --lp4-b1y: 22%; --lp4-b2x: 60%; --lp4-b2y: 72%; --lp4-b3x: 88%; --lp4-b3y: 35%; --lp4-b4x: 12%; --lp4-b4y: 58%; --lp4-b5x: 70%; --lp4-b5y: 15%; --lp4-b6x: 18%; --lp4-b6y: 82%; }
        25%  { --lp4-b1x: 12%; --lp4-b1y: 82%; --lp4-b2x: 85%; --lp4-b2y:  8%; --lp4-b3x: 30%; --lp4-b3y: 92%; --lp4-b4x: 78%; --lp4-b4y: 20%; --lp4-b5x: 55%; --lp4-b5y: 68%; --lp4-b6x: 42%; --lp4-b6y: 12%; }
        37%  { --lp4-b1x: 68%; --lp4-b1y: 45%; --lp4-b2x: 22%; --lp4-b2y: 60%; --lp4-b3x: 82%; --lp4-b3y: 18%; --lp4-b4x: 50%; --lp4-b4y: 88%; --lp4-b5x: 10%; --lp4-b5y: 50%; --lp4-b6x: 88%; --lp4-b6y: 30%; }
        50%  { --lp4-b1x:  8%; --lp4-b1y: 18%; --lp4-b2x: 90%; --lp4-b2y: 82%; --lp4-b3x: 48%; --lp4-b3y: 55%; --lp4-b4x: 20%; --lp4-b4y: 35%; --lp4-b5x: 80%; --lp4-b5y: 40%; --lp4-b6x: 35%; --lp4-b6y: 75%; }
        62%  { --lp4-b1x: 72%; --lp4-b1y: 78%; --lp4-b2x: 35%; --lp4-b2y: 12%; --lp4-b3x: 15%; --lp4-b3y: 70%; --lp4-b4x: 88%; --lp4-b4y: 42%; --lp4-b5x: 45%; --lp4-b5y: 88%; --lp4-b6x: 60%; --lp4-b6y: 22%; }
        75%  { --lp4-b1x: 28%; --lp4-b1y: 38%; --lp4-b2x: 78%; --lp4-b2y: 55%; --lp4-b3x: 62%; --lp4-b3y: 28%; --lp4-b4x: 35%; --lp4-b4y: 75%; --lp4-b5x: 92%; --lp4-b5y: 60%; --lp4-b6x: 12%; --lp4-b6y: 42%; }
        87%  { --lp4-b1x: 55%; --lp4-b1y: 88%; --lp4-b2x: 15%; --lp4-b2y: 35%; --lp4-b3x: 92%; --lp4-b3y: 72%; --lp4-b4x: 62%; --lp4-b4y: 12%; --lp4-b5x: 30%; --lp4-b5y: 20%; --lp4-b6x: 72%; --lp4-b6y: 85%; }
        100% { --lp4-b1x:  5%; --lp4-b1y: 55%; --lp4-b2x: 92%; --lp4-b2y: 18%; --lp4-b3x: 72%; --lp4-b3y: 90%; --lp4-b4x: 40%; --lp4-b4y:  5%; --lp4-b5x: 25%; --lp4-b5y: 30%; --lp4-b6x: 78%; --lp4-b6y: 65%; }
    }

    /* ─── Shell — PRYZM4 animated gradient background ─────────────────
       Six blobs total: original 4 + 2 smaller accent blobs in deeper
       violet-indigo tones. The accent blobs (b5, b6) use tighter ellipses
       (45–50%) so their contribution is subtle — roughly +20% colour density.
    ─────────────────────────────────────────────────────────────────── */
    .lp-shell {
        position: fixed;
        inset: 0;
        background:
            radial-gradient(ellipse 90% 70% at var(--lp4-b1x) var(--lp4-b1y), var(--lp4-c1) 0%, transparent 65%),
            radial-gradient(ellipse 70% 60% at var(--lp4-b2x) var(--lp4-b2y), var(--lp4-c2) 0%, transparent 58%),
            radial-gradient(ellipse 65% 55% at var(--lp4-b3x) var(--lp4-b3y), var(--lp4-c3) 0%, transparent 55%),
            radial-gradient(ellipse 55% 45% at var(--lp4-b4x) var(--lp4-b4y), var(--lp4-c4) 0%, transparent 50%),
            radial-gradient(ellipse 35% 29% at var(--lp4-b5x) var(--lp4-b5y), var(--lp4-c5) 0%, transparent 48%),
            radial-gradient(ellipse 32% 27% at var(--lp4-b6x) var(--lp4-b6y), var(--lp4-c6) 0%, transparent 45%),
            #f3f0ff;
        animation: lp4-mesh-flow 65s ease-in-out infinite;
        display: flex;
        flex-direction: column;
        font-family: var(--app-font);
        color: var(--app-text);
        /* §HERO-ONLY (founder 2026-08-27) — 'hidden', not 'auto': the landing is
           deliberately a single non-scrolling hero for now. This is the SECOND
           half of the flag in landingMarkup.ts ('LANDING_HERO_ONLY'); that
           constant empties the stacked video sections, this line stops the page
           scrolling to whatever remains below. Revert BOTH together — flipping
           one alone leaves either a scrollable near-empty page or unreachable
           sections. Restore to 'auto' when the flag goes false. */
        overflow-y: hidden;
        overflow-x: hidden;
        z-index: 10;
    }
    @media (prefers-reduced-motion: reduce) {
        .lp-shell { animation: none; }
    }

    /* ─── Navbar — KRETZ-modelled violet bar (founder brief 2026-08-09) ──
       Supersedes the motif.io transparent header of 2026-08-07. The reference
       is a SLIM bar with three bands:
         1. brand   — the wordmark alone, far left. The pyramid glyph is gone
                      from the bar: the reference puts a plain wordmark here,
                      and the PRYZM tile mark now closes the row on the right.
         2. links   — optically CENTRED in the bar. Auto margins cannot centre
                      them (the brand and action bands have very different
                      widths), so the pill is absolutely positioned at 50% and
                      translated back. .lp-nav is position:relative already.
         3. actions — Contact sales → Log in → Get started → Book a demo, then
                      the TILE MARK, closing the row at the far right.
       Colour: the reference bar is white; the founder's is BRAND VIOLET, so
       #6600FF fills the bar and every control inverts to white-on-violet.
       C51 §2.1.4 / C43 §1.5 tokens only — #6600FF, #4A00B7, #ffffff. The
       primary "Book a demo" pill inverts to SOLID WHITE with #4A00B7 text
       (16.9:1): a solid-purple pill on a purple bar would vanish, and the
       brand direction is white + purple with NO black.
       Sticky is now safe (it was refused while the bar was transparent over
       the deep-purple plate, which killed the outlined CTAs' contrast); an
       OPAQUE violet bar keeps every control legible over anything it crosses.
    ────────────────────────────────────────────────────────────────── */
    .lp-nav {
        display: flex;
        align-items: center;
        gap: 16px;
        padding: 0 18px;
        min-height: 64px;
        /* position:relative is also what the centred links pill and the mobile
           drawer (top:100%) resolve against — sticky preserves that. */
        position: sticky;
        top: 0;
        z-index: 60;
        /* §NAV-GRADIENT (founder brief 2026-08-10, polished round 2) — a soft
           RADIAL pool of the deeper violet centred behind the logo tile, dissolving
           into the canonical brand violet across the bar.
           WHY RADIAL, NOT A LEFT-TO-RIGHT RAMP: round 1 used
           linear-gradient(to right, #4A00B7, #6600FF). A full-width linear ramp
           spends its entire length inside the narrow band between two
           nearly-identical violets, so 8-bit quantisation makes it read as BANDING
           — and it still left a visible edge where the tile met the bar, the very
           defect it was meant to remove. A radial pool concentrates the dark where
           the tile actually is and then has ~80 pc of the bar to dissolve over,
           which is enough distance for the step to vanish.
           Anchor 42px = the mark centre (18px bar padding + half of the 40px tile
           + its margin), so the pool tracks the MARK, not the viewport edge.
           The outer stop is rgba(102,0,255,0) — the same rgb as the base colour at
           zero alpha. Fading to plain transparent interpolates through
           premultiplied black and greys the middle of the wash.
           #4A00B7 is the canonical pryzm-purple-darker token and #6600FF the
           canonical pryzm-purple; the two intermediate values only shape the
           falloff between those tokens and add no new brand colour.
           White text clears AA across the whole range (the darker end only raises
           contrast), so no C43 §1.5 pair changes meaning.
           No backticks in this comment: the stylesheet is a JS template literal,
           so one would terminate it (esbuild: Expected ; but found to). */
        /* Founder round 8 (2026-08-10): (a) the left pool STRONGER/darker —
           stops deepened one shade (#3A0094 at the mark); (b) the whole bar 20pc
           TRANSLUCENT (alpha 0.80 on every stop + base) so the page reads
           through it on scroll; a light blur keeps the white nav type legible
           over busy video frames. */
        background-color: rgba(102,0,255,0.80);
        background-image: radial-gradient(150% 320% at 42px 50%,
            rgba(58,0,148,0.86) 0%, rgba(68,2,178,0.83) 22%, rgba(90,17,226,0.81) 46%, rgba(102,0,255,0) 82%);
        backdrop-filter: blur(10px) saturate(1.15);
        -webkit-backdrop-filter: blur(10px) saturate(1.15);
        box-shadow: 0 1px 0 rgba(255,255,255,0.16), 0 6px 20px rgba(40,0,110,0.18);
        flex-shrink: 0;
    }
    .lp-nav-brand {
        display: flex;
        align-items: center;
        gap: 14px;
        flex-shrink: 0;
        text-decoration: none;
        cursor: pointer;
        margin-right: auto;
        min-width: 151px;
        color: #ffffff;
    }
    /* §NAV-WORDMARK (founder brief 2026-09-17, screenshot: a bare pyramid at
       top-left and no wordmark). The mark and the word are ONE lockup, so the
       14px gap above — sized for the mark standing alone — is closed here.
       ⛔ WHITE, AND SCOPED TO THE BAR. The bar is the brand violet, where white
       is 8.1:1. The same word on the white Product panel or the pricing section
       would be invisible, which is why this is .lp-nav-wordmark inside the bar
       and never a global .lp-wordmark that a light surface could pick up. */
    .lp-nav-brand:focus-visible { outline: 2px solid #ffffff; outline-offset: 2px; border-radius: 10px; }
    .lp-nav-wordmark {
        font-family: var(--app-font);
        font-size: 17px;
        font-weight: 750;
        letter-spacing: 0.10em;
        color: #ffffff;
        line-height: 1;
        /* The mark's right edge is mask-faded INTO the bar, so the word begins
           inside that fade rather than after it — otherwise the pair reads as
           two elements with a gap between them, not as one lockup. */
        margin-left: -12px;
        white-space: nowrap;
        user-select: none;
    }
    @media (max-width: 480px) {
        /* The bar still has to hold four actions at phone width. */
        .lp-nav-wordmark { font-size: 15px; letter-spacing: 0.08em; margin-left: -10px; }
    }
    .lp-logo-icon {
        width: 48px;
        height: 48px;
        display: block;
        filter: drop-shadow(0 1px 6px rgba(80,20,180,0.22));
        flex-shrink: 0;
        overflow: visible;
    }
    /* The brand descender under the wordmark was REMOVED 2026-08-10 per founder
       brief — the hero subhead already states what PRYZM is, so repeating it in the
       bar was noise. Rule deleted rather than left dead: an unused selector is a
       trap for the next editor. (The class name is intentionally not written here —
       MarketingPages.test.ts asserts the rendered page contains no trace of it, and
       this stylesheet is inlined into that page.) */
    /* ── The centred nav links ──────────────────────────────────────────
       DEFAULT is normal flow with auto margins — an in-flow flex item can
       never overlap its neighbours. True viewport-centring costs
       position:absolute, which takes the pill OUT of flow and so CAN collide
       with the brand and action bands once they grow; at ~1024px they do.
       So absolute centring is opted into only at ≥1200px, where the measured
       bands (brand 151px + actions ≈ 520px + mark 50px) leave the centre
       clear. Below that the pill floats between them instead — visually the
       same intent, structurally incapable of overlapping. */
    .lp-nav-links {
        box-sizing: border-box;
        display: flex;
        align-items: center;
        gap: 2px;
        flex: 0 0 auto;
        margin: 0 auto;
        padding: 4px 6px;
        background: rgba(255,255,255,0.13);
        backdrop-filter: blur(18px);
        -webkit-backdrop-filter: blur(18px);
        border: 1px solid rgba(255,255,255,0.24);
        border-radius: 999px;
    }
    @media (min-width: 1200px) {
        .lp-nav-links {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            margin: 0;
        }
    }
    /* 769–1023px: the four CTAs + the mark cannot all fit beside a centred
       pill. Drop the two quietest (they remain reachable from /contact and
       the hero CTA respectively); the emphasis ramp keeps its top two. */
    @media (max-width: 1023px) and (min-width: 769px) {
        .lp-nav-link { padding: 5px 9px; font-size: 11.5px; }
    }
    /* Shared by the <a> links AND by the dropdown trigger <button>s that
       SolutionsDropdown/ResourcesDropdown inject — hence the button resets. */
    .lp-nav-link {
        /* Founder round 5 (2026-08-10): top-nav type at ~90% — 13.5px/7x13
           -> 12px/5x11; round 8 (same day): one more notch, 12 -> 11.5px and
           5x11 -> 4x10, "Solutions / Resources / Pricing" slightly smaller. */
        font-size: 11.5px;
        color: #ffffff;
        text-decoration: none;
        font-weight: 500;
        padding: 4px 10px;
        border-radius: 999px;
        transition: background 0.12s, color 0.12s;
        display: flex;
        align-items: center;
        gap: 3px;
        background: none;
        border: none;
        font-family: var(--app-font);
        cursor: pointer;
        white-space: nowrap;
    }
    .lp-nav-link:hover { background: rgba(255,255,255,0.20); color: #ffffff; }
    .lp-nav-actions {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-shrink: 0;
        margin-left: auto;
    }
    /* ── The tile mark — closes the header on the RIGHT (founder brief).
       Fixed height with width:auto so the intrinsic 128×131 ratio is kept and
       CLS is 0 (the <img> carries width/height attributes). The source PNG is
       alpha-matted around the tile, so the rounded corners sit cleanly on the
       violet bar; the subtle ring just separates the two violets. */
    /* Round 3 (founder): the mark is a SEAMLESS strip — the pyramid on its own
       violet field, flush with the bar's left edge (negative margin cancels the
       bar padding), full bar height, and mask-faded rightward so the strip's
       gradient melts into the bar's token gradient. No radius, no ring, no
       drop shadow: the shadow is baked into the photograph. */
    .lp-nav-mark {
        display: block;
        height: 64px;
        width: auto;
        flex-shrink: 0;
        margin-left: -18px;
        margin-right: 4px;
        -webkit-mask-image: linear-gradient(to right, #000 55%, transparent 100%);
        mask-image: linear-gradient(to right, #000 55%, transparent 100%);
    }
    /* ─────────────────────────────────────────────────────────────────
       §PRODUCT-NAV — the Product ▾ disclosure and its panel.
       NB no backticks anywhere in these comments: this whole stylesheet is ONE
       template literal and a single backtick terminates it (cf. ab6b353e, where
       exactly this shattered the bundle). The first draft of this block did it.
       ─────────────────────────────────────────────────────────────────
       ⭐ HOW A ONE-ITEM MENU IS MADE TO LOOK DELIBERATE.
       The reference this borrows its shape from fills a wide multi-column
       panel with two product lines and their sub-pages. PRYZM has ONE line
       and ONE page, and padding the panel out with invented rows was not an
       option — so the panel is re-proportioned instead of re-populated:
         - a NARROW fixed column (330px), not a full-bleed mega-menu. At that
           width one group fills it; at mega-menu width it would read as a
           menu with its other items missing.
         - the left RAIL (dot + vertical PRODUCT label) is kept, because it is
           what makes a small panel read as a SECTION of a system rather than
           as a stray tooltip.
         - vertical rhythm carries the weight the missing columns would have:
           product name → one-line description → a full-width row with its own
           label, blurb and arrow. Three sizes of type in one column reads as
           composed; one link in a grid reads as empty.
       ⛔ .lp-product-body is a GRID that already flows to two columns at
       560px+ IF a second group is ever added. Nothing about this design has to
       be undone to grow — which is the test of whether small was a decision.  */
    .lp-product { position: relative; }
    /* Remove the UA disclosure triangle in every engine that draws one. */
    .lp-product-summary::-webkit-details-marker { display: none; }
    .lp-product-summary::marker { content: ''; }
    .lp-product-summary {
        display: inline-flex; align-items: center; gap: 6px;
        list-style: none;
        border-radius: 999px;
        font-family: var(--app-font);
        font-size: 12px; font-weight: 600;
        color: #ffffff;
        padding: 6px 12px;
        cursor: pointer;
        white-space: nowrap;
        user-select: none;
        touch-action: manipulation;
        -webkit-tap-highlight-color: transparent;
        transition: background 0.16s;
    }
    .lp-product-summary:hover { background: rgba(255,255,255,0.14); }
    /* Focus must be visible on a violet bar — the UA default ring is not. */
    .lp-product-summary:focus-visible {
        outline: 2px solid #ffffff; outline-offset: 2px;
    }
    .lp-product-caret { transition: transform 0.18s; }
    .lp-product[open] .lp-product-caret,
    .lp-product:hover .lp-product-caret { transform: rotate(180deg); }

    .lp-product-panel {
        position: absolute;
        top: calc(100% + 12px);
        left: 0;
        z-index: 60;
        display: flex;
        width: 330px;
        gap: 16px;
        padding: 18px;
        background: #ffffff;
        border: 1px solid var(--app-border, #dde3f0);
        border-radius: var(--app-radius-lg, 16px);
        box-shadow: 0 28px 64px -28px rgba(26,32,53,0.42), 0 2px 8px rgba(26,32,53,0.07);
        /* Hidden by visibility rather than display, so the transition runs and
           the panel keeps its box for measurement. visibility:hidden also
           removes it from the tab order, which opacity:0 alone does NOT —
           an invisible but tabbable menu is a keyboard trap. */
        opacity: 0; visibility: hidden;
        transform: translateY(-6px);
        transition: opacity 0.16s, transform 0.16s, visibility 0.16s;
    }
    /* THREE ways in, on purpose:
         [open]        — click / tap / Enter / Space on the summary (touch + keyboard)
         :hover        — pointer, matching the reference's feel
         :focus-within — tabbing INTO the menu reveals it without a click
       ⛔ Hover alone would make this unusable on a phone. */
    .lp-product[open] > .lp-product-panel,
    .lp-product:hover > .lp-product-panel,
    .lp-product:focus-within > .lp-product-panel {
        opacity: 1; visibility: visible; transform: translateY(0);
    }
    .lp-product-rail {
        display: flex; flex-direction: column; align-items: center; gap: 10px;
        flex: 0 0 auto; padding-top: 3px;
    }
    .lp-product-dot {
        width: 7px; height: 7px; border-radius: 50%;
        background: var(--app-accent, #6600FF);
    }
    .lp-product-rail-label {
        writing-mode: vertical-rl;
        font-size: 9px; font-weight: 700; letter-spacing: 0.18em;
        color: var(--app-text-muted, #7a8aaa);
    }
    .lp-product-body { display: grid; gap: 22px; min-width: 0; flex: 1 1 auto; }
    .lp-product-name {
        display: flex; align-items: center; gap: 8px;
        margin: 0 0 4px;
        font-size: 14px; font-weight: 700; letter-spacing: -0.01em;
        color: var(--app-text, #1a2035);
    }
    .lp-product-chip {
        font-size: 9px; font-weight: 700; letter-spacing: 0.08em;
        color: #ffffff;
        background: var(--app-accent, #6600FF);
        border-radius: 999px; padding: 2px 7px;
    }
    .lp-product-desc {
        margin: 0 0 14px;
        font-size: 12px; line-height: 1.5;
        color: var(--app-text-2, #5a6a85);
    }
    .lp-product-link {
        display: grid;
        grid-template-columns: 1fr auto;
        align-items: center;
        gap: 2px 10px;
        padding: 10px 12px;
        border-radius: var(--app-radius-md, 12px);
        text-decoration: none;
        transition: background 0.14s;
    }
    .lp-product-link:hover { background: rgba(102,0,255,0.06); }
    .lp-product-link:focus-visible {
        outline: 2px solid var(--app-accent, #6600FF); outline-offset: 1px;
    }
    .lp-product-link-label {
        font-size: 13px; font-weight: 650;
        color: var(--app-text, #1a2035);
    }
    .lp-product-link-blurb {
        grid-column: 1;
        font-size: 11.5px; line-height: 1.45;
        color: var(--app-text-muted, #7a8aaa);
    }
    .lp-product-arrow {
        grid-column: 2; grid-row: 1 / span 2;
        color: var(--app-accent, #6600FF);
        opacity: 0; transform: translateX(-3px);
        transition: opacity 0.14s, transform 0.14s;
    }
    .lp-product-link:hover .lp-product-arrow,
    .lp-product-link:focus-visible .lp-product-arrow { opacity: 1; transform: translateX(0); }
    /* If a second product line ever lands, the panel widens and the body flows
       to two columns. Nothing here has to be rewritten to grow. */
    @media (min-width: 900px) {
        .lp-product-panel:has(.lp-product-group + .lp-product-group) { width: 620px; }
        .lp-product-body:has(.lp-product-group + .lp-product-group) {
            grid-template-columns: repeat(2, minmax(0, 1fr));
        }
    }
    /* Phone: the panel would overflow a 330px-wide viewport pinned to left:0,
       so it becomes a full-width sheet under the bar with a 16px gutter. */
    @media (max-width: 768px) {
        .lp-product-panel {
            position: fixed;
            left: 16px; right: 16px;
            width: auto;
            top: 68px;
        }
    }
    @media (prefers-reduced-motion: reduce) {
        .lp-product-panel, .lp-product-caret, .lp-product-arrow { transition: none; }
    }

    /* Shared pill geometry for the four action CTAs. */
    .lp-nav-login,
    .lp-nav-contact,
    .lp-nav-demo {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        border-radius: 999px;
        font-family: var(--app-font);
        /* Founder round 5: action pills track the smaller nav type (13.5 -> 12px). */
        font-size: 12px;
        cursor: pointer;
        white-space: nowrap;
        text-decoration: none;
        touch-action: manipulation;
        -webkit-tap-highlight-color: transparent;
        user-select: none;
        transition: background 0.16s, border-color 0.16s, color 0.16s, transform 0.16s;
    }
    /* The emphasis ramp is UNCHANGED (text → outlined → tinted → solid); only
       the ground it sits on flipped from pale lavender to violet, so each step
       inverts. Contrast is stated per pill against the #6600FF bar. */
    /* 1+2 — Log in AND Contact: outlined in white, identical.
       §NAV-MINIMAL (founder brief 2026-08-28, screenshot): Contact used to be
       step 1 of the ramp — "quietest, plain text", a :hover rule and NO base
       style at all. That was survivable while it sat in a four-step ramp beside
       Get-started and Pricing. With the bar trimmed to three actions it had no
       neighbours to be quiet against, and an unstyled anchor next to two pills
       read as a rendering fault rather than a hierarchy. The ramp is now
       outlined → outlined → solid, and Contact shares Log in's rule outright so
       the two cannot drift apart on a later edit. #fff on #6600FF = 8.1:1. */
    .lp-nav-login,
    .lp-nav-contact {
        background: rgba(255,255,255,0.10);
        backdrop-filter: blur(16px);
        -webkit-backdrop-filter: blur(16px);
        border: 1px solid rgba(255,255,255,0.55);
        color: #ffffff;
        font-weight: 600;
        padding: 6px 14px;
    }
    .lp-nav-login:hover,
    .lp-nav-contact:hover { background: rgba(255,255,255,0.20); border-color: #ffffff; }
    /* 3 — Get started for free: tinted secondary. */
    .lp-nav-cta:hover { background: rgba(255,255,255,0.32); transform: translateY(-1px); }
    /* 4 — Book a demo: THE primary, INVERTED. On the old pale bar this was a
       solid #6600FF pill; on a #6600FF bar that pill would disappear, so it
       flips to solid white with #4A00B7 text (16.9:1). Still white + purple,
       still no black (memory: preview-color-unified-pryzm-purple). */
    .lp-nav-demo {
        background: #ffffff;
        border: 1px solid #ffffff;
        color: #4A00B7;
        font-weight: 600;
        padding: 7px 16px;
        box-shadow: 0 6px 18px rgba(20,0,60,0.24);
    }
    .lp-nav-demo:hover { background: #F2ECFF; border-color: #F2ECFF; transform: translateY(-1px); }
    /* C43 — visible focus on every header control. The ring must clear the
       VIOLET bar now, so it is white (8.1:1 on #6600FF) with a violet outer
       offset ring so it also reads if a control's own fill is white. */
    .lp-nav-link:focus-visible,
    .lp-nav-contact:focus-visible,
    .lp-nav-login:focus-visible,
    .lp-nav-cta:focus-visible,
    .lp-nav-demo:focus-visible,
    .lp-nav-brand:focus-visible,
    .lp-hamburger:focus-visible {
        outline: 2px solid #ffffff;
        outline-offset: 2px;
        box-shadow: 0 0 0 4px rgba(20,0,60,0.35);
    }

    /* ─── Hero — full-bleed video (founder brief 2026-08-09) ───────────
       KRETZ-modelled: the video fills the viewport directly under the bar and
       the copy floats in a glass control centred on it. This REVERSES the
       2026-08-07 note above ("the hero no longer claims a full viewport") —
       the reference is explicitly a viewport-filling hero, and the product
       showcase now lives one scroll down.

       100dvh (not 100vh) so mobile browser chrome collapsing does not leave a
       gap or cause a jump; 64px is .lp-nav's min-height. min-height keeps the
       panel from being crushed on short landscape phones.
    ────────────────────────────────────────────────────────────────── */
    .lp-hero {
        flex: 0 0 auto;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 24px 24px 40px;
        text-align: center;
        position: relative;
    }
    .lp-hero--video {
        height: calc(100dvh - 64px);
        min-height: 420px;
        padding: 24px;
        overflow: hidden;
        /* The bar is sticky; without an explicit stacking context the video
           can paint over the mobile drawer. */
        isolation: isolate;
    }

    /* ── The video plate ─────────────────────────────────────────────────
       The gradient IS the fallback surface: it paints before the poster
       decodes, it is what shows under prefers-reduced-motion, and it is what
       shows if the MP4 fails to load. There is therefore no state in which
       this hero is black or empty. Colours are the same violet family the
       poster LQIP was generated from, so the swap is invisible. */
    .lp-hero-media {
        position: absolute;
        inset: 0;
        overflow: hidden;
        background:
            radial-gradient(ellipse 85% 90% at 28% 30%, #6600FF 0%, transparent 70%),
            radial-gradient(ellipse 75% 80% at 78% 78%, #4A00B7 0%, transparent 72%),
            #1A0640;
        z-index: 0;
    }
    .lp-hero-video {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        /* Never let a decorative video widen the document. */
        max-width: 100%;
    }
    /* Scrim — the copy must clear AA over ANY frame of an unknown video.
       Deep violet, not black (brand: white + purple, no black). */
    .lp-hero-media::after {
        content: '';
        position: absolute;
        inset: 0;
        background:
            linear-gradient(to right,  rgba(26,6,64,0.46) 0%, rgba(26,6,64,0.16) 52%, rgba(26,6,64,0.00) 100%),
            linear-gradient(to bottom, rgba(26,6,64,0.34) 0%, rgba(26,6,64,0.15) 38%, rgba(26,6,64,0.56) 100%);
        pointer-events: none;
    }
    /* ── White outro bridge (rounds 6) ───────────────────────────────────
       hero.mp4 ENDS on a white frame; the next section (.lp-vsec--fade)
       opens under a matching white gradient. This ::before paints the hero's
       half of that bridge: a short white ramp at the very bottom of the
       frame, ABOVE the scrim (z-index 1 beats the ::after's auto), so the
       video's white outro melts across the section boundary instead of
       hitting the violet scrim. CSS-only — no scroll-jacking, no JS. */
    .lp-hero-media::before {
        content: '';
        position: absolute;
        left: 0; right: 0; bottom: 0;
        height: 16dvh;
        z-index: 1;
        background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.82) 72%, #ffffff 100%);
        pointer-events: none;
    }

    /* ── The copy column — bottom-left, no card ──────────────────────────
       2026-08-10 (founder brief, reference homepage). The glass panel is
       RETIRED. The reference sets type directly on the film and anchors it
       bottom-left; a bordered blurred card reads as a dialog and boxes the
       hierarchy in, which is the one thing the brief asked to undo.

       Removing the card removes a guaranteed backdrop, so legibility moves to
       the scrim above, which is now weighted to the LEFT as well as the bottom.
       Worst case is a pure-white video frame: at the bottom-left the two
       gradients composite to ~0.90 alpha of #1A0640, i.e. an effective backdrop
       near #33215A (rel. luminance ≈ 0.021) — #ffffff clears ~17:1 there,
       against the 4.5:1 the normal-size supporting line needs (C43 §1.5). The
       scrim is opaque enough that this holds for ANY frame, which is the whole
       point of not depending on the video's own content.
    ────────────────────────────────────────────────────────────────── */
    .lp-hero-panel {
        position: relative;
        z-index: 1;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        text-align: left;
        width: min(1180px, 100%);
        margin-right: auto;
        padding: 0;
    }
    /* Left rag + bottom anchor. Scoped to the video hero so any surface still
       using the pale-ground centred hero is untouched. */
    .lp-hero--video {
        align-items: flex-start;
        /* Founder round 7 (2026-08-10): rounds 4-6 kept lifting a BOTTOM-anchored
           block (72 → 148 → 190 → 300px → 38dvh) and the founder still marked it
           too low — a bottom anchor can never reach the upper third. The block is
           now TOP-anchored: it STARTS max(96px, 14dvh) below the nav (~150px at
           1080p), putting eyebrow + headline + subtitle + "Start here" in the
           upper third on every viewport height. */
        justify-content: flex-start;
        text-align: left;
        /* Round 8: "slightly down" from round 7's upper-third anchor —
           max(96px,14dvh) -> max(140px,20dvh), per the founder's marked box. */
        padding: max(140px, 20dvh) 56px 48px;
    }

    /* ─── The date eyebrow — small, uppercase, widely tracked ──────────
       Sits ABOVE the headline and is the first thing in the reading order,
       matching the reference's mission-date line. 13px at 600 weight in white
       over the scrim is normal-size text and clears AA by a wide margin. */
    .lp-hero-dateline {
        font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        font-size: clamp(11px, 0.95vw, 13px);
        font-weight: 600;
        letter-spacing: 0.30em;
        color: #4A00B7;
        text-transform: uppercase;
        margin: 0 0 16px;
        line-height: 1;
    }

    /* Hero logo block REMOVED 2026-08-10 (founder brief): the centred pyramid
       competed with the headline for the same focal point, and the mark now lives
       once in the nav. The 3-D spinner mount went with it — rules deleted rather
       than left dead so the next editor is not misled by an unused selector. */
    /* Scale the lg spinner (44×56 px base) — 50% of original 2.8× hero size */
    .lp-hero-spinner {
        transform: scale(1.4);
        transform-origin: center center;
        overflow: visible;
        flex-shrink: 0;
    }
    /* logo-name is removed from DOM in PRYZM4 hero — kept as hidden no-op */
    .lp-hero-logo-name { display: none; }

    /* The small product-name line that used to sit between the glyph and the
       headline is GONE (2026-08-10): the date eyebrow above now occupies that
       slot, and the product name opens the supporting line instead. Rules
       deleted rather than left dead. */

    /* ─── Hero heading — "DEVELOPMENT. COMPUTED." ───────────────────────
       COLOUR RULING (2026-08-07, measured — do not "restore" white).
       The founder chose PRYZM purple over black. The background stayed the
       animated lavender field, so the choice was re-measured against it
       (WCAG 2.1 relative luminance, gradient sampled at its lightest #f3f0ff
       and darkest #b8a2ff):
         #6600FF → 6.22:1 lightest, 3.07:1 darkest → PASSES AA large text
         #ffffff → 1.12:1 lightest, 2.27:1 darkest → FAILS everywhere
       So purple is BOTH what the founder asked for and the only one that
       passes. The old white treatment leaned on a text-shadow, not contrast.
       The subhead is normal-size text (needs 4.5:1), which #6600FF does not
       clear at the darkest point — it uses pryzm-purple-darker #4A00B7
       (4.68:1 darkest / 9.48:1 lightest). Both are C43 §1.5 tokens.
    ────────────────────────────────────────────────────────────────── */
    .lp-hero-heading {
        font-size: clamp(30px, 6.0vw, 88px);
        font-weight: 800;
        font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        line-height: 1.02;
        color: #6600FF;
        margin: 0 0 20px;
        letter-spacing: -0.03em;
        max-width: none;
        text-transform: uppercase;
    }

    /* ─── Hero supporting line — product name + the positioning sentence ── */
    .lp-hero-sub {
        font-size: clamp(14px, 1.35vw, 18px);
        color: #4A00B7;
        line-height: 1.5;
        margin: 0 0 30px;
        max-width: 540px;
        font-weight: 500;
        letter-spacing: 0.01em;
    }
    /* The product name leads the line at heavier weight — it is a label, not a
       separate block, so it stays inline and inherits the same size. */
    .lp-hero-sub-brand {
        font-weight: 700;
        letter-spacing: 0.05em;
        color: #4A00B7;
    }

    /* ─── Video-hero colour override (2026-08-09) ──────────────────────
       The measured ruling above applies to the PALE LAVENDER ground. The
       video hero's ground is the violet scrim (#1A0640 at 0.24–0.58 over an
       unknown frame), where #6600FF text would fail outright. Over that
       scrim's LIGHTEST point the effective backdrop is still darker than
       #6B4FB0 (rel. luminance ≤ 0.17), so #ffffff clears 4.5:1 for the
       normal-size subhead and far more for the display heading. White is the
       only choice that holds over an unknown video, and it is a brand colour.
       Scoped to .lp-hero--video so the pale-ground ruling stays intact for
       any surface that still uses it. */
    .lp-hero--video .lp-hero-heading { color: #ffffff; }
    .lp-hero--video .lp-hero-sub { color: rgba(255,255,255,0.94); }
    .lp-hero--video .lp-hero-dateline { color: rgba(255,255,255,0.88); }
    .lp-hero--video .lp-hero-sub-brand { color: #ffffff; }
    /* The video hero's headline is SMALLER than the pale-ground one (founder
       brief 2026-08-10): the reference keeps it bold and dominant but nowhere
       near viewport-filling, and shrinking it is what makes room for the
       eyebrow above and the supporting line below to read as one hierarchy
       rather than three competing blocks. */
    .lp-hero--video .lp-hero-heading {
        /* Round 4 (founder, SpaceX reference): tighter leading + tracking and
           a heavier cut so the two lines read as one dense block, per the
           reference's condensed mission titles. */
        font-size: clamp(30px, 4.0vw, 58px);
        letter-spacing: -0.035em;
        line-height: 0.98;
        font-weight: 900;
        margin: 0 0 18px;
        max-width: 16ch;
    }
    .lp-hero--video .lp-hero-sub { max-width: 620px; }
    /* Primary CTA over video: solid white, purple text (16.9:1) — the same
       inversion the header's "Book a demo" pill takes on the violet bar. */
    .lp-hero--video .lp-hero-btn {
        background: #ffffff;
        border-color: #ffffff;
        color: #4A00B7;
        /* Round 4 (founder): CTA at ~50% — the reference's WATCH button is a
           quiet, small affordance under the title, not a billboard. */
        font-size: 13px;
        font-style: normal;
        font-weight: 600;
        letter-spacing: 0.02em;
        padding: 7px 15px;
        min-height: 0;
        box-shadow: 0 6px 18px rgba(12,0,40,0.30);
    }
    .lp-hero--video .lp-hero-btn:hover {
        background: #F2ECFF;
        filter: none;
    }
    .lp-hero--video .lp-hero-btn:focus-visible {
        outline: 2px solid #ffffff;
        outline-offset: 3px;
        box-shadow: 0 0 0 5px rgba(20,0,60,0.40);
    }

    /* ─── AMA fade-in animation — mirrors MIAW CursorPrompt.css entry ────
       Slides up 20px while fading in — same easing as MIAW logoFadeUp.
       Applied to heading, subtitle, and CTA with increasing delays so
       each element arrives sequentially (heading → sub → button).
    ────────────────────────────────────────────────────────────────── */
    @keyframes lp-ama-fadein {
        from { opacity: 0; transform: translateY(20px); }
        to   { opacity: 1; transform: translateY(0); }
    }

    /* Staggered entrance on hero elements — the eyebrow arrives FIRST, so the
       motion reads in the same order as the type hierarchy. */
    .lp-hero-dateline {
        animation: lp-ama-fadein 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
        animation-delay: 0s;
    }
    .lp-hero-heading {
        animation: lp-ama-fadein 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
        animation-delay: 0.15s;
    }
    .lp-hero-sub {
        animation: lp-ama-fadein 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
        animation-delay: 0.42s;
    }

    /* ─── Hero CTA row — left-ragged with the rest of the column. ────── */
    .lp-hero-ctas {
        display: flex;
        gap: 12px;
        align-items: center;
        justify-content: flex-start;
        flex-wrap: wrap;
        margin-bottom: 0;
        transform: none;
    }

    /* ─── Floating "alive" bob — runs forever after the entrance ─────── */
    @keyframes lp-btn-float {
        0%   { transform: translateY(0px); }
        50%  { transform: translateY(-9px); }
        100% { transform: translateY(0px); }
    }

    /* ─── "Start here" glass-pill CTA — MIAW CursorPrompt style ──────────
       entrance: fades up at 0.75s delay (0.75s duration → done at 1.5s)
       float:    gentle bob begins at 1.6s, loops every 3.4s — "alive" feel
       Hover brightens via filter so it doesn't fight the transform animation.
    ────────────────────────────────────────────────────────────────── */
    .lp-hero-btn {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        background: rgba(0,0,0,0.28);
        backdrop-filter: blur(16px);
        -webkit-backdrop-filter: blur(16px);
        color: #fff;
        border: 1px solid rgba(255,255,255,0.22);
        border-radius: 999px;
        font-size: 9px;
        font-weight: 300;
        font-style: italic;
        padding: 8px 17px;
        cursor: pointer;
        font-family: var(--app-font);
        letter-spacing: 0.06em;
        white-space: nowrap;
        touch-action: manipulation;
        -webkit-tap-highlight-color: transparent;
        user-select: none;
        transition: background 0.18s, box-shadow 0.18s, filter 0.18s;
    }
    .lp-hero-btn--enter {
        animation:
            lp-ama-fadein 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.75s both,
            lp-btn-float  3.4s ease-in-out 1.6s infinite;
    }
    .lp-hero-btn:hover {
        background: rgba(0,0,0,0.44);
        filter: brightness(1.18);
        box-shadow: 0 8px 28px rgba(60,10,160,0.26);
        animation-play-state: paused;
    }
    .lp-hero-btn:active {
        filter: brightness(0.92);
    }
    @media (prefers-reduced-motion: reduce) {
        .lp-hero-dateline, .lp-hero-heading, .lp-hero-sub, .lp-hero-btn--enter { animation: none; }
        .lp-hero-ctas { transform: none; }
        /* WCAG 2.1 SC 2.2.2 — no auto-playing motion for users who asked for
           none. The apex ships ZERO JS (CSP default-src 'none'), so the video
           cannot be paused programmatically; hiding it is the only CSS-only
           lever. .lp-hero-media's brand-violet gradient remains as the still
           backdrop, so the hero still reads exactly as designed.
           HONEST LIMIT: display:none stops the video PAINTING, and browsers
           throttle or refuse playback for non-rendered media, but the spec
           does not guarantee the bytes are never fetched. Eliminating the
           fetch entirely needs a <script> the apex is not allowed to have. */
        .lp-hero-video { display: none; }
        .lp-vsec-video { display: none; }
    }

    /* ─── Stacked full-viewport video sections (rounds 6, SpaceX ref) ─────
       Three more 100dvh sections after the hero: hero_02 → hero_03 → Hero_04.
       The nav bar is sticky, so these deliberately run the FULL viewport and
       slide under it — the SpaceX treatment. Each carries a small bottom-left
       caption block (eyebrow + label) over the same violet scrim family as
       the hero, so legibility never depends on the video's own content.
       Fallback ground (paints before/without the video) is the same violet
       radial field as .lp-hero-media — there is no state where a section is
       empty or black-on-white. */
    .lp-vsec {
        position: relative;
        height: 100dvh;
        min-height: 420px;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-end;
        overflow: hidden;
        isolation: isolate;
        padding: 24px 56px 96px;
    }
    .lp-vsec-media {
        position: absolute;
        inset: 0;
        overflow: hidden;
        background:
            radial-gradient(ellipse 85% 90% at 28% 30%, #6600FF 0%, transparent 70%),
            radial-gradient(ellipse 75% 80% at 78% 78%, #4A00B7 0%, transparent 72%),
            #1A0640;
        z-index: 0;
    }
    .lp-vsec-video {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        max-width: 100%;
    }
    /* Caption scrim — lighter than the hero's (a label, not a headline, sits
       here) but still weighted to the bottom-left corner the copy occupies.
       Deep violet, never black (brand ruling). */
    .lp-vsec-media::after {
        content: '';
        position: absolute;
        inset: 0;
        background:
            linear-gradient(to right,  rgba(26,6,64,0.32) 0%, rgba(26,6,64,0.10) 48%, rgba(26,6,64,0.00) 100%),
            linear-gradient(to bottom, rgba(26,6,64,0.00) 55%, rgba(26,6,64,0.52) 100%);
        pointer-events: none;
    }
    .lp-vsec-panel {
        position: relative;
        z-index: 1;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        text-align: left;
        width: min(1180px, 100%);
        margin-right: auto;
    }
    .lp-vsec-eyebrow {
        font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        font-size: clamp(11px, 0.95vw, 13px);
        font-weight: 600;
        letter-spacing: 0.30em;
        color: rgba(255,255,255,0.88);
        text-transform: uppercase;
        margin: 0 0 12px;
        line-height: 1;
    }
    .lp-vsec-title {
        font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        font-size: clamp(24px, 2.6vw, 40px);
        font-weight: 900;
        letter-spacing: -0.03em;
        line-height: 1.0;
        color: #ffffff;
        text-transform: uppercase;
        margin: 0;
        max-width: 20ch;
    }
    /* Transition 1 — hero → hero_02: SMOOTH. hero.mp4's white outro (bridged
       by .lp-hero-media::before above) melts into this section under a long
       white gradient falling from its top edge. Scroll-linked in effect but
       purely paint: as the boundary crosses the viewport the two gradients
       read as one continuous white field dissolving into the video. */
    .lp-vsec--fade::before {
        content: '';
        position: absolute;
        top: 0; left: 0; right: 0;
        height: 30dvh;
        z-index: 2;
        background: linear-gradient(to bottom, #ffffff 0%, rgba(255,255,255,0.55) 42%, rgba(255,255,255,0) 100%);
        pointer-events: none;
    }
    /* Transition 3 — hero_03 → Hero_04: STRAIGHT. Video 4 is dark content;
       the section takes a clean hard edge (no bridge, no blend) and its
       fallback ground drops to the deepest violet so even the pre-video
       frame reads dark. */
    .lp-vsec--dark .lp-vsec-media {
        background: #0E0420;
    }

    /* Product-showcase styles (the Paris 3D-Site screenshot plate) DELETED
       2026-08-10 (founder, rounds 6) with the section itself — the stacked
       video sections carry the product story now. */

    /* ─── Hero feature tags ───────────────────────────────────────────── */
    .lp-hero-tags {
        display: flex;
        gap: 0;
        align-items: center;
        justify-content: center;
        flex-wrap: wrap;
        color: rgba(90,77,122,0.72);
        font-size: 12.5px;
        letter-spacing: 0.01em;
    }
    .lp-hero-tag {
        padding: 0 12px;
        border-right: 1px solid rgba(139,92,246,0.22);
        line-height: 1;
    }
    .lp-hero-tag:last-child { border-right: none; }
    .lp-hero-tag:first-child { padding-left: 0; }

    /* ─── Legacy compat — mosaic tiles kept for graceful no-op ──────────
       LandingPageMosaic queries .lp-mosaic-container; since it is absent
       from the PRYZM4 hero layout the mosaic constructor simply skips.
       These rules are retained so any external tooling that references
       the class names doesn't produce parser errors. ──────────────────── */
    .lp-mosaic-container { display: none; }
    .lp-mosaic-track, .lp-mosaic-inner, .lp-mosaic-tile,
    .lp-mosaic-tile--placeholder, .lp-mosaic-media { display: none; }

    /* ─── Legacy hero-card — kept for backward compat, not rendered ───── */
    .lp-hero-card { display: none; }

    /* ─── Feature strip — legacy, not rendered in PRYZM4 ─────────────── */
    .lp-features { display: none; }
    .lp-feature, .lp-feature-icon, .lp-feature-text,
    .lp-feature-title, .lp-feature-desc { display: none; }

    /* ─── Legacy compat classes (retained, not rendered) ─────────────── */
    .lp-hero-bg, .lp-hero-cards, .lp-preview-card,
    .lp-slider-container, .lp-slider-slide, .lp-slider-media,
    .lp-slider-fallback, .lp-hero-overlay { display: none; }

    /* ── Scroll-reveal utility (used by LandingPageScrollReveal.ts) ──── */
    .lp-reveal {
        opacity: 0;
        transform: translateY(40px);
        transition: opacity 0.65s ease, transform 0.65s ease;
    }
    .lp-reveal--visible {
        opacity: 1;
        transform: translateY(0);
    }

    /* ── Stream 2 — Bespoke / Enterprise section ─────────────────────── */
    .lp-bespoke {
        width: 100%;
        background: var(--app-violet-soft);
        padding: 80px 24px;
        box-sizing: border-box;
    }
    .lp-bespoke-inner {
        max-width: 1100px;
        margin: 0 auto;
        display: flex;
        gap: 64px;
        align-items: center;
    }
    .lp-bespoke-col { flex: 1; }
    .lp-bespoke-heading {
        font-size: 28px;
        font-weight: 700;
        color: var(--app-text);
        margin: 0 0 16px;
        letter-spacing: -0.3px;
        line-height: 1.25;
    }
    .lp-bespoke-desc {
        font-size: 15px;
        color: var(--app-text-2);
        line-height: 1.6;
        margin: 0 0 28px;
    }
    .lp-bespoke-actions {
        display: flex;
        gap: 12px;
        flex-wrap: wrap;
    }
    .lp-bespoke-actions button:first-child {
        height: 44px;
        padding: 0 24px;
        background: var(--app-gradient);
        color: #fff;
        font-size: 14px;
        font-weight: 700;
        border: none;
        border-radius: var(--app-radius-sm);
        cursor: pointer;
        font-family: var(--app-font);
        box-shadow: var(--app-shadow-glow);
        transition: opacity 0.15s, transform 0.12s;
    }
    .lp-bespoke-actions button:first-child:hover { opacity: 0.88; transform: translateY(-1px); }
    .lp-bespoke-actions button:last-child {
        height: 44px;
        padding: 0 24px;
        background: transparent;
        color: var(--app-violet-1);
        font-size: 14px;
        font-weight: 600;
        border: 1.5px solid var(--app-violet-1);
        border-radius: var(--app-radius-sm);
        cursor: pointer;
        font-family: var(--app-font);
        transition: background 0.15s, color 0.15s;
    }
    .lp-bespoke-actions button:last-child:hover {
        background: var(--app-violet-soft);
        color: var(--app-violet-2);
    }
    .lp-bespoke-list {
        list-style: none;
        padding: 0;
        margin: 0;
        display: flex;
        flex-direction: column;
        gap: 12px;
    }
    .lp-bespoke-list li {
        font-size: 14px;
        color: var(--app-text-2);
        padding-left: 20px;
        position: relative;
        line-height: 1.5;
    }
    .lp-bespoke-list li::before {
        content: "✓";
        position: absolute;
        left: 0;
        color: var(--app-violet-1);
        font-weight: 700;
    }
    @media (max-width: 768px) {
        .lp-bespoke-inner { flex-direction: column; gap: 32px; }
    }
    /* RELOCATED 2026-08-07 (apex header pass): this block used to live at the
       tail of SOLUTIONS_STYLES, which prerender-apex.mjs does NOT inline — so
       the apex shipped with NO hamburger / drawer / responsive rules at all.
       That was invisible only because .lp-nav was display:none; un-hiding the
       nav exposed an unstyled drawer on desktop. These are LANDING styles and
       belong in LANDING_PAGE_STYLES. */
    /* ─────────────────────────────────────────────────────────────────── */
    /* MOB-001-LP  Mobile Responsiveness                                   */
    /* Contract: docs/05-guides/mobile/MOBILE_CONTRACT.md §2.3                       */
    /* ─────────────────────────────────────────────────────────────────── */

    /* ── Hamburger button (visible on mobile only) ─────────────────────── */
    .lp-hamburger {
        display: none;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        background: none;
        border: none;
        border-radius: 8px;
        cursor: pointer;
        /* White: the bar it sits on is now #6600FF (8.1:1). */
        color: #ffffff;
        flex-shrink: 0;
        transition: background 0.12s;
        margin-left: 6px;
    }
    .lp-hamburger:hover { background: rgba(255,255,255,0.18); }
    .lp-hamburger svg { display: block; }

    /* ── Mobile nav drawer ─────────────────────────────────────────────── */
    .lp-mobile-drawer {
        display: none;
        flex-direction: column;
        background: #ffffff;
        border-bottom: 1px solid #ebebeb;
        overflow: hidden;
        max-height: 0;
        transition: max-height 0.3s ease;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        z-index: 100;
        box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    }
    .lp-mobile-drawer--open {
        max-height: 600px;
    }
    .lp-mobile-drawer-links {
        display: flex;
        flex-direction: column;
        padding: 8px 0;
        border-bottom: 1px solid #f0f0f0;
    }
    .lp-mobile-drawer-link {
        display: flex;
        align-items: center;
        min-height: 44px;
        padding: 10px 20px;
        font-size: 15px;
        font-weight: 500;
        color: #1a1a1a;
        text-decoration: none;
        background: none;
        border: none;
        font-family: var(--app-font);
        cursor: pointer;
        text-align: left;
        transition: background 0.12s;
    }
    .lp-mobile-drawer-link:hover { background: #f5f5f5; }
    .lp-mobile-drawer-actions {
        display: flex;
        flex-direction: column;
        gap: 10px;
        padding: 16px 20px;
        padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    }
    .lp-mobile-drawer-login {
        display: flex;
        align-items: center;
        justify-content: center;
        min-height: 44px;
        background: none;
        border: 1.5px solid #1a1a1a;
        border-radius: 10px;
        font-size: 15px;
        font-weight: 600;
        color: #1a1a1a;
        cursor: pointer;
        font-family: var(--app-font);
        transition: background 0.12s;
    }
    .lp-mobile-drawer-login:hover { background: #f5f5f5; }
    .lp-mobile-drawer-cta {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 48px;
        background: #111111;
        color: #fff;
        border: none;
        border-radius: 999px;
        font-size: 15px;
        font-weight: 500;
        cursor: pointer;
        font-family: var(--app-font);
        transition: background 0.15s;
    }
    .lp-mobile-drawer-cta:hover { background: #333; }
    /* Mobile "Book a demo" — the drawer's primary, mirroring .lp-nav-demo. */
    .lp-mobile-drawer-demo {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 48px;
        background: #6600FF;
        color: #ffffff;
        border: none;
        border-radius: 999px;
        font-size: 15px;
        font-weight: 600;
        cursor: pointer;
        font-family: var(--app-font);
        text-decoration: none;
        transition: background 0.15s;
    }
    .lp-mobile-drawer-demo:hover { background: #4A00B7; }
    .lp-mobile-drawer-contact {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 44px;
        background: none;
        border: 1.5px solid rgba(26,26,26,0.55);
        border-radius: 999px;
        font-size: 14px;
        font-weight: 500;
        color: #1a1a1a;
        cursor: pointer;
        font-family: var(--app-font);
        transition: background 0.12s, border-color 0.12s;
    }
    .lp-mobile-drawer-contact:hover { background: rgba(0,0,0,0.05); border-color: rgba(26,26,26,0.80); }

    /* ─────────────────────────── @media ≤768px ─────────────────────────── */
    @media (max-width: 768px) {
        /* Navbar: compact. Keeps position:relative from the base rule so the
           absolutely-positioned drawer still resolves against it. */
        .lp-nav {
            align-items: center;
            min-height: 56px;
            padding: 0 12px;
        }
        /* Hide desktop nav links and the four action CTAs. The ACTIONS ROW
           itself stays laid out — the tile mark lives inside it, and the
           founder's brief puts that mark on the right at every width. */
        .lp-nav-links { display: none; }
        /* .lp-nav-contact added 2026-08-28 — it is back in the bar (§NAV-MINIMAL)
           and without it here it was the ONE action still visible at mobile while
           its two neighbours hid, beside the hamburger that already offers it. */
        .lp-nav-login,
        .lp-nav-contact,
        .lp-nav-demo { display: none; }
        .lp-nav-actions { gap: 4px; }
        .lp-nav-mark { height: 56px; margin-left: -12px; margin-right: 2px; }
        /* Show hamburger */
        .lp-hamburger { display: flex; }
        /* Show mobile drawer */
        .lp-mobile-drawer { display: flex; }

        /* ── APEX mobile header (C51 §2.1.1: correct with JS DISABLED) ──
           The hamburger + drawer are a JS affordance, so mode:'apex' does not
           emit them at all. Instead the apex header WRAPS to two rows —
           brand + "Book a demo" on row 1, the links pill centred on row 2 —
           which keeps every nav destination reachable and crawlable on a
           390px viewport with zero script. */
        .lp-nav--apex {
            flex-wrap: wrap;
            height: auto;
            align-items: center;
            padding: 10px 12px;
            row-gap: 8px;
        }
        /* The links pill is absolutely positioned on desktop; on the wrapped
           apex header it must return to NORMAL FLOW to occupy row 2. */
        .lp-nav--apex .lp-nav-links {
            display: flex;
            position: static;
            transform: none;
            order: 3;
            flex: 1 0 100%;
            justify-content: center;
            margin: 0;
        }
        .lp-nav--apex .lp-nav-link { padding: 7px 11px; font-size: 13px; }
        .lp-nav--apex .lp-nav-actions { display: flex; margin: 0 0 0 auto; }
        /* Only the primary CTA survives the narrow row (plus the tile mark). */
        /* Pre-existing typo, fixed 2026-08-28 while editing this list: the third
           selector repeated .lp-nav--apex twice with whitespace between, making
           it a DESCENDANT selector requiring one .lp-nav--apex inside another.
           It matched nothing, so the primary CTA silently missed this
           narrow-apex sizing while its two neighbours got it.
           (No backticks in this comment — it is inside a template literal.) */
        .lp-nav--apex .lp-nav-contact,
        .lp-nav--apex .lp-nav-login,
        .lp-nav--apex .lp-nav-demo { display: inline-flex; padding: 9px 16px; font-size: 13px; }

        /* Scale down logo */
        .lp-logo-icon { width: 36px; height: 36px; }
        .lp-nav-brand { gap: 10px; min-width: 0; }

        /* Hero: full-width, single centred column */
        .lp-hero {
            padding: 12px 16px 28px;
            justify-content: center;
        }
        /* 56px is the mobile .lp-nav min-height. */
        .lp-hero--video {
            height: calc(100dvh - 56px);
            min-height: 460px;
            padding: 16px 20px 44px;
        }
        .lp-hero-panel {
            width: 100%;
            padding: 0;
        }
        .lp-hero--video .lp-hero-btn {
            width: 100%;
            justify-content: center;
        }
        .lp-hero--video .lp-hero-ctas { width: 100%; }
        .lp-hero-card {
            padding: 32px 24px 36px;
            max-width: 100%;
            width: 100%;
        }
        .lp-hero-dateline { font-size: 11px; letter-spacing: 0.26em; margin-bottom: 12px; }
        .lp-hero-heading,
        .lp-hero--video .lp-hero-heading {
            font-size: 34px;
            letter-spacing: -0.02em;
            max-width: none;
            margin-bottom: 14px;
        }
        .lp-hero-sub,
        .lp-hero--video .lp-hero-sub {
            font-size: 14px;
            max-width: 100%;
            margin-bottom: 24px;
        }

        /* Showcase responsive rules deleted with the section (2026-08-10). */

        /* Bespoke section */
        .lp-bespoke { padding: 48px 16px; }
        .lp-bespoke-inner { flex-direction: column; gap: 28px; }
        .lp-bespoke-actions { flex-direction: column; }
        .lp-bespoke-actions button:first-child,
        .lp-bespoke-actions button:last-child {
            width: 100%;
            justify-content: center;
        }

        /* Solutions/Resources dropdowns: hide on mobile (drawer replaces them) */
        .lp-res-dropdown,
        .lp-sol-dropdown { display: none !important; }

        /* Resource/Solutions pages: reduce padding */
        .lp-res-page-header,
        .lp-sol-page-header { padding: 0 16px; }
        .lp-res-page-content,
        .lp-sol-page-content { padding: 0 16px; }
    }

    /* ─────────────────────────── @media ≤480px ─────────────────────────── */
    @media (max-width: 480px) {
        .lp-hero-card {
            padding: 24px 16px 28px;
        }
        .lp-hero-heading,
        .lp-hero--video .lp-hero-heading {
            font-size: 28px;
            letter-spacing: -0.02em;
        }
        .lp-hero-sub,
        .lp-hero--video .lp-hero-sub { font-size: 13px; }
        .lp-hero-dateline { font-size: 10.5px; letter-spacing: 0.24em; }
        .lp-hero-btn {
            width: 100%;
            text-align: center;
            padding: 15px 28px;
        }
        .lp-bespoke { padding: 36px 12px; }
        .lp-bespoke-heading { font-size: 22px; }
    }

    /* ─────────────────────────────────────────────────────────────────
       Shared page frame — both form pages and the pricing page sit on the
       same soft lilac wash rather than flat white, so the white cards have
       something to lift off.
       ───────────────────────────────────────────────────────────────── */
    .mf-page {
        min-height: 100vh;
        background:
            radial-gradient(1100px 620px at 12% -10%, rgba(102,0,255,0.10), transparent 62%),
            radial-gradient(900px 520px at 100% 0%, rgba(139,92,246,0.09), transparent 60%),
            #ffffff;
        font-family: var(--app-font, 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
        color: var(--app-text, #1a2035);
        box-sizing: border-box;
    }
    .mf-page *, .mf-page *::before, .mf-page *::after { box-sizing: border-box; }

    /* ── Standalone page header (demo /contact) ───────────────────
       A dedicated bar — NOT the landing shell/nav. Purple brand mark,
       white background, no black surfaces. Mirrors the in-app header
       language but is its own markup (the founder's "motif page" ask). */
    .mf-header {
        display: flex; align-items: center; justify-content: space-between;
        max-width: 1120px; margin: 0 auto;
        padding: 20px 24px;
        border-bottom: 1px solid var(--app-border, #dde3f0);
    }
    .mf-brand {
        font-size: 18px; font-weight: 800; letter-spacing: -0.02em;
        color: var(--app-accent, #6600FF); text-decoration: none;
    }
    .mf-brand:hover, .mf-brand:focus-visible { text-decoration: underline; }
    .mf-nav { display: flex; gap: 28px; align-items: center; }
    .mf-nav a {
        font-size: 14px; font-weight: 600; text-decoration: none;
        color: var(--app-text, #1a2035);
    }
    .mf-nav a:hover, .mf-nav a:focus-visible { color: var(--app-accent, #6600FF); }
    .mf-nav a[aria-current="page"] { color: var(--app-accent, #6600FF); }
    .mf-nav .mf-nav-cta {
        font-weight: 700;
        color: #ffffff;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
        border-radius: 999px;
        padding: 8px 18px;
        transition: transform 0.14s, box-shadow 0.14s;
        box-shadow: 0 6px 16px -8px rgba(102,0,255,0.6);
    }
    .mf-nav .mf-nav-cta:hover { transform: translateY(-1px); }

    /* ── Focus. One rule, everything focusable, always visible ──────────
       :focus-visible only, so a mouse click does not leave a ring, but every
       keyboard path does. A form a keyboard user cannot see their way through
       is not accessible, and the default ring is invisible on white. */
    .mf-page a:focus-visible,
    .mf-page button:focus-visible,
    .mf-page input:focus-visible,
    .mf-page select:focus-visible,
    .mf-page textarea:focus-visible {
        outline: 3px solid var(--app-accent, #6600FF);
        outline-offset: 2px;
        border-radius: 6px;
    }

    /* Screen-reader-only text — used to give two identical "Talk to sales"
       links distinguishable accessible names. */
    .pt-sr-only {
        position: absolute !important;
        width: 1px; height: 1px;
        padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
        white-space: nowrap; border: 0;
    }

    /* ─────────────────────────────────────────────────────────────────
       The form panel — intro left, white card right
       ───────────────────────────────────────────────────────────────── */
    .mf-panel {
        display: grid;
        grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
        gap: 56px;
        align-items: start;
        max-width: 1120px;
        margin: 0 auto;
        padding: 72px 24px 96px;
    }
    .mf-eyebrow {
        margin: 0 0 14px;
        font-size: 11px; font-weight: 700; letter-spacing: 0.16em;
        text-transform: uppercase;
        color: var(--app-accent, #6600FF);
    }
    .mf-heading {
        margin: 0 0 16px;
        font-size: clamp(30px, 4.4vw, 46px);
        line-height: 1.08;
        font-weight: 800;
        letter-spacing: -0.03em;
        color: var(--app-text, #1a2035);
    }
    .mf-sub {
        margin: 0 0 28px;
        font-size: 16px; line-height: 1.6;
        color: var(--app-text-2, #5a6a85);
        max-width: 44ch;
    }
    .mf-points { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }
    .mf-points li {
        position: relative;
        padding-left: 28px;
        font-size: 14.5px; line-height: 1.5;
        color: var(--app-text-2, #5a6a85);
    }
    /* The tick is decorative — a text bullet would be announced by a screen
       reader as content the sentence does not need. */
    .mf-points li::before {
        content: '';
        position: absolute; left: 0; top: 6px;
        width: 15px; height: 9px;
        border-left: 2.2px solid var(--app-accent, #6600FF);
        border-bottom: 2.2px solid var(--app-accent, #6600FF);
        transform: rotate(-45deg);
        border-radius: 1px;
    }

    /* ── The card ──────────────────────────────────────────────────── */
    .mf-card {
        background: var(--app-panel-bg, #ffffff);
        border: 1px solid var(--app-border, #dde3f0);
        border-radius: var(--app-radius-lg, 16px);
        box-shadow: 0 24px 60px -30px rgba(26,32,53,0.28), 0 2px 6px rgba(26,32,53,0.05);
        padding: 32px;
    }
    .mf-anchor { display: block; height: 0; }
    .mf-form { display: block; }
    .mf-required-note {
        margin: 0 0 20px;
        font-size: 12px;
        color: var(--app-text-muted, #7a8aaa);
    }
    .mf-req { color: var(--app-accent, #6600FF); margin-left: 3px; font-weight: 700; }

    .mf-fields {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 16px 14px;
    }
    .mf-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
    .mf-field--full { grid-column: 1 / -1; }
    .mf-label {
        font-size: 13px; font-weight: 600;
        color: var(--app-text, #1a2035);
        display: flex; align-items: baseline; gap: 6px;
    }
    .mf-hint {
        font-size: 11.5px; font-weight: 500;
        color: var(--app-text-muted, #7a8aaa);
    }
    .mf-input {
        width: 100%;
        font-family: inherit;
        font-size: 14.5px;
        color: var(--app-text, #1a2035);
        background: #ffffff;
        border: 1.5px solid var(--app-border, #dde3f0);
        border-radius: var(--app-radius-md, 12px);
        padding: 11px 13px;
        transition: border-color 0.14s, box-shadow 0.14s;
        -webkit-appearance: none; appearance: none;
    }
    .mf-input::placeholder { color: var(--app-text-muted, #7a8aaa); }
    .mf-input:hover { border-color: #c8cfe4; }
    .mf-input:focus { border-color: var(--app-accent, #6600FF); box-shadow: 0 0 0 4px rgba(102,0,255,0.10); }
    .mf-textarea { resize: vertical; min-height: 116px; line-height: 1.55; }
    .mf-select {
        /* The caret is drawn rather than left to the UA, because appearance:none
           removes the platform one and a select with no caret reads as a
           disabled text input. Inline SVG — no asset request. */
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%237a8aaa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: right 13px center;
        padding-right: 36px;
    }

    /* ── Errors. The <p role="alert"> is EMPTY until the client fills it:
       an alert region that ships with text announces a failure on page load.
       Empty + populated-on-failure is what makes it announce once, when it
       is true. ─────────────────────────────────────────────────────────── */
    .mf-error {
        margin: 0;
        font-size: 12.5px;
        line-height: 1.4;
        color: #c0263c;
        min-height: 0;
    }
    .mf-error:empty { display: none; }
    .mf-field[data-mf-invalid="true"] .mf-input {
        border-color: #c0263c;
        box-shadow: 0 0 0 4px rgba(192,38,60,0.10);
    }

    .mf-status {
        margin: 0 0 18px;
        padding: 12px 14px;
        border-radius: var(--app-radius-md, 12px);
        font-size: 13.5px; line-height: 1.5;
        border: 1px solid #f0c2ca;
        background: #fdf2f4;
        color: #8d1c2e;
    }
    .mf-status[hidden] { display: none; }
    /* The no-JS error banner is hidden until the server redirects to #error;
       CSS :target is the whole mechanism (see marketingFormsMarkup.ts). */
    .mf-status--nojs-error { display: none; }
    #error:target ~ .mf-form .mf-status--nojs-error { display: block; }

    /* ── Consent + honeypot ──────────────────────────────────────────── */
    .mf-consent {
        display: flex; align-items: flex-start; gap: 10px;
        margin: 20px 0 0;
    }
    .mf-checkbox {
        flex: 0 0 auto;
        width: 17px; height: 17px; margin: 2px 0 0;
        accent-color: var(--app-accent, #6600FF);
    }
    .mf-consent-label {
        font-size: 13px; line-height: 1.5;
        color: var(--app-text-2, #5a6a85);
    }
    /* ⛔ NOT display:none. Off-canvas instead — the cruder bots skip fields the
       page hides outright, the naive ones happily fill this one. aria-hidden +
       tabindex=-1 in the markup keep it off both the a11y tree and the tab
       order, so no human ever meets it. */
    .mf-hp {
        position: absolute !important;
        left: -9999px; top: auto;
        width: 1px; height: 1px; overflow: hidden;
    }

    /* ── Submit ──────────────────────────────────────────────────────── */
    .mf-submit {
        margin-top: 24px;
        width: 100%;
        font-family: inherit;
        font-size: 15px; font-weight: 650;
        color: #ffffff;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
        border: none;
        border-radius: 999px;
        padding: 14px 22px;
        cursor: pointer;
        transition: transform 0.14s, box-shadow 0.14s, filter 0.14s;
        box-shadow: 0 10px 26px -12px rgba(102,0,255,0.7);
    }
    .mf-submit:hover { transform: translateY(-1px); box-shadow: 0 14px 30px -12px rgba(102,0,255,0.8); }
    .mf-submit:active { transform: translateY(0); }
    .mf-submit[disabled] { filter: grayscale(0.35) opacity(0.7); cursor: progress; transform: none; }

    .mf-privacy {
        margin: 16px 0 0;
        font-size: 12px; line-height: 1.55;
        color: var(--app-text-muted, #7a8aaa);
    }
    .mf-privacy-link { color: var(--app-accent, #6600FF); text-decoration: underline; }

    /* ── Tier note (arrived from a pricing card) ─────────────────────── */
    .mf-tier-note {
        margin: 0 0 18px;
        padding: 11px 14px;
        border-radius: var(--app-radius-md, 12px);
        background: rgba(102,0,255,0.06);
        border: 1px solid rgba(102,0,255,0.18);
        font-size: 13px; line-height: 1.5;
        color: var(--app-text, #1a2035);
    }
    .mf-tier-change { color: var(--app-accent, #6600FF); text-decoration: underline; margin-left: 4px; }

    /* ── Success ─────────────────────────────────────────────────────── */
    .mf-success { text-align: center; padding: 24px 8px 12px; }
    .mf-success[hidden] { display: none; }
    /* No-JS: the server 303s to #sent, and :target reveals this / hides the
       form. With JS the client toggles [hidden] instead and never navigates. */
    #sent:target ~ .mf-form { display: none; }
    #sent:target ~ .mf-success { display: block !important; }
    .mf-success-mark {
        width: 52px; height: 52px; margin: 0 auto 18px;
        display: grid; place-items: center;
        border-radius: 50%;
        color: #ffffff;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
    }
    .mf-success-title { margin: 0 0 10px; font-size: 21px; font-weight: 750; letter-spacing: -0.02em; }
    .mf-success-body { margin: 0 0 22px; font-size: 14.5px; line-height: 1.6; color: var(--app-text-2, #5a6a85); }
    .mf-success-link {
        display: inline-block;
        font-family: inherit; font-size: 14px; font-weight: 600;
        color: var(--app-accent, #6600FF);
        background: none; border: 1.5px solid var(--app-border, #dde3f0);
        border-radius: 999px; padding: 10px 22px; cursor: pointer;
        text-decoration: none;
    }
    .mf-success-link:hover { border-color: var(--app-accent, #6600FF); background: rgba(102,0,255,0.05); }

    /* ─────────────────────────────────────────────────────────────────
       §PRICING-TWO-TIER — the two cards
       ───────────────────────────────────────────────────────────────── */
    .pt-section { max-width: 1040px; margin: 0 auto; padding: 72px 24px 96px; }
    .pt-hero { text-align: center; margin-bottom: 52px; }
    .pt-eyebrow {
        margin: 0 0 14px;
        font-size: 11px; font-weight: 700; letter-spacing: 0.16em;
        text-transform: uppercase; color: var(--app-accent, #6600FF);
    }
    .pt-heading {
        margin: 0 auto 16px;
        max-width: 18ch;
        font-size: clamp(30px, 4.6vw, 46px);
        line-height: 1.1; font-weight: 800; letter-spacing: -0.03em;
        color: var(--app-text, #1a2035);
    }
    .pt-sub {
        margin: 0 auto;
        max-width: 60ch;
        font-size: 16px; line-height: 1.6;
        color: var(--app-text-2, #5a6a85);
    }
    .pt-cards {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 24px;
        align-items: stretch;
    }
    .pt-card {
        display: flex; flex-direction: column;
        background: var(--app-panel-bg, #ffffff);
        border: 1px solid var(--app-border, #dde3f0);
        border-radius: var(--app-radius-lg, 16px);
        padding: 32px 30px;
        box-shadow: 0 20px 50px -34px rgba(26,32,53,0.30);
    }
    .pt-card--featured {
        border-color: var(--app-accent, #6600FF);
        box-shadow: 0 26px 60px -30px rgba(102,0,255,0.42);
    }
    .pt-card-name {
        margin: 0 0 14px;
        font-size: 15px; font-weight: 700; letter-spacing: 0.02em;
        color: var(--app-text, #1a2035);
    }
    .pt-price { margin: 0 0 6px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }
    .pt-price-amount {
        font-size: 40px; font-weight: 800; letter-spacing: -0.035em;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
        -webkit-background-clip: text; background-clip: text;
        -webkit-text-fill-color: transparent;
        /* Fallback for the handful of engines that ignore background-clip:text —
           without it the amount renders invisible, which on a PRICING page is
           the worst possible degradation. */
        color: var(--app-accent, #6600FF);
    }
    .pt-price-unit { font-size: 13.5px; font-weight: 550; color: var(--app-text-2, #5a6a85); }
    /* ⚠ The unit is UNDECIDED (see pricingTiers.ts [BUSINESS_PRICE_UNIT]). This
       is styled to be impossible to mistake for finished copy — dashed amber,
       not the quiet grey a real unit gets. It disappears the moment the constant
       is set. */
    .pt-price-unit--unset {
        color: #8a5a00;
        background: #fff6e0;
        border: 1px dashed #d9a441;
        border-radius: 6px;
        padding: 2px 8px;
        font-size: 12px; font-weight: 600;
    }
    .pt-footnote { margin: 0 0 14px; font-size: 12px; line-height: 1.5; color: var(--app-text-muted, #7a8aaa); }
    .pt-card-summary {
        margin: 12px 0 20px;
        font-size: 14.5px; line-height: 1.55; font-weight: 600;
        color: var(--app-text, #1a2035);
    }
    .pt-features { margin: 0 0 28px; padding: 0; list-style: none; display: grid; gap: 11px; }
    .pt-feature {
        position: relative; padding-left: 27px;
        font-size: 14px; line-height: 1.5;
        color: var(--app-text-2, #5a6a85);
    }
    .pt-feature::before {
        content: '';
        position: absolute; left: 0; top: 5px;
        width: 14px; height: 8px;
        border-left: 2.2px solid var(--app-accent, #6600FF);
        border-bottom: 2.2px solid var(--app-accent, #6600FF);
        transform: rotate(-45deg); border-radius: 1px;
    }
    .pt-cta {
        margin-top: auto;
        display: block; text-align: center; text-decoration: none;
        font-size: 14.5px; font-weight: 650;
        color: var(--app-accent, #6600FF);
        background: #ffffff;
        border: 1.5px solid var(--app-accent, #6600FF);
        border-radius: 999px;
        padding: 13px 22px;
        transition: background 0.14s, color 0.14s, transform 0.14s;
    }
    .pt-cta:hover { background: rgba(102,0,255,0.06); transform: translateY(-1px); }
    .pt-cta--featured {
        color: #ffffff;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
        border-color: transparent;
        box-shadow: 0 10px 26px -12px rgba(102,0,255,0.7);
    }
    .pt-cta--featured:hover { background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%)); filter: brightness(1.06); }
    .pt-foot { margin: 40px 0 0; text-align: center; font-size: 14px; color: var(--app-text-2, #5a6a85); }
    .pt-foot-link { color: var(--app-accent, #6600FF); text-decoration: underline; }

    /* ─────────────────────────────────────────────────────────────────
       Mobile. One breakpoint each — both grids collapse to a single
       column and the page keeps a 16px gutter with no horizontal scroll.
       ───────────────────────────────────────────────────────────────── */
    @media (max-width: 900px) {
        .mf-panel { grid-template-columns: minmax(0, 1fr); gap: 36px; padding: 52px 16px 72px; }
        .pt-cards { grid-template-columns: minmax(0, 1fr); }
        .pt-section { padding: 52px 16px 72px; }
    }
    @media (max-width: 560px) {
        .mf-fields { grid-template-columns: minmax(0, 1fr); }
        .mf-card { padding: 22px 18px; }
        .pt-card { padding: 26px 20px; }
        /* 16px is the iOS threshold below which focusing an input zooms the
           viewport; the fields are 14.5px everywhere else on purpose. */
        .mf-input { font-size: 16px; }
    }

    @media (prefers-reduced-motion: reduce) {
        .mf-page *, .mf-page *::before, .mf-page *::after {
            transition-duration: 0.01ms !important;
            animation-duration: 0.01ms !important;
        }
    }

    .ov-page-inner { max-width: 760px; margin: 0 auto; padding: 72px 24px 96px; }
    .ov-hero { margin-bottom: 36px; }
    .ov-eyebrow {
        margin: 0 0 14px;
        font-size: 11px; font-weight: 700; letter-spacing: 0.16em;
        text-transform: uppercase; color: var(--app-accent, #6600FF);
    }
    .ov-heading {
        margin: 0;
        font-size: clamp(32px, 5vw, 50px);
        line-height: 1.06; font-weight: 800; letter-spacing: -0.035em;
        color: var(--app-text, #1a2035);
    }

    /* ⚠ The draft banner and the pending blocks share ONE visual language with
       the undecided price unit on /pricing (.pt-price-unit--unset): dashed
       amber. A reviewer should have to learn "this is not decided yet" once,
       not once per surface. */
    .ov-draft-banner {
        margin: 0 0 40px;
        padding: 13px 16px;
        border: 1px dashed #d9a441;
        background: #fff6e0;
        border-radius: var(--app-radius-md, 12px);
        font-size: 13.5px; line-height: 1.55;
        color: #8a5a00;
    }
    .ov-section { margin: 0 0 30px; }
    .ov-section-heading {
        margin: 0 0 12px;
        font-size: 21px; font-weight: 750; letter-spacing: -0.02em;
        color: var(--app-text, #1a2035);
    }
    .ov-section-body { margin: 0 0 14px; font-size: 15.5px; line-height: 1.65; color: var(--app-text-2, #5a6a85); }
    .ov-points { margin: 0; padding: 0; list-style: none; display: grid; gap: 11px; }
    .ov-points li {
        position: relative; padding-left: 27px;
        font-size: 15px; line-height: 1.55; color: var(--app-text-2, #5a6a85);
    }
    .ov-points li::before {
        content: '';
        position: absolute; left: 0; top: 6px;
        width: 14px; height: 8px;
        border-left: 2.2px solid var(--app-accent, #6600FF);
        border-bottom: 2.2px solid var(--app-accent, #6600FF);
        transform: rotate(-45deg); border-radius: 1px;
    }
    .ov-section--pending {
        border: 1px dashed #d9a441;
        background: #fffdf7;
        border-radius: var(--app-radius-md, 12px);
        padding: 18px 20px;
    }
    .ov-pending-chip {
        display: inline-block; margin: 0 0 10px;
        font-size: 10.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
        color: #8a5a00; background: #fff6e0;
        border: 1px solid #e3c184; border-radius: 999px; padding: 3px 10px;
    }
    .ov-pending-intent { margin: 0; font-size: 14px; line-height: 1.6; color: var(--app-text-2, #5a6a85); }

    .ov-cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 44px; }
    .ov-cta {
        display: inline-block; text-decoration: none;
        font-size: 14.5px; font-weight: 650;
        color: var(--app-accent, #6600FF);
        background: #ffffff;
        border: 1.5px solid var(--app-accent, #6600FF);
        border-radius: 999px; padding: 12px 24px;
        transition: background 0.14s, transform 0.14s;
    }
    .ov-cta:hover { background: rgba(102,0,255,0.06); transform: translateY(-1px); }
    .ov-cta--primary {
        color: #ffffff;
        background: var(--app-gradient, linear-gradient(135deg, #8B5CF6 0%, #6600FF 100%));
        border-color: transparent;
        box-shadow: 0 10px 26px -12px rgba(102,0,255,0.7);
    }
    @media (max-width: 900px) { .ov-page-inner { padding: 52px 16px 72px; } }
