/* ===================================================================
   Theme tokens. The app is DARK by default with a light toggle on the
   profile page; the landing is pinned dark and never follows the toggle.

   There is no build step and vendor/tailwind.css is a frozen pre-purged
   file containing ZERO `dark:` rules, so theming is done with custom
   properties instead: the toggle sets data-theme on <html> and every
   component that references var(--...) flips with it. Anything that must
   NOT follow the theme carries its own data-theme="dark" (these are
   attribute selectors, so they work at any depth) -- that is how #landing
   is pinned.

   --on-accent is for text on a SOLID accent fill only (buttons, avatars,
   badges). A heading using it is white-on-white in light mode; headings
   take --text. Likewise --good/--warn/--bad are for marks and fills;
   text on a tinted chip uses --good-text/--warn-text/--bad-text.
   =================================================================== */
:root,[data-theme="dark"]{
  --bg: #0b1220;
  --surface: #111c33;
  --surface-2: #0e1729;
  --surface-3: #1f2c4a;
  --line: #1f2c4a;
  --line-2: #29385c;
  --line-soft: rgba(148,163,184,.18);
  --text: #f1f5f9;
  --text-2: #cbd5e1;
  --text-3: #94a3b8;
  --text-4: #64748b;
  --accent: #60a5fa;
  --accent-2: #93c5fd;
  --accent-bg: #2563eb;
  --on-accent: #ffffff;
  --good: #34d399;
  --warn: #fbbf24;
  --bad: #f87171;
  --good-bg: rgba(52,211,153,.14);
  --warn-bg: rgba(251,191,36,.14);
  --bad-bg: rgba(248,113,113,.12);
  --good-text: #6ee7b7;
  --warn-text: #fcd34d;
  --bad-text: #fca5a5;
  --accent-soft: rgba(96,165,250,.14);
  --accent-line: rgba(96,165,250,.3);
  --fill-1: rgba(148,163,184,.16);
  --fill-2: rgba(148,163,184,.10);
  --fill-3: rgba(148,163,184,.06);
  --glass: rgba(17,28,51,.72);
  --glass-solid: rgba(10,18,32,.96);
  --shadow: rgba(0,0,0,.45);
  --logo-filter: brightness(0) invert(1);
  --logo-op: .82;
  /* Strengthened from .12 once the firm colours were sourced: three of them are
     real brand navies (Five Rings #002B4F measures 1.2:1 against this surface --
     invisible), and the ring is the thing that keeps a single brand hex legible
     in both themes. At .38 the ring alone is 3.6:1 against the surface, so the
     dot reads as an object even when its fill does not. */
  --dot-ring: rgba(255,255,255,.38);
  /* The wordmark's accent slash -- the glyph standing in for the I. On dark it
     is the ramp's step 2; on light it must darken to #1d4ed8, because #60a5fa
     on a white ground fails contrast. It is NOT --accent: that token is #2563eb
     in light mode, and the mark's own ramp tops out at #1d4ed8. */
  --logo-slash: #60a5fa;
  --danger-btn: #dc2626;
  color-scheme: dark;
}
/* Light = the palette the live site already uses: Tailwind greys + the live
   difficulty colours (.difficulty-easy #22c55e / -medium #f59e0b / -hard #ef4444
   and their -bg tints), so light mode matches production rather than inventing. */
[data-theme="light"]{
  --bg: #f9fafb;              /* gray-50  */
  --surface: #ffffff;
  --surface-2: #f9fafb;       /* gray-50  */
  --surface-3: #f3f4f6;       /* gray-100 — the live hover fill */
  --line: #e5e7eb;            /* gray-200 */
  --line-2: #d1d5db;          /* gray-300 */
  --line-soft: rgba(17,24,39,.08);
  --text: #111827;            /* gray-900 */
  --text-2: #374151;          /* gray-700 */
  --text-3: #4b5563;          /* gray-600 */
  --text-4: #6b7280;          /* gray-500 */
  --accent: #2563eb;          /* blue-600 */
  --accent-2: #1e40af;        /* blue-800 */
  --accent-bg: #2563eb;
  --on-accent: #ffffff;
  --good: #22c55e;            /* .difficulty-easy   */
  --warn: #f59e0b;            /* .difficulty-medium */
  --bad:  #ef4444;            /* .difficulty-hard   */
  --good-bg: #dcfce7;         /* .difficulty-bg-easy   */
  --warn-bg: #fef3c7;         /* .difficulty-bg-medium */
  --bad-bg:  #fee2e2;         /* .difficulty-bg-hard   */
  --good-text: #166534;
  --warn-text: #b45309;
  --bad-text:  #991b1b;
  --accent-soft: #eff6ff;     /* blue-50  */
  --accent-line: #bfdbfe;     /* blue-200 */
  --fill-1: #f3f4f6;
  --fill-2: #f9fafb;
  --fill-3: rgba(17,24,39,.03);
  --glass: #ffffff;
  --glass-solid: #ffffff;
  --shadow: rgba(17,24,39,.08);
  --logo-filter: brightness(0);
  --logo-op: .72;
  --dot-ring: rgba(17,24,39,.48);   /* see the dark note; 3.2:1 on white */
  --logo-slash: #1d4ed8;
  --danger-btn: #dc2626;
  color-scheme: light;
}

/* ===================================================================
   Control metrics.

   Every pill on the page used to derive its own height from
   font-size x whatever line-height that font happened to produce, plus
   ad-hoc vertical padding. Nothing matched: the filter bar shipped a
   36.6px search box beside a 34.9px select beside a 37.8px button, and
   the nav a 33.6px icon beside a 34.8px Pricing beside a 35.8px Log in.

   Worse, the TEXT inside them was centred by LEADING -- half the line
   box above the glyphs, half below -- which only lands on the optical
   centre when the font's ascent and descent are symmetric. They never
   are (the fallback sans is ascent-heavy, and the timer's monospace face
   is the worst offender at 1.5em of natural leading), so every label sat
   a pixel or so off its box.

   Both go away the same way: pin the height to a token, set
   line-height: 1 so the line box is the em box, and centre with flexbox
   instead of with leading. Anything that shares a row shares a token.
   =================================================================== */
:root {
  --ctl-h: 2.25rem;      /* filter bar + nav: search, selects, buttons */
  --ctl-h-sm: 2.1rem;    /* icon-only circles and avatars */
}

        /* Custom scrollbar */
        ::-webkit-scrollbar { width: 8px; height: 8px; }
        ::-webkit-scrollbar-track { background: var(--fill-3); border-radius: 10px; }
        ::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 10px; }
        ::-webkit-scrollbar-thumb:hover { background: var(--text-4); }
        /* The <body> classes that used to paint this (bg-gray-100 text-gray-800)
           were removed so the theme variables can win: a Tailwind utility class
           beats a bare element selector on specificity. */
        /* ===== Wordmark face =====
           Styrene B Bold, and ONLY for the logo wordmark -- body copy keeps the
           stack above. Note that stack: it names 'Inter', which this repo has
           never actually loaded, so the site has always rendered Helvetica. A
           declared family is not an available one; that is exactly why this
           @font-face exists and why the rollout was verified with
           document.fonts.check rather than by eye.
           font-display: swap -- the lockup is above the fold on every page, and
           a blocking logo is worse than one that reflows once. The die never
           swaps, so the reflow is confined to the wordmark's own width. */
        @font-face {
            font-family: 'StyreneB';
            src: url('../fonts/StyreneB-Bold.woff2') format('woff2');
            font-weight: 700;
            font-style: normal;
            font-display: swap;
        }
        body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text-2); }
        .difficulty-easy { color: #22c55e; }
        .difficulty-medium { color: #f59e0b; }
        .difficulty-hard { color: #ef4444; }
        .difficulty-bg-easy { background-color: #dcfce7; border-color: #22c55e; color: #166534; }
        .difficulty-bg-medium { background-color: #fef3c7; border-color: #f59e0b; color: #b45309; }
        .difficulty-bg-hard { background-color: #fee2e2; border-color: #ef4444; color: #991b1b; }
        @property --p{ syntax: '<number>'; inherits: true; initial-value: 0; }
        .pie { --p: 0; --b: 22px; --w: 150px; width: var(--w); aspect-ratio: 1; position: relative; display: inline-grid; margin: 5px; place-content: center; font-size: 25px; font-weight: bold; }
        .pie:before, .pie:after { content: ""; position: absolute; border-radius: 50%; }
        .pie.multi:before { 
            inset: 0;
            background-image: none; 
            background: conic-gradient(
                var(--c-easy, #22c55e) calc(var(--p-easy, 0) * 1%), 
                var(--c-medium, #f59e0b) 0 calc(var(--p-medium-total, 0) * 1%), 
                var(--c-hard, #ef4444) 0 calc(var(--p-hard-total, 0) * 1%),
                var(--fill-1) 0
            );
            -webkit-mask: radial-gradient(farthest-side, #0000 calc(99% - var(--b)), #000 calc(100% - var(--b))); 
            mask: radial-gradient(farthest-side, #0000 calc(99% - var(--b)), #000 calc(100% - var(--b))); 
        }
        .pie-center-text { font-size: 2rem; font-weight: 700; color: var(--text); }
        
        /* No max-width here: this inline block loads after vendor/tailwind.css,
           so a cap here would override the markup's max-w-none and squeeze
           prompts/solutions into a narrow column with scrollbars on wide math. */
        .prose { color: var(--text-2); }
        .prose p, .prose ul, .prose ol, .prose blockquote { margin-top: 1.25em; margin-bottom: 1.25em; }
        .prose h1 { font-size: 2.25rem; font-weight: 800; color: var(--text); } .prose h2 { font-size: 1.875rem; font-weight: 700; color: var(--text); } .prose h3 { font-size: 1.5rem; font-weight: 600; color: var(--text); }
        .prose ul { list-style-type: disc; padding-left: 1.75em; } .prose ol { list-style-type: decimal; padding-left: 1.75em; }
        .prose a { color: var(--accent); text-decoration: underline; }
        .prose code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background-color: var(--surface-3); padding: 0.2em 0.4em; font-size: 85%; border-radius: 0.25rem; }
        /* overflow-y is stated on every overflow-x:auto box in this file on purpose:
           leaving it off computes it to `auto`, which grows a stray second
           scrollbar the moment the content overshoots by a pixel. */
        .prose pre { background-color: var(--surface-3); padding: 1em; border-radius: 0.5rem; overflow-x: auto; overflow-y: hidden; }
        .prose blockquote { border-left: 0.25em solid var(--line); padding-left: 1em; font-style: italic; color: var(--text-3); }
        .katex { font-size: 1.1em; }
        /* Display math does not reflow: a wide line measures far more than a phone
           container (585px inside 295px at 375px wide), and the global
           html, body { overflow-x: hidden } means the cut-off half cannot be
           scrolled into view at all. Let each block scroll on its own axis.
           Horizontal only -- but `overflow-x: auto` on its own does NOT leave the
           other axis alone: CSS computes overflow-y to `auto` as well, so a tall
           equation (a 4-row matrix overshoots its line box by ~2px) grew its own
           vertical scrollbar right next to the page's, and the question view ended
           up with two bars doing different things. Turn overflow-y off explicitly
           and pad the block so the overshoot lands in the padding, not the clip.
           Thin scrollbar and bottom padding so the bar never sits on the math. */
        .prose .katex-display, #mock-prompt .katex-display {
            overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
            padding-top: .4rem; padding-bottom: .5rem; }
        /* Display equations break OUT of the 48rem prose measure to 56rem, centred
           on the column, each still scrolling on its own axis. Solutions are full of
           long derivations and they genuinely need the room; the prose around them
           must NOT get wider with them. Capped against the viewport so the breakout
           can never introduce a horizontal page scroll on a phone. */
        #view-question .qa-prompt .katex-display {
            width: min(56rem, calc(100vw - 3rem));
            margin-left: 50%;
            transform: translateX(-50%);
        }
        /* KaTeX inherits `color`, so the maths themes for free in both modes. */
        /* Ultra-wide screens: the rows stretched to full width (1,528px at 1920)
           while the question detail correctly caps at max-w-7xl. Match that cap so
           the two views line up instead of the list sprawling. 80rem = max-w-7xl. */
        #view-list { max-width: 80rem; margin-left: auto; margin-right: auto; }

        /* ===== Question bank: shell, toolbar, chips, table, rail ===== */
        .b-shell { display: grid; grid-template-columns: 1fr 17rem; gap: 1.25rem; align-items: start; }
        /* The list column is the query container for the row layout below: the rail
           opening takes 17rem off it, so the table has to answer to its own width
           rather than the viewport's -- keyed to the viewport it kept seven columns
           in a 736px box and pushed Saved off the edge. */
        .b-main { min-width: 0; container-type: inline-size; container-name: blist; }

        .t-bar { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-bottom: .75rem; }
        .t-search { flex: 1; min-width: 12rem; height: var(--ctl-h); display: flex; align-items: center; gap: .5rem; border: 1px solid var(--line-2); border-radius: .55rem; padding: 0 .7rem; background: var(--surface); color: var(--text-4); }
        .t-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; color: var(--text); font-size: .88rem; line-height: 1; font-family: inherit; }
        .t-search input::placeholder { color: var(--text-4); }
        .t-search i, .t-search svg { width: 1rem; height: 1rem; flex-shrink: 0; }
        /* Width comes from --sel-w, which sizeFilterFields() measures from the
           SELECTED option. `auto` is the fallback if the JS never runs: the old
           behaviour (sized to the longest option), which is ugly but never clips.
           The row wraps (.t-bar is flex-wrap: wrap), so pills that do not fit drop
           to a second line rather than squashing, and .t-search (flex: 1) swallows
           whatever is left on its line. NO width transition -- see the JS. */
        /* appearance: none so the chevron is OURS. The native indicator is drawn
           by the UA inside the padding box at a width we cannot read, so
           sizeFilterFields() had to guess it (SELECT_ARROW_PX) -- it guessed 26px
           against Chrome's ~20px and every select carried six pixels of dead air
           between its label and its arrow, which is what made the text read as
           left-shifted rather than centred. Now the arrow zone is a number both
           sides know: --sel-arrow, mirrored by SELECT_ARROW_PX in app.js. */
        .t-sel { --sel-arrow: 1.5rem;
            height: var(--ctl-h); box-sizing: border-box;
            border: 1px solid var(--line-2); border-radius: .55rem;
            padding: 0 var(--sel-arrow) 0 .7rem; font-size: .85rem; line-height: 1;
            color: var(--text-2); background: var(--surface); cursor: pointer; font-family: inherit;
            appearance: none; -webkit-appearance: none;
            background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                              linear-gradient(135deg, currentColor 50%, transparent 50%);
            background-position: right 1.02rem top 50%, right .7rem top 50%;
            background-size: .32rem .32rem, .32rem .32rem;
            background-repeat: no-repeat;
            width: var(--sel-w, auto); max-width: 18rem; text-overflow: ellipsis; }
        .t-icon { display: inline-flex; align-items: center; justify-content: center; gap: .4rem; height: var(--ctl-h); box-sizing: border-box; border: 1px solid var(--line-2); border-radius: .55rem; padding: 0 .7rem; background: var(--surface); color: var(--text-3); cursor: pointer; font-size: .85rem; line-height: 1; font-weight: 600; font-family: inherit; }
        .t-icon:hover { background: var(--surface-3); color: var(--text); }
        .t-icon i, .t-icon svg { width: 1rem; height: 1rem; }
        .t-actions { display: flex; gap: .5rem; }
        .t-chips { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; margin-bottom: .85rem; min-height: 1.5rem; }
        .t-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; line-height: 1; font-weight: 600; color: var(--accent-2); background: var(--accent-soft); border: 1px solid var(--accent-line); border-radius: 999px; padding: .2rem .5rem .2rem .65rem; text-transform: capitalize; }
        .t-chip button { background: none; border: none; color: var(--accent); cursor: pointer; display: flex; padding: 0; }
        .t-chip i, .t-chip svg { width: .8rem; height: .8rem; }
        .t-clear { font-size: .76rem; color: var(--text-3); background: none; border: none; cursor: pointer; text-decoration: underline; font-family: inherit; }
        .t-count { font-size: .8rem; color: var(--text-3); margin-left: auto; font-variant-numeric: tabular-nums; }

        .b1-tbl { width: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; overflow: hidden; border-collapse: collapse; }
        .b1-tbl th { text-align: left; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-3); font-weight: 700; padding: .7rem 1rem; background: var(--surface-2); border-bottom: 1px solid var(--line); white-space: nowrap; }
        .b1-tbl th.s { cursor: pointer; user-select: none; }
        .b1-tbl th.s:hover { color: var(--text); }
        .b1-tbl td { padding: .7rem 1rem; border-bottom: 1px solid var(--line); font-size: .9rem; color: var(--text-2); }
        .b1-tbl tr:last-child td { border-bottom: none; }
        .b1-tbl tbody tr:hover { background: var(--surface-2); }
        .b1-right { text-align: right; }
        .b1-msg { padding: 1.5rem !important; text-align: center; color: var(--text-3); }
        .b1-nm { font-weight: 600; color: var(--text); text-decoration: none; }
        .b1-nm:hover { color: var(--accent); }
        .b1-dot { display: inline-block; width: .5rem; height: .5rem; border-radius: 50%; margin-right: .45rem; vertical-align: middle; }
        .b1-chip { display: inline-block; font-size: .72rem; color: var(--text-3); background: var(--fill-2); border: 1px solid var(--line-2); border-radius: .3rem; padding: .1rem .4rem; white-space: nowrap; }
        /* "+11": how many firms the cell is not showing. Quieter than the firm name
           itself -- it is a footnote, not a second firm. The tooltip lists them. */
        .b1-more { margin-left: .25rem; color: var(--text-4); background: none; border-color: var(--line); cursor: help; }
        .b1-diff { white-space: nowrap; }
        .b1-ico { display: inline-flex; background: none; border: none; padding: 0; cursor: pointer; }
        .b1-ico i, .b1-ico svg { width: 1.15rem; height: 1.15rem; }
        .b1-ok { color: var(--good); }
        .b1-no { color: var(--text-4); }
        .b1-star { color: var(--warn); }
        .b1-star .b1-filled { fill: currentColor; }
        .b1-no:hover { color: var(--text-2); }

        /* Rail. Collapsing swaps the grid tracks rather than hiding a panel in
           place, so the table actually gains the width. */
        /* Sticky, for the same reason the handle is: the shell is 1,204 rows tall
           inside a scrolling <main>, so a rail pinned to the top of the grid opens
           where you cannot see it if you were reading further down. It rides the
           top of the viewport instead. max-height + overflow keeps it honest on a
           short window; on a tall one nothing scrolls and no bar appears. */
        .b-rail { min-width: 0; position: sticky; top: 0; align-self: start;
                  max-height: calc(100vh - 6rem); overflow-y: auto; scrollbar-width: thin; }
        /* Collapsed: no gap, so the handle sits FLUSH against the table instead of
           floating 1.25rem off it with empty page beside it. #view-list is capped at
           80rem, so on a wide screen "the right edge of the shell" is mid-screen --
           the handle has to read as a tab attached to the table, not as something
           pinned to a window edge that is not there. */
        .b-shell.rail-out { grid-template-columns: 1fr 2.4rem; gap: 0; }
        .b-panel { background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: 1.1rem; }
        .b-ph { display: flex; align-items: center; gap: .5rem; }
        .b-ph h4 { font-size: .95rem; font-weight: 800; color: var(--text); margin: 0; }
        .b-hide { margin-left: auto; background: none; border: none; color: var(--text-4); cursor: pointer; display: flex; padding: .15rem; border-radius: .3rem; }
        .b-hide:hover { color: var(--text); background: var(--fill-2); }
        .b-hide i, .b-hide svg { width: 1.05rem; height: 1.05rem; }
        .b-pbody { margin-top: .9rem; }
        .b-donut-wrap { display: flex; justify-content: center; align-items: center; margin-bottom: .5rem; }
        .b-mini { display: none; align-items: center; gap: .6rem; margin-top: .55rem; }
        .b-mini .p-bar { flex: 1; margin-top: 0; }
        .b-mini span { font-size: .78rem; color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
        .b-rows { margin-top: 1rem; }
        .b-drow { display: flex; justify-content: space-between; padding: .38rem 0; font-size: .88rem; color: var(--text-2); }
        .b-drow b { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text); }
        .b-total { display: flex; justify-content: space-between; padding-top: .6rem; margin-top: .3rem; border-top: 1px solid var(--line); font-weight: 800; font-size: .9rem; color: var(--text); }
        /* Sticky rather than stretched: the mockup's shell held eight rows, the real
           one holds 1,204, and align-self:stretch made the handle 57,000px tall.
           Pinned to the edge is what it is for. */
        /* Rounded on the OUTSIDE, square and border-less on the side that meets the
           table, so it reads as a tab growing out of the table's right edge. It was
           the other way round, which only made sense pinned to a window edge. */
        /* margin-top pushes it down past the toolbar so it STARTS level with the
           table's top edge, not with the search box. The toolbar wraps at narrow
           widths, so its height cannot be hard coded: --b-head-h is measured by a
           ResizeObserver in app.js. Sticky still pins it to the top of the
           scroller once you scroll past that. */
        .b-handle { align-self: start; position: sticky; top: 0; margin-top: var(--b-head-h, 0px); height: 13rem; width: 2.4rem; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .8rem; background: var(--surface); border: 1px solid var(--line); border-left: none; border-radius: 0 .7rem .7rem 0; color: var(--text-3); cursor: pointer; padding: .8rem 0; }
        .b-handle:hover { background: var(--surface-3); color: var(--text); }
        .b-handle.hidden { display: none; }
        .b-handle i, .b-handle svg { width: 1.05rem; height: 1.05rem; }
        .b-handle-tx { writing-mode: vertical-rl; font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; font-weight: 700; }
        .b-handle-n { writing-mode: vertical-rl; font-size: .8rem; font-weight: 800; color: var(--accent-2); font-variant-numeric: tabular-nums; }

        /* Below 900px the rail is a strip ABOVE the list rather than a column
           beside it, so it swaps the donut for a single bar and the handle (which
           has no edge to pin to) goes away. */
        @media (max-width: 900px) {
            .b-shell, .b-shell.rail-out { grid-template-columns: 1fr; gap: .7rem; }
            /* A strip above the list, so it scrolls away with the content rather
               than sticking over it. */
            .b-rail { order: -1; position: static; max-height: none; overflow: visible; }
            .b-rail.hidden { display: none; }
            .b-handle { display: none !important; }
            .b-panel { padding: .7rem .9rem; }
            .b-ph h4 { font-size: .85rem; }
            .b-pbody { margin-top: 0; }
            .b-donut-wrap, .b-rows { display: none; }
            .b-mini { display: flex; }
        }

        /* Mobile "Filters" toggle. The button and the collapsing only exist below
           768px; from there up every rule here is inert and the filter bar is
           exactly as it was. */
        .qp-filters-toggle { display: none; align-items: center; gap: .4rem; padding: .48rem .7rem;
            border: 1px solid var(--line-2); border-radius: .55rem; font-size: .85rem; font-weight: 600;
            color: var(--text-2); background: var(--surface); cursor: pointer; font-family: inherit; }
        .qp-filters-toggle:hover { background: var(--surface-3); color: var(--text); }
        .qp-filters-toggle i, .qp-filters-toggle svg { width: 1rem; height: 1rem; }
        .qp-filters-count { background: var(--accent-bg); color: var(--on-accent); border-radius: 999px;
            font-size: .7rem; font-weight: 700; padding: 0 .35rem; min-width: 1.1rem; text-align: center; }
        @media (max-width: 767px) {
            .qp-filters-toggle { display: inline-flex; }
            #filter-bar.qp-filters-collapsed .qp-filter-field,
            #filter-bar.qp-filters-collapsed #filter-actions { display: none; }
            .qp-filter-field { width: 100%; flex-basis: 100%; max-width: none; }
        }

        /* ===== Question detail: one continuous reading column =====
           The measure is ASYMMETRIC on purpose. Prose sits at 48rem, just above the
           60-75 characters a line that reads best; widening body text past this
           makes it harder to read, not easier, because the eye loses the line on the
           return sweep. Display equations break OUT to 56rem with their own
           horizontal scroll, because a long derivation genuinely needs the room and
           must never push the page sideways. Do not collapse the two. */
        .qa-wrap { max-width: 48rem; margin: 0 auto; padding-bottom: 1rem; }
        /* display: flex, not inline-flex. As an inline-level box it sat on an
           anonymous line box, so the browser added half-leading above and below
           it -- roughly 6px of nothing -- on top of the margin, and that dead
           band between "Back to list" and the control pill was pure wasted
           height. A block-level flex box has no line box around it. */
        .q-back { display: flex; align-items: center; gap: .4rem; color: var(--text-3); font-size: .88rem; line-height: 1; background: none; border: none; cursor: pointer; padding: 0; margin-bottom: .7rem; font-family: inherit; }
        .q-back:hover { color: var(--text); }
        .q-back i, .q-back svg { width: .9rem; height: .9rem; }
        .qa-title { font-family: ui-serif, Georgia, "Times New Roman", serif; font-size: 2rem; font-weight: 500; text-align: center; letter-spacing: -.015em; color: var(--text); }
        .qa-meta { display: flex; gap: .45rem; flex-wrap: wrap; justify-content: center; margin-top: .9rem; }
        .q-pill { font-size: .72rem; font-weight: 700; padding: .2rem .55rem; border-radius: 999px; border: 1px solid; }
        .q-tag { color: var(--accent-2); background: var(--accent-soft); border-color: var(--accent-line); }
        .q-firm { color: var(--text-2); background: var(--surface-3); border-color: var(--line); }
        /* Body text is --text-2, not --text: pure white on near-black haloes over a
           long passage. Measured dark: body 12.4:1, title 17.1:1, muted 7.0:1. */
        .qa-prompt { font-size: 1.14rem; line-height: 1.8; margin-top: 2rem; color: var(--text-2); }

        /* Floating pill toolbar */
        .qa1-bar { display: flex; align-items: center; gap: .3rem; justify-content: center; background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: .3rem .45rem; width: max-content; max-width: 100%; margin: 0 auto 1.6rem; flex-wrap: wrap; }
        .qc-btn { width: var(--ctl-h-sm); height: var(--ctl-h-sm); box-sizing: border-box; border-radius: 50%; border: none; background: none; color: var(--text-3); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
        .qc-btn:hover { background: var(--surface-3); color: var(--text); }
        .qc-btn.on { color: var(--accent); }
        .qc-btn.star.on { color: var(--warn); }
        .qc-btn.star.on .b1-filled { fill: currentColor; }
        .qc-btn i, .qc-btn svg { width: 1.05rem; height: 1.05rem; pointer-events: none; }
        /* The digits are monospace and everything else in the bar is not. A mono
           face carries far more natural leading (~1.5em) and a lower baseline
           than the sans beside it, so centring the two BOXES with align-items
           left the clock visibly low against "Start". line-height: 1 on both
           children collapses each box onto its own em box, and then centring the
           boxes really does centre the glyphs. */
        .qc-timer { display: inline-flex; align-items: center; gap: .4rem; height: var(--ctl-h-sm); box-sizing: border-box; border: 1px solid var(--line); border-radius: 999px; padding: 0 .3rem 0 .7rem; background: var(--surface-2); }
        .qc-time { display: inline-flex; align-items: center; justify-content: flex-end; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem; line-height: 1; color: var(--text-2); font-variant-numeric: tabular-nums; min-width: 2.4rem; text-align: right; }
        .qc-timer.running .qc-time { color: var(--accent); }
        .qc-go { display: inline-flex; align-items: center; height: 1.5rem; border: none; background: none; color: var(--accent); font-weight: 700; font-size: .78rem; line-height: 1; cursor: pointer; padding: 0 .45rem; font-family: inherit; border-radius: .3rem; }
        .qc-go:hover { background: var(--accent-soft); }
        .qc-reset { color: var(--text-4); }
        .qc-sep { width: 1px; height: 1.1rem; background: var(--line-2); }

        /* Hint, notes, reveal, solution */
        .qs-hint-wrap { margin-top: 1.8rem; }
        .qs-hint-btn { background: none; border: none; padding: 0; font-weight: 600; font-size: .9rem; color: var(--accent); cursor: pointer; font-family: inherit; }
        .qs-hint { margin-top: .7rem; padding: 1rem 1.1rem; background: var(--surface-3); border: 1px solid var(--line); border-radius: .55rem; color: var(--text-3); font-size: 1rem; line-height: 1.75; }
        .qs-notes { margin-top: 1.8rem; }
        .qs-lbl { display: block; font-size: .82rem; font-weight: 700; color: var(--text-3); margin-bottom: .4rem; }
        .qs-ta { width: 100%; min-height: 5.5rem; border: 1px solid var(--line-2); border-radius: .5rem; padding: .7rem .8rem; background: var(--surface); color: var(--text-2); font-size: .93rem; line-height: 1.6; font-family: inherit; resize: vertical; }
        .qs-ta:focus { outline: none; border-color: var(--accent); }
        .qs-note-hint { font-size: .78rem; color: var(--text-4); margin-top: .4rem; }
        .qs-reveal { margin-top: 2.2rem; }
        .qs-reveal-btn { width: 100%; border: 1px solid var(--line-2); background: var(--surface); color: var(--accent); font-weight: 700; font-size: .92rem; padding: .75rem; border-radius: .55rem; cursor: pointer; font-family: inherit; }
        .qs-reveal-btn:hover { background: var(--surface-3); }
        .qs-sol { margin-top: 2.4rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
        .qs-sol-h { display: flex; align-items: center; justify-content: space-between; font-size: .78rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--text-4); }
        .qs-sol-h button { background: none; border: none; color: var(--accent); font-weight: 700; font-size: .82rem; cursor: pointer; font-family: inherit; letter-spacing: 0; text-transform: none; }
        .qs-sol-body { margin-top: 1.1rem; }
        .qs-answer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 2rem; padding: .8rem 1rem; border: 1px solid var(--good); background: var(--good-bg); border-radius: .55rem; }
        .qs-ans-lbl { font-size: .8rem; font-weight: 700; color: var(--good-text); letter-spacing: .03em; text-transform: uppercase; }
        .qs-ans-val { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1.05rem; font-weight: 700; color: var(--good-text); }

        /* Sticky answer bar. It holds ONE LINE. The free-question upsell renders
           above it in normal flow and this bar is hidden, never filled -- see
           setAnswerRowLocked(). */
        .qa-bar { position: sticky; bottom: 0; margin-top: 2.4rem; background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: .7rem; display: flex; gap: .6rem; box-shadow: 0 -2px 12px var(--shadow); }
        .q-input { flex: 1; min-width: 0; border: 1px solid var(--line-2); border-radius: .55rem; padding: .6rem .8rem; color: var(--text); font-size: .93rem; background: var(--surface-2); font-family: inherit; }
        .q-input:focus { outline: none; border-color: var(--accent); }
        .q-input::placeholder { color: var(--text-4); }
        .q-btn { background: var(--accent-bg); color: var(--on-accent); border: none; border-radius: .55rem; padding: .6rem 1.2rem; font-weight: 700; font-size: .92rem; cursor: pointer; font-family: inherit; }
        .q-btn:disabled { opacity: .6; cursor: not-allowed; }

        /* Sort direction indicator on the sortable table headers. */
        .qp-sort-arrow { margin-left: .3rem; font-size: .6rem; color: var(--text-4); }

        /* ===== App nav =====
           Restated in the landing palette so landing -> app is one continuous
           surface. Tab labels are spelled out, not abbreviated. */
        .ap-nav { display: flex; align-items: center; gap: 1.2rem; padding: .7rem 1.5rem; background: var(--glass-solid); border-bottom: 1px solid var(--line); flex-shrink: 0; }
        .ap-nav .ld-logo { cursor: pointer; }
        /* One property, because the die and the wordmark are both sized in em
           off this. The colour still has to be restated: .ld-logo-name is #fff
           for the pinned-dark landing, and this nav follows the theme. */
        .ap-nav .ld-logo { font-size: 1rem; }
        .ap-nav .ld-logo-name { color: var(--text); }
        .ap-tabs { display: flex; gap: .3rem; margin: 0 auto; }
        .ap-tab { display: inline-flex; align-items: center; justify-content: center; height: var(--ctl-h); box-sizing: border-box; background: none; border: none; color: var(--text-3); font-weight: 600; font-size: .9rem; line-height: 1; padding: 0 .8rem; border-radius: .45rem; cursor: pointer; position: relative; white-space: nowrap; font-family: inherit; }
        .ap-tab:hover { color: var(--text); background: var(--fill-3); }
        .ap-tab.on { color: var(--text); }
        .ap-tab.on::after { content: ""; position: absolute; left: .8rem; right: .8rem; bottom: -.72rem; height: 2px; background: var(--accent-bg); border-radius: 2px; }
        /* BETA pill on the forum tab, and on the forum item in the account menu
           (below 640px the tab row is gone and the menu is the only way in, so the
           badge has to exist in both places or it disappears on a phone). Same pill
           language as the mockup's .fo-beta -- tiny, uppercase, fully rounded --
           but in accent tokens rather than white-on-blue, because unlike the forum
           banner this sits on the plain nav surface in both themes. */
        /* inline-flex + line-height 1, not inline-block: as an inline-block the
           badge was centred by the leading of a 1.5 line box around four
           ascender-only capitals, which put BETA about a pixel above the middle
           of its own pill. padding-top is a hair over padding-bottom because
           all-caps text has no descender -- the optical centre of a cap-height
           run sits below the geometric one. */
        .ap-beta { display: inline-flex; align-items: center; vertical-align: middle; margin-left: .4rem;
            font-size: .6rem; line-height: 1; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
            padding: .28rem .4rem .24rem; border-radius: 999px; background: var(--accent-soft);
            color: var(--accent-2); border: 1px solid var(--accent-line); }
        .acct-item .ap-beta { margin-left: .45rem; }
        .ap-right { display: flex; align-items: center; gap: .6rem; }
        .ap-av { display: inline-flex; align-items: center; justify-content: center; width: var(--ctl-h); height: var(--ctl-h); padding-top: .12em; box-sizing: border-box; border-radius: 50%; background: var(--accent-2); color: var(--on-accent); border: none; font-weight: 700; font-size: .8rem; line-height: 1; cursor: pointer; font-family: inherit; flex-shrink: 0; }
        .ap-menu-wrap { position: relative; }
        .ap-icon { display: inline-flex; align-items: center; justify-content: center; width: var(--ctl-h); height: var(--ctl-h); box-sizing: border-box; border: 1px solid var(--line-2); border-radius: .5rem; background: none; color: var(--text-3); cursor: pointer; }
        .ap-icon:hover { background: var(--fill-2); color: var(--text); }
        .ap-icon i, .ap-icon svg { width: 1rem; height: 1rem; }
        .ap-pricing { display: inline-flex; align-items: center; justify-content: center; height: var(--ctl-h); box-sizing: border-box; font-size: .85rem; line-height: 1; font-weight: 700; color: var(--on-accent); background: var(--accent-bg); padding: 0 .9rem; border: none; border-radius: .5rem; cursor: pointer; font-family: inherit; white-space: nowrap; }
        .ap-login { display: inline-flex; align-items: center; justify-content: center; height: var(--ctl-h); box-sizing: border-box; font-size: .85rem; line-height: 1; font-weight: 600; color: var(--accent-2); background: transparent; border: 1px solid var(--accent-line); padding: 0 .9rem; border-radius: .5rem; cursor: pointer; font-family: inherit; white-space: nowrap; }
        .ap-login:hover { background: var(--accent-soft); }
        /* The account area is only rendered when logged in, which is how it sat out
           the control-metrics pass and kept four different heights in one row:
           a 29.6px chip, a 29.6px Upgrade, a 25.2px plan badge and a 33.6px
           avatar. Same token, same line-height: 1, same flex centring as the rest
           of the nav -- the badge alone stays short, because it is a LABEL and
           not a control, and reads as deliberate once it is centred. */
        .ap-upgrade { display: inline-flex; align-items: center; height: var(--ctl-h); box-sizing: border-box; font-size: .75rem; line-height: 1; font-weight: 700; color: var(--accent-2); background: none; border: 1px solid var(--accent-line); padding: 0 .6rem; border-radius: .45rem; cursor: pointer; font-family: inherit; white-space: nowrap; }
        .ap-upgrade:hover { background: var(--accent-soft); }
        .ap-chip { display: inline-flex; align-items: center; gap: .35rem; height: var(--ctl-h); box-sizing: border-box; font-size: .75rem; line-height: 1; font-weight: 700; color: var(--warn-text); background: var(--warn-bg); border: 1px solid var(--warn); padding: 0 .6rem; border-radius: .45rem; cursor: pointer; font-family: inherit; white-space: nowrap; }
        .ap-chip-dot { width: .4rem; height: .4rem; border-radius: 50%; background: var(--warn); }
        /* Account dropdown. The JS re-positions it to position:fixed on open so the
           header's overflow:hidden (the mobile scroll-away) cannot clip it. */
        .acct-menu { position: absolute; right: 0; top: calc(100% + .4rem); min-width: 12rem; z-index: 50; background: var(--surface); border: 1px solid var(--line); border-radius: .5rem; box-shadow: 0 12px 30px var(--shadow); padding: .25rem 0; }
        .acct-item { display: block; width: 100%; text-align: left; padding: .5rem .9rem; font-size: .875rem; color: var(--text-2); white-space: nowrap; background: none; border: none; cursor: pointer; font-family: inherit; }
        .acct-item:hover { background: var(--fill-2); color: var(--text); }
        .acct-item.text-blue-600 { color: var(--accent); }
        .acct-item.text-red-600 { color: var(--bad); }
        .acct-item.text-amber-700 { color: var(--warn-text); }

        /* R1 gift: icon only, no label, so it reads as a utility button rather than
           an ad. The tooltip carries the pitch. Folds into the account menu below
           640px, where the "Refer a friend" item is the same action. */
        .rf-icon { width: 2.1rem; height: 2.1rem; border-radius: .5rem; border: 1px solid var(--good); background: var(--good-bg); color: var(--good-text); display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; }
        .rf-icon:hover { filter: brightness(1.15); }
        .rf-icon i, .rf-icon svg { width: 1rem; height: 1rem; }
        @media (max-width: 640px) { .rf-icon { display: none; } }

        /* R2 toast: bottom-right, non-blocking. The container carries an opaque
           background because .rf-card's --good-bg is a translucent tint: floating
           over the rail card (which is the same component) it stacked two greens
           and both texts showed through each other. */
        .rf-toast { position: fixed; right: 1.25rem; bottom: 1.25rem; width: min(21rem, calc(100vw - 2.5rem)); z-index: 60; background: var(--bg); border-radius: .7rem; box-shadow: 0 18px 40px -12px var(--shadow); }
        .rf-toast.hidden { display: none; }
        /* R3 rail card, under Progress. */
        .b-rail-refer:not(:empty) { margin-top: .8rem; }
        @media (max-width: 900px) { .b-rail-refer { display: none; } }
        /* Backfill: the purged Tailwind build ships space-y-2 but not space-y-3,
           so stacked form fields (auth modal) rendered with no gap. */
        .space-y-3 > * + * { margin-top: 0.75rem; }
        #mock-timer.urgent { color: #dc2626; border-color: #fca5a5; }

        /* --- THIS IS THE FIX --- */
        .katex-mathml {
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
        /* ----------------------- */

        /* ===== Landing page (custom CSS — the vendored Tailwind build is purged,
           so landing styles live here rather than as new utility classes) ===== */
        /* 100dvh (where supported) tracks the *actual* mobile viewport as the URL
           bar collapses/expands — plain 100vh leaves a light gap below the footer. */
        #landing { min-height: 100vh; min-height: 100dvh; background: #0b1220; color: #e5e7eb; display: flex; flex-direction: column; overflow-x: hidden; }
        /* App routes follow the theme; routeShell() pins this to the landing dark
           inline while the landing is on screen, and clears the inline style
           again when an app route takes over. */
        html { background: var(--bg); }
        html, body { overflow-x: hidden; max-width: 100%; } /* no sideways scroll on any viewport */
        /* position/z-index are load-bearing: the hero's canvas and vignette are
           position:absolute, and a positioned element paints above a
           non-positioned sibling regardless of DOM order -- so without this the
           vignette covers the whole nav. */
        .ld-nav { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.5rem; max-width: 72rem; margin: 0 auto; width: 100%; position: relative; z-index: 3; }
        /* ===== The lockup =====
           Mark plus wordmark, per design/logo-directions.html (L1). The mark is
           a bare isometric die -- no rounded-square container, no letter inside
           it. The flat-blue-square-with-a-glyph is the pattern this replaced,
           so reintroducing the container to hold the die would undo the point.

           SIZE THE DIE BY HEIGHT ONLY. Its viewBox is 120x104, not square; a
           width or a square box distorts it. Everything here is in em off
           .ld-logo's own font-size, so the two size overrides below
           (.ap-nav, .lp-logo) each need to set one property, not four.

           The old .ld-logo-badge carried padding-top: .12em to optically centre
           a lone capital Q in its box. There is no longer a letter to centre --
           on an SVG that padding is a straight .12em push off-centre, so it is
           gone along with the background and the border-radius. */
        .ld-logo { display: flex; align-items: center; gap: .62em; font-size: 1.125rem; }
        /* 1.75em, and the viewBox is TIGHT (25.36 12 69.28 80), so this is the
           height of the die itself. The deck's 1.5em is measured on the padded
           120x104 box, where 23% of the height is empty margin and the visible
           die comes out at 1.154em -- 18.5px in a nav whose buttons are 36px.
           Next to them the mark read as an afterthought, and noticeably smaller
           than the 32px badge it replaced. Same drawing, sized to the company
           it keeps.

           gap is .62em rather than the deck's .42em for the same reason: the
           padded box carried ~.37em of its own dead space on the right, so the
           deck's optical gap was nearer .79em. Removing the padding without
           reopening the gap would have jammed the wordmark against the die. */
        .ld-logo-die { height: 1.75em; width: auto; flex: none; display: block; overflow: visible; }
        .ld-logo-name { font-family: 'StyreneB', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: 700; font-size: 1em; letter-spacing: -.015em; line-height: 1; color: #fff; white-space: nowrap; }
        .ld-logo-slash { color: var(--logo-slash); }
        /* Necker flip. Fills only, so the silhouette never moves and nothing in
           the nav reflows; a transition rather than an animation, so it reverses
           cleanly when the cursor leaves halfway. The right face is deliberately
           untouched -- it is the one face both readings of the solid agree on. */
        .ld-logo-die .ld-die-top, .ld-logo-die .ld-die-left { transition: fill .45s ease; }
        .ld-logo:hover .ld-die-top { fill: #1d4ed8; }
        .ld-logo:hover .ld-die-left { fill: #bfdbfe; }
        @media (prefers-reduced-motion: reduce) {
            .ld-logo-die .ld-die-top, .ld-logo-die .ld-die-left { transition: none; }
        }
        /* nowrap: at 375px the logged-in label wrapped to three or four lines
           inside the pill. */
        .ld-login-btn { background: transparent; color: #93c5fd; border: 1px solid #1e3a8a; padding: .45rem 1rem; border-radius: .5rem; font-weight: 600; font-size: .875rem; cursor: pointer; white-space: nowrap; }
        .ld-login-btn:hover { background: #111c33; }
        /* ===== Landing hero =====
           Two-column: copy plus a translucent question card on the left, the
           reactive Brownian band in its own layout band below, firm marquee under
           that with the label BELOW the logos. A dimmed number-wall canvas sits
           behind the copy. The old centred .ld-hero / .ld-stats block is gone. */
        .rx-wrap { position: relative; }
        .rx-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
        /* Fade the wall out from the left so it never fights the headline. */
        .rx-canvas.rightmask { -webkit-mask-image: linear-gradient(to right, transparent 34%, #000 60%); mask-image: linear-gradient(to right, transparent 34%, #000 60%); }
        .d-vign2 { position: absolute; inset: 0; background: linear-gradient(100deg, #0b1220 26%, rgba(11,18,32,.72) 46%, rgba(11,18,32,.3) 78%); pointer-events: none; }
        .rx-hero { position: relative; z-index: 2; max-width: 74rem; margin: 0 auto; padding: 3.6rem 1.5rem 3rem; display: grid; grid-template-columns: 1.06fr .94fr; gap: 3.2rem; align-items: center; }
        .m-eyebrow { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .74rem; letter-spacing: .2em; text-transform: uppercase; color: #60a5fa; }
        /* The serif-italic accent in a second colour is reserved for exactly TWO
           headlines: this one and pricing's "an easy decision". Overused, the
           device stops carrying any emphasis at all. Do not add a third. */
        .m-h1 { font-family: ui-serif, Georgia, "Iowan Old Style", "Times New Roman", serif; font-weight: 500; color: #fff; line-height: 1.07; letter-spacing: -0.015em; margin-top: 1.2rem; font-size: clamp(2rem, 3.6vw, 3rem); }
        .m-h1 em { font-style: italic; color: #93c5fd; }
        .m-sub { color: #9ca3af; line-height: 1.65; margin-top: 1.2rem; font-size: 1.02rem; max-width: 29rem; }
        .rx-ctas { margin-top: 1.9rem; display: flex; gap: .85rem; flex-wrap: wrap; }
        .m-cta { background: #fff; color: #0b1220; font-weight: 700; padding: .85rem 1.8rem; border-radius: .5rem; font-size: 1rem; border: none; cursor: pointer; font-family: inherit; }
        .m-cta:hover { background: #e2e8f0; }
        .m-cta-ghost { background: transparent; color: #cbd5e1; font-weight: 600; padding: .85rem 1.7rem; border-radius: .5rem; font-size: 1rem; border: 1px solid #2f4470; cursor: pointer; font-family: inherit; }
        .m-cta-ghost:hover { border-color: #4c6ba8; color: #fff; }
        .m-ticks { color: #64748b; font-size: .84rem; display: flex; gap: 1.2rem; flex-wrap: wrap; margin-top: 1.1rem; }
        /* Direct children only: the price is a nested <span data-price>, and as a
           flex item it picked up the gap and rendered as "£20 /month". */
        .m-ticks > span { display: inline-flex; align-items: center; gap: .35rem; }
        .m-ticks i { color: #34d399; font-weight: 700; font-style: normal; }
        .rx-stage { position: relative; perspective: 1200px; }
        .rx-tilt { position: relative; transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateZ(var(--lift, 0px)); transition: transform .14s ease-out; transform-style: preserve-3d; }
        .rx-badge { position: absolute; top: -1.9rem; right: .2rem; display: inline-flex; align-items: center; gap: .4rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .71rem; color: #64748b; letter-spacing: .09em; text-transform: uppercase; }
        .rx-dot { width: .4rem; height: .4rem; border-radius: 50%; background: #34d399; animation: rx-blink 2s ease-in-out infinite; }
        @keyframes rx-blink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }

        /* Translucent question card, glassy enough that the walk shows through. */
        .m-card { position: relative; background: rgba(17,28,51,.72); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid rgba(96,165,250,.22); border-radius: .9rem; box-shadow: 0 30px 70px -22px rgba(0,0,0,.9), inset 0 1px 0 rgba(255,255,255,.05); overflow: hidden; }
        .m-card-top { display: flex; align-items: center; gap: .5rem; padding: .8rem 1.1rem; border-bottom: 1px solid rgba(96,165,250,.14); background: rgba(10,18,32,.5); }
        .m-firm { background: rgba(37,99,235,.18); border: 1px solid rgba(96,165,250,.3); color: #bfdbfe; font-size: .72rem; font-weight: 700; padding: .22rem .55rem; border-radius: .35rem; }
        .m-diff { font-size: .72rem; font-weight: 700; padding: .22rem .55rem; border-radius: .35rem; background: rgba(16,185,129,.14); color: #34d399; border: 1px solid rgba(52,211,153,.3); }
        .m-diff.m-diff-hard { background: rgba(248,113,113,.13); color: #fca5a5; border-color: rgba(248,113,113,.32); }
        .m-topic { margin-left: auto; color: #64748b; font-size: .73rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }
        .m-card-body { padding: 1.3rem 1.35rem 1.1rem; text-align: left; }
        .m-card-body h3 { color: #fff; font-size: 1.1rem; font-weight: 700; font-family: ui-serif, Georgia, serif; }
        .m-card-body p { color: #a8b3c4; font-size: .94rem; line-height: 1.6; margin-top: .55rem; }
        .m-math { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #93c5fd; background: rgba(37,99,235,.14); padding: 0 .3rem; border-radius: .25rem; }
        .m-answer { margin-top: 1.05rem; display: flex; gap: .6rem; }
        .m-input { flex: 1; background: rgba(10,18,32,.6); border: 1px solid rgba(96,165,250,.2); border-radius: .5rem; padding: .6rem .8rem; color: #64748b; font-size: .9rem; }
        .m-check { background: #2563eb; color: #fff; border: none; border-radius: .5rem; padding: .6rem 1.1rem; font-weight: 700; font-size: .9rem; cursor: pointer; font-family: inherit; }
        .m-locked { border-top: 1px solid rgba(96,165,250,.14); padding: .95rem 1.35rem; background: rgba(10,18,32,.5); display: flex; align-items: center; gap: .6rem; }
        .m-blur { flex: 1; color: #475569; font-size: .87rem; filter: blur(3.5px); user-select: none; text-align: left; }
        .m-lock { color: #93c5fd; font-size: .78rem; font-weight: 700; white-space: nowrap; }

        /* The walk gets a real layout band, so the gap above it is structural. */
        .walk-band { position: relative; width: 100%; }
        .walk-band canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
        .band-sep { height: 7.5rem; margin-top: 4rem; margin-bottom: 1rem; }
        .ld-trust-label-below { margin-top: 1.1rem; }

        @media (max-width: 900px) {
            .rx-hero { grid-template-columns: 1fr; gap: 2.5rem; padding-top: 2.4rem; }
            .rx-tilt { transform: none; }
            .rx-badge { position: static; display: flex; margin-bottom: .6rem; }
        }
        @media (max-width: 640px) {
            /* On a phone the wall is confined to the top 68% of the hero, so it
               fades out just below the "No card required" line and never fights
               the firm logos. Same brightness as desktop -- data-dim stays 1,
               because 0.8 made the phone hero visibly dimmer than the desktop
               one. If the copy ever reads as fighting the wall, dial data-dim
               back rather than touching the base alpha. */
            /* The right-hand mask exists to keep the wall off a left-hand copy
               column. On a phone the copy is full width, so the mask has nothing
               to protect and the vertical vignette does the work instead -- which
               is what the mocked phone frame does. */
            .rx-canvas[data-wall] { height: 68%; -webkit-mask-image: none; mask-image: none; }
            .d-vign2 { background: linear-gradient(180deg, rgba(11,18,32,.5) 0%, rgba(11,18,32,.72) 40%, rgba(11,18,32,.92) 68%, #0b1220 100%); }
            .band-sep { height: 5rem; margin-top: 2rem; }
        }
        /* min(240px, 100%) keeps each track from ever exceeding the container width,
           so on a narrow phone the grid cleanly collapses to one column instead of
           overflowing to the right. */
        /* ===== Landing below the fold =====
           A restyle of the four existing blocks in the hero's visual language:
           numbered mono eyebrow, serif heading, bordered dark panel. Always dark
           (#landing carries data-theme="dark"), so these are literals, matching
           the mockup, rather than theme tokens. */
        .lf { padding: 3rem 1.5rem; }
        .lf-in { max-width: 72rem; margin: 0 auto; }
        .lf-in-mid { max-width: 60rem; }
        .lf-in-narrow { max-width: 48rem; }
        .lf-eyebrow { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .72rem; letter-spacing: .2em; text-transform: uppercase; color: #60a5fa; text-align: center; }
        .lf-eyebrow-left { text-align: left; }
        .lf-h2 { font-family: ui-serif, Georgia, serif; font-weight: 500; color: #fff; font-size: clamp(1.6rem,3.2vw,2.2rem); text-align: center; margin-top: .7rem; letter-spacing: -.015em; }
        /* The second of exactly two serif-italic accents on the whole landing. */
        .lf-h2 em { font-style: italic; color: #93c5fd; }
        .lf-lead { color: #94a3b8; text-align: center; max-width: 38rem; margin: .9rem auto 0; line-height: 1.65; font-size: 1rem; }
        .lf-fine { color: #64748b; font-size: .82rem; margin-top: 1.5rem; text-align: center; }
        .lf-fine a { color: #93c5fd; text-decoration: none; }
        .lf-fine a:hover { text-decoration: underline; }

        /* 1 - feature cards */
        .lf-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem,100%),1fr)); gap: 1rem; margin-top: 2.4rem; }
        .lf-step { background: rgba(17,28,51,.72); border: 1px solid #1f2c4a; border-radius: .8rem; padding: 1.4rem 1.3rem; position: relative; }
        .lf-num { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .72rem; font-weight: 800; color: #60a5fa; letter-spacing: .14em; }
        .lf-step h3 { color: #fff; font-size: 1.02rem; font-weight: 700; margin-top: .7rem; }
        .lf-step p { color: #94a3b8; font-size: .9rem; line-height: 1.6; margin-top: .5rem; }

        /* 2 - Cambridge block, same panel treatment as everything else */
        .lf-cam { display: flex; gap: 1.4rem; align-items: center; background: rgba(17,28,51,.72); border: 1px solid #1f2c4a; border-radius: .9rem; padding: 1.6rem 1.7rem; }
        .lf-crest { flex-shrink: 0; height: 3.2rem; width: auto; }
        .lf-cam h3 { font-family: ui-serif, Georgia, serif; color: #fff; font-size: 1.25rem; font-weight: 500; margin-top: .55rem; }
        .lf-cam p { color: #94a3b8; font-size: .94rem; line-height: 1.7; margin-top: .6rem; }
        @media (max-width: 640px) { .lf-cam { flex-direction: column; align-items: flex-start; gap: .9rem; } }

        /* 3 - pricing */
        .lf-prices { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem,100%), 1fr)); gap: .9rem; margin-top: 2.2rem; }
        .lf-price { background: rgba(17,28,51,.72); border: 1px solid #1f2c4a; border-radius: .8rem; padding: 1.4rem 1.3rem; text-align: left; display: flex; flex-direction: column; position: relative; }
        .lf-price.best { border-color: rgba(96,165,250,.45); background: rgba(30,58,138,.22); }
        .lf-price .flag { position: absolute; top: -.65rem; left: 1.3rem; background: #2563eb; color: #fff; font-size: .66rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; padding: .18rem .55rem; border-radius: 999px; }
        .lf-price .nm { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: #60a5fa; font-weight: 700; }
        .lf-price .amt { font-size: 2.1rem; font-weight: 800; color: #fff; letter-spacing: -.03em; margin-top: .5rem; }
        .lf-price .amt span { font-size: .95rem; font-weight: 600; color: #94a3b8; letter-spacing: 0; }
        .lf-price .note { color: #94a3b8; font-size: .88rem; line-height: 1.6; margin: .7rem 0 1.3rem; flex: 1; }
        .lf-price-btn { width: 100%; }

        /* 4 - FAQ and the closing CTA */
        .lf-faq { display: flex; flex-direction: column; gap: .6rem; margin-top: 2rem; }
        .lf-fq { background: rgba(17,28,51,.72); border: 1px solid #1f2c4a; border-radius: .7rem; padding: 0 1.2rem; }
        .lf-fq summary { color: #fff; font-weight: 600; font-size: .98rem; line-height: 1.5; cursor: pointer; list-style: none; padding: 1.05rem 1.6rem 1.05rem 0; position: relative; }
        .lf-fq summary::-webkit-details-marker { display: none; }
        .lf-fq summary::after { content: "+"; position: absolute; right: .1rem; top: 50%; transform: translateY(-50%); color: #93c5fd; font-size: 1.3rem; font-weight: 400; }
        .lf-fq[open] summary::after { content: "\2212"; }
        .lf-fq p { color: #94a3b8; font-size: .93rem; line-height: 1.7; margin: 0; padding: 0 0 1.15rem; }
        .lf-final { text-align: center; margin-top: 3.4rem; }

        .ld-nav-link { background: none; border: none; color: #d1d5db; font-weight: 600; font-size: .92rem; cursor: pointer; white-space: nowrap; }
        .ld-nav-link:hover { color: #fff; }
        /* gap rather than the old margin-right: at 375px the logo and the nav
           rendered flush against each other as "QuantInterviewPricing". */
        .ld-nav { gap: .75rem; }
        .ld-nav-right { display: flex; align-items: center; gap: .75rem; }
        /* width: 100% is load-bearing, do not drop it. #landing is a flex
           column, and this element's auto left/right margins switch off
           align-items: stretch -- so without a definite width it falls back
           to fit-content sizing. The logo track below is width: max-content
           (~2900px), so its min-content contribution would pin this box at
           max-width on phones and the strip would be clipped by #landing's
           overflow-x: hidden. A definite width stops intrinsic sizing from
           being consulted at all. (min-width: 0 does NOT fix this: it sets a
           floor without changing the fit-content computation.) */
        .ld-trust { width: 100%; max-width: 72rem; margin: 0 auto; padding: 0 1.5rem 2.6rem; text-align: center; position: relative; z-index: 2; }
        .ld-trust-label { color: #64748b; font-size: .74rem; text-transform: uppercase; letter-spacing: .12em; font-weight: 600; }
        /* Scrolling firm-logo strip. The track holds the logo set TWICE and
           slides by exactly -50%, so the second copy lands where the first
           started and the loop is seamless. Both copies must stay identical:
           if you add a firm, add it to both, or the seam will visibly jump.
           .ld-trust-viewport is overflow:hidden with an edge fade, so logos
           enter and leave without a hard clip. */
        .ld-trust-viewport { margin-top: 1.15rem; overflow: hidden; -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent); }
        .ld-trust-track { display: flex; width: max-content; animation: ld-trust-scroll 80s linear infinite; }
        .ld-trust-viewport:hover .ld-trust-track { animation-play-state: paused; }
        .ld-trust-group { display: flex; align-items: center; flex-shrink: 0; }
        /* Colour normalisation. The sources are a mess: some are black ink on
           transparency (mastered for a white page, so invisible here), some
           are already white, some are full colour (Goldman blue, DRW teal,
           Virtu teal, GSA a gradient). brightness(0) crushes every opaque
           pixel to black regardless of its original colour, then invert(1)
           lifts it to white -- so all fifteen land on one uniform white
           silhouette. Both steps leave alpha untouched, so transparency and
           GSA's gradient fade survive. Do NOT swap this for a bare invert(1):
           that hue-shifts colour logos (Goldman's blue comes out orange). */
        .ld-trust-group img { height: 1.6rem; width: auto; display: block; margin: 0 2.1rem; filter: brightness(0) invert(1); opacity: .6; }
        /* Per-firm optical sizing: the source files bake in wildly different
           amounts of internal padding, and a stacked square lockup reads much
           heavier than a wide wordmark at the same height. These are eyeballed
           against each other on the rendered strip, not derived from the
           files. Retune by looking, not by arithmetic. */
        .ld-trust-group img[data-firm="citadel"]   { height: 1.15rem; }
        .ld-trust-group img[data-firm="optiver"]   { height: 2.1rem; }
        .ld-trust-group img[data-firm="akuna"]     { height: 1.85rem; }
        .ld-trust-group img[data-firm="imc"]       { height: 1.75rem; }
        .ld-trust-group img[data-firm="sig"]       { height: 1.5rem; }
        .ld-trust-group img[data-firm="hrt"]       { height: 1.5rem; }
        .ld-trust-group img[data-firm="fiverings"] { height: 1.2rem; }
        /* Goldman is a two-line stacked lockup, so at a shared height each
           line is only about half of it. Sized up to match the single-line
           wordmarks in optical weight. */
        .ld-trust-group img[data-firm="goldman"]   { height: 1.75rem; }
        .ld-trust-group img[data-firm="drw"]       { height: 1.35rem; }
        .ld-trust-group img[data-firm="virtu"]     { height: 1.5rem; }
        .ld-trust-group img[data-firm="jump"]      { height: 1.45rem; }
        .ld-trust-group img[data-firm="gsa"]       { height: 1.45rem; }
        @keyframes ld-trust-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
        /* Reduced motion: park the animation and hand the user a normal
           scrollable strip instead, so the content stays reachable. */
        @media (prefers-reduced-motion: reduce) {
            .ld-trust-viewport { overflow-x: auto; overflow-y: hidden; }
            .ld-trust-track { animation: none; }
        }
        /* .ld-cam / .ld-partners / .ld-faq lived here. All three are restyled
           above as .lf-cam and .lf-faq; the partner CTA moved to the footer. */

        .ld-footer { border-top: 1px solid #1f2c4a; padding: 1.5rem; text-align: center; font-size: .85rem; color: #6b7280; margin-top: auto; }
        .ld-footer a { color: #93c5fd; text-decoration: none; }
        .ld-footer a:hover { text-decoration: underline; }
        .ld-footer-partner { display: block; margin-top: .5rem; color: #64748b; }

        /* ===== Mobile pass ===== */
        .table-scroll { width: 100%; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
        @media (max-width: 767px) {
            #app > header { flex-wrap: wrap; row-gap: .5rem; padding-left: 1rem; padding-right: 1rem; }
            /* overflow-y: hidden is load-bearing. Without it the computed value is
               `auto` and the tab row grew a stubby vertical scrollbar of its own
               inside the navbar, a few pixels from the page's. */
            .ap-tabs { order: 3; width: 100%; margin: 0; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; padding-bottom: .15rem; }
            .ap-tab { padding: 0 .7rem; font-size: .82rem; }
            .ap-tab.on::after { bottom: -.1rem; }
            /* Scroll-away header: collapse the (multi-row) header when reading down
               the list, reveal it again on any scroll up. Mobile only. */
            #app > header { transition: max-height .25s ease, padding .25s ease, opacity .2s ease; max-height: 220px; overflow: hidden; }
            #app > header.header-collapsed { max-height: 0; padding-top: 0; padding-bottom: 0; opacity: 0; border-bottom-width: 0; }
            #app main { padding: .75rem; }
            .qa-title { font-size: 1.5rem; }
            .pie { --w: 120px; }
        }

        /* Seven columns cannot survive a narrow column, and horizontally scrolling
           the main list is worse than re-flowing it. Below 900px OF THE LIST COLUMN
           -- a container query, not a viewport one, because opening the rail takes
           17rem off the list and the seven columns need ~875px -- each row becomes
           a card WITHOUT touching the JS, the markup or its event delegation: the
           <tr> becomes a wrapping flex row and `order` puts the cells where the
           mocked card wants them.
             line 1: [status]  question name  [saved]
             line 2: topic · difficulty · firm
           Tag is the one that goes -- four chips do not fit a 375px line, and the
           filters remain the way to reach it. */
        @container blist (max-width: 900px) {
            #view-list .table-scroll { overflow-x: visible; }
            #view-list table, #view-list tbody { display: block; }
            #view-list thead { display: none; }
            #view-list .b1-tbl { border: none; background: none; }
            #view-list tbody tr {
                display: flex;
                flex-wrap: wrap;
                align-items: center;
                gap: .4rem;
                padding: .75rem .85rem;
                margin-bottom: .5rem;
                background: var(--surface);
                border: 1px solid var(--line);
                border-radius: .6rem;
            }
            #view-list tbody tr:hover { background: var(--surface); }
            #view-list tbody td { padding: 0 !important; border: none; white-space: normal; }
            #view-list tbody td.toggle-solved { order: 1; }
            #view-list tbody td:nth-child(2) { order: 2; flex: 1; min-width: 0; }
            #view-list tbody td:nth-child(2) a { font-size: .95rem; font-weight: 600; }
            #view-list tbody td:nth-child(7) { order: 3; text-align: right; }
            /* Zero-height full-width break, so the metadata always starts a new
               line instead of trailing after the bookmark star. */
            #view-list tbody tr::after { content: ""; order: 4; flex-basis: 100%; height: 0; }
            #view-list tbody td:nth-child(3) { order: 5; }
            #view-list tbody td:nth-child(5) { order: 6; font-size: .78rem; color: var(--text-3); }
            #view-list tbody td:nth-child(6) { order: 7; }
            #view-list tbody td:nth-child(4) { display: none; }
            #view-list tbody .b1-ico i, #view-list tbody .b1-ico svg { width: 1.3rem; height: 1.3rem; }
        }

        /* Bottom tab bar. Below 640px it replaces the nav's tab row; #app is a
           flex column at viewport height, so the bar is simply its last child and
           needs no fixed positioning. */
        .mob-bottom { display: none; }
        /* ===== App lockup while the tab row is inline =====
           The wordmark hides HERE, and only here. The rule this replaces hid it
           at <=767px, which is exactly backwards: at 767 and below .ap-tabs has
           order 3 and width 100%, so it sits on its own row and the top row
           holds nothing but the lockup and the account area -- measured at
           700px there are 379 spare pixels and the wordmark was being dropped
           for no reason at all.

           The band that is actually tight is the one just ABOVE that, where the
           tabs come back inline and the top row has to fit lockup + tabs +
           account area. Measured at 768px the nav overflowed by 50px.

           1100 is the upper bound because the account area is not a fixed
           width: a free, unverified user carries Upgrade, a Verify email chip,
           the referral icon AND the avatar, which measures 270px against the
           91px a paying verified user sees. That worst case needs 1021px with
           the wordmark shown, so anything below about 1100 has no honest margin.
           Do not narrow this band by testing while logged in as a paid user --
           that is the widest the right-hand side ever gets, not the norm. */
        @media (min-width: 768px) and (max-width: 1099px) {
            .ap-nav .ld-logo-name { display: none; }
        }

        /* ===== Landing lockup on a phone =====
           The all-caps wordmark is 44px WIDER than the "QuantInterview" it
           replaced (caps have no lowercase to be narrow in), and the landing
           nav had no slack: at 375px the lockup pushed "Log in" clean off the
           right edge. The nav's 1.5rem side padding is not available to take
           it back -- that padding aligns the logo with the hero headline
           underneath, and trimming it would visibly break that alignment.

           So the lockup steps down instead, and below 375px the wordmark drops
           out entirely and the die stands alone. Dropping it is only an option
           because the die IS the mark now; the old bare "Q" badge would have
           been an unreadable blue square on its own. The app nav already does
           the same thing between 641 and 767px.

           375px is the cut because it is the narrowest mainstream phone, and
           measured there the lockup still clears "Log in" by 21px. It is worth
           holding the name that low: 360 would technically fit too, but only by
           6px, which is no margin at all for a font metric moving under us. */
        @media (max-width: 640px) {
            .ld-nav .ld-logo { font-size: .85rem; }
        }
        @media (max-width: 374px) {
            .ld-nav .ld-logo-name { display: none; }
        }
        @media (max-width: 640px) {
            .mob-bottom { display: flex; background: var(--glass-solid); border-top: 1px solid var(--line); flex-shrink: 0; padding-bottom: env(safe-area-inset-bottom, 0); }
            /* Five tabs at 375px is 75px each; min-width: 0 stops a label from
               forcing the row wider than the screen. */
            .mob-bottom button { flex: 1; min-width: 0; background: none; border: none; color: var(--text-4); font-size: .66rem; font-weight: 600; padding: .55rem 0 .6rem; display: flex; flex-direction: column; align-items: center; gap: .2rem; cursor: pointer; font-family: inherit; }
            .mob-bottom button.on { color: var(--accent-2); }
            .mob-bottom i, .mob-bottom svg { width: 1.15rem; height: 1.15rem; }
            /* The tab row moves to the bottom bar; the top bar keeps the logo, the
               referral icon and the avatar. */
            .ap-tabs { display: none; }
            .ap-nav { padding: .6rem 1rem; gap: .6rem; }
            /* font-size goes on .ld-logo, not on the name: the die is 1.5em of
               the LOCKUP's size, so shrinking only the wordmark would leave a
               full-size die beside a smaller word. */
            .ap-nav .ld-logo { font-size: .95rem; }
            .ap-right { margin-left: auto; gap: .45rem; }
            /* The "Me" tab in the bottom bar is the profile entry point on a
               phone, so this duplicate would only make the header wrap. */
            #btn-profile-anon { display: none; }
            .ap-pricing, .ap-login { padding: 0 .7rem; font-size: .8rem; }
            /* The scroll-away header would hide the only remaining nav. */
            #app > header.header-collapsed { max-height: 220px; padding-top: .6rem; padding-bottom: .6rem; opacity: 1; }
            .qa-wrap { padding-bottom: 0; }
            .rf-toast { right: .75rem; bottom: .75rem; }
        }

        /* ---- Paywall UI (hand-written: the vendored Tailwind build is purged) ---- */
        .qp-free-badge { display: inline-block; margin-left: .4rem; padding: .05rem .45rem; border-radius: .375rem; background: var(--good-bg); color: var(--good-text); font-size: .68rem; font-weight: 700; vertical-align: middle; letter-spacing: .02em; }
        /* Free to open, but the solution is paid: amber so it reads as "partly". */
        .qp-freeq-badge { display: inline-block; margin-left: .4rem; padding: .05rem .45rem; border-radius: .375rem; background: var(--warn-bg); color: var(--warn-text); font-size: .68rem; font-weight: 700; vertical-align: middle; letter-spacing: .02em; white-space: nowrap; }
        .qp-lock { width: .9rem; height: .9rem; display: inline-block; margin-left: .35rem; color: var(--text-4); vertical-align: -2px; }
        /* .qp-pricing-box and the .qp-plan-* card rules lived here. The pricing
           modal and paywallHtml() now share one .pw-plan component, so they can
           no longer drift apart. */
        /* PRO / LIFETIME. All caps, so there is no descender to fill the space under
           the glyphs and leading-based centring floats it high -- the same defect
           the nav's BETA badge had. Fixed height on a round number, flex centred,
           with the cap-height nudge in em. */
        .qp-plan-badge { display: inline-flex; align-items: center; height: 1.5rem; box-sizing: border-box; padding: .1em .5rem 0; border-radius: .375rem; background: var(--accent-soft); color: var(--accent-2); border: 1px solid var(--accent-line); font-size: .7rem; line-height: 1; font-weight: 700; white-space: nowrap; }
        /* Inline paywall (locked question / locked solution): the plans render
           right inside the reading column, no modal detour. The pricing modal
           reuses the same .pw-plan cards, so they are defined once. */
        .pw-box { background: var(--accent-soft); border: 1px solid var(--accent-line); border-radius: .6rem; padding: 1.5rem; text-align: center; margin-top: 2.2rem; }
        .pw-head { font-weight: 600; color: var(--text); margin-bottom: .25rem; }
        .pw-sub { font-size: .875rem; color: var(--text-3); margin-bottom: 1.1rem; }
        /* 26rem cap plus the 560px drop to one column is what makes this fit the
           narrow reading column on a phone. */
        .pw-plans { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; max-width: 26rem; margin: 0 auto; text-align: left; }
        @media (max-width: 560px) { .pw-plans { grid-template-columns: 1fr; } }
        .pw-plans.single { grid-template-columns: 1fr; }
        .pw-plan { position: relative; border: 1px solid var(--line); border-radius: .6rem; padding: 1rem; display: flex; flex-direction: column; background: var(--surface); }
        .pw-plan.best { border-color: var(--accent-bg); box-shadow: 0 0 0 1px var(--accent-bg); }
        .pw-flag { position: absolute; top: -.65rem; right: .75rem; background: var(--accent-bg); color: var(--on-accent); font-size: .65rem; font-weight: 700; padding: .1rem .5rem; border-radius: .375rem; letter-spacing: .03em; }
        .pw-nm { font-weight: 600; color: var(--text-2); font-size: .9rem; }
        .pw-pr { font-size: 1.6rem; font-weight: 800; color: var(--text); margin: .15rem 0; letter-spacing: -.02em; }
        .pw-pr span { font-size: .85rem; font-weight: 500; color: var(--text-4); }
        .pw-sb { font-size: .78rem; color: var(--text-4); margin-bottom: .75rem; flex: 1; }
        .pw-buy { width: 100%; background: var(--accent-bg); color: var(--on-accent); padding: .5rem 1rem; border: none; border-radius: .5rem; font-weight: 600; font-size: .85rem; cursor: pointer; font-family: inherit; }
        .pw-buy:disabled { opacity: .6; cursor: not-allowed; }
        .pw-fine { font-size: .75rem; color: var(--text-4); margin-top: .8rem; }
        #q-submit-input:disabled { background: var(--surface-3); color: var(--text-4); cursor: not-allowed; }
        /* ===== Explore: playlist cards =====
           This page is the ONLY home for per-topic and per-firm progress browsing
           (the bank rail is the live counter, the profile is the account), so
           nothing here may be cut. Every card is progress-against-the-bank rather
           than a raw count. */
        .qp-exp { max-width: 72rem; margin: 0 auto; }
        .x-head { margin-bottom: 1.75rem; }
        .x-head h2 { font-size: 1.5rem; font-weight: 800; color: var(--text); letter-spacing: -.02em; }
        .x-head p { color: var(--text-3); font-size: .92rem; margin-top: .35rem; max-width: 42rem; line-height: 1.55; }
        .x-block { margin-bottom: 2rem; }
        .x-bh { display: flex; align-items: baseline; gap: .65rem; flex-wrap: wrap; margin-bottom: .85rem; }
        .x-bh h3 { font-size: 1.05rem; font-weight: 700; color: var(--text); }
        .x-bh p { color: var(--text-3); font-size: .82rem; }
        .x-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(268px, 100%), 1fr)); gap: .7rem; }
        .x-more { display: inline-flex; align-items: center; gap: .3rem; margin-top: .75rem; padding: .4rem .8rem; font-size: .82rem; font-weight: 600; color: var(--accent-2); background: none; border: 1px solid var(--accent-line); border-radius: .5rem; cursor: pointer; font-family: inherit; }
        .x-more:hover { background: var(--accent-soft); }
        .x-more i, .x-more svg { width: 1rem; height: 1rem; }
        .x6-card { display: flex; align-items: center; gap: .8rem; background: var(--surface); border: 1px solid var(--line); border-radius: .65rem; padding: .8rem .95rem; cursor: pointer; width: 100%; color: var(--text); text-align: left; transition: border-color .14s, transform .14s, background .14s; }
        .x6-card:hover { border-color: var(--accent); background: var(--surface-3); transform: translateY(-2px); }
        /* Fixed box either way, so the 23 firms on a monogram line up with the 15
           that have a logo file. */
        .x6-mark { flex-shrink: 0; width: 2.5rem; height: 2rem; display: flex; align-items: center; justify-content: center; }
        .x6-mark img { max-width: 100%; max-height: 1.25rem; width: auto; filter: var(--logo-filter); opacity: var(--logo-op); }
        .x6-mono { font-size: .68rem; font-weight: 800; letter-spacing: .04em; color: var(--text-3); border: 1px solid var(--line-2); border-radius: .35rem; padding: .2rem .35rem; }
        .x6-mid { flex: 1; min-width: 0; }
        .x6-top { display: flex; align-items: center; gap: .45rem; }
        /* The inset ring is what makes one brand hex legible in BOTH themes.
           Without it the navy firms measure 1.6:1 on white. */
        .x6-dot { width: .5rem; height: .5rem; border-radius: 50%; flex-shrink: 0; box-shadow: inset 0 0 0 1px var(--dot-ring); }
        /* capitalize, not uppercase: the API stores topics and difficulties in
           lower case but firm names cased correctly, and capitalize leaves an
           already-capital letter alone (SIG stays SIG). */
        .x6-nm { font-weight: 600; font-size: .9rem; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-transform: capitalize; }
        .x6-bar { display: block; height: 5px; border-radius: 999px; background: var(--fill-1); margin-top: .5rem; overflow: hidden; }
        .x6-bar i { display: block; height: 100%; border-radius: 999px; }
        .x6-ct { flex-shrink: 0; text-align: right; font-size: .72rem; color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
        .x6-ct b { display: block; font-size: .95rem; font-weight: 700; color: var(--text); }
        .x6-done b { color: var(--good); }
        @media (max-width: 767px) { .x-grid { grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); } }

        /* ===== Shared: progress bar (profile, Explore, bank rail) ===== */
        .p-bar { height: 6px; border-radius: 999px; background: var(--fill-1); overflow: hidden; margin-top: .35rem; }
        .p-bar i { display: block; height: 100%; border-radius: 999px; background: var(--accent); }

        /* ===== Refer a friend card (profile, bank rail, mock results, toast) ===== */
        .rf-card { border: 1px solid var(--good); background: var(--good-bg); border-radius: .7rem; padding: 1rem 1.1rem; display: flex; align-items: center; gap: .9rem; }
        /* Ink, not --on-accent. The mockup pairs white with --good here, but white
           on #34d399 measures 1.9:1 (and 2.3:1 on the light #22c55e), which fails
           both as text and as a graphical object. This is the same trap the brief
           called out for white on --bad, so the same fix applies: near-black ink
           clears in both themes (9.8:1 dark, 6.9:1 light). */
        .rf-ic { flex-shrink: 0; width: 2.2rem; height: 2.2rem; border-radius: .55rem; background: var(--good); color: #0b1220; display: flex; align-items: center; justify-content: center; }
        .rf-ic i, .rf-ic svg { width: 1.1rem; height: 1.1rem; }
        .rf-tx { flex: 1; min-width: 0; }
        .rf-tx b { display: block; font-size: .92rem; color: var(--good-text); }
        .rf-tx span { font-size: .82rem; color: var(--good-text); }
        .rf-go { background: var(--good); color: #0b1220; border: none; border-radius: .5rem; padding: .5rem .9rem; font-weight: 700; font-size: .84rem; cursor: pointer; white-space: nowrap; font-family: inherit; }
        .rf-x { background: none; border: none; color: var(--good); cursor: pointer; display: flex; padding: .2rem; }
        .rf-card.col { flex-direction: column; align-items: flex-start; gap: .7rem; position: relative; }
        .rf-card.col .rf-x { position: absolute; top: .5rem; right: .5rem; }
        .rf-card.col .rf-go { width: 100%; }
        .rf-cap { font-size: .78rem; color: var(--text-4); margin-top: .5rem; font-style: italic; }

        /* ===== Profile page (#/profile) ===== */
        .pf { background: var(--bg); color: var(--text); padding: 2rem 1.75rem 2.5rem; }
        .pf-in { max-width: 60rem; margin: 0 auto; }
        .pf-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
        .pf-av { width: 3.4rem; height: 3.4rem; border-radius: 50%; background: var(--accent-bg); color: var(--on-accent); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.3rem; flex-shrink: 0; }
        .pf-head h2 { font-size: 1.35rem; font-weight: 800; color: var(--text); }
        .pf-head p { color: var(--text-3); font-size: .88rem; margin-top: .15rem; }
        .pf-plan { margin-left: auto; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
        .pf-badge { font-size: .74rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--accent-2); background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 999px; padding: .28rem .7rem; }
        .pf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(13rem,100%),1fr)); gap: .8rem; margin-top: 1.8rem; }
        .pf-stat { background: var(--glass); border: 1px solid var(--line); border-radius: .7rem; padding: 1rem 1.1rem; }
        .pf-stat b { display: block; font-size: 1.7rem; font-weight: 800; color: var(--text); letter-spacing: -.02em; line-height: 1.15; font-variant-numeric: tabular-nums; }
        .pf-stat span { font-size: .8rem; color: var(--text-3); }
        .pf-cols { display: grid; grid-template-columns: 1.25fr .75fr; gap: 1rem; margin-top: 1rem; align-items: start; }
        @media (max-width: 860px) { .pf-cols { grid-template-columns: 1fr; } }
        .pf-panel { background: var(--glass); border: 1px solid var(--line); border-radius: .7rem; padding: 1.1rem 1.2rem; }
        .pf-panel + .pf-panel { margin-top: 1rem; }
        .pf-panel h3 { font-size: .95rem; font-weight: 800; color: var(--text); margin-bottom: .9rem; }
        .pf-row { display: flex; align-items: center; gap: .7rem; padding: .42rem 0; }
        .pf-row .nm { font-size: .88rem; color: var(--text-2); min-width: 8.5rem; }
        .pf-row .p-bar { flex: 1; margin-top: 0; }
        .pf-row .ct { font-size: .8rem; color: var(--text-3); font-variant-numeric: tabular-nums; min-width: 4.6rem; text-align: right; }
        .pf-empty { font-size: .86rem; color: var(--text-3); line-height: 1.6; }
        .pf-danger { border-color: var(--bad); }
        .pf-danger h3 { color: var(--bad); }
        .pf-danger p { color: var(--text-3); font-size: .85rem; line-height: 1.6; margin-bottom: .9rem; }
        .pf-del { background: none; border: 1px solid var(--bad); color: var(--bad); border-radius: .5rem; padding: .55rem .9rem; font-weight: 700; font-size: .85rem; cursor: pointer; display: inline-flex; align-items: center; gap: .45rem; }
        .pf-del:hover { background: var(--bad-bg); }
        .pf-link { background: none; border: 1px solid var(--line); color: var(--accent-2); border-radius: .5rem; padding: .5rem .85rem; font-weight: 600; font-size: .84rem; cursor: pointer; }
        .pf-link:hover { background: var(--fill-2); }
        /* Appearance: not in the mockup (its switcher was mockup-only chrome), so
           this reuses the mockup's own segmented-control language (.b-toggle). */
        .pf-seg { display: flex; background: var(--fill-2); border: 1px solid var(--line); border-radius: .5rem; padding: .2rem; max-width: 18rem; }
        .pf-seg button { flex: 1; text-align: center; font-size: .82rem; font-weight: 600; padding: .4rem; border-radius: .38rem; color: var(--text-4); background: none; border: none; cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; }
        .pf-seg button.on { background: var(--accent-soft); color: var(--text); }
        .pf-seg button i, .pf-seg button svg { width: .95rem; height: .95rem; }
        .pf-note { font-size: .8rem; color: var(--text-4); margin-top: .6rem; line-height: 1.55; }

        /* app.css loads AFTER vendor/tailwind.css, so any `display` we set in a
           hand-written class beats Tailwind's .hidden purely on source order --
           the element stays visible when the JS toggles .hidden onto it. Every
           hand-written class that sets `display` AND is ever toggled needs a guard
           here. Add to this list whenever you add such a class. */
        .qa-bar.hidden,
        .b-donut-wrap.hidden,
        .es-row.hidden,
        .dl-back.hidden,
        .dl-row .dl-cancel.hidden,
        .qs-answer.hidden,
        .rf-card.hidden,
        .b-handle.hidden,
        .b-mini.hidden,
        .t-chips.hidden,
        .pf-seg.hidden,
        .pw-plans.hidden,
        .pw-plan.hidden,
        .pw-box.hidden { display: none; }

        /* ===== Mock interview =====
           Ported, not redesigned: same controls, defaults and copy as live. */
        .mk-wrap { max-width: 48rem; margin: 0 auto; }
        .mk-panel { background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: 1.6rem 1.7rem; }
        .mk-h { display: flex; align-items: center; gap: .7rem; }
        .mk-h i, .mk-h svg { width: 1.5rem; height: 1.5rem; color: var(--accent); }
        .mk-h h2 { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em; color: var(--text); }
        .mk-lead { color: var(--text-3); margin: .35rem 0 1.3rem; font-size: .95rem; }
        .mk-note { display: flex; gap: .7rem; background: var(--accent-soft); border: 1px solid var(--accent-line); border-radius: .55rem; padding: .85rem 1rem; font-size: .87rem; color: var(--text-2); margin-bottom: 1.4rem; }
        .mk-note.hidden { display: none; }
        .mk-note i, .mk-note svg { flex-shrink: 0; width: 1.15rem; height: 1.15rem; color: var(--accent); }
        .mk-link { background: none; border: none; padding: 0; color: var(--accent); font-weight: 700; text-decoration: underline; cursor: pointer; font-family: inherit; font-size: inherit; }
        .mk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1.4rem; }
        @media (max-width: 640px) { .mk-grid { grid-template-columns: 1fr; } }
        .mk-lbl { display: block; font-size: .82rem; font-weight: 600; color: var(--text-3); margin-bottom: .3rem; }
        .mk-sel { width: 100%; border: 1px solid var(--line-2); border-radius: .5rem; padding: .5rem .7rem; font-size: .9rem; color: var(--text-2); background: var(--surface-2); font-family: inherit; }
        .mk-start { display: inline-flex; align-items: center; gap: .5rem; background: var(--accent-bg); color: var(--on-accent); border: none; border-radius: .55rem; padding: .65rem 1.3rem; font-weight: 700; font-size: .95rem; cursor: pointer; font-family: inherit; }
        .mk-start i, .mk-start svg { width: 1rem; height: 1rem; }
        .mk-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: .9rem; gap: .6rem; flex-wrap: wrap; }
        .mk-prog { font-size: .8rem; font-weight: 700; color: var(--text-4); text-transform: uppercase; letter-spacing: .09em; }
        .mk-bar-r { display: flex; align-items: center; gap: 1rem; }
        .mk-timer { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1.2rem; font-weight: 700; color: var(--text); background: var(--surface); border: 1px solid var(--line); border-radius: .5rem; padding: .2rem .7rem; }
        #mock-timer.urgent { color: var(--bad); border-color: var(--bad); }
        .mk-quit { background: none; border: none; color: var(--text-4); font-size: .85rem; cursor: pointer; font-family: inherit; }
        .mk-quit:hover { color: var(--bad); }
        .mk-tags { display: flex; gap: .45rem; flex-wrap: wrap; margin-bottom: .9rem; }
        .mk-q { font-size: 1.25rem; font-weight: 700; letter-spacing: -.015em; color: var(--text); }
        .mk-prompt { margin-top: 1rem !important; font-size: 1.05rem; }
        .mk-answer { display: flex; gap: .6rem; margin-top: 1.8rem; flex-wrap: wrap; }
        .mk-skip { border: 1px solid var(--line-2); background: none; color: var(--text-2); border-radius: .55rem; padding: .6rem 1.1rem; font-weight: 600; font-size: .9rem; cursor: pointer; font-family: inherit; }
        .mk-fine { font-size: .78rem; color: var(--text-4); margin-top: .8rem; }
        .mk-score-panel { text-align: center; }
        .mk-eyebrow { font-size: .78rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-4); }
        .mk-score { font-size: 3.2rem; font-weight: 800; letter-spacing: -.03em; margin: .3rem 0 .1rem; color: var(--text); }
        .mk-score-sub { color: var(--text-3); font-size: .9rem; margin-bottom: 1.4rem; }
        .mk-acts { display: flex; gap: .7rem; justify-content: center; flex-wrap: wrap; }
        .mk-ghost { display: inline-flex; align-items: center; gap: .45rem; border: 1px solid var(--line-2); background: none; color: var(--text-2); border-radius: .55rem; padding: .55rem 1.1rem; font-weight: 600; font-size: .88rem; cursor: pointer; font-family: inherit; }
        .mk-ghost i, .mk-ghost svg { width: 1rem; height: 1rem; }
        .mk-list { margin-top: .9rem; }
        .mk-row-wrap { margin-bottom: .45rem; }
        .mk-row { display: flex; align-items: center; gap: .8rem; background: var(--surface); border: 1px solid var(--line); border-radius: .55rem; padding: .7rem .9rem; flex-wrap: wrap; }
        .mk-mark { flex-shrink: 0; width: 1.4rem; height: 1.4rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: .8rem; font-weight: 800; }
        .mk-mark.ok { background: var(--good-bg); color: var(--good-text); }
        .mk-mark.no { background: var(--bad-bg); color: var(--bad-text); }
        .mk-mark.skip { background: var(--fill-1); color: var(--text-4); }
        .mk-rn { flex: 1; min-width: 8rem; display: flex; flex-direction: column; }
        .mk-rn button { background: none; border: none; padding: 0; text-align: left; font-size: .92rem; font-weight: 700; color: var(--text); cursor: pointer; font-family: inherit; }
        .mk-rn button:hover { color: var(--accent); }
        .mk-rn span { font-size: .78rem; color: var(--text-4); }
        .mk-ra { font-size: .8rem; color: var(--text-3); }
        .mk-ra code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--fill-1); border-radius: .25rem; padding: 0 .3rem; }
        .mk-solbtn { flex-shrink: 0; background: none; border: none; color: var(--accent); font-weight: 700; font-size: .82rem; cursor: pointer; font-family: inherit; }
        .mk-sol { margin: .5rem 0 0; padding: .8rem .9rem; background: var(--surface-2); border: 1px solid var(--line); border-radius: .55rem; font-size: .88rem; }
        #mock-referral:not(:empty) { margin-top: 1.4rem; }

        /* ===== Cambridge forum =====
           The banner keeps its blue gradient in BOTH themes: it is the one
           element here that should read as a badge rather than a surface, so it
           is deliberately not tokenised. */
        .fo-banner { background: linear-gradient(100deg, #1d4ed8, #1e3a8a); border-radius: .7rem; padding: 1.4rem 1.5rem; color: #fff; margin-bottom: 1rem; }
        .fo-bh { display: flex; align-items: center; gap: .55rem; flex-wrap: wrap; }
        .fo-bh h2 { font-size: 1.2rem; font-weight: 800; }
        .fo-bh i, .fo-bh svg { width: 1.15rem; height: 1.15rem; }
        .fo-beta { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; background: rgba(255,255,255,.22); padding: .1rem .5rem; border-radius: 999px; }
        .fo-blead { font-size: .87rem; color: #dbeafe; margin-top: .45rem; max-width: 36rem; line-height: 1.6; }
        .fo-blead code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #fff; font-weight: 600; }
        .fo-brow { display: flex; gap: 1.3rem; margin-top: .9rem; font-size: .82rem; color: #dbeafe; flex-wrap: wrap; }
        .fo-brow span { display: inline-flex; align-items: center; gap: .35rem; }
        .fo-brow i, .fo-brow svg { width: .95rem; height: .95rem; }
        .fo-compose { background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: .9rem; margin-bottom: 1rem; }
        .fo-in, .fo-ta { width: 100%; border: 1px solid var(--line-2); border-radius: .5rem; padding: .55rem .7rem; font-size: .9rem; color: var(--text); background: var(--surface-2); margin-bottom: .5rem; font-family: inherit; }
        .fo-in::placeholder, .fo-ta::placeholder { color: var(--text-4); }
        .fo-in:focus, .fo-ta:focus { outline: none; border-color: var(--accent); }
        .fo-ta { min-height: 4.5rem; resize: vertical; }
        .fo-crow { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
        .fo-tagsel { width: auto; font-size: .82rem; padding: .35rem .6rem; }
        .fo-post { background: var(--accent-bg); color: var(--on-accent); border: none; border-radius: .5rem; padding: .42rem 1.2rem; font-weight: 700; font-size: .85rem; cursor: pointer; font-family: inherit; }
        .fo-th { background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: 1rem 1.1rem; margin-bottom: .6rem; }
        .fo-pinned { border-color: var(--warn); }
        .fo-pin { width: .9rem; height: .9rem; color: var(--warn); }
        .fo-pin-tag { color: var(--warn-text); background: var(--warn-bg); border-color: var(--warn); }
        .fo-th-h { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
        .fo-meta { font-size: .78rem; color: var(--text-4); display: inline-flex; align-items: center; gap: .3rem; }
        .fo-meta b { color: var(--text-2); font-weight: 600; }
        .fo-tick { width: .85rem; height: .85rem; color: var(--accent); }
        .fo-t { font-size: 1rem; font-weight: 700; margin: .5rem 0 .35rem; color: var(--text); }
        .fo-b { font-size: .9rem; color: var(--text-3); line-height: 1.65; }
        .fo-f { display: flex; gap: 1.1rem; margin-top: .7rem; }
        .fo-act { display: inline-flex; align-items: center; gap: .25rem; background: none; border: none; padding: 0; color: var(--accent); font-weight: 600; font-size: .82rem; cursor: pointer; font-family: inherit; }
        .fo-act.on { color: var(--accent-2); }
        .fo-act i, .fo-act svg { width: 1rem; height: 1rem; }
        .fo-replies { margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--line); }
        .fo-replies.hidden { display: none; }
        .fo-reply { padding-left: .9rem; border-left: 2px solid var(--line); margin-bottom: .8rem; }
        .fo-reply:last-child { margin-bottom: 0; }

        /* ===== Modals =====
           One dialog shell for all five, on the same rgba(0,0,0,.5) backdrop the
           live site uses. On a dark app that backdrop still has to separate the
           dialog from the page behind it, which is why the panel is --surface
           with a --line border rather than a tint. */
        .dl-back { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center; z-index: 50; padding: 1rem; }
        .dl-back.hidden { display: none; }
        .dl-box { background: var(--surface); border: 1px solid var(--line); border-radius: .6rem; box-shadow: 0 18px 45px rgba(0,0,0,.4); padding: 1.4rem; width: 100%; max-width: 22rem; max-height: 90vh; overflow-y: auto; }
        .dl-box.dl-wide { max-width: 26rem; }
        .dl-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: .5rem; }
        .dl-h h3 { font-size: 1.05rem; font-weight: 700; color: var(--text); }
        .dl-x { background: none; border: none; color: var(--text-4); cursor: pointer; padding: 0; display: flex; }
        .dl-x:hover { color: var(--text); }
        .dl-x i, .dl-x svg { width: 1.15rem; height: 1.15rem; }
        .dl-t { font-size: 1.05rem; font-weight: 700; margin-bottom: .4rem; color: var(--text); }
        .dl-msg { font-size: .9rem; color: var(--text-3); line-height: 1.6; margin-bottom: 1.2rem; }
        .dl-msg:empty { margin-bottom: 0; }
        .dl-lead { font-size: .87rem; color: var(--text-3); margin-bottom: 1rem; line-height: 1.55; }
        .dl-strong { font-weight: 700; color: var(--text-2); }
        .dl-gwrap { display: flex; justify-content: center; }
        .dl-or { display: flex; align-items: center; gap: .7rem; margin: .8rem 0; }
        .dl-or.hidden { display: none; }
        .dl-or i { flex: 1; height: 1px; background: var(--line); }
        .dl-or span { font-size: .75rem; color: var(--text-4); }
        .dl-tabs { display: flex; border-bottom: 1px solid var(--line); margin-bottom: .9rem; }
        .dl-tab { flex: 1; background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: .5rem; font-size: .87rem; font-weight: 600; color: var(--text-4); cursor: pointer; font-family: inherit; }
        .dl-tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 700; }
        .dl-field { width: 100%; border: 1px solid var(--line-2); border-radius: .5rem; padding: .55rem .7rem; font-size: .9rem; color: var(--text); background: var(--surface-2); margin-bottom: .55rem; font-family: inherit; }
        .dl-field::placeholder { color: var(--text-4); }
        .dl-field:focus { outline: none; border-color: var(--accent); }
        .dl-upper { text-transform: uppercase; }
        .dl-full { width: 100%; }
        .dl-link { width: 100%; background: none; border: none; text-align: center; font-size: .85rem; color: var(--accent); cursor: pointer; font-family: inherit; padding: .55rem 0 0; }
        .dl-link.hidden { display: none; }
        .dl-note { font-size: .78rem; margin: -.2rem 0 .55rem; }
        .dl-note.hidden { display: none; }
        .dl-good { color: var(--good-text); }
        .dl-bad { color: var(--bad); font-size: .85rem; margin-bottom: .55rem; }
        .dl-bad.hidden { display: none; }
        .dl-warn-tx { color: var(--warn-text); }
        .dl-warn { background: var(--warn-bg); border: 1px solid var(--warn); border-radius: .5rem; padding: .7rem .8rem; margin-bottom: .9rem; font-size: .78rem; color: var(--warn-text); }
        .dl-warn.hidden { display: none; }
        .dl-warn a { display: block; margin-top: .3rem; color: var(--accent); text-decoration: underline; word-break: break-all; }
        .dl-eyebrow { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--text-4); margin-bottom: .3rem; }
        .dl-code { display: flex; align-items: center; gap: .5rem; border: 1px solid var(--line-2); background: var(--surface-2); border-radius: .5rem; padding: .45rem .6rem; margin-bottom: .8rem; }
        .dl-code code, .dl-code input { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem; color: var(--text); background: none; border: none; overflow: hidden; text-overflow: ellipsis; }
        .dl-code input:focus { outline: none; }
        .dl-copy { background: none; border: none; color: var(--accent); font-weight: 700; font-size: .78rem; cursor: pointer; font-family: inherit; flex-shrink: 0; }
        .dl-stats { display: flex; gap: 1rem; border-top: 1px solid var(--line); padding-top: .8rem; font-size: .78rem; color: var(--text-3); flex-wrap: wrap; }
        .dl-stats b { color: var(--text); font-size: .95rem; }
        .dl-row { display: flex; gap: .6rem; }
        .dl-cancel { flex: 1; border: 1px solid var(--line-2); background: none; color: var(--text-2); border-radius: .5rem; padding: .55rem; font-weight: 600; font-size: .88rem; cursor: pointer; font-family: inherit; }
        .dl-cancel.hidden { display: none; }
        .dl-ok { flex: 1; border: none; background: var(--accent-bg); color: var(--on-accent); border-radius: .5rem; padding: .55rem; font-weight: 700; font-size: .88rem; cursor: pointer; font-family: inherit; }
        /* Destructive confirmations only. White on --bad measures 2.8:1 in dark
           and fails; --danger-btn is 4.8:1. --bad stays for text and borders. */
        .dl-danger { flex: 1; border: none; background: var(--danger-btn); color: #fff; border-radius: .5rem; padding: .55rem; font-weight: 700; font-size: .88rem; cursor: pointer; font-family: inherit; }
        .dl-plans { margin-bottom: .3rem; max-width: none; }
        .dl-owned { text-align: center; border: 1px solid var(--accent-line); background: var(--accent-soft); border-radius: .55rem; padding: 1.5rem 1.2rem; }
        .dl-owned.hidden { display: none; }
        .dl-owned-ic { width: 2rem; height: 2rem; color: var(--accent); margin: 0 auto .6rem; display: block; }
        .dl-owned-t { font-weight: 700; color: var(--text); font-size: .95rem; }
        .dl-owned-s { font-size: .85rem; color: var(--text-3); margin-top: .35rem; line-height: 1.55; }
        #pricing-modal .pw-fine { text-align: center; margin-top: 1rem; }
        #pricing-modal .pw-fine a { color: var(--accent); text-decoration: underline; }

        /* ===== legal.html =====
           A separate static file, linked from the footer and from checkout. Long
           form legal text is the one place the reading measure genuinely matters,
           so it is capped at 44rem. This page has no app.js, so its prices are
           plain literals kept in step with the PRICING constant by hand. */
        .lp-page { min-height: 100vh; background: var(--bg); padding: 2rem 1.25rem 4rem; }
        .lp { max-width: 44rem; margin: 0 auto; }
        .lp-nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-bottom: 1.3rem; border-bottom: 1px solid var(--line); margin-bottom: 2rem; }
        .lp-logo { text-decoration: none; }
        .lp-logo { font-size: 1rem; }
        .lp-logo .ld-logo-name { color: var(--text); }
        .lp-back { background: none; border: 1px solid var(--line-2); color: var(--text-2); border-radius: .45rem; padding: .35rem .8rem; font-size: .82rem; font-weight: 600; cursor: pointer; font-family: inherit; text-decoration: none; white-space: nowrap; }
        .lp-back:hover { background: var(--fill-2); color: var(--text); }
        .lp-h1 { font-family: ui-serif, Georgia, "Times New Roman", serif; font-size: 2.1rem; font-weight: 500; letter-spacing: -.02em; color: var(--text); }
        .lp-upd { font-size: .82rem; color: var(--text-4); margin-top: .3rem; }
        .lp-toc { display: flex; gap: 1.1rem; flex-wrap: wrap; margin: 1.4rem 0 2rem; padding: .8rem 1rem; background: var(--surface); border: 1px solid var(--line); border-radius: .55rem; }
        .lp-toc a { color: var(--accent); font-size: .85rem; font-weight: 600; text-decoration: none; }
        .lp-toc a:hover { text-decoration: underline; }
        .lp-h2 { font-size: 1.1rem; font-weight: 700; margin-top: 1.8rem; color: var(--text); }
        .lp-h3 { font-size: .95rem; font-weight: 700; margin: 1.4rem 0 .35rem; color: var(--text-2); }
        .lp-p { font-size: .97rem; line-height: 1.8; color: var(--text-2); margin-top: .6rem; }
        .lp-ul { padding-left: 1.3rem; margin: .5rem 0 .8rem; }
        .lp-ul li { list-style: disc; font-size: .97rem; line-height: 1.8; color: var(--text-2); margin-bottom: .45rem; }
        .lp-page a { color: var(--accent); }
        .lp-page b { color: var(--text); }

        /* ===== Empty, loading and error states =====
           The live page showed a bare "Loading questions..." row and had no
           designed empty or error state at all. Every card here carries a button
           that DOES something -- that is the difference between an empty state
           and a dead end. */
        .es-card { background: var(--surface); border: 1px solid var(--line); border-radius: .7rem; padding: 1.6rem 1.4rem; text-align: center; max-width: 26rem; margin: 0 auto; }
        .es-ic { width: 2.6rem; height: 2.6rem; border-radius: 50%; background: var(--fill-1); color: var(--text-3); display: flex; align-items: center; justify-content: center; margin: 0 auto .8rem; }
        .es-ic i, .es-ic svg { width: 1.2rem; height: 1.2rem; }
        .es-card.es-err .es-ic { background: var(--bad-bg); color: var(--bad-text); }
        .es-card h4 { font-size: .98rem; font-weight: 700; color: var(--text); }
        .es-card p { font-size: .87rem; color: var(--text-3); line-height: 1.6; margin: .4rem 0 1rem; }
        .es-btn { border: 1px solid var(--line-2); background: none; color: var(--accent); border-radius: .5rem; padding: .45rem 1rem; font-weight: 700; font-size: .85rem; cursor: pointer; font-family: inherit; }
        .es-btn:hover { background: var(--accent-soft); }
        .es-btn:disabled { opacity: .6; cursor: not-allowed; }
        /* The rail is narrow, so its copy of the card drops the panel chrome. */
        #rail-empty .es-card { border: none; background: none; padding: .4rem 0 0; }
        #rail-empty.hidden { display: none; }
        .es-skel { text-align: left; }
        .es-row { display: flex; gap: .8rem; padding: .8rem .9rem; border: 1px solid var(--line); border-radius: .5rem; margin-bottom: .4rem; background: var(--surface); }
        .es-row i { display: block; height: .7rem; border-radius: 999px; background: var(--fill-1); animation: es-pulse 1.4s ease-in-out infinite; }
        @keyframes es-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
        @media (prefers-reduced-motion: reduce) { .es-row i { animation: none; } }

        /* ===================================================================
           Phone overrides that must come LAST.
           This file grew section by section, so several component blocks are
           defined further down than the mobile pass near the top. Equal
           specificity means source order decides, and a media query does not
           change that -- an override up there loses to the base rule down here.
           Anything that has to beat a later block lives in this final sweep.
           =================================================================== */
        @media (max-width: 640px) {
            /* Two-up stats rather than one stacked column, as mocked. */
            .pf { padding: 1.25rem 1rem 1rem; }
            .pf-grid { grid-template-columns: 1fr 1fr; margin-top: 1.1rem; }
            .pf-stat { padding: .8rem .9rem; }
            .pf-stat b { font-size: 1.45rem; }
            .pf-head h2 { font-size: 1.15rem; }
            .pf-plan { margin-left: 0; width: 100%; }
            .pf-cols { margin-top: .8rem; }
            .qa1-bar { width: 100%; justify-content: space-between; }
            .qa-title { font-size: 1.5rem; }
            .qa-prompt { font-size: 1rem; margin-top: 1.3rem; }
            .mk-panel { padding: 1.1rem 1rem; }
            .mk-wrap, .qa-wrap { max-width: 100%; }
        }

/* ===================================================================
   .hidden, last word.

   Tailwind's .hidden is a single class, so is every component class in
   this file, and this file loads AFTER vendor/tailwind.css -- which means
   the moment a component sets its own `display` (the control-metrics pass
   above gave one to most of the pills so they could centre with flexbox),
   that display quietly beat .hidden and the element stayed visible. The
   timer's Reset button is how we found it: it shipped hidden and came
   back permanently on.

   Re-stating .hidden here, at the very end, puts it after every component
   rule at equal specificity, so source order settles it. No !important --
   anything that legitimately needs to out-rank .hidden can still do it
   with specificity, exactly as before.
   =================================================================== */
.hidden { display: none; }
