        /* ── Design tokens — "MarketWatch Terminal" dark theme ──
           Every color in the file below is a var() reference into this
           block, so the whole app's palette lives in one place; nudge a
           value here to retint everything that uses it. */
        :root {
            --navy: #0a0f1e;
            --navy-2: #0d1526;
            --navy-3: #111b30;
            --accent: #3b82f6;
            --accent-hover: #5b9dff;
            --accent-soft: rgba(59, 130, 246, 0.14);
            --bg: #0a0e18;
            --surface: #0e1524;
            --border: #1c2740;
            --border-strong: #2a3a5c;
            --text: #e6ebf5;
            --text-2: #aab3c8;
            --text-3: #7c869e;
            --text-4: #545f78;
            --green: #22c55e;
            --green-bg: rgba(34, 197, 94, 0.12);
            --green-brd: rgba(34, 197, 94, 0.35);
            --red: #f0454f;
            --red-bg: rgba(240, 69, 79, 0.13);
            --red-brd: rgba(240, 69, 79, 0.35);
            --amber: #e0a030;
            --amber-bg: rgba(224, 160, 48, 0.12);
            --amber-brd: rgba(224, 160, 48, 0.35);
            --blue: #3b82f6;
            --blue-bg: rgba(59, 130, 246, 0.14);
            --blue-brd: rgba(59, 130, 246, 0.4);
            --purple: #a679f0;
            --purple-bg: rgba(166, 121, 240, 0.13);
            --purple-brd: rgba(166, 121, 240, 0.35);
            --slate: #8a96ad;
            --slate-bg: rgba(138, 150, 173, 0.12);
            --slate-brd: rgba(138, 150, 173, 0.3);
            --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
            --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.35);
            --shadow-lg: 0 8px 28px rgba(0, 0, 0, 0.5);
            --radius: 8px;
            /* Single terminal-style typeface throughout — headlines,
               labels and figures alike — per the "same font size/style
               everywhere" pass; --font-mono is kept as its own name
               (rather than merged into --font-headline) purely so
               call sites stay self-documenting about intent. */
            --font-headline: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
            /* ── Consistent type scale — every font-size in this file
               resolves to one of these six steps instead of the ~16 ad
               hoc pixel values used previously, so comparable elements
               (all badges, all buttons, all body text, etc.) always
               match exactly. */
            --fs-1: 10px;
            --fs-2: 11px;
            --fs-3: 12px;
            --fs-4: 13px;
            --fs-5: 15px;
            --fs-6: 18px;

            /* ── Panel toolbar tokens (News panel-header's chip/control
               row, Events/Live Wire panel headers) — kept separate from
               --surface/--text because .site-header (the top brand bar)
               stays dark navy in BOTH themes, while these toolbar strips
               flip with the theme; see :root[data-theme="light"] below. */
            --panel-header-bg: linear-gradient(180deg, var(--navy) 0%, var(--navy-2) 100%);
            --panel-header-text: #ffffff;
            --panel-header-text-dim: rgba(255, 255, 255, 0.75);
            --panel-header-control-bg: rgba(255, 255, 255, 0.06);
            --panel-header-control-bg-hover: rgba(255, 255, 255, 0.14);
            --panel-header-control-border: rgba(255, 255, 255, 0.2);
            --chip-bg: rgba(255, 255, 255, 0.05);
            --chip-border: rgba(255, 255, 255, 0.16);
            --chip-text: var(--text-2);
        }

        /* ── Light theme override — applied via data-theme="light" on
           <html> (see initTheme() in app.js). Only the tokens that
           actually differ between themes are redefined here; every rule
           in the file that reads a var() below re-themes automatically. */
        :root[data-theme="light"] {
            --bg: #f3f5f8;
            --surface: #ffffff;
            --border: #e2e6ec;
            --border-strong: #ccd3dc;
            --text: #1a2230;
            --text-2: #475060;
            --text-3: #6b7280;
            --text-4: #98a1af;
            --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
            --shadow-md: 0 2px 8px rgba(16, 24, 40, 0.06);
            --shadow-lg: 0 8px 24px rgba(16, 24, 40, 0.08);

            /* Panel toolbar strips go light gray with blue-outlined
               chips, per the light-theme screenshot — .site-header
               itself is untouched (no --navy override), so the top
               brand bar stays dark navy in this theme too. */
            --panel-header-bg: #eef1f6;
            --panel-header-text: var(--text);
            --panel-header-text-dim: var(--text-3);
            --panel-header-control-bg: #ffffff;
            --panel-header-control-bg-hover: var(--bg);
            --panel-header-control-border: var(--border-strong);
            --chip-bg: #ffffff;
            --chip-border: var(--blue-brd);
            --chip-text: var(--accent);
        }

        * { box-sizing: border-box; margin: 0; padding: 0; }

        html { -webkit-text-size-adjust: 100%; height: 100%; }

        /* height:100% + flex column so .page (and inside it, the price
           strip + main-grid) can size itself to "however much viewport
           height is left after the header" without hardcoding a header
           height anywhere — see .page and .main-grid below. */
        body {
            height: 100%;
            display: flex;
            flex-direction: column;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background: var(--bg);
            color: var(--text);
            line-height: 1.55;
            font-size: var(--fs-5);
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
        }

        a { color: var(--accent); text-decoration: none; }
        a:hover { color: var(--accent-hover); }

        /* ── Header ── */
        .site-header {
            flex: none;
            position: sticky;
            top: 0;
            z-index: 100;
            background: linear-gradient(180deg, var(--navy) 0%, var(--navy-2) 100%);
            color: #fff;
            box-shadow: 0 1px 0 rgba(255,255,255,0.06), 0 4px 16px rgba(11,31,58,0.25);
        }
        .header-inner {
            max-width: 1600px;
            margin: 0 auto;
            padding: 14px 32px;
            display: flex;
            align-items: center;
            gap: 16px;
            flex-wrap: wrap;
        }
        .brand {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-right: auto;
        }
        .brand-mark {
            width: 36px;
            height: 36px;
            border-radius: 8px;
            background: linear-gradient(135deg, #1d6fe0 0%, #0f5bd8 55%, #0b3d8f 100%);
            display: flex;
            align-items: center;
            justify-content: center;
            color: #fff;
            font-weight: 800;
            font-size: var(--fs-6);
            letter-spacing: -0.5px;
            box-shadow: 0 2px 8px rgba(15,91,216,0.4);
        }
        .brand-name {
            font-family: var(--font-headline);
            font-size: var(--fs-6);
            font-weight: 700;
            letter-spacing: 0.2px;
            color: #fff;
            white-space: nowrap;
        }
        .brand-sub {
            font-size: var(--fs-2);
            color: #9fb6d4;
            letter-spacing: 0.4px;
            text-transform: uppercase;
            margin-top: 1px;
        }
        .brand-name { text-transform: uppercase; letter-spacing: 0.6px; }

        .header-right {
            display: flex;
            align-items: center;
            gap: 14px;
        }
        .header-clock {
            font-family: var(--font-mono);
            font-size: var(--fs-3);
            /* Fixed light color, not a theme-flipping token — .site-header
               stays dark navy in both themes, so this text must too. */
            color: #9fb6d4;
            letter-spacing: 0.3px;
            white-space: nowrap;
        }
        .theme-toggle-btn { font-size: var(--fs-4); padding: 9px 12px; }

        /* ── Desktop notifications toggle (global — covers both the News
           Feed and the Live Wire panel) ── */
        .notify-btn.active {
            background: var(--green);
            border-color: var(--green);
            color: #fff;
        }
        .notify-btn.active:hover { background: #0c6a45; }

        /* ── Header controls ── */
        .header-controls {
            display: flex;
            align-items: center;
            gap: 10px;
            flex-wrap: wrap;
        }
        .source-select {
            appearance: none;
            -webkit-appearance: none;
            background: var(--panel-header-control-bg) url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E") no-repeat right 12px center;
            border: 1px solid var(--panel-header-control-border);
            color: var(--panel-header-text);
            font-size: var(--fs-4);
            font-weight: 500;
            padding: 9px 36px 9px 14px;
            border-radius: 6px;
            cursor: pointer;
            min-width: 220px;
            transition: border-color 0.15s, background-color 0.15s;
        }
        .source-select:hover { background-color: var(--panel-header-control-bg-hover); }
        .source-select:focus { outline: none; border-color: var(--accent); }
        .source-select option { color: var(--text); background: var(--surface); }

        .load-btn {
            background: linear-gradient(180deg, #2b7dff 0%, var(--accent) 100%);
            color: #fff;
            font-size: var(--fs-4);
            font-weight: 600;
            border: 1px solid rgba(255,255,255,0.12);
            padding: 9px 20px;
            border-radius: 6px;
            cursor: pointer;
            letter-spacing: 0.2px;
            transition: background 0.15s, transform 0.05s, box-shadow 0.15s;
            box-shadow: 0 2px 6px rgba(15,91,216,0.35);
            white-space: nowrap;
        }
        .load-btn:hover { background: linear-gradient(180deg, #3b86ff 0%, #0c4cb8 100%); box-shadow: 0 3px 10px rgba(15,91,216,0.45); }
        .load-btn:active { transform: translateY(1px); }
        .load-btn:disabled { background: #4a5c7a; cursor: not-allowed; box-shadow: none; border-color: transparent; }

        /* Secondary/outline style: same shape as .load-btn but visually
           lighter, since hard refresh is a heavier, less-frequent action
           and shouldn't compete with the primary "Load Latest News" cta.
           Theme-aware (panel-header tokens) — the primary blue .load-btn
           above stays blue-on-white-text in both themes (it's already a
           strong, self-contained accent color), only this outline style
           needs to flip with the surrounding toolbar. */
        .hard-refresh-btn {
            background: var(--panel-header-control-bg);
            border-color: var(--panel-header-control-border);
            color: var(--panel-header-text);
            box-shadow: none;
        }
        .hard-refresh-btn:hover { background: var(--panel-header-control-bg-hover); box-shadow: none; }
        .hard-refresh-btn:disabled { background: var(--panel-header-control-bg); color: var(--panel-header-text-dim); border-color: transparent; opacity: 0.6; }

        /* Same outline style as Hard Refresh when off; switches to a
           green "live" look while auto-refresh is running. */
        .auto-refresh-btn {
            background: var(--panel-header-control-bg);
            border-color: var(--panel-header-control-border);
            color: var(--panel-header-text);
            box-shadow: none;
        }
        .auto-refresh-btn:hover { background: var(--panel-header-control-bg-hover); box-shadow: none; }
        .auto-refresh-btn.active {
            background: var(--green);
            border-color: var(--green);
            color: #fff;
        }
        .auto-refresh-btn.active:hover { background: #0c6a45; }
        .auto-refresh-btn { position: relative; }
        .auto-refresh-spinner {
            display: none;
            width: 11px; height: 11px;
            border: 2px solid var(--panel-header-control-border);
            border-top-color: var(--panel-header-text);
            border-radius: 50%;
            margin-right: 6px;
            vertical-align: -1px;
        }
        .auto-refresh-btn.loading .auto-refresh-spinner {
            display: inline-block;
            animation: spin 0.8s linear infinite;
        }

        /* ── .site-header exemption ── #notifyBtn/#themeToggleBtn reuse
           .load-btn/.hard-refresh-btn, but .site-header always stays
           dark navy regardless of theme (see :root[data-theme="light"]
           above — --navy is never overridden), so these two buttons are
           pinned to their original white-on-navy look in both themes
           rather than following the new panel-header tokens. */
        .site-header .hard-refresh-btn {
            background: rgba(255,255,255,0.06);
            border-color: rgba(255,255,255,0.22);
            color: #fff;
        }
        .site-header .hard-refresh-btn:hover { background: rgba(255,255,255,0.14); }

        /* ── Main layout: full-width price strip, then News (2/3) |
           Events+Live Wire stacked (1/3) ──
           .page fills whatever viewport height is left below the sticky
           header (via the body flex-column set up above); the price
           strip takes its natural (small) height and everything else
           splits the remainder. Each scrollable region — the strip
           horizontally, News and the right column's two panels
           vertically — scrolls its OWN overflow; the page itself
           doesn't scroll. Falls back to a stacked, page-scrolling layout
           below 900px — see the @media block further down.

           Dense-terminal styling: flat panels, hairline (1px solid)
           dividers instead of gaps/shadows/rounded corners on all of
           the structural containers below (.panel, .price-strip-wrap) —
           no --radius, no --shadow-* anywhere in this section. */
        .page {
            flex: 1 1 auto;
            min-height: 0;
            display: flex;
            flex-direction: column;
            max-width: 1900px;
            width: 100%;
            margin: 0 auto;
            padding: 10px 14px 8px;
        }

        /* ── Full-width price strip ──
           Wraps to as many lines as needed instead of scrolling — how
           many lines depends entirely on how many prices are currently
           selected in the toggle row below, so no fixed height here. */
        .price-strip-wrap {
            flex: none;
            display: flex;
            flex-direction: column;
            border: 1px solid var(--border);
            border-bottom: none;
            background: var(--surface);
        }
        .price-strip {
            display: flex;
            flex-wrap: wrap;
        }
        /* A deselected item stays in the DOM (still gets live price
           updates — see renderPriceQuote in app.js) but takes no space
           and isn't rendered, so toggling it back on shows a current
           value immediately rather than a stale one. */
        .price-strip-item.price-item-hidden { display: none; }

        /* ── Which-prices-shown toggle row ──
           One small chip per index/commodity; click to show/hide it in
           the strip above. Selection persists via localStorage (see
           PRICE_SELECTED_KEY in app.js) — deliberately plain
           outline/fill states, not a new accent color, per the
           "keep color minimal" rule. */
        .price-toggle-row {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 4px;
            padding: 4px 10px 5px;
            border-top: 1px solid var(--border);
        }
        .price-toggle-chip {
            appearance: none;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.2px;
            padding: 2px 7px;
            border: 1px solid var(--border);
            background: var(--surface);
            color: var(--text-4);
            cursor: pointer;
        }
        .price-toggle-chip:hover { border-color: var(--border-strong); color: var(--text-2); }
        .price-toggle-chip.on {
            background: var(--text);
            border-color: var(--text);
            color: var(--surface);
        }
        .price-strip-updated {
            margin-left: auto;
            padding-left: 10px;
            font-size: var(--fs-1);
            font-family: var(--font-mono);
            color: var(--text-4);
            white-space: nowrap;
        }

        /* ── Two columns below the strip ── */
        .main-grid {
            flex: 1 1 auto;
            min-height: 0;
            display: grid;
            grid-template-columns: 2fr 1fr;
            border: 1px solid var(--border);
        }
        .panel-news { border-right: 1px solid var(--border); }
        .right-column {
            display: flex;
            flex-direction: column;
            min-height: 0;
            min-width: 0;
        }
        .panel-events { border-bottom: 1px solid var(--border); }

        /* Flat panel — no radius, no shadow. Each panel scrolls its own
           body (see .panel-body); the panel frame itself never scrolls. */
        .panel {
            display: flex;
            flex-direction: column;
            min-height: 0;
            min-width: 0;
            background: var(--surface);
            flex: 1 1 50%; /* only meaningful for the two right-column panels; overridden n/a for panel-news which fills its grid cell */
        }
        /* Same navy toolbar language as before, tightened up — see
           "reduce padding/spacing throughout" in the styling pass.
           flex-wrap lets the News panel's several buttons wrap onto a
           second row at narrower widths instead of overflowing. */
        .panel-header {
            flex: none;
            display: flex;
            align-items: center;
            gap: 6px;
            flex-wrap: wrap;
            padding: 5px 10px;
            background: var(--panel-header-bg);
            border-bottom: 1px solid var(--panel-header-control-border);
            color: var(--panel-header-text);
        }
        .panel-title {
            font-weight: 700;
            font-size: var(--fs-3);
            letter-spacing: 0.2px;
            color: var(--panel-header-text);
            white-space: nowrap;
        }
        .panel-header-controls {
            display: flex;
            align-items: center;
            gap: 5px;
            margin-left: auto;
        }
        /* The scrollable body every panel's actual content lives in —
           min-height:0 is what lets a flex child shrink below its
           content's natural height so overflow-y:auto can actually
           kick in, instead of the panel silently growing past its
           allotted space. Tight padding — dense, not spacious, per the
           terminal-style pass. */
        .panel-body {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: auto;
            padding: 6px 8px;
        }
        @media (max-width: 900px) {
            /* Primary target is desktop — this is a "don't break on a
               narrow viewport" fallback, not a tuned mobile layout.
               Stack in the requested order (price strip, News, Events,
               Live Wire) and let the PAGE scroll instead of trying to
               force everything into one screen's height; each panel
               keeps its own internal scroll beyond a cap so none of them
               can still balloon indefinitely. DOM order already IS
               strip → News → Events → Live Wire, so a plain block stack
               reproduces the requested order with no reordering rules
               needed. */
            .page { overflow-y: auto; }
            .main-grid {
                display: block;
                height: auto;
            }
            .panel-news {
                border-right: none;
                border-bottom: 1px solid var(--border);
                max-height: 60vh;
                margin-bottom: 10px;
            }
            .right-column { display: block; }
            .panel-events {
                max-height: 50vh;
                margin-bottom: 10px;
                border-bottom: 1px solid var(--border);
            }
            .panel-livewire { max-height: 50vh; }
        }

        /* Still used by the month-grid modal's #eventCalendarUpdated
           (see the modal section further down) — the price strip's own
           "updated" label is .price-strip-updated, above. The old
           tile-grid price display (.price-ticker-item and friends) plus
           the per-item market-status badge are gone: the strip is a
           single compact row now (see .price-strip-item above), and
           market-status's own green/amber/slate coloring didn't fit the
           "green/red only" color rule this pass — the underlying
           market-hours calculation (getMarketStatus, updateMarketStatuses
           in app.js) is untouched and still runs, it just has nothing to
           paint into anymore. */
        .price-ticker-updated {
            display: block;
            margin-top: 12px;
            padding-top: 10px;
            border-top: 1px solid var(--border);
            font-size: var(--fs-2);
            color: var(--text-4);
            text-align: right;
        }

        /* Events panel controls — sit inside .panel-header-controls in
           the panel's shared navy header (see main-layout section
           above). */
        .event-category-select {
            font-size: var(--fs-2);
            padding: 3px 6px;
            min-width: 0;
        }
        .event-refresh-btn {
            font-size: var(--fs-3);
            padding: 3px 9px;
        }
        .event-refresh-btn.loading { opacity: 0.6; pointer-events: none; }

        /* ── Events panel: compact upcoming-agenda list (default view) ── */
        .event-agenda-list {
            display: flex;
            flex-direction: column;
        }
        .event-agenda-day-label {
            margin-top: 8px;
            padding-bottom: 3px;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            color: var(--text-4);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        .event-agenda-day-label:first-child { margin-top: 0; }

        /* ── Click-to-expand full month-calendar modal ──
           The month grid itself (.event-cal-*) is unchanged from its
           original inline placement — only its container moved here. */
        .modal-overlay {
            display: none;
            position: fixed;
            inset: 0;
            background: rgba(11, 19, 35, 0.55);
            z-index: 1000;
            align-items: center;
            justify-content: center;
            padding: 24px;
        }
        .modal-overlay.open { display: flex; }
        .modal-box {
            background: var(--surface);
            border-radius: var(--radius);
            box-shadow: var(--shadow-lg);
            width: 100%;
            max-width: 480px;
            max-height: min(680px, 90vh);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        .modal-header {
            flex: none;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 14px 18px;
            background: linear-gradient(180deg, var(--navy) 0%, var(--navy-2) 100%);
            color: #fff;
        }
        .modal-title { font-weight: 700; font-size: var(--fs-5); }
        .modal-close-btn {
            appearance: none;
            background: none;
            border: none;
            color: rgba(255,255,255,0.8);
            font-size: 22px;
            line-height: 1;
            cursor: pointer;
            padding: 0 2px;
        }
        .modal-close-btn:hover { color: #fff; }
        .modal-content {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: auto;
            padding: 18px 20px 20px;
        }

        /* Month grid ── */
        .event-cal-nav {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 10px;
        }
        .event-cal-nav-btn {
            appearance: none;
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: 6px;
            width: 24px;
            height: 24px;
            line-height: 1;
            cursor: pointer;
            font-size: var(--fs-4);
            color: var(--text-2);
        }
        .event-cal-nav-btn:hover { border-color: var(--border-strong); }
        .event-cal-month-label {
            font-family: var(--font-mono);
            font-weight: 700;
            font-size: var(--fs-4);
            color: var(--text);
            min-width: 120px;
            text-align: center;
        }
        .event-cal-today-btn {
            margin-left: auto;
            font-size: var(--fs-2);
            font-weight: 600;
            padding: 3px 10px;
            border-radius: 5px;
            border: 1px solid var(--border);
            background: var(--bg);
            color: var(--text-3);
            cursor: pointer;
        }
        .event-cal-today-btn:hover { background: var(--border); }
        .event-cal-weekdays {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 4px;
            margin-bottom: 4px;
        }
        .event-cal-weekdays span {
            text-align: center;
            font-size: var(--fs-1);
            font-weight: 700;
            color: var(--text-4);
            text-transform: uppercase;
            letter-spacing: 0.4px;
        }
        .event-cal-grid {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 4px;
        }
        .event-cal-cell {
            min-height: 50px;
            padding: 4px 5px;
            border: 1px solid var(--border);
            background: var(--bg);
            cursor: pointer;
            display: flex;
            flex-direction: column;
            gap: 4px;
            transition: border-color 0.15s, background 0.15s;
        }
        .event-cal-cell:hover { border-color: var(--border-strong); }
        .event-cal-cell.other-month { opacity: 0.4; }
        /* Blue here is the site's one established UI accent (also used
           for links/buttons) marking today/selected — not a new
           category color, so it's kept under the "keep color minimal"
           rule rather than dropped. */
        .event-cal-cell.today { border-color: var(--blue); }
        .event-cal-cell.selected { background: var(--blue-bg); border-color: var(--blue); }
        .event-cal-date-num { font-family: var(--font-mono); font-size: var(--fs-2); font-weight: 700; color: var(--text-2); }
        .event-cal-cell.today .event-cal-date-num { color: var(--blue); }
        .event-cal-dots { display: flex; gap: 3px; flex-wrap: wrap; }
        /* Neutral gray, not per-category color (used to be
           blue/green/amber/slate for Earnings/Corporate/Management/Macro)
           — see the "keep color minimal" styling pass. Which categories
           landed on a day is still available in the day-detail panel
           below, via the text category tag. */
        .event-cal-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--text-4); }
        .event-cal-day-detail {
            margin-top: 14px;
            padding-top: 12px;
            border-top: 1px solid var(--border);
        }
        .event-cal-day-detail-heading {
            font-family: var(--font-mono);
            font-size: var(--fs-2);
            font-weight: 700;
            color: var(--text-3);
            margin-bottom: 8px;
        }
        @media (max-width: 480px) {
            .event-cal-cell { min-height: 38px; }
            .event-cal-date-num { font-size: var(--fs-1); }
        }

        /* Flat rows, hairline-divided — same treatment as .article-card,
           not individual filled/bordered/rounded boxes. */
        .event-items {
            display: flex;
            flex-direction: column;
        }
        .event-item {
            display: flex;
            align-items: flex-start;
            gap: 8px;
            padding: 5px 4px;
            border-bottom: 1px solid var(--border);
        }
        .event-item:last-child { border-bottom: none; }
        /* Plain neutral text label, no per-category tinted background
           (used to be blue/green/amber/slate for Earnings/Corporate/
           Management/Macro) — see the "keep color minimal" styling pass.
           The category name itself is still right there in the text. */
        .event-category-tag {
            flex: none;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            padding: 1px 5px;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            margin-top: 1px;
            color: var(--text-3);
            background: none;
            border: 1px solid var(--border);
        }
        .event-item-body { flex: 1; min-width: 0; }
        .event-item-title {
            font-size: var(--fs-3);
            font-weight: 600;
            color: var(--text);
        }
        .event-item-title a { color: inherit; text-decoration: none; }
        .event-item-title a:hover { text-decoration: underline; }
        .event-item-desc {
            font-size: var(--fs-2);
            color: var(--text-3);
            margin-top: 2px;
            overflow: hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
        }
        .event-item-meta {
            flex: none;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            gap: 2px;
        }
        .event-item-type {
            font-size: var(--fs-1);
            color: var(--text-4);
            white-space: nowrap;
            text-align: right;
        }
        .event-item-source {
            font-size: var(--fs-1);
            color: var(--text-4);
            opacity: 0.75;
            white-space: nowrap;
        }
        .event-calendar-empty {
            font-size: var(--fs-3);
            color: var(--text-4);
            padding: 10px 2px;
        }

        /* News panel's toolbar reuses .panel-header as its base (see the
           main-layout section above) — this just tightens the control
           sizing so the several buttons + two dropdowns have a fighting
           chance of fitting two-per-row instead of one-per-row at News's
           narrower-than-before width (2/3 of the page, not the full
           width it used to get). */
        .news-panel-header { align-items: flex-start; justify-content: space-between; }
        .news-panel-header .source-select { font-size: var(--fs-2); padding: 5px 24px 5px 8px; min-width: 0; flex: 1 1 110px; }
        .news-panel-header .load-btn { font-size: var(--fs-2); padding: 5px 9px; }
        .news-panel-body { padding: 8px 10px 10px; }

        .news-controls {
            display: flex;
            align-items: center;
            gap: 5px;
            flex: none;
        }
        /* Compact icon+label control buttons (Refresh/Hard/Auto/+Source/
           −Source) — same underlying .load-btn/.hard-refresh-btn/
           .auto-refresh-btn classes and click handlers as before, just a
           denser terminal-style label. */
        .ctrl-btn {
            font-size: var(--fs-2);
            padding: 5px 10px;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }
        .ctrl-icon { margin-right: 4px; }

        /* Live Wire panel header reuses .panel-header too — just needs
           the 𝕏 glyph sized to match the title text next to it. */
        .panel-livewire .twitter-icon { font-size: var(--fs-4); }

        /* ── Full-width price strip: compact inline stats ──
           symbol, value, %change — a single row, no card/box per item,
           just a hairline vertical divider between entries (border-right)
           so the strip reads as one continuous ticker tape rather than a
           row of separate boxes. Numeric fields (value, %change) use the
           monospace font per the dense-terminal styling pass. */
        .price-strip-item {
            flex: none;
            display: flex;
            align-items: baseline;
            gap: 6px;
            padding: 6px 12px;
            border-right: 1px solid var(--border);
            white-space: nowrap;
        }
        .price-strip-symbol {
            font-weight: 700;
            font-size: var(--fs-1);
            color: var(--text-3);
            letter-spacing: 0.2px;
        }
        .price-strip-value {
            font-family: var(--font-mono);
            font-size: var(--fs-4);
            font-weight: 700;
            color: var(--text);
        }
        .price-strip-change {
            font-family: var(--font-mono);
            font-size: var(--fs-2);
            font-weight: 700;
        }
        .price-strip-change.up { color: var(--green); }
        .price-strip-change.down { color: var(--red); }
        /* Plain neutral text label, no tinted pill background — see the
           "keep color minimal" pass (this used to be blue/slate-tinted). */
        .price-strip-tag {
            font-size: var(--fs-1);
            font-weight: 700;
            color: var(--text-4);
            letter-spacing: 0.3px;
        }

        /* Muted while Live is off (the default) so an inactive Live Wire
           panel doesn't compete for attention with News/Events — only
           the CONTENT dims; the header (including the toggle itself)
           stays fully visible so it's obvious how to turn it on. */
        .twitter-panel-content {
            transition: opacity 0.2s ease, filter 0.2s ease;
        }
        .twitter-panel-content.muted {
            opacity: 0.5;
            filter: grayscale(0.4);
        }
        .twitter-icon { font-size: var(--fs-5); }
        .twitter-stream-btn {
            margin-left: auto;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            font-size: var(--fs-2);
            font-weight: 700;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            color: var(--panel-header-text-dim);
            background: var(--panel-header-control-bg);
            border: 1px solid var(--panel-header-control-border);
            border-radius: 12px;
            padding: 4px 10px;
            cursor: pointer;
            transition: background 0.15s, border-color 0.15s, color 0.15s;
        }
        .twitter-stream-btn:hover {
            background: var(--panel-header-control-bg-hover);
        }
        .twitter-stream-btn.active {
            color: #d1414a;
            background: rgba(240,69,79,0.15);
            border-color: rgba(240,69,79,0.4);
        }
        .twitter-live-dot {
            width: 7px;
            height: 7px;
            border-radius: 50%;
            background: var(--panel-header-text-dim);
        }
        .twitter-stream-btn.active .twitter-live-dot {
            background: #ff3b3b;
            box-shadow: 0 0 0 0 rgba(255, 59, 59, 0.6);
            animation: twitter-live-pulse 1.6s ease-out infinite;
        }
        @keyframes twitter-live-pulse {
            0%   { box-shadow: 0 0 0 0 rgba(255, 59, 59, 0.6); }
            70%  { box-shadow: 0 0 0 7px rgba(255, 59, 59, 0); }
            100% { box-shadow: 0 0 0 0 rgba(255, 59, 59, 0); }
        }
        /* Tracked-account chips + add-handle row — account management,
           sits between the panel header and the tweet list. */
        .twitter-accounts-row {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            padding: 10px 12px 0;
        }
        .twitter-account-chip {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            font-size: var(--fs-2);
            font-weight: 600;
            color: var(--text-2);
            background: var(--bg);
            border: 1px solid var(--border);
            border-left: 3px solid var(--slate);
            padding: 3px 4px 3px 9px;
            border-radius: 20px;
        }
        .twitter-account-remove {
            appearance: none;
            border: none;
            background: none;
            cursor: pointer;
            width: 16px;
            height: 16px;
            line-height: 16px;
            border-radius: 50%;
            color: var(--text-4);
            font-size: var(--fs-4);
            padding: 0;
        }
        .twitter-account-remove:hover { background: var(--red-bg); color: var(--red); }
        .twitter-add-row {
            display: flex;
            gap: 6px;
            padding: 8px 12px 10px;
            border-bottom: 1px solid var(--border);
        }
        .twitter-add-input {
            flex: 1;
            min-width: 0;
            font-size: var(--fs-3);
            padding: 6px 10px;
            border: 1px solid var(--border-strong);
            border-radius: 6px;
            color: var(--text);
        }
        .twitter-add-input:focus { outline: none; border-color: var(--accent); }
        .twitter-add-btn {
            font-size: var(--fs-3);
            font-weight: 600;
            padding: 6px 14px;
            border-radius: 6px;
            border: 1px solid var(--blue-brd);
            background: var(--accent-soft);
            color: var(--accent);
            cursor: pointer;
        }
        .twitter-add-btn:hover { background: var(--blue-brd); }

        /* Not independently scrollable — .panel-body (its containing
           block, see the main-layout section) is the ONE scroll
           container per panel. This used to have its own max-height +
           overflow-y for the old single-column sidebar layout; nesting
           that inside panel-body would mean two independent scrollbars
           fighting each other. */
        .twitter-feed {
            padding: 2px;
        }
        .twitter-empty {
            padding: 14px 10px;
            text-align: center;
            color: var(--text-3);
            font-size: var(--fs-3);
        }
        .tweet-card {
            display: flex;
            align-items: flex-start;
            gap: 8px;
            padding: 6px 6px;
            border-bottom: 1px solid var(--border);
            color: var(--text);
            transition: background 0.1s;
            position: relative;
        }
        .tweet-card:hover {
            background: var(--bg);
            color: var(--text);
        }
        .tweet-card:last-child { border-bottom: none; }
        /* Same left-edge-stripe-only freshness language as .article-card
           (red = last 5 min, green = last 12h, the Twitter panel's own
           tighter windows) — no tinted background or animated glow, for
           the same reason and the same consistent dense-terminal look. */
        .tweet-card.tweet-brand-new { border-left: 3px solid #ff2222; }
        .tweet-card.tweet-recent-window { border-left: 3px solid var(--green); }
        .tweet-avatar {
            flex: none;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: var(--fs-1);
            font-weight: 700;
            color: #fff;
            letter-spacing: 0.2px;
        }
        .tweet-body { min-width: 0; flex: 1; }
        .tweet-handle {
            font-size: var(--fs-2);
            font-weight: 700;
            color: var(--text);
            margin-bottom: 1px;
        }
        .tweet-text {
            font-size: var(--fs-3);
            line-height: 1.35;
            white-space: pre-wrap;
            word-break: break-word;
        }
        .tweet-time {
            margin-top: 3px;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            color: var(--text-3);
        }
        /* Feed header */
        .feed-header {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            gap: 10px;
            flex-wrap: wrap;
            margin-bottom: 6px;
            padding-bottom: 6px;
            border-bottom: 1px solid var(--border);
        }
        .feed-title {
            font-family: var(--font-headline);
            font-size: var(--fs-5);
            font-weight: 700;
            color: var(--text);
            letter-spacing: -0.2px;
        }
        .feed-title span { color: var(--accent); }
        .feed-meta { font-size: var(--fs-2); color: var(--text-4); }

        .feed-search { margin-bottom: 10px; }
        .feed-search-input {
            width: 100%;
            box-sizing: border-box;
            padding: 8px 12px;
            border: 1px solid var(--border);
            border-radius: 8px;
            background: var(--surface);
            color: var(--text);
            font-size: var(--fs-2);
            font-family: inherit;
        }
        .feed-search-input:focus {
            outline: none;
            border-color: var(--accent);
        }
        .feed-search-input::placeholder { color: var(--text-4); }

        /* ── Legend (subtle) ── */
        .legend {
            display: flex;
            gap: 14px;
            flex-wrap: wrap;
            align-items: center;
            margin-bottom: 6px;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            color: var(--text-3);
        }
        .legend-item { display: inline-flex; align-items: center; gap: 6px; }
        .legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
        .legend-dot.red { background: #ff2222; box-shadow: 0 0 0 3px rgba(255,34,34,0.2); }
        .legend-dot.green { background: var(--green); box-shadow: 0 0 0 3px rgba(14,122,77,0.15); }
        .legend-dot.gray { background: var(--text-4); }

        /* ── Article cards — dense terminal rows, not cards ──
           Flat: no radius, no shadow, no gap between rows — a hairline
           bottom border does the separating instead, so the list reads
           as one continuous dense table rather than a stack of floating
           cards. */
        .feed { display: flex; flex-direction: column; }
        .article-card {
            background: var(--surface);
            border-bottom: 1px solid var(--border);
            padding: 6px 10px;
            line-height: 1.35;
            transition: background 0.1s;
            position: relative;
        }
        .article-card:hover { background: var(--bg); }

        /* freshness accent — a left-edge border stripe only (red = last
           15 min, green = last 24h). Used to also carry an animated
           "alarm" glow around the whole card plus a pulsing corner dot;
           both dropped (from here and from .tweet-card.tweet-brand-new,
           which used to carry the same treatment) so the freshness
           signal is exactly the border stripe and nothing more —
           consistent with the dense, flat, minimal-color styling used
           throughout. */
        .article-card.brand-new { border-left: 3px solid #ff2222; }
        .article-card.recent-day { border-left: 3px solid var(--green); }
        /* Keyboard-navigation highlight (J/K to move, Enter to expand
           the AI-analysis panel) — a distinct outline so it reads
           clearly even on top of a freshness border-left stripe. */
        .article-card.selected {
            background: var(--blue-bg);
            box-shadow: inset 2px 0 0 var(--accent);
        }

        /* meta row */
        .card-meta {
            display: flex;
            align-items: center;
            gap: 8px;
            flex-wrap: wrap;
            margin-bottom: 3px;
        }
        /* Plain neutral label, no tinted background — used to be a blue
           pill (var(--accent-soft)); every single article card carries
           one of these, so it was the single biggest source of color on
           the page. See the "keep color minimal" styling pass. */
        .source-tag {
            display: inline-flex;
            align-items: center;
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            color: var(--text-3);
            background: none;
            border: 1px solid var(--border);
            padding: 1px 6px;
        }
        .pub-time {
            font-size: var(--fs-3);
            color: var(--text-3);
            display: inline-flex;
            align-items: center;
            gap: 5px;
        }
        .pub-time .pub-abs {
            font-weight: 600;
            color: var(--text-2);
        }
        .pub-time .pub-rel {
            color: var(--text-4);
        }
        .pub-time .clock {
            width: 12px; height: 12px;
            position: relative;
            display: inline-block;
        }
        .pub-tz-extra {
            display: block;
            font-size: var(--fs-2);
            color: var(--text-4);
            margin-top: 1px;
        }
        .pub-tz-extra .tz-sep { margin: 0 5px; opacity: 0.6; }
        .fresh-label {
            font-size: var(--fs-1);
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.3px;
            padding: 1px 7px;
            border-radius: 10px;
        }
        .fresh-label.brand-new {
            background: #ff2222;
            color: #fff;
            border: 1px solid #ff2222;
            animation: alarm-badge-glow 0.9s ease-in-out infinite;
        }
        @keyframes alarm-badge-glow {
            0%, 100% { box-shadow: 0 0 0 rgba(255,34,34,0); }
            50% { box-shadow: 0 0 8px 2px rgba(255,34,34,0.7); }
        }
        .fresh-label.day { background: var(--green-bg); color: var(--green); border: 1px solid var(--green-brd); }

        /* title */
        .card-title {
            font-family: var(--font-headline);
            font-size: var(--fs-5);
            font-weight: 700;
            line-height: 1.3;
            color: var(--text);
            margin-bottom: 2px;
            letter-spacing: -0.1px;
        }
        .card-title a { color: inherit; }
        .card-title a:hover { color: var(--accent); text-decoration: underline; text-decoration-color: rgba(15,91,216,0.4); text-underline-offset: 2px; }

        /* ── LLM enrichment disclosure ──
           Purple throughout, because it is the one accent the cards don't
           already spend: blue means "link", and green/red/amber all mean
           freshness. Purple therefore reads as one consistent signal —
           "this came from the model, not the publisher". */
        .enrich { margin-top: 7px; }
        .enrich-toggle {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            width: fit-content;
            cursor: pointer;
            list-style: none;          /* Firefox default marker */
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            color: var(--purple);
            background: var(--purple-bg);
            border: 1px solid var(--purple-brd);
            border-radius: 10px;
            padding: 2px 9px;
        }
        .enrich-toggle::-webkit-details-marker { display: none; }
        .enrich-toggle:hover { background: rgba(166, 121, 240, 0.24); }
        .enrich-toggle:focus-visible {
            outline: 2px solid var(--purple);
            outline-offset: 2px;
        }
        /* Caret drawn in CSS rather than the native marker, so it sits
           inside the pill and points down once the panel is open. */
        .enrich-toggle::before {
            content: "";
            width: 0; height: 0;
            border-left: 3.5px solid transparent;
            border-right: 3.5px solid transparent;
            border-top: 4.5px solid currentColor;
            transform: rotate(-90deg);
            transition: transform 0.15s ease;
        }
        .enrich[open] > .enrich-toggle::before { transform: rotate(0deg); }
        @media (prefers-reduced-motion: reduce) {
            .enrich-toggle::before { transition: none; }
        }

        .enrich-body {
            margin-top: 7px;
            padding: 9px 11px;
            background: var(--bg);
            border: 1px solid var(--border);
            border-left: 3px solid var(--purple-brd);
            border-radius: var(--radius);
            display: flex;
            flex-direction: column;
            gap: 9px;
        }
        .enrich-label {
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.4px;
            text-transform: uppercase;
            color: var(--text-4);
            margin-bottom: 3px;
        }
        .enrich-summary {
            font-size: var(--fs-4);
            line-height: 1.5;
            color: var(--text-2);
            margin: 0;
        }

        .asset-chips { display: flex; flex-wrap: wrap; gap: 5px; }
        .asset-chip {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            font-size: var(--fs-2);
            font-weight: 600;
            color: var(--text-2);
            background: var(--surface);
            border: 1px solid var(--border-strong);
            border-radius: 10px;
            padding: 2px 8px;
        }
        .asset-chip .asset-sym {
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            color: var(--accent);
        }
        .asset-chip .asset-class {
            font-size: var(--fs-1);
            text-transform: uppercase;
            letter-spacing: 0.3px;
            font-weight: 700;
            color: var(--text-4);
        }

        .event-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
        .event-item {
            padding-left: 8px;
            border-left: 2px solid var(--border-strong);
        }
        .event-item .event-type {
            font-family: var(--font-mono);
            font-size: var(--fs-1);
            font-weight: 700;
            letter-spacing: 0.3px;
            text-transform: uppercase;
            color: var(--purple);
        }
        .event-item .event-meta {
            font-size: var(--fs-1);
            color: var(--text-4);
            margin-left: 6px;
        }
        .event-item .event-desc {
            font-size: var(--fs-3);
            line-height: 1.45;
            color: var(--text-2);
            margin-top: 1px;
        }

        /* ── Pagination ── */
        .pagination {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            flex-wrap: wrap;
            margin-top: 26px;
            padding-top: 18px;
            border-top: 1px solid var(--border);
        }
        .page-btn {
            min-width: 38px;
            height: 38px;
            padding: 0 12px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            font-size: var(--fs-4);
            font-weight: 600;
            color: var(--text-2);
            background: var(--surface);
            border: 1px solid var(--border-strong);
            border-radius: 6px;
            cursor: pointer;
            transition: background 0.15s, border-color 0.15s, color 0.15s;
            user-select: none;
        }
        .page-btn:hover:not(:disabled):not(.active) {
            background: var(--accent-soft);
            border-color: var(--blue-brd);
            color: var(--accent);
        }
        .page-btn.active {
            background: var(--accent);
            border-color: var(--accent);
            color: #fff;
            cursor: default;
            box-shadow: 0 2px 6px rgba(15,91,216,0.35);
        }
        .page-btn:disabled {
            opacity: 0.45;
            cursor: not-allowed;
        }
        .page-btn.ellipsis {
            border: none;
            background: transparent;
            cursor: default;
            color: var(--text-4);
            min-width: 24px;
            padding: 0 4px;
        }
        .page-info {
            width: 100%;
            text-align: center;
            font-size: var(--fs-3);
            color: var(--text-4);
            margin-top: 4px;
        }

        /* empty / error / spinner */
        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: var(--text-3);
        }
        .empty-state .big { font-size: var(--fs-6); font-weight: 600; color: var(--text-2); margin-bottom: 6px; }
        .empty-state .sub { font-size: var(--fs-4); }
        .error-state {
            text-align: center;
            padding: 40px 20px;
            color: var(--red);
            background: var(--red-bg);
            border: 1px solid var(--red-brd);
            border-radius: var(--radius);
            font-size: var(--fs-4);
        }

        .spinner { display: none; text-align: center; padding: 50px 20px; }
        .spinner.active { display: block; }
        .spinner-ring {
            width: 34px; height: 34px;
            border: 3px solid var(--border-strong);
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
            margin: 0 auto 12px;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
        .spinner-text { color: var(--text-3); font-size: var(--fs-4); }

        .progress-bar-wrap {
            display: flex;
            align-items: center;
            gap: 10px;
            max-width: 320px;
            margin: 14px auto 0;
        }
        .progress-bar-track {
            flex: 1;
            height: 7px;
            background: var(--slate-bg);
            border: 1px solid var(--slate-brd);
            border-radius: 5px;
            overflow: hidden;
        }
        .progress-bar-fill {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, #2b7dff, var(--accent));
            border-radius: 5px;
            transition: width 0.25s ease-out;
        }
        .progress-bar-percent {
            font-size: var(--fs-3);
            font-weight: 700;
            color: var(--text-3);
            min-width: 32px;
            text-align: right;
        }

        /* footer — persistent status bar: sync count, last-updated time,
           keyboard-nav hint. */
        .site-footer {
            flex: none;
            margin-top: 0;
            padding: 8px 14px;
            border-top: 1px solid var(--border);
            text-align: center;
            font-family: var(--font-mono);
            font-size: var(--fs-2);
            color: var(--text-4);
            letter-spacing: 0.2px;
        }
        .footer-sep { margin: 0 8px; opacity: 0.5; }

        /* ── Responsive ── */
        @media (max-width: 768px) {
            .header-inner { padding: 12px 16px; }
            .brand-sub { display: none; }
            .source-select { min-width: 100%; flex: 1; }
            .load-btn { flex: 1; }
            .page { padding: 10px 10px 30px; }
        }
        @media (max-width: 480px) {
            .brand-name { font-size: var(--fs-5); }
        }
