:root {
    --bg-dark: #0a0b10;
    --bg-panel: #13151c;
    --text-primary: #e2e8f0;
    --text-secondary: #94a3b8;
    --accent: #3b82f6;
    --accent-glow: rgba(59, 130, 246, 0.5);
    
    --elite-color: #f59e0b;
    --elite-glow: rgba(245, 158, 11, 0.4);
    
    --hc-color: #10b981;
    --hc-glow: rgba(16, 185, 129, 0.4);

    --border: rgba(255, 255, 255, 0.08);
    --glass-bg: rgba(19, 21, 28, 0.7);
    --row-hover: rgba(255, 255, 255, 0.03);
    --sticky-bg: #1a1d26;
    --th-bg: rgba(255, 255, 255, 0.02);
    --th-hover-bg: rgba(255, 255, 255, 0.05);

    --font-heading: 'Outfit', sans-serif;
    --font-body: 'Inter', sans-serif;
}

body.light-mode {
    --bg-dark: #f5f7fa;
    --bg-panel: #ffffff;
    --text-primary: #0f172a;
    --text-secondary: #475569;
    --border: rgba(0, 0, 0, 0.1);
    --glass-bg: rgba(255, 255, 255, 0.7);
    --row-hover: rgba(0, 0, 0, 0.03);
    --sticky-bg: #eef2f7;
    --th-bg: rgba(0, 0, 0, 0.03);
    --th-hover-bg: rgba(0, 0, 0, 0.05);
}

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

body {
    background-color: var(--bg-dark);
    color: var(--text-primary);
    font-family: var(--font-body);
    -webkit-font-smoothing: antialiased;
    min-height: 100vh;
    overflow-x: hidden;
}

/* Scrollbar styling */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
::-webkit-scrollbar-track {
    background: var(--bg-dark); 
}
::-webkit-scrollbar-thumb {
    background: #334155; 
    border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
    background: #475569; 
}

/* Layout */
.app-container {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

/* Header */
.glass-header {
    background: var(--glass-bg);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
    padding: 1.5rem 2rem;
    position: sticky;
    top: 0;
    z-index: 100;
}

.header-content {
    /* 3-col grid centers the title; controls floated to the right column. */
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    width: 100%;
    margin: 0 auto;
    gap: 1rem;
}
.header-content .logo-area {
    grid-column: 2;
    text-align: center;
}
.header-content .controls-area {
    grid-column: 3;
    justify-self: end;
}

.logo-area h1 {
    font-family: var(--font-heading);
    font-size: 1.8rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: #fff;
}

.logo-area h1 span {
    color: var(--accent);
}

.subtitle {
    font-size: 0.9rem;
    color: var(--text-secondary);
    margin-top: 0.2rem;
    font-weight: 500;
}

/* Toggle Switch and Language Selectors */
.controls-area {
    display: flex;
    align-items: center;
    gap: 1.5rem;
}

.lang-selector {
    display: flex;
    background: rgba(0, 0, 0, 0.2);
    border-radius: 8px;
    border: 1px solid var(--border);
    overflow: hidden;
}

.lang-btn {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    padding: 0.5rem 1rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    font-family: inherit;
}

.lang-btn:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.05);
}

.lang-btn.active {
    background: rgba(59, 130, 246, 0.2);
    color: var(--accent);
    box-shadow: inset 0 -2px 0 var(--accent);
}

/* Main Content & Table */
.table-container {
    flex: 1;
    padding: 1rem 1.25rem;
    overflow: hidden; /* we handle scroll in wrapper */
    display: flex;
    justify-content: center;
}

.table-wrapper {
    /* width:auto lets the wrapper hug the table on wide screens so the flex parent can center it.
       max-width:100% keeps it capped on narrow screens so the overflow:auto scroll still works. */
    width: auto;
    max-width: 100%;
    background: var(--bg-panel);
    border-radius: 12px;
    border: 1px solid var(--border);
    overflow: auto;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    /* Hide scrollbars to reclaim space — wheel, keyboard, and touch still scroll */
    scrollbar-width: none;           /* Firefox */
    -ms-overflow-style: none;        /* legacy Edge / IE */
}
.table-wrapper::-webkit-scrollbar {  /* Chromium, Safari */
    display: none;
}

/* Filter bar above the list — Chokepoint/Bottleneck slider plus inline explainer */
.position-filter-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    padding: 0.9rem 1.25rem 0.5rem;
    flex-wrap: wrap;
}
.position-explainer {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    font-size: 0.78rem;
    color: var(--text-secondary);
    line-height: 1.45;
    text-align: left;
}
.position-explainer strong {
    color: var(--text-primary);
    font-weight: 600;
}

/* Sliding-pill segmented control: a moving accent thumb shows which side is active */
.position-toggle {
    position: relative;
    display: inline-flex;
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 4px;
    overflow: hidden;
}
body.light-mode .position-toggle {
    background: rgba(0, 0, 0, 0.05);
}
.position-toggle-thumb {
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: 4px;
    /* Thumb spans one third of the inner area (container minus 8px padding).
       translateX(100%) moves it by exactly one button width since each button
       is also 1/3 of the inner area. */
    width: calc((100% - 8px) / 3);
    border-radius: 999px;
    background: var(--accent);
    box-shadow: 0 0 12px var(--accent-glow);
    transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
    pointer-events: none;
    z-index: 0;
}
.position-toggle.chokepoint-active .position-toggle-thumb {
    transform: translateX(100%);
}
.position-toggle.bottleneck-active .position-toggle-thumb {
    transform: translateX(200%);
}
.position-btn {
    position: relative;
    z-index: 1;
    flex: 1;
    min-width: 120px;
    padding: 0.5rem 1.1rem;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    border-radius: 999px;
    transition: color 0.28s ease;
}
.position-btn.active {
    color: #ffffff;
}
.position-btn:hover:not(.active) {
    color: var(--text-primary);
}

table {
    /* width:auto keeps columns content-sized so they don't stretch to fill a 4K viewport */
    width: auto;
    border-collapse: collapse;
    text-align: left;
    white-space: nowrap;
}

th, td {
    padding: 0.4rem 0.5rem;
    border-bottom: 1px solid var(--border);
}

th {
    background: var(--th-bg);
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.82rem;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    position: sticky;
    top: 0;
    z-index: 10;
    backdrop-filter: blur(10px);
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.2s, background 0.2s;
}

th:hover {
    color: var(--text-primary);
    background: var(--th-hover-bg);
}

/* Sort icons visible on desktop. Mobile hides them via the 480px media
   query to claw back ~14px per column. */
th .sort-icon {
    display: inline-block;
    margin-left: 5px;
    font-size: 0.72rem;
    opacity: 0.3;
}
th.asc .sort-icon,
th.desc .sort-icon {
    opacity: 1;
    color: var(--accent);
}

/* Base Truncated Style */
td {
    font-size: 0.92rem;
    color: var(--text-primary);
    vertical-align: middle;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Cap cell width so a long Target string ('GBp 900-1,600 (FY2028-31...')
       doesn't stretch its column past everything else. Truncates with
       ellipsis; full text still visible on row tap-expand. */
    max-width: 200px;
}

/* Sticky left columns: SuperCycle (1), Chart button (2), and Ticker (3).
   SuperCycle uses content-sized wrapping boxes (widest 4-tag layout needs
   ~92px). Chart column is a narrow strip for the icon button. Ticker
   carries a logo + symbol. All three stay pinned during horizontal scroll
   so the chart icon and symbol are always reachable on phones. */
th:nth-child(1), td:nth-child(1) { width: 95px; max-width: 105px; min-width: 88px; text-align: center; position: sticky; left: 0; z-index: 20; background: var(--bg-panel); }
th:nth-child(2), td:nth-child(2) { width: 36px; max-width: 36px; min-width: 32px; text-align: center; position: sticky; left: 95px; z-index: 20; background: var(--bg-panel); padding-left: 2px; padding-right: 2px; }
th:nth-child(3), td:nth-child(3) { width: 110px; max-width: 130px; min-width: 100px; position: sticky; left: 131px; z-index: 20; background: var(--bg-panel); border-right: 1px solid var(--border); }

/* Fix header z-index for sticky cols */
th:nth-child(1), th:nth-child(2), th:nth-child(3) {
    z-index: 30;
    background: var(--sticky-bg);
}

/* Expansion logic */
tbody tr {
    cursor: pointer;
    transition: background-color 0.2s;
}

tbody tr:hover td {
    background-color: var(--row-hover);
}

tr.row-expanded td {
    white-space: pre-wrap;
    overflow: visible;
    text-overflow: clip;
    max-width: none;
    vertical-align: top;
    background-color: rgba(255, 255, 255, 0.02); /* slight highlight when expanded */
}

/* Ensure sticky columns maintain their background in expanded/hover states */
/* But also get the hover/expanded background mix */
tbody tr:hover td:nth-child(1),
tbody tr:hover td:nth-child(2),
tbody tr:hover td:nth-child(3) {
    background-color: var(--sticky-bg);
}
tr.row-expanded td:nth-child(1),
tr.row-expanded td:nth-child(2),
tr.row-expanded td:nth-child(3) {
    background-color: var(--sticky-bg);
}

/* === SuperCycle column: tiny color-coded pills =====
   Each box is content-sized (not forced to 50% column width), so labels
   like "800G" and "Other" never get truncated. flex-wrap lets boxes flow
   onto a second row when needed. Up to 4 tags per row in the data; with
   typical character widths, AI+CPO fits one row and 800G+1.6T fits the
   next, giving a natural 2x2 layout for the maximum case without forcing
   it visually. */
.cycle-cell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 2px;
    width: 100%;
    line-height: 1;
}
.cycle-box {
    flex: 0 0 auto;
    font-size: 0.6rem;
    font-weight: 700;
    padding: 2px 5px;
    border-radius: 3px;
    text-align: center;
    color: #ffffff;
    letter-spacing: 0;
    white-space: nowrap;
}
.cycle-box[data-cycle="AI"]     { background: #3b82f6; }
.cycle-box[data-cycle="CPO"]    { background: #8b5cf6; }
.cycle-box[data-cycle="800G"]   { background: #10b981; }
.cycle-box[data-cycle="1.6T"]   { background: #06b6d4; }
.cycle-box[data-cycle="Other"]  { background: #6b7280; }

/* === Mobile P/E year-tag ===
   Tiny '27-28 label stacked above the 'P/E' header text on phones, so
   the time window of the multiplier is unambiguous. Block-display
   creates the two-line stack within the table cell. Hidden by default
   so it doesn't appear in Cycle mode — labelFor only renders this
   span in mobile P/E mode. */
.pe-year-tag {
    display: block;
    font-size: 0.48rem;
    font-weight: 500;
    color: var(--text-secondary);
    line-height: 1;
    letter-spacing: 0;
    margin-bottom: 1px;
    text-transform: none;
}

/* === Cycle/P-E swap button (mobile-only) ===
   Appears in the SuperCycle column header on phones, lets the user flip
   the column's contents between cycle pills and the 2027-28 P/E value.
   Hidden on desktop, where both columns are visible already. */
.col-mode-toggle {
    display: none;   /* desktop: hidden — both columns are visible */
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin-left: 2px;
    padding: 0;
    background: rgba(59, 130, 246, 0.12);
    border: 1px solid rgba(59, 130, 246, 0.4);
    border-radius: 4px;
    color: var(--accent);
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    vertical-align: middle;
    transition: background 0.18s ease, color 0.18s ease;
}
.col-mode-toggle:hover {
    background: var(--accent);
    color: #ffffff;
}

/* === 2027-28 P/E rendering =====================================
   We show only the multiplier range (e.g. '20-28x') on both desktop
   and mobile. The narrative annotation that lives in parens in the
   xlsx is dropped at render — deep-dive markdown carries the
   commentary if/when a deeper read is wanted. */
.pe-range {
    font-weight: 600;
    color: var(--text-primary);
}
.pe-compact {
    display: inline-block;
    font-weight: 600;
    font-size: 0.72rem;
    color: var(--text-primary);
    line-height: 1.1;
    text-align: center;
    width: 100%;
}

/* Mobile: tighter padding + smaller font */
@media (max-width: 480px) {
    .cycle-box {
        font-size: 0.5rem;
        padding: 1px 3px;
        border-radius: 2px;
    }
    .cycle-cell {
        gap: 1px;
    }
}

/* === Chart-icon button (between Cycle and Ticker) ===
   Small icon-only affordance that opens the TradingView modal. Sits in its
   own narrow sticky-left column so it's always reachable on phones, no
   matter how far the user has horizontally scrolled the table. */
.chart-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.chart-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: rgba(59, 130, 246, 0.1);
}
.chart-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.chart-btn svg {
    display: block;
}

/* === TradingView chart container inside the modal ===
   The iframe needs an explicit-height parent. JS adds .modal-chart to the
   modal-content when entering chart mode so we can strip the padded
   markdown layout in favor of edge-to-edge. Two selectors so the rule
   works in every browser (the :has() form is the future, the .modal-chart
   class is the reliable one today). */
.modal-content.modal-chart,
.modal-content:has(.tv-chart-container) {
    padding: 0;
    /* Override modal-content's overflow-y so the chart iframe can fill the
       container without an unwanted outer scrollbar. */
    overflow: hidden;
}
.tv-chart-container {
    width: 100%;
    /* Use 100% of the available modal-content height so the chart fills
       whatever space the modal panel gives it. Fallback fixed height for
       legacy flex implementations. */
    height: 100%;
    min-height: 480px;
    display: flex;
    flex-direction: column;
    /* Light/dark backdrop while the iframe loads — prevents a flash of the
       modal background showing through during TradingView's boot. */
    background: var(--bg-panel);
}
/* Top-right toolbar — always-visible "Open in TradingView" link gives
   adblocker users a way out even before the blocked-state overlay fires. */
.tv-chart-toolbar {
    flex-shrink: 0;
    display: flex;
    justify-content: flex-end;
    padding: 0.4rem 0.65rem;
    border-bottom: 1px solid var(--border);
    background: var(--bg-panel);
}
.tv-open-link {
    color: var(--accent);
    font-size: 0.78rem;
    font-weight: 600;
    text-decoration: none;
    padding: 0.25rem 0.55rem;
    border-radius: 5px;
    border: 1px solid transparent;
    transition: background 0.18s ease, border-color 0.18s ease;
}
.tv-open-link:hover {
    background: rgba(59, 130, 246, 0.1);
    border-color: rgba(59, 130, 246, 0.4);
}
.tv-chart-frame-wrap {
    position: relative;
    flex: 1;
    min-height: 0;
}
.tv-chart-container iframe.tv-chart-iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    /* Absolute fill so the iframe stretches to the frame-wrap regardless of
       browser flex-layout quirks (some Safari versions don't honor 100% on
       iframe inside a flex:1 parent without this). */
    position: absolute;
    inset: 0;
}

/* Mobile: tighten toolbar, reduce min-height so the chart fits short
   landscape viewports. */
@media (max-width: 480px) {
    .tv-chart-container {
        min-height: 360px;
    }
    .tv-chart-toolbar {
        padding: 0.35rem 0.5rem;
    }
    .tv-open-link {
        font-size: 0.72rem;
        padding: 0.2rem 0.45rem;
    }
}

/* === Ticker cell with logo (left of symbol, à la TradingView) === */
.ticker-cell {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    overflow: hidden;
}
.ticker-logo {
    position: relative;
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    border-radius: 50%;
    /* Letter avatar shows behind the image; gradient varies by --accent
       so the visual still pops in light/dark. */
    background: linear-gradient(135deg, var(--accent), #1e40af);
    overflow: hidden;
    font-size: 0.7rem;
    font-weight: 700;
    color: #ffffff;
    text-transform: uppercase;
    line-height: 1;
}
.ticker-logo::before {
    content: attr(data-initial);
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ticker-logo img {
    /* Image sits on top of the letter avatar; if onerror hides it, the
       letter remains visible. White background keeps logos with
       transparent backgrounds readable on the dark theme. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #ffffff;
    z-index: 1;
}
.ticker-symbol {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}


.badge {
    display: inline-block;
    padding: 0.18rem 0.4rem;
    border-radius: 5px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.badge.elite {
    background: rgba(245, 158, 11, 0.15);
    color: var(--elite-color);
    border: 1px solid rgba(245, 158, 11, 0.3);
    box-shadow: 0 0 10px var(--elite-glow);
}

.badge.score-glow {
    background: rgba(0, 255, 128, 0.2);
    color: #00ff80;
    border: 1px solid #00ff80;
    box-shadow: 0 0 20px 6px rgba(0, 255, 128, 0.6);
    text-shadow: 0 0 5px rgba(0, 255, 128, 0.8);
}

/* Tiered score badges for Total / Base / Entry.
   Fill intensifies as score rises so higher numbers have more "green volume". */
.badge.score-tier-1 {
    background: rgba(16, 185, 129, 0.18);
    color: #34d399;
    border: 1px solid rgba(16, 185, 129, 0.4);
}
.badge.score-tier-2 {
    background: rgba(16, 185, 129, 0.55);
    color: #ffffff;
    border: 1px solid rgba(16, 185, 129, 0.8);
}
.badge.score-tier-3 {
    background: #047857;
    color: #ffffff;
    border: 1px solid #065f46;
    box-shadow: 0 0 18px rgba(16, 185, 129, 0.5);
    text-shadow: 0 0 4px rgba(4, 120, 87, 0.9);
}

body.light-mode .badge.score-tier-1 {
    background: #d1fae5;
    color: #065f46;
    border-color: #6ee7b7;
}
body.light-mode .badge.score-tier-2 {
    background: #10b981;
    color: #ffffff;
    border-color: #059669;
}
body.light-mode .badge.score-tier-3 {
    background: #064e3b;
    color: #ffffff;
    border-color: #022c22;
    box-shadow: 0 0 10px rgba(5, 150, 105, 0.35);
    text-shadow: none;
}

/* Red tiers — mirror of the green tiers, deeper red as the score drops */
.badge.score-tier-red-1 {
    background: rgba(239, 68, 68, 0.18);
    color: #f87171;
    border: 1px solid rgba(239, 68, 68, 0.4);
}
.badge.score-tier-red-2 {
    background: rgba(239, 68, 68, 0.55);
    color: #ffffff;
    border: 1px solid rgba(239, 68, 68, 0.8);
}
.badge.score-tier-red-3 {
    background: #991b1b;
    color: #ffffff;
    border: 1px solid #7f1d1d;
    box-shadow: 0 0 18px rgba(239, 68, 68, 0.5);
    text-shadow: 0 0 4px rgba(153, 27, 27, 0.9);
}

body.light-mode .badge.score-tier-red-1 {
    background: #fee2e2;
    color: #991b1b;
    border-color: #fecaca;
}
body.light-mode .badge.score-tier-red-2 {
    background: #ef4444;
    color: #ffffff;
    border-color: #dc2626;
}
body.light-mode .badge.score-tier-red-3 {
    background: #7f1d1d;
    color: #ffffff;
    border-color: #450a0a;
    box-shadow: 0 0 10px rgba(220, 38, 38, 0.35);
    text-shadow: none;
}

/* Gradient score badge — colors are computed per-row and applied inline.
   This class just handles the shared structural bits. */
.badge.score-gradient {
    border: 1px solid transparent;
    font-weight: 700;
    min-width: 26px;
    text-align: center;
}

/* === EXTREME score badge (Total/Entry > 100, scale-breaking) ===
   Layered on top of .score-gradient: a gold border + slow pulsing glow,
   plus a subtle text-shadow so the number itself pops a touch. Rare by
   design — only fires when ratio > 4× AND base ≥ 80. */
.badge.score-extreme {
    /* The inline `background:` (max-green from scoreColor) stays. We just
       overlay a gold border and a pulsing gold halo. */
    border: 2px solid #fbbf24 !important;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
    /* Slight inset glow + outer pulsing glow */
    animation: extreme-glow 2.4s ease-in-out infinite;
    position: relative;
    z-index: 1;
}
@keyframes extreme-glow {
    0%, 100% {
        box-shadow:
            inset 0 0 4px rgba(255, 220, 130, 0.35),
            0 0 6px rgba(251, 191, 36, 0.45),
            0 0 14px rgba(251, 191, 36, 0.25);
    }
    50% {
        box-shadow:
            inset 0 0 6px rgba(255, 220, 130, 0.55),
            0 0 12px rgba(251, 191, 36, 0.85),
            0 0 24px rgba(251, 191, 36, 0.55);
    }
}

/* Light mode: same gold but slightly subdued so it's not blinding on white */
body.light-mode .badge.score-extreme {
    border-color: #b45309 !important;
    text-shadow: 0 0 3px rgba(255, 255, 255, 0.6);
    animation: extreme-glow-light 2.4s ease-in-out infinite;
}
@keyframes extreme-glow-light {
    0%, 100% {
        box-shadow:
            inset 0 0 4px rgba(180, 83, 9, 0.25),
            0 0 6px rgba(217, 119, 6, 0.45),
            0 0 14px rgba(217, 119, 6, 0.2);
    }
    50% {
        box-shadow:
            inset 0 0 6px rgba(180, 83, 9, 0.45),
            0 0 12px rgba(217, 119, 6, 0.75),
            0 0 22px rgba(217, 119, 6, 0.45);
    }
}

/* Mobile: keep the gold border but tone down the animation distance so
   the glow doesn't bleed across the tight cell padding. */
@media (max-width: 480px) {
    .badge.score-extreme {
        border-width: 1.5px !important;
        animation: extreme-glow-mobile 2.4s ease-in-out infinite;
    }
    @keyframes extreme-glow-mobile {
        0%, 100% { box-shadow: 0 0 4px rgba(251, 191, 36, 0.5); }
        50%      { box-shadow: 0 0 8px rgba(251, 191, 36, 0.85); }
    }
}

/* Theme-toggle icon buttons — compact and center the SVG */
.theme-btn {
    padding: 0.4rem 0.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.theme-btn svg {
    display: block;
}

/* Flag buttons — swap the text label for a compact flag icon */
.flag-btn {
    padding: 0.4rem 0.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.flag-icon {
    display: block;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
    pointer-events: none; /* clicks pass through to the button */
}
body.light-mode .flag-icon {
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}

.badge.hc {
    background: rgba(16, 185, 129, 0.15);
    color: var(--hc-color);
    border: 1px solid rgba(16, 185, 129, 0.3);
}

.badge.standard {
    background: rgba(148, 163, 184, 0.15);
    color: var(--text-secondary);
    border: 1px solid var(--border);
}

/* Animations */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

tbody tr {
    animation: fadeIn 0.4s ease forwards;
    opacity: 0;
}

/* Dropdown Styles */
.dropdown-container {
    position: relative;
    display: inline-block;
}

.dropdown-btn {
    border: 1px solid var(--border);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.2);
}

.dropdown-content {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 120%;
    right: 0;
    background: var(--sticky-bg);
    min-width: 220px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.8);
    z-index: 300;
    border-radius: 8px;
    border: 1px solid var(--border);
    padding: 0.8rem 0;
    max-height: 400px;
    overflow-y: auto;
}

.dropdown-content.hidden {
    display: none;
}

.dropdown-item {
    padding: 0.5rem 1.2rem;
    color: var(--text-primary);
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    gap: 0.8rem;
    cursor: pointer;
    transition: background 0.2s;
    user-select: none;
}

.dropdown-item:hover {
    background: rgba(255, 255, 255, 0.08);
}

.dropdown-item input {
    cursor: pointer;
    accent-color: var(--accent);
    width: 14px;
    height: 14px;
}

/* Narrow screens: tighten everything further so columns aren't padded out on mobile */
@media (max-width: 768px) {
    .glass-header {
        padding: 0.8rem 0.9rem;
    }
    .header-content {
        flex-wrap: wrap;
        gap: 0.6rem;
    }
    .controls-area {
        gap: 0.6rem;
        flex-wrap: wrap;
    }
    .logo-area h1 {
        font-size: 1.3rem;
    }
    .subtitle {
        font-size: 0.75rem;
    }
    .table-container {
        padding: 0.5rem;
    }
    th, td {
        padding: 0.35rem 0.4rem;
        font-size: 0.8rem;
    }
    td {
        max-width: 160px;
    }
    .badge {
        padding: 0.15rem 0.35rem;
        font-size: 0.7rem;
    }
    /* Shrink the sticky left columns. Three sticky cols now: Cycle (1),
       Chart button (2), Ticker (3). Each col's `left:` matches the cumulative
       `width:` of the preceding sticky cols, so they tile flush during
       horizontal scroll without overlap. */
    th:nth-child(1), td:nth-child(1) { width: 80px; max-width: 90px; min-width: 76px; }
    th:nth-child(2), td:nth-child(2) { width: 30px; max-width: 30px; min-width: 28px; left: 80px; }
    th:nth-child(3), td:nth-child(3) { width: 96px; max-width: 110px; min-width: 88px; left: 110px; }
}

/* Phone-portrait optimization (iPhone 16 Pro Max ≈ 440px CSS, smaller phones
   down to ~360px). Goal: every data column visible at a glance, no horizontal
   scroll. Hide the Name column, kill sort icons, collapse padding, and force
   the position-type toggle's buttons to equal widths so the slider thumb
   doesn't bleed across button boundaries. */
@media (max-width: 480px) {
    /* === Header chrome === */
    .glass-header {
        padding: 0.55rem 0.6rem;
    }
    .header-content {
        /* Stack title above controls on phone — saves horizontal real estate */
        grid-template-columns: 1fr;
        gap: 0.5rem;
    }
    .header-content .logo-area,
    .header-content .controls-area {
        grid-column: 1;
        justify-self: center;
    }
    .logo-area h1 {
        font-size: 1.15rem;
    }
    .subtitle {
        font-size: 0.68rem;
    }
    .controls-area {
        gap: 0.4rem;
    }

    /* === Position-type toggle ===
       Force equal-width segments so the slider thumb (which assumes 1/3
       widths) doesn't bleed past the active button. min-width: 80px fits
       "Chokepoint" at the chosen font; flex:1 lets them all grow equally
       when there's extra room. */
    .position-filter-bar {
        gap: 0.5rem;
        padding: 0.5rem 0.4rem 0.3rem;
    }
    .position-btn {
        flex: 1 1 0;
        min-width: 80px;
        font-size: 0.72rem;
        padding: 0.4rem 0.4rem;
    }
    .position-explainer {
        font-size: 0.68rem;
    }

    /* === Table: hide Target on mobile, kill sort icons, collapse padding ===
       Name was already removed from simpleCols entirely. We hide Target
       (the long 'GBp 900-1,600 (FY2028…)' values) and keep Upside (the
       compact '2.4x-5.4x' multiplier) since Upside reads cleanly in the
       narrow phone column and is the more useful at-a-glance signal.
       Both reappear in landscape (>480px). */
    .table-container {
        padding: 0.3rem 0.2rem;
    }

    /* Hide the 2027-28 P/E column (10th — replaced Target) on phones.
       Keeps the row scannable on iPhone vertical with Cycle / Chart /
       Ticker / Total / Base / Entry / Price / Chg% / Upside all
       visible. The P/E value remains reachable via the Cycle/P-E swap
       button in the SuperCycle column header. */
    th:nth-child(10), td:nth-child(10) {
        display: none;
    }

    /* Reveal the Cycle/P-E swap button on phones (hidden by default on
       desktop where both columns are visible side-by-side). */
    .col-mode-toggle {
        display: inline-flex;
        width: 14px;
        height: 14px;
        font-size: 0.55rem;
        border-radius: 3px;
        margin-left: 1px;
    }

    /* Sort glyph (↕ ↑ ↓) eats too much horizontal real estate on small
       screens — every column header gives up ~12px to it. Hidden on phone;
       headers are still clickable, the cursor stays pointer, and the
       hover/active background still telegraphs sortability. */
    th .sort-icon {
        display: none;
    }

    th, td {
        padding: 0.28rem 0.18rem;
        font-size: 0.7rem;
    }
    th {
        font-size: 0.62rem;
        letter-spacing: 0;
        padding: 0.3rem 0.18rem;
    }
    td {
        max-width: 110px;
    }

    /* Sticky columns on phone: SuperCycle (1) + Chart (2) + Ticker (3).
       Tight budget — iPhone 15 Pro Max vertical is ~430px CSS pixels.
       3 sticky cols claim ~152px, leaving ~278px for the 6 remaining
       data cols (Total/Base/Entry/Price/Chg%/Upside) at ~46px each.
       Cumulative left offsets are critical: each col's left = sum of
       preceding cols' width, so they tile flush during scroll. */
    th:nth-child(1), td:nth-child(1) {
        width: 64px; max-width: 72px; min-width: 60px;
        padding-left: 0.1rem; padding-right: 0.1rem;
    }
    th:nth-child(2), td:nth-child(2) {
        width: 24px; max-width: 24px; min-width: 22px;
        left: 64px;
        padding-left: 1px; padding-right: 1px;
    }
    th:nth-child(3), td:nth-child(3) {
        width: 64px; max-width: 76px; min-width: 60px;
        left: 88px;
        padding-left: 0.1rem; padding-right: 0.15rem;
    }

    /* Smaller chart button on phones to fit the narrow sticky column. */
    .chart-btn {
        width: 22px;
        height: 22px;
        border-radius: 4px;
    }
    .chart-btn svg {
        width: 12px;
        height: 12px;
    }

    /* Smaller ticker logo on phones — 14px circle with tighter gap. */
    .ticker-logo {
        width: 14px;
        height: 14px;
        font-size: 0.5rem;
    }
    .ticker-cell {
        gap: 0.25rem;
    }

    /* Score badges — minimum width removed so cells collapse to badge size,
       padding tightened so Total/Base/Entry don't accumulate visual gaps. */
    .badge {
        padding: 0.08rem 0.25rem;
        font-size: 0.62rem;
        min-width: 0;
        border-radius: 3px;
    }
    /* Disable the gradient glow on small screens — it bleeds visually at
       this size and adds no information. */
    .badge.score-gradient {
        box-shadow: none;
    }
}

/* === Deep-Dive Modal ============================================
   Triggered by clicking a ticker. Content fetched from /deep-dives/<TICKER>.md
   on demand and rendered with marked.js. Sticky header guarantees the close
   button stays accessible no matter how far the user scrolls. */
.modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4vh 1rem;
    /* Make the modal itself the scroll container if a tiny phone forces it. */
    overflow: auto;
}
.modal.hidden {
    display: none;
}
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    cursor: pointer;
}
.modal-panel {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 760px;
    max-height: 92vh;
    background: var(--bg-panel);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal-header {
    /* Sticky-ish: not strictly position:sticky because the panel is flex
       column with the content overflow:auto below — header sits at top of
       the flex layout, which is functionally the same. Always visible. */
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1rem 0.85rem 1.25rem;
    border-bottom: 1px solid var(--border);
    background: var(--bg-panel);
}
.modal-header h2 {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    /* Truncate long titles instead of wrapping to keep the header compact */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.modal-close {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text-secondary);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.18s, color 0.18s, border-color 0.18s;
    margin-left: 0.75rem;
}
.modal-close:hover {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-primary);
    border-color: var(--text-secondary);
}
body.light-mode .modal-close:hover {
    background: rgba(0, 0, 0, 0.05);
}
.modal-content {
    overflow-y: auto;
    overflow-x: hidden;
    padding: 1.25rem 1.5rem 1.5rem;
    flex: 1;
    line-height: 1.6;
    -webkit-overflow-scrolling: touch;
}
.modal-loading {
    color: var(--text-secondary);
    text-align: center;
    padding: 2rem 0;
}

/* Markdown content styling — keep readable, not too dense */
.modal-content h1,
.modal-content h2,
.modal-content h3,
.modal-content h4 {
    font-family: var(--font-heading);
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.25;
    margin-top: 1.4em;
    margin-bottom: 0.5em;
}
.modal-content h1:first-child,
.modal-content h2:first-child,
.modal-content h3:first-child {
    margin-top: 0;
}
.modal-content h1 { font-size: 1.4rem; border-bottom: 1px solid var(--border); padding-bottom: 0.4rem; }
.modal-content h2 { font-size: 1.15rem; }
.modal-content h3 { font-size: 1rem; }
.modal-content h4 { font-size: 0.92rem; color: var(--text-secondary); }
.modal-content p { margin: 0.75em 0; }
.modal-content ul, .modal-content ol { padding-left: 1.4em; margin: 0.6em 0; }
.modal-content li { margin: 0.25em 0; }
.modal-content strong { color: var(--text-primary); font-weight: 600; }
.modal-content em { color: var(--text-primary); }
.modal-content code {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85em;
    background: rgba(255, 255, 255, 0.06);
    padding: 0.1em 0.35em;
    border-radius: 4px;
}
body.light-mode .modal-content code {
    background: rgba(0, 0, 0, 0.06);
}
.modal-content pre {
    background: rgba(0, 0, 0, 0.25);
    padding: 0.9rem 1rem;
    border-radius: 6px;
    overflow-x: auto;
    margin: 0.8em 0;
}
body.light-mode .modal-content pre {
    background: rgba(0, 0, 0, 0.06);
}
.modal-content pre code {
    background: transparent;
    padding: 0;
    font-size: 0.83em;
    line-height: 1.5;
}
.modal-content a {
    color: var(--accent);
    text-decoration: none;
}
.modal-content a:hover {
    text-decoration: underline;
}
.modal-content blockquote {
    border-left: 3px solid var(--accent);
    margin: 0.8em 0;
    padding: 0.4em 1em;
    color: var(--text-secondary);
}
.modal-content hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 1.5em 0;
}
/* Tables inside the deep-dive modal need to *override* the main portfolio
   table's td rules (white-space:nowrap, overflow:hidden, ellipsis,
   max-width:200px). Otherwise cell contents like 'Contracted' or 'F...'
   get truncated to a few characters. */
.md-table-wrap {
    overflow-x: auto;
    margin: 0.9em 0;
    -webkit-overflow-scrolling: touch;
    /* Subtle border so the scroll-container is visually contained */
    border-radius: 6px;
}
.md-table-wrap table {
    margin: 0;
}
.modal-content table {
    border-collapse: collapse;
    width: 100%;
    table-layout: auto;
    font-size: 0.88em;
}
.modal-content th,
.modal-content td {
    /* RESET the global td/th rules from the portfolio table */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    max-width: none;
    vertical-align: top;
    line-height: 1.45;
    /* Modal-specific styling */
    border: 1px solid var(--border);
    padding: 0.45em 0.7em;
    text-align: left;
    font-size: inherit;
    color: var(--text-primary);
}
.modal-content th {
    background: rgba(255, 255, 255, 0.04);
    font-family: var(--font-body);
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    /* Override the main table's sticky+pointer th */
    position: static;
    cursor: default;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    z-index: auto;
}
body.light-mode .modal-content th {
    background: rgba(0, 0, 0, 0.04);
}

/* Ticker symbol — default rendering is plain text. The link styling
   (accent color, dotted underline, ↗ chevron) is gated on the
   .has-deep-dive class which the JS adds only for tickers present in
   deep-dives/index.json. Tickers without a deep-dive look ordinary —
   implicit signal to the user that there's nothing to click through to. */
.ticker-symbol {
    /* Plain text, no affordance. */
    cursor: default;
    color: var(--text-primary);
    text-decoration: none;
}

/* Link affordance, only when a deep-dive exists for this ticker */
.ticker-symbol.has-deep-dive {
    cursor: pointer;
    color: var(--accent);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: text-decoration-style 0.15s ease, text-decoration-thickness 0.15s ease;
}
.ticker-symbol.has-deep-dive::after {
    content: " ↗";
    font-size: 0.8em;
    color: var(--accent);
    opacity: 0.7;
    transition: opacity 0.2s ease;
    margin-left: 2px;
    font-weight: 700;
}
.ticker-symbol.has-deep-dive:hover {
    text-decoration-style: solid;
    text-decoration-thickness: 2px;
}
.ticker-symbol.has-deep-dive:hover::after {
    opacity: 1;
}

/* === SuperCycle multi-select filter + deep-dive search ==========
   3-column grid keeps the pill group visually centered while the
   search input pins to the far right. Spacer column on the left
   mirrors the search column's width so the pills stay optically
   centered. Composes with the position-toggle (All/Chokepoint/
   Bottleneck) above. Each pill is independently toggleable. Active
   pill = filled accent, inactive = outlined. */
.supercycle-bar {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem;
    padding: 0 1.25rem 0.5rem;
}
.sc-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
}
.sc-spacer {
    /* empty mirror of the search column — keeps the pill group optically
       centered between equal-width side columns */
}
.supercycle-bar .filter-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-right: 0.4rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}
.sc-pill {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-secondary);
    padding: 0.32rem 0.8rem;
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
    user-select: none;
}
.sc-pill:hover:not(.active) {
    color: var(--text-primary);
    border-color: var(--text-secondary);
}
.sc-pill.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #ffffff;
}
.sc-pill.active:hover {
    /* keep the active state visually stable on hover */
    background: var(--accent);
}

/* === Deep-dive search input + result dropdown ==================
   Lives in the right column of the supercycle-bar grid. Dropdown
   floats absolutely below the input so its height doesn't bump the
   filter row. */
.sc-search {
    position: relative;
    justify-self: end;
    width: 220px;
    max-width: 100%;
}
.sc-search-input {
    width: 100%;
    padding: 0.4rem 0.6rem 0.4rem 1.9rem;
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 0.82rem;
    transition: border-color 0.18s ease, background 0.18s ease;
    /* Override default search-input clear button (×) which doesn't match our theme */
    -webkit-appearance: none;
    appearance: none;
}
body.light-mode .sc-search-input {
    background: rgba(0, 0, 0, 0.04);
}
.sc-search-input::placeholder {
    color: var(--text-secondary);
}
.sc-search-input:focus {
    outline: none;
    border-color: var(--accent);
    background: rgba(0, 0, 0, 0.35);
}
body.light-mode .sc-search-input:focus {
    background: rgba(0, 0, 0, 0.02);
}
.sc-search-icon {
    position: absolute;
    left: 0.55rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-secondary);
    pointer-events: none;
    display: inline-flex;
    align-items: center;
}
.sc-search-results {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--sticky-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
    z-index: 200;
    max-height: 320px;
    overflow-y: auto;
}
.sc-search-results.hidden {
    display: none;
}
.sc-search-result {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    padding: 0.45rem 0.7rem;
    cursor: pointer;
    border-bottom: 1px solid var(--border);
    transition: background 0.12s ease;
}
.sc-search-result:last-child {
    border-bottom: none;
}
.sc-search-result:hover,
.sc-search-result.active {
    background: rgba(59, 130, 246, 0.12);
}
.sc-search-result-ticker {
    font-weight: 600;
    color: var(--accent);
    font-size: 0.85rem;
}
.sc-search-result-name {
    font-size: 0.74rem;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sc-search-empty {
    padding: 0.7rem 0.9rem;
    color: var(--text-secondary);
    font-size: 0.82rem;
    text-align: center;
}

/* Mobile: tighter pills, smaller font */
@media (max-width: 480px) {
    .supercycle-bar {
        /* Collapse to single column: pills row above, search row below */
        grid-template-columns: 1fr;
        gap: 0.3rem;
        padding: 0 0.5rem 0.4rem;
    }
    .sc-spacer {
        display: none;   /* not needed when stacked */
    }
    .sc-search {
        width: 100%;
        justify-self: stretch;
    }
    .supercycle-bar .filter-label {
        font-size: 0.68rem;
        margin-right: 0.2rem;
        flex-basis: 100%;
        text-align: center;
        margin-bottom: 0.1rem;
    }
    .sc-pill {
        padding: 0.25rem 0.55rem;
        font-size: 0.72rem;
    }
}

/* === Deep-dive discovery banner ================================ */
.hint-banner {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0 1.25rem 0.5rem;
    padding: 0.55rem 0.9rem;
    background: rgba(59, 130, 246, 0.1);
    border: 1px solid rgba(59, 130, 246, 0.3);
    border-radius: 8px;
    color: var(--text-primary);
    font-size: 0.88rem;
    /* Pulse for the first ~6 seconds (3 cycles × 2s) to draw the eye, then
       settle. Animation runs once on first paint; localStorage dismissal
       skips it on subsequent visits via JS adding the .dismissed class. */
    animation: hint-pulse 2s ease-in-out 0s 3;
}
.hint-banner.dismissed {
    display: none;
}
.hint-icon {
    flex-shrink: 0;
    font-size: 1.05rem;
}
.hint-text {
    flex: 1;
    line-height: 1.35;
}
.hint-arrow {
    color: var(--accent);
    font-weight: 700;
}
.hint-dismiss {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 6px;
    transition: background 0.18s, color 0.18s;
}
.hint-dismiss:hover {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-primary);
}
body.light-mode .hint-dismiss:hover {
    background: rgba(0, 0, 0, 0.06);
}
@keyframes hint-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
    50%      { box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.18); }
}

/* Mobile: tighten the banner so it doesn't dominate the small viewport */
@media (max-width: 480px) {
    .hint-banner {
        margin: 0 0.5rem 0.4rem;
        padding: 0.5rem 0.7rem;
        font-size: 0.78rem;
        gap: 0.4rem;
    }
    .hint-icon {
        font-size: 0.95rem;
    }
}

/* Mobile tweaks for the modal — full-screen feel, header always reachable
   even when iOS Safari's URL bar is overlaying the top of the viewport. */
@media (max-width: 480px) {
    .modal {
        /* Respect iOS safe-area insets so the modal panel sits below the
           URL bar / dynamic island and above the home indicator. The
           header would otherwise hide behind the URL bar with no way
           to access the close button. The 0 fallbacks keep non-iOS
           browsers happy. */
        padding: env(safe-area-inset-top, 0)
                 env(safe-area-inset-right, 0)
                 env(safe-area-inset-bottom, 0)
                 env(safe-area-inset-left, 0);
        /* Anchor panel to top of the safe area instead of centering it,
           which on iOS Safari was offsetting the panel and hiding the
           header above the visible viewport. */
        align-items: stretch;
    }
    .modal-panel {
        max-width: 100%;
        max-height: none;
        height: 100%;
        border-radius: 0;
        border: none;
    }
    .modal-header {
        padding: 0.7rem 0.85rem;
        flex-shrink: 0;
        position: relative;
        z-index: 2;
        /* Subtle border-bottom + slight bg helps the header read as a
           distinct sticky region above the scrolling content. */
    }
    .modal-header h2 {
        font-size: 0.95rem;
    }
    .modal-close {
        /* Bigger tap target on phones — Apple HIG recommends ≥44px */
        width: 44px;
        height: 44px;
        font-size: 1.5rem;
    }
    .modal-content {
        padding: 1rem 1.1rem 1.5rem;
        font-size: 0.92rem;
    }
}

