/* BEPANDORA KPI — stylesheet
   Deep teal accent. Manual light/dark toggle (no OS-preference auto-flip):
   theme is applied via `data-theme="dark"` on <html>, persisted in
   localStorage. See base.html for the inline FOUC-free apply script.       */

:root {
    /* LIGHT theme — pure white page, soft-grey grouped cards (Apple/iOS
       grouped-list style). Cards stand out from the page via a subtle
       grey tint rather than a heavy shadow, so the eye lands on numbers,
       not card chrome.                                                      */
    --bg:         #ffffff;            /* page bg, pure white */
    --bg-alt:     #fafafa;            /* slight off-white for subtle zones */
    --surface:    #f5f5f7;            /* card surfaces — light grey */
    --surface-2:  #ebebef;            /* nested surfaces / hover */
    --border:     #e3e3e8;            /* hairlines */
    --border-soft:#eeeef2;            /* even softer hairlines */
    --text:       #0a0a0c;            /* near-black */
    --text-soft:  #3f3f46;            /* secondary text */
    --muted:      #6b6b75;            /* labels / hints */
    --accent:     #0d9488;            /* deep teal — primary brand */
    --accent-2:   #0f766e;            /* deeper teal for hover / chart-2 */
    --accent-soft:#ccfbf1;            /* tint for active / hover bg */
    --accent-rgb: 13, 148, 136;       /* same colour as --accent, comma-separated
                                         so rgba()/glow shadows can swap with
                                         the accent palette automatically.   */
    --pos:        #10b981;            /* green deltas */
    --neg:        #ef4444;            /* red deltas */
    --warn:       #f59e0b;
    --bad:        #ef4444;
    --shadow-sm:  0 1px 2px rgba(10,10,12,0.04);
    --shadow:     0 1px 3px rgba(10,10,12,0.06), 0 1px 2px rgba(10,10,12,0.04);
    --shadow-lg:  0 10px 15px -3px rgba(10,10,12,0.06), 0 4px 6px -4px rgba(10,10,12,0.05);
    --radius:     12px;
    --radius-lg:  16px;
    --radius-sm:  8px;
    --gap:        14px;
    --sidebar-w:  240px;
    --font:       ui-sans-serif, -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;

    color-scheme: light;
}

/* DARK theme — applied when <html data-theme="dark"> is set. Brighter
   muted/text values than the original auto-dark we shipped, so labels
   and axis ticks stay readable. Teal accent shifts to a brighter tone
   that survives a dark background.                                          */
[data-theme="dark"] {
    --bg:         #0a0a0c;            /* page bg, very dark */
    --bg-alt:     #15151a;
    --surface:    #18181b;            /* cards lift slightly off the page */
    --surface-2:  #27272a;            /* nested / hover */
    --border:     #2c2c34;
    --border-soft:#22222a;
    --text:       #fafafa;            /* near-white */
    --text-soft:  #d4d4d8;
    --muted:      #a1a1aa;            /* much brighter than before */
    --accent:     #2dd4bf;            /* brighter teal so it pops on dark */
    --accent-2:   #14b8a6;
    --accent-soft:#0f3a36;            /* dark teal tint for active bg */
    --accent-rgb: 45, 212, 191;
    --shadow-sm:  0 1px 2px rgba(0,0,0,0.4);
    --shadow:     0 1px 3px rgba(0,0,0,0.45), 0 1px 2px rgba(0,0,0,0.35);
    --shadow-lg:  0 10px 25px rgba(0,0,0,0.5);

    color-scheme: dark;
}

/* PURPLE accent — opted in via <html data-accent="purple">. Overrides
   the teal `--accent*` triplet only; everything else (text, bg, borders)
   stays whatever the current theme dictates. The `--accent-rgb` variable
   is what drives the glow-layer shadows, so flipping accent immediately
   re-tints every glow alongside the active-state colours.               */
[data-accent="purple"] {
    --accent:      #6366f1;
    --accent-2:    #4f46e5;
    --accent-soft: #e0e7ff;
    --accent-rgb:  99, 102, 241;
}

/* NOIR accent — "Higgsfield style in black + purple", opted in via
   <html data-accent="noir">. Purple accent triplet only; surfaces stay
   the NEUTRAL dark palette (none of the purple-vibe background/border
   tint overrides below apply, since those are gated on
   [data-accent="purple"]). Result: black app, purple reserved for
   actions/active states. */
[data-accent="noir"] {
    --accent:      #a78bfa;
    --accent-2:    #8b5cf6;
    --accent-soft: #e0e7ff;
    --accent-rgb:  167, 139, 250;
}
[data-theme="dark"][data-accent="noir"] {
    --accent-soft: #241b3f;
}
[data-theme="dark"][data-accent="purple"] {
    /* Full "purple vibe" mode — tint the background, surfaces, and borders
       with a violet undertone so the whole app reads as one cohesive
       atmosphere instead of "teal-dark with purple highlights". Inspired
       by the AdsByAI / Linear / Vercel dark-purple aesthetic. */
    --bg:         #0a0714;            /* very dark purple-black, not pure black */
    --bg-alt:     #110a1f;            /* slight purple lift */
    --surface:    #16101f;            /* cards, sidebar, panels */
    --surface-2:  #251a3a;            /* hover / nested */
    --border:     #2a1f44;            /* purple-tinted borders */
    --border-soft:#1e1532;            /* even softer */
    --accent:      #a78bfa;           /* violet-400, brighter on dark */
    --accent-2:    #8b5cf6;
    --accent-soft: #2d1f5b;
    --accent-rgb:  167, 139, 250;
}

/* Ambient background spotlight — soft violet radial gradient anchored
   at the top of the viewport so every page has that "light source
   above the fold" feeling from the inspiration screenshot. Fixed
   positioning so it stays put as the user scrolls. Pointer-events
   none so it never blocks clicks. Hidden everywhere except dark+purple. */
[data-theme="dark"][data-accent="purple"] body::before {
    content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
    background:
        radial-gradient(ellipse 80% 50% at 50% -10%,
            rgba(139, 92, 246, 0.22),
            transparent 60%),
        radial-gradient(ellipse 60% 50% at 100% 20%,
            rgba(124, 58, 237, 0.12),
            transparent 50%),
        radial-gradient(ellipse 60% 40% at 0% 80%,
            rgba(99, 102, 241, 0.10),
            transparent 50%);
}
/* Make sure the actual page content sits ABOVE the ambient gradient. */
[data-theme="dark"][data-accent="purple"] .app-topbar,
[data-theme="dark"][data-accent="purple"] .app-shell {
    position: relative; z-index: 1;
}

/* NOIR ambient — same violet spotlight as the full-purple vibe, but it
   sits on the FLAT black surfaces (noir doesn't tint --bg/surfaces). So
   you get the purple glow atmosphere without the purple-washed cards:
   Higgsfield-style black with a purple light source. Slightly tighter +
   dimmer than full-purple so pure-black surfaces don't muddy. */
[data-theme="dark"][data-accent="noir"] body::before {
    content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
    background:
        radial-gradient(ellipse 75% 45% at 50% -12%,
            rgba(139, 92, 246, 0.18),
            transparent 60%),
        radial-gradient(ellipse 55% 45% at 100% 18%,
            rgba(124, 58, 237, 0.10),
            transparent 50%),
        radial-gradient(ellipse 55% 40% at 0% 82%,
            rgba(99, 102, 241, 0.08),
            transparent 50%);
}
[data-theme="dark"][data-accent="noir"] .app-topbar,
[data-theme="dark"][data-accent="noir"] .app-shell {
    position: relative; z-index: 1;
}

/* Cards get a subtle inner purple gradient + stronger border glow in
   purple mode — same trick the inspiration uses to make cards feel
   "alive" instead of flat. Targets every common card class so any
   future page picks it up automatically. */
[data-theme="dark"][data-accent="purple"] .chart-card,
[data-theme="dark"][data-accent="purple"] .kpi-tile,
[data-theme="dark"][data-accent="purple"] .funnel-card,
[data-theme="dark"][data-accent="purple"] .cads-tile,
[data-theme="dark"][data-accent="purple"] .cads-chart-card,
[data-theme="dark"][data-accent="purple"] .cads-funnel-card,
[data-theme="dark"][data-accent="purple"] .hero-strip,
[data-theme="dark"][data-accent="purple"] #fb-split-card {
    background: linear-gradient(180deg,
        rgba(139, 92, 246, 0.04) 0%,
        rgba(22, 16, 31, 0)      40%),
        var(--surface);
    border-color: rgba(139, 92, 246, 0.18);
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.4),
        inset 0 1px 0 rgba(167, 139, 250, 0.06);
}
/* Sidebar matches the card treatment so the whole frame reads as one. */
/* Chrome (sidebar + topbar) matches Poster OS's flat near-black #0c0f16 —
   Jannik 2026-07-20 wanted the main nav bar as dark as Poster OS, not
   purple-tinted. Neutral borders to match. */
[data-theme="dark"][data-accent="purple"] .sidebar {
    background: #0c0f16;
    border-right-color: #1c1c26;
}
[data-theme="dark"][data-accent="purple"] .app-topbar {
    background: #0c0f16;
    border-bottom-color: #1c1c26;
}

/* Primary buttons get a stronger violet-to-indigo gradient in purple
   mode — same look as the "Launch" buttons in the reference. */
[data-theme="dark"][data-accent="purple"] .btn-primary {
    background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%);
    border-color: transparent;
    color: #fff;
}

/* Tone down the "money green" in purple mode. Big dollar values that
   were rendered in `--pos` to feel "positive" clash with the violet
   atmosphere — there's too much green competing with the accent. We
   switch them to white/text so the eye reads them as data, not as a
   green delta. Small +/- delta lines stay green because their
   semantic ("up vs down") is genuinely useful information. */
[data-theme="dark"][data-accent="purple"] .plat-bd-value {
    color: var(--text);
}
/* Conversion-ads tiles : "Revenue" tile renders in #10b981 hardcoded
   too — flip it to text so the layout doesn't read as a wall of
   green either. */
[data-theme="dark"][data-accent="purple"] .cads-tile.tile-revenue .value {
    color: var(--text);
}
/* The "today-line" inside KPI tiles uses --pos green for positive
   deltas. Keep the SEMANTIC (positive = good) but desaturate the
   green to a softer mint that doesn't fight with the purple
   atmosphere. Negatives stay full-strength red — they're a warning
   signal that should still grab attention. */
[data-theme="dark"][data-accent="purple"] .kpi-tile .today-line .pos,
[data-theme="dark"][data-accent="purple"] .kpi-tile .delta.pos,
[data-theme="dark"][data-accent="purple"] .kpi-tile .arrow {
    color: #6ee7b7;        /* emerald-300, much softer than --pos */
}
/* Tracking-pill dot — still want "all good" to read as good, but the
   bright green dot competes with purple. Switch to a soft violet
   indicator since the pill text already says "tracking". */
[data-theme="dark"][data-accent="purple"] .tracking-pill .dot {
    background: rgba(var(--accent-rgb), 0.9);
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
}

/* ──────────────────────────────────────────────────────────────────────
   GREEN / TEAL ambient mode — same atmospheric treatment as purple but
   in the brand teal. Purple mode bathes the dark UI in a violet glow
   ("shining on the black bits"); the default teal accent had only the
   pop-of-colour active states but no ambient atmosphere, so the dark
   theme felt flat by comparison. This block mirrors every purple effect
   below (ambient body gradient, card inner glow + border, sidebar
   gradient, topbar tint) but tuned to the teal palette. We gate on
   `:not(purple):not(noir)` so the rules apply only for the default teal
   accent — noir is deliberately FLAT black (no ambient glow at all),
   and purple has its own violet block above.
   ────────────────────────────────────────────────────────────────────── */
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) body::before {
    content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
    background:
        radial-gradient(ellipse 80% 50% at 50% -10%,
            rgba(45, 212, 191, 0.18),
            transparent 60%),
        radial-gradient(ellipse 60% 50% at 100% 20%,
            rgba(20, 184, 166, 0.10),
            transparent 50%),
        radial-gradient(ellipse 60% 40% at 0% 80%,
            rgba(13, 148, 136, 0.09),
            transparent 50%);
}
/* Page content sits ABOVE the ambient gradient so it stays interactive
   and the glow only paints through the page's empty / surface gaps. */
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .app-topbar,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .app-shell {
    position: relative; z-index: 1;
}
/* The three ambient-glow rules above set position:relative on .app-topbar,
   which overrode its base position:sticky and made the top bar scroll away.
   Restore sticky (its z-index already clears the glow). Same specificity as
   those rules + later in source order, so this wins. */
[data-theme="dark"][data-accent="purple"] .app-topbar,
[data-theme="dark"][data-accent="noir"] .app-topbar,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .app-topbar {
    position: sticky; top: 0; z-index: 31;
}
/* Cards pick up a faint teal inner gradient + border glow so they feel
   "lit from above" against the ambient backdrop — same trick the
   purple mode uses. The list of selectors matches the purple block
   exactly so any card class one mode hits the other mode also hits. */
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .chart-card,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .kpi-tile,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .funnel-card,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .cads-tile,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .cads-chart-card,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .cads-funnel-card,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .hero-strip,
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) #fb-split-card {
    background: linear-gradient(180deg,
        rgba(45, 212, 191, 0.035) 0%,
        rgba(13, 17, 23, 0)       40%),
        var(--surface);
    border-color: rgba(45, 212, 191, 0.14);
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.4),
        inset 0 1px 0 rgba(94, 234, 212, 0.05);
}
/* Sidebar gets the same lift gradient so the full frame reads as one
   atmosphere instead of "glowing main, flat side". */
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .sidebar {
    background: #0c0f16;
    border-right-color: #1c1c26;
}
[data-theme="dark"]:not([data-accent="purple"]):not([data-accent="noir"]) .app-topbar {
    background: #0c0f16;
    border-bottom-color: #1c1c26;
}
/* Noir accent — same flat Poster OS dark for the nav chrome. */
[data-theme="dark"][data-accent="noir"] .sidebar {
    background: #0c0f16;
    border-right-color: #1c1c26;
}
[data-theme="dark"][data-accent="noir"] .app-topbar {
    background: #0c0f16;
    border-bottom-color: #1c1c26;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    font-size: 14px;
    line-height: 1.5;
}

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

/* ============================================================
   APP SHELL : left sidebar + main area
   ============================================================ */
/* ───── Global top bar — sits above the entire app shell ───── */
.app-topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; justify-content: space-between;
    height: 48px; padding: 0 18px;
    background: #0a0a0c; color: #e5e5ea;
    border-bottom: 1px solid #18181b;
}
.app-topbar-left {
    display: flex; align-items: center; gap: 10px;
    font-size: 13px; color: rgba(255,255,255,0.85);
}
.app-topbar-left .app-topbar-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 6px;
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    color: #fff; font-weight: 800; font-size: 10px; letter-spacing: 0.02em;
}
.app-topbar-left .app-topbar-app-name { font-weight: 600; letter-spacing: 0.01em; }
.app-topbar-brand { display: inline-flex; align-items: center; text-decoration: none; line-height: 0; }
.app-topbar-brand .app-topbar-wm { display: block; }
.app-topbar-brand:hover { opacity: 0.9; }
.app-topbar-right { display: flex; align-items: center; gap: 8px; }
.topbar-pill {
    display: inline-flex; align-items: center; gap: 6px;
    height: 28px; padding: 0 12px;
    border-radius: 999px;
    background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.88);
    border: 1px solid rgba(255,255,255,0.08);
    font-size: 12px; font-weight: 500; font-family: inherit;
    cursor: default;
    transition: background 0.1s, border-color 0.1s;
}
/* Buttons in the topbar still cursor:pointer; the timezone pill is now a
   plain <span> so it stays cursor:default. */
button.topbar-pill { cursor: pointer; }
button.topbar-pill:hover { background: rgba(255,255,255,0.10); border-color: rgba(255,255,255,0.14); }
.topbar-pill .dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--pos);   /* green = ok */
    box-shadow: 0 0 0 3px rgba(16,185,129,0.18);
}
.topbar-pill .dot.warn { background: var(--warn); box-shadow: 0 0 0 3px rgba(245,158,11,0.18); }
.topbar-pill .dot.bad,
.topbar-pill .dot.error,
.topbar-pill .dot.critical { background: var(--bad); box-shadow: 0 0 0 3px rgba(239,68,68,0.22); }
.topbar-pill svg { opacity: 0.7; }
@media (max-width: 720px) {
    .app-topbar-left .app-topbar-app-name { display: none; }
}

.app-shell { display: flex; min-height: calc(100vh - 48px); }

/* Chromeless variant — used when a page is loaded with ?embedded=1
   inside an <iframe> on another page (e.g. /team/users/<id> hosts the
   daily-entry view in a modal). No topbar, no sidebar — the iframe
   only renders the page's own content. */
.app-shell-embedded {
    min-height: 100vh;
    padding: 0;
}
.app-shell-embedded .main {
    width: 100%;
    max-width: none;
    padding: 14px 18px;
}

.sidebar {
    width: var(--sidebar-w);
    background: var(--surface);
    border-right: 1px solid var(--border);
    /* Brand mark moved to the global topbar — first nav item gets a bit
       more breathing room from the top edge so it doesn't crash into the
       topbar's border. */
    padding: 14px 14px;
    /* Sit below the 48px topbar — sticky offset matches the topbar height. */
    position: sticky; top: 48px; height: calc(100vh - 48px);
    display: flex; flex-direction: column; gap: 4px;
    flex-shrink: 0;
    z-index: 10;
}
.sidebar .brand {
    display: flex; align-items: center; gap: 10px;
    padding: 6px 10px 18px 10px;
    font-weight: 800; letter-spacing: -0.01em;
    color: var(--text); font-size: 16px;
}
.sidebar .brand .brand-dot {
    width: 28px; height: 28px; border-radius: 7px;
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    display: inline-flex; align-items: center; justify-content: center;
    color: #fff; font-weight: 800; font-size: 13px;
}
.side-nav { display: flex; flex-direction: column; gap: 1px; flex: 1; }
.side-nav a {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px; border-radius: var(--radius-sm);
    font-size: 14px; color: var(--text-soft); font-weight: 500;
    transition: background 0.1s, color 0.1s;
}
.side-nav a:hover {
    background: var(--surface-2); color: var(--text);
}
.side-nav a.active {
    background: var(--accent-soft); color: var(--accent-2); font-weight: 600;
}
.side-nav a.active svg { color: var(--accent-2); }
.side-nav a svg { flex-shrink: 0; opacity: 0.85; }
.side-nav .nav-section {
    padding: 18px 12px 6px 12px; font-size: 11px; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600;
}

/* "Drill-in" nav link (Conversion Ads) — looks like a regular nav item
   but with a right-chevron at the far edge, hinting the click leads to
   its own dedicated view rather than expanding inline. */
.side-nav .nav-drill {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px; border-radius: var(--radius-sm);
    font-size: 14px; color: var(--text-soft); font-weight: 500;
    transition: background 0.1s, color 0.1s;
}
.side-nav .nav-drill:hover {
    background: var(--surface-2); color: var(--text);
}
.side-nav .nav-drill.active {
    background: var(--accent-soft); color: var(--accent-2); font-weight: 600;
}
.side-nav .nav-drill svg:first-child { flex-shrink: 0; opacity: 0.85; }
.side-nav .nav-drill .nav-drill-arrow {
    margin-left: auto; opacity: 0.5;
    transition: opacity 0.1s, transform 0.1s;
}
.side-nav .nav-drill:hover .nav-drill-arrow {
    opacity: 1; transform: translateX(2px);
}
.side-nav .nav-drill > span { flex: 1; min-width: 0; }

/* ───── Section switcher (Organic ↔ Conversion Ads) ─────
   Sits at the very top of both sub-sidebars. Two pills side-by-side,
   active one filled, inactive one ghost — clicking the ghost pill
   drills into the other section. Replaces the older "Back" link
   pattern; it's faster (1 click) and shows where you are AND what
   else exists at the same time. */
.section-switcher {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: 4px;
    padding: 2px; margin-bottom: 14px;
    background: var(--surface-2);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
}
.section-switcher .section-pill {
    display: inline-flex; align-items: center; justify-content: center;
    gap: 6px;
    padding: 7px 8px; border-radius: calc(var(--radius-sm) - 2px);
    font-size: 12px; font-weight: 600;
    color: var(--muted);
    transition: background 0.12s, color 0.12s;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.section-switcher .section-pill:hover {
    color: var(--text); background: var(--bg);
}
.section-switcher .section-pill.active {
    background: var(--accent); color: #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.section-switcher .section-pill.active:hover {
    background: var(--accent-2); color: #fff;
}
.section-switcher .section-pill svg { flex-shrink: 0; opacity: 0.95; }
.section-switcher .section-pill > span {
    min-width: 0; overflow: hidden; text-overflow: ellipsis;
}

/* ───── Section sub-sidebar header ─────
   Reminds the operator which area they're in. Shared by both Organic
   Marketing and Conversion Ads so the visual treatment is identical. */
.side-nav-section .nav-section-header {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 12px 12px 12px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--border);
    font-size: 12px; font-weight: 700;
    color: var(--muted); letter-spacing: 0.04em;
    text-transform: uppercase;
}
.side-nav-section .nav-section-header svg {
    color: var(--accent-2); flex-shrink: 0;
}
/* Sub-nav items (e.g. platform pickers under Performance) */
.side-nav a.sub-nav-item {
    padding: 6px 12px 6px 32px; font-size: 13px; font-weight: 500;
    color: var(--muted);
}
.side-nav a.sub-nav-item:hover { color: var(--text); background: var(--surface-2); }
.side-nav a.sub-nav-item.active {
    background: var(--accent-soft); color: var(--accent-2); font-weight: 600;
}
.side-nav a.sub-nav-item .sub-nav-icon {
    display: inline-flex; width: 14px; height: 14px;
    align-items: center; justify-content: center;
    font-size: 10px; opacity: 0.6;
}
.side-nav a.sub-nav-item svg { width: 14px; height: 14px; opacity: 0.9; }
/* "All platforms" sits at the bottom of the platform list as the
   catch-all fall-through. Visual hairline divider above it makes it read
   as a different category from the per-platform sub-items right above. */
.side-nav a.sub-nav-item.sub-nav-all {
    margin-top: 6px; padding-top: 8px;
    border-top: 1px solid var(--border);
}

/* Brand-coloured logos for the platform sub-nav. The SVGs use
   fill="currentColor", so colouring the icon just means setting `color`
   on a child wrapper. We isolate it to the SVG with a CSS variable so
   the text label keeps its own muted/active palette. */
.side-nav a.sub-nav-item[data-plat="instagram"] svg { color: #ec4899; opacity: 1; }
.side-nav a.sub-nav-item[data-plat="x"]         svg { color: var(--text); opacity: 1; }
.side-nav a.sub-nav-item[data-plat="facebook"]  svg { color: #1877f2; opacity: 1; }
[data-theme="dark"] .side-nav a.sub-nav-item[data-plat="x"] svg { color: #ffffff; }

/* Keep the brand colour even when the item is active — so the logo never
   gets washed out by the accent-soft active background. */
.side-nav a.sub-nav-item[data-plat="instagram"].active svg { color: #ec4899; }
.side-nav a.sub-nav-item[data-plat="x"].active         svg { color: var(--text); }
.side-nav a.sub-nav-item[data-plat="facebook"].active  svg { color: #1877f2; }

/* ────── Collapsible nav group (e.g. Performance → platform sub-items) ──────
   The parent button looks like a nav <a> but is a <button> so it never
   navigates; clicking only flips the expanded state on the group. */
.side-nav .nav-group { display: contents; }
.side-nav .nav-toggle {
    /* Mirror .side-nav a styles since this is a button, not a link */
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px; border-radius: var(--radius-sm);
    font-size: 14px; color: var(--text-soft); font-weight: 500;
    transition: background 0.1s, color 0.1s;
    background: transparent; border: none; cursor: pointer;
    font-family: inherit; text-align: left; width: 100%;
}
.side-nav .nav-toggle:hover { background: var(--surface-2); color: var(--text); }
.side-nav .nav-toggle.active {
    background: var(--accent-soft); color: var(--accent-2); font-weight: 600;
}
.side-nav .nav-toggle.active svg { color: var(--accent-2); }
.side-nav .nav-toggle > svg:first-child { flex-shrink: 0; opacity: 0.85; }
.side-nav .nav-toggle > span { flex: 1; min-width: 0; }
.side-nav .nav-chevron {
    flex-shrink: 0; opacity: 0.55;
    transition: transform 0.15s ease;
}
.side-nav .nav-group.expanded .nav-chevron { transform: rotate(90deg); }

/* Sub-list visibility, driven by the .expanded class on the group.
   Uses display (not a max-height accordion): a max-height:0 clip was
   intermittently failing to hide the Account-management sub-list on its own
   page, leaving it stuck open. display:none is deterministic — it can't fail
   to collapse. Both groups (Performance + Account mgmt) share this rule so
   they behave identically. */
.side-nav .nav-sub {
    display: none;
    flex-direction: column; gap: 1px;
    padding-top: 2px;
}
.side-nav .nav-group.expanded .nav-sub {
    display: flex;
}

/* Pre-paint expansion (set on <html> by the bootstrap script in <head>),
   so the sidebar paints in the correct state on first frame — no flicker. */
html.nav-perf-open .side-nav #nav-group-performance .nav-sub { display: flex; }
html.nav-perf-open .side-nav #nav-group-performance .nav-chevron { transform: rotate(90deg); }
/* Mirror class — user explicitly closed the group; override the server-side
   `.expanded` that Jinja adds on the group's own pages so the sub-list doesn't
   flash open for one frame before the JS collapses it. Higher specificity
   (extra html.<class> selector) wins over `.nav-group.expanded .nav-sub`. */
html.nav-perf-closed .side-nav #nav-group-performance.expanded .nav-sub { display: none; }
html.nav-perf-closed .side-nav #nav-group-performance.expanded .nav-chevron { transform: rotate(0deg); }

/* Same pre-paint rules for the Account-management folder. */
html.nav-acctmgmt-open .side-nav #nav-group-acctmgmt .nav-sub { display: flex; }
html.nav-acctmgmt-open .side-nav #nav-group-acctmgmt .nav-chevron { transform: rotate(90deg); }
html.nav-acctmgmt-closed .side-nav #nav-group-acctmgmt.expanded .nav-sub { display: none; }
html.nav-acctmgmt-closed .side-nav #nav-group-acctmgmt.expanded .nav-chevron { transform: rotate(0deg); }

/* Conversion Ads is no longer a collapsible group — it's a drill-in link
   that swaps the entire sidebar for its own dedicated sub-nav on
   /conversion-ads/* routes. So the open/closed pre-paint rules that
   used to live here were dropped. */

.side-nav hr {
    margin: 12px 8px; border: 0; border-top: 1px solid var(--border-soft);
}

.sidebar .user-menu {
    margin-top: auto; padding: 12px 10px 4px;
    border-top: 1px solid var(--border-soft);
    display: flex; flex-direction: column; gap: 8px;
}
.sidebar .user-label {
    font-size: 13px; color: var(--text); font-weight: 500;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar .user-role { font-size: 11px; color: var(--muted); }
.sidebar .user-actions {
    display: flex; align-items: center; gap: 6px;
}
.sidebar #logout-btn {
    flex: 1; text-align: left; padding: 6px 10px; border-radius: var(--radius-sm);
    background: transparent; border: 1px solid var(--border); color: var(--text-soft);
    font-size: 12px; cursor: pointer; transition: background 0.1s;
}
.sidebar #logout-btn:hover { background: var(--surface-2); color: var(--text); }

/* Theme toggle (sun / moon) */
.theme-toggle {
    width: 32px; height: 32px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 999px;
    background: transparent; border: 1px solid var(--border);
    color: var(--text-soft);
    cursor: pointer; transition: background 0.1s, color 0.1s, transform 0.1s;
}
.theme-toggle:hover { background: var(--surface-2); color: var(--text); }
.theme-toggle:active { transform: scale(0.95); }
.theme-toggle .icon-sun  { display: none; }
.theme-toggle .icon-moon { display: block; }
[data-theme="dark"] .theme-toggle .icon-sun  { display: block; }
[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

/* Accent-colour toggle — two tiny circle swatches stacked diagonally
   so the button reads as "pick a colour" without needing a label.
   Whichever accent is currently active gets a thin ring around its
   swatch via the data-accent attribute. */
.accent-toggle {
    gap: 2px; padding: 0; position: relative;
}
.accent-swatch {
    width: 10px; height: 10px; border-radius: 50%;
    display: inline-block;
    box-shadow: 0 0 0 1.5px transparent;
    transition: box-shadow 0.15s, transform 0.1s;
}
.accent-swatch.swatch-teal   { background: linear-gradient(135deg, #2dd4bf, #0d9488); }
.accent-swatch.swatch-purple { background: linear-gradient(135deg, #a78bfa, #6366f1); }
/* Active accent gets a soft ring around its swatch — clear visual
   feedback for "this is what you're on right now". */
html:not([data-accent="purple"]) .accent-swatch.swatch-teal {
    box-shadow: 0 0 0 1.5px var(--accent),
                0 0 8px 1px rgba(var(--accent-rgb), 0.6);
}
[data-accent="purple"] .accent-swatch.swatch-purple {
    box-shadow: 0 0 0 1.5px var(--accent),
                0 0 8px 1px rgba(var(--accent-rgb), 0.6);
}

.muted { color: var(--muted); font-size: 13px; }

/* Mobile : sidebar collapses into a horizontal scrolling rail */
@media (max-width: 768px) {
    .app-shell { flex-direction: column; }
    .sidebar {
        width: 100%; height: auto; position: sticky; top: 0;
        flex-direction: row; align-items: center; gap: 4px;
        padding: 10px 12px; overflow-x: auto;
        border-right: 0; border-bottom: 1px solid var(--border);
    }
    .sidebar .brand { padding: 4px 8px 4px 0; flex-shrink: 0; }
    .side-nav {
        flex-direction: row; flex-wrap: nowrap; flex: 1;
        overflow-x: auto; gap: 2px;
    }
    .side-nav .nav-section { display: none; }
    .side-nav hr { display: none; }
    .side-nav a { white-space: nowrap; flex-shrink: 0; }
    .sidebar .user-menu {
        flex-direction: row; align-items: center;
        margin-top: 0; padding: 0 0 0 8px;
        border-top: 0; border-left: 1px solid var(--border-soft);
        flex-shrink: 0;
    }
    .sidebar .user-label { max-width: 120px; }
}

/* ---------- Buttons ---------- */
.btn-primary, .btn-ghost, .btn-tiny {
    font: inherit;
    border-radius: 999px;
    padding: 8px 16px;
    cursor: pointer;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.btn-primary {
    background: var(--accent-2);
    color: #fff;
    border-color: var(--accent-2);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent-2); }
.btn-tiny { padding: 4px 10px; font-size: 12px; margin-right: 4px; }
.btn-danger { color: var(--bad); border-color: var(--border); }
.btn-danger:hover { background: var(--bad); color: #fff; border-color: var(--bad); }

/* ---------- Layout ---------- */
.main {
    flex: 1; min-width: 0;
    padding: 28px 32px 60px;
    /* Cap the working area so widgets don't sprawl on ultrawide monitors.
       The chart in particular was rendering with the same data spread
       across 1700+ px which read as "super stretched". 1400px keeps
       proportions sensible while leaving generous breathing room. */
    max-width: 1480px;
    margin: 0 auto;
    width: 100%;
}
@media (max-width: 768px) {
    .main { padding: 20px 16px 40px; }
}
.page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
}
.page-head h1 { margin: 0; font-size: 22px; font-weight: 700; }
.filters { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.filters label { display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--muted); }
.filters input, .filters select {
    font: inherit; padding: 6px 10px; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--surface); color: var(--text);
}

/* ---------- KPI grid ---------- */
.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: var(--gap);
    margin-bottom: 24px;
}
.kpi-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    box-shadow: var(--shadow);
}
.kpi-label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.kpi-value { font-size: 24px; font-weight: 700; margin-top: 4px; }
.kpi-card.warn .kpi-value { color: var(--warn); }
.kpi-card.bad .kpi-value { color: var(--bad); }

/* ---------- Leaderboard ---------- */
.leaderboard {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 24px;
}
.leaderboard h2 { font-size: 14px; margin: 0 0 8px; color: var(--muted); font-weight: 600; }
.lb-list {
    list-style: none;
    padding: 0; margin: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.lb-list li {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    align-items: center;
}
.lb-list li:last-child { border-bottom: none; }
.lb-name { font-weight: 500; }
.lb-meta { font-size: 12px; color: var(--muted); }
.lb-pct { font-weight: 600; }
.pos { color: var(--pos); }
.neg { color: var(--neg); }

/* ---------- Tables ---------- */
.table-wrap { margin-bottom: 28px; }
.table-wrap h2 { font-size: 14px; margin: 0 0 8px; color: var(--muted); font-weight: 600; }
.data-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.data-table th, .data-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: 13px;
}
.data-table th {
    background: var(--bg);
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    font-size: 11px;
    letter-spacing: 0.04em;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Status pills ---------- */
.status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.status-active     { background: rgba(16,185,129,0.12); color: var(--pos); }
.status-muted      { background: rgba(245,158,11,0.12); color: var(--warn); }
.status-banned     { background: rgba(239,68,68,0.12); color: var(--bad); }
.status-paused     { background: rgba(107,105,100,0.15); color: var(--muted); }
.status-private    { background: rgba(15,118,110,0.12); color: var(--accent-2); }

/* ---------- Login ---------- */
.login-body {
    background: var(--bg);
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}
.login-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 32px;
    width: 100%;
    max-width: 360px;
    box-shadow: var(--shadow);
}
.login-title {
    margin: 0 0 6px;
    font-size: 22px;
    font-weight: 800;
    letter-spacing: 0.04em;
    color: var(--accent);
}
.login-sub { margin: 0 0 24px; color: var(--muted); }
.login-card form { display: flex; flex-direction: column; gap: 14px; }
.login-card label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.login-card input {
    font: inherit; padding: 10px 12px; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--bg); color: var(--text);
}
.error-msg { color: var(--bad); margin: 0; font-size: 13px; }

/* ---------- Dialogs ---------- */
dialog {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px;
    background: var(--surface);
    color: var(--text);
    max-width: 420px;
    width: 100%;
    box-shadow: var(--shadow);
}
dialog::backdrop { background: rgba(0,0,0,0.4); }
dialog h3 { margin: 0 0 16px; }
dialog form { display: flex; flex-direction: column; gap: 12px; }
dialog label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
dialog input, dialog select {
    font: inherit; padding: 8px 10px; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--bg); color: var(--text);
}
.dlg-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

/* ---------- Bouncy link preview block (inside the picker dialog) ---------- */
.link-preview {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 8px 12px;
    margin-top: -4px;
    font-size: 12px;
    line-height: 1.6;
}
.link-preview:empty { display: none; }
.lp-row { display: block; }
.lp-key {
    color: var(--muted);
    text-transform: uppercase;
    font-size: 10px;
    letter-spacing: 0.04em;
    margin-right: 8px;
}
.lp-pill {
    display: inline-block;
    color: #fff;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 600;
}

/* ---------- Responsive ---------- */
@media (max-width: 720px) {
    .leaderboard { grid-template-columns: 1fr; }
    .data-table { font-size: 12px; }
    .topbar-inner { flex-wrap: wrap; }
}

/* ============================================================
   Shared timeframe picker (templates/_timeframe_picker.html)
   Horizontal segmented control with 4 preset buttons.
   ============================================================ */
.tfpicker {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px; border: 1px solid var(--border);
    background: var(--surface); border-radius: 12px;
}
.tfpicker-btn {
    appearance: none; border: 0; cursor: pointer;
    padding: 8px 16px; border-radius: 8px;
    font: inherit; font-size: 13px; font-weight: 500;
    background: transparent; color: var(--text-soft);
    white-space: nowrap;
    transition: background 0.1s, color 0.1s;
}
.tfpicker-btn:hover { background: var(--surface-2); color: var(--text); }
.tfpicker-btn.active {
    background: var(--accent-soft); color: var(--accent-2);
    font-weight: 600;
}

/* Pre-paint period restore : when the bootstrap script in <head> sets
   `html[data-tf-period="..."]` based on the URL hash, we override the
   server-rendered `.active` class so the picker paints with the right
   button highlighted from the very first frame — no "7d → 24h" flash
   while JS catches up. The picker JS later mutates classes normally; the
   override only matters for the initial paint. */
html[data-tf-period] .tfpicker-btn.active {
    background: transparent; color: var(--text-soft); font-weight: 500;
}
html[data-tf-period="yesterday"] .tfpicker-btn[data-period="yesterday"],
html[data-tf-period="24h"]       .tfpicker-btn[data-period="24h"],
html[data-tf-period="today"]     .tfpicker-btn[data-period="today"],
html[data-tf-period="7d"]        .tfpicker-btn[data-period="7d"],
html[data-tf-period="30d"]       .tfpicker-btn[data-period="30d"],
html[data-tf-period="90d"]       .tfpicker-btn[data-period="90d"] {
    background: var(--accent-soft); color: var(--accent-2); font-weight: 600;
}
.tfpicker-btn:focus-visible {
    outline: 2px solid var(--accent); outline-offset: -1px;
}
.tfpicker-btn:disabled { opacity: 0.35; cursor: not-allowed; }
@media (max-width: 720px) {
    .tfpicker { flex-wrap: wrap; }
}

/* ════════════════════════════════════════════════════════════════════
   "Shine" layer — subtle glow + gradient borders on the few elements
   that benefit from it (active pills, primary CTAs, brand marks).
   Stays inside the existing teal palette so the app's identity reads
   as one coherent thing instead of "every theme accent ever".
   ════════════════════════════════════════════════════════════════════ */

/* Active timeframe button — gradient border + soft outer glow. The
   ::before paints a 1px gradient ring on top of the background; the
   box-shadow gives the radiating halo. All shadows reference
   --accent-rgb so the colour flips with the palette toggle. */
.tfpicker-btn.active {
    position: relative;
    box-shadow: 0 0 18px -2px rgba(var(--accent-rgb), 0.45);
}
.tfpicker-btn.active::before {
    content: ''; position: absolute; inset: 0;
    border-radius: inherit; padding: 1.5px;
    background: linear-gradient(135deg,
        rgba(var(--accent-rgb), 0.95) 0%,
        rgba(var(--accent-rgb), 0.35) 50%,
        rgba(var(--accent-rgb), 0.95) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    pointer-events: none;
}

/* Primary button — give it the same outer halo so the user's eye
   lands on the "do something" CTA before the read-only widgets. */
.btn-primary {
    position: relative;
    box-shadow: 0 0 22px -4px rgba(var(--accent-rgb), 0.55);
    transition: box-shadow 0.15s, filter 0.1s;
}
.btn-primary:hover {
    box-shadow: 0 0 28px -2px rgba(var(--accent-rgb), 0.75);
}

/* Topbar brand mark — soft inner gradient so the corner of the screen
   has a tiny "this is alive" signal. */
.app-topbar-left .app-topbar-mark {
    box-shadow: 0 0 14px -2px rgba(var(--accent-rgb), 0.55), inset 0 1px 0 rgba(255,255,255,0.18);
}

/* Sidebar brand dot — same treatment. Comment out if it competes
   with the topbar mark on light theme. */
.sidebar .brand .brand-dot {
    box-shadow: 0 0 18px -3px rgba(var(--accent-rgb), 0.6), inset 0 1px 0 rgba(255,255,255,0.2);
}

/* ─── Global toast — auto-dismissing pop-ups ───────────────────────────
   Fixed bottom-right, stacks vertically. Each toast slides in from
   below, lives for ~4 s, slides out. Three kinds : 'ok' (accent
   colour), 'warn' (amber), 'err' (red).                              */
.toast-host {
    position: fixed; right: 18px; bottom: 18px; z-index: 9999;
    display: flex; flex-direction: column; gap: 8px;
    pointer-events: none;   /* clicks pass through to the page beneath */
}
.toast {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 12px 16px;
    font-size: 13px; line-height: 1.4;
    color: var(--text);
    box-shadow: 0 6px 24px -4px rgba(0,0,0,0.35),
                0 0 18px -4px rgba(var(--accent-rgb), 0.35);
    min-width: 240px; max-width: 360px;
    opacity: 0; transform: translateY(8px);
    transition: opacity 0.2s, transform 0.2s;
    pointer-events: auto;
}
.toast.in { opacity: 1; transform: translateY(0); }
.toast-ok   { border-left: 3px solid var(--accent-2); }
.toast-warn { border-left: 3px solid var(--warn); }
.toast-err  { border-left: 3px solid var(--bad); }

/* Reusable utility: glowing horizontal divider. Drop one in between
   page sections that need a stronger visual break (e.g. above
   "Top performing accounts"). */
.glow-divider {
    height: 1px; margin: 28px 0;
    background: linear-gradient(90deg,
        transparent 0%,
        var(--accent-2) 50%,
        transparent 100%);
    box-shadow: 0 0 10px 1px rgba(var(--accent-rgb), 0.55);
    border: none;
}

/* Active platform pill + metric tab — global glow on top of whatever
   each page already styles. Scoped tightly enough that it doesn't
   leak to non-active siblings or unrelated pills. */
.plat-pill.active,
.metric-tabs button.active {
    box-shadow: 0 0 18px -3px rgba(var(--accent-rgb), 0.5);
}

/* Sidebar active item — currently a flat tint; add a left-edge glow
   bar so the eye finds "where am I" without scanning the whole rail. */
.side-nav a.active {
    position: relative;
}
.side-nav a.active::before {
    content: ''; position: absolute;
    left: -1px; top: 6px; bottom: 6px; width: 2px;
    background: var(--accent-2);
    box-shadow: 0 0 8px 1px rgba(var(--accent-rgb), 0.7);
    border-radius: 2px;
}

/* Light theme — these glows look great on dark backgrounds and a bit
   garish on white. Tone them down across the board when the user
   hasn't opted into dark mode. */
html:not([data-theme="dark"]) .tfpicker-btn.active,
html:not([data-theme="dark"]) .btn-primary,
html:not([data-theme="dark"]) .app-topbar-left .app-topbar-mark,
html:not([data-theme="dark"]) .sidebar .brand .brand-dot,
html:not([data-theme="dark"]) .plat-pill.active,
html:not([data-theme="dark"]) .metric-tabs button.active {
    box-shadow: 0 0 12px -4px rgba(var(--accent-rgb), 0.35);
}
