/*
 * Site-wide design language for logged-in pages. The marketing
 * landing in home.css owns the body.landing scope; this file owns
 * everything else. Aim is to match the slate palette + sharper
 * typography of the landing without redoing every per-page template.
 *
 * Token cheatsheet (slate scale):
 *   --bg-page:    #f6f8fb (page surface, off-white-blue)
 *   --bg-card:    #ffffff (card surface)
 *   --line:       #e2e8f0 (default border)
 *   --line-soft:  #eef2f7 (interior divider)
 *   --text:       #0f172a (headings)
 *   --text-mid:   #475569 (body copy)
 *   --text-muted: #64748b (secondary)
 *   --brand:      #0f172a (slate-900, used in navbar + accents)
 *   --brand-soft: #1e293b (slate-800, hover)
 *   --accent:     #6c8cff (matcher accent — kept consistent with
 *                          existing matcher.css overrides)
 */

/*
 * Token system
 * ============
 * Three layers:
 *  1. Surfaces — backgrounds for page / cards / elevated chrome / sunken
 *     (think: page > card > table-th > input). The ladder lets each
 *     surface step distinguish itself from its parent in both themes.
 *  2. Lines + text — borders, dividers, body / muted text.
 *  3. Semantic statuses — win / lose / pending / void / info / warn,
 *     each with a `-bg` (chip background) and `-fg` (chip text). These
 *     replace the hand-rolled `.status-won` / `.pill-progress` style
 *     blocks that used to live in every per-page template.
 *
 * Adding a new token? Define it in BOTH this :root block (light) AND
 * the [data-theme="dark"] block lower in the file. Never reference a
 * hard-coded hex from a per-page template — use the token. The
 * matcher's red/blue back-lay accent is a legitimate exception because
 * those are brand-coloured cells, not status states.
 */
:root {
    /* Surfaces */
    --bg-page: #f6f8fb;
    --bg-card: #ffffff;
    --surface-elevated: #fbfcfe;     /* card-header, table-th tint */
    --surface-sunken:   #f7f8fa;     /* input bg inside a card, toolbar shelf */
    --surface-hover:    #f0f3ff;     /* row hover */
    --surface-zebra:    #fafbfd;     /* alternating row */
    --surface-muted:    #f0f1f4;     /* abandoned / disabled card */

    /* Lines + text */
    --line: #e2e8f0;
    --line-soft: #eef2f7;
    --line-strong: #d6d9e0;          /* chip / pill borders */
    --text: #0f172a;
    --text-mid: #475569;
    --text-muted: #64748b;

    /* Brand */
    --brand: #0f172a;
    --brand-soft: #1e293b;
    --accent: #6c8cff;
    --accent-strong: #0a6ef0;        /* matcher back odds, links */
    --accent-pink: #d2176c;          /* matcher lay odds */

    /* Semantic status pills + figures.
       The {bg, fg} pair drives both the .app-pill chip background +
       text colour AND any "value" text that should read as
       win/loss/pending in stat-card values.
       The `-fg-strong` variant is for plain text figures (numbers in
       a card) where the chip background isn't drawn but the colour
       still signals positive/negative. */
    --status-win-bg:     #e7f8ee;
    --status-win-fg:     #0b7a3b;
    --status-win-fg-strong: #0b7a3b;
    --status-loss-bg:    #fdecec;
    --status-loss-fg:    #b51b1b;
    --status-loss-fg-strong: #b51b1b;
    --status-pending-bg: #fff7e0;
    --status-pending-fg: #7a5a0b;
    --status-void-bg:    #f0f1f4;
    --status-void-fg:    #56606b;
    --status-info-bg:    #e6efff;
    --status-info-fg:    #0a4a9f;
    --status-warn-bg:    #fff3e0;
    --status-warn-fg:    #8a4b0b;

    /* Geometry + shadow */
    --radius: 12px;
    --radius-sm: 8px;
    --shadow-card: 0 1px 2px rgba(15, 23, 42, .04),
                   0 8px 24px -18px rgba(15, 23, 42, .25);
}

/* Body background — the off-white slate gives every page a sense of
 * "surface" without being grey enough to look dull. body.landing
 * overrides this in home.css. */
body {
    background: var(--bg-page);
    color: var(--text);
    font-feature-settings: "ss01", "cv02", "cv03";
}

/* Typography. We don't ship a webfont; the system-ui stack on macOS,
 * Windows, and Android all give us a sharper feel than Bootstrap's
 * default. -letter-spacing on display headings borrowed from the
 * landing for visual continuity. */
body,
.btn,
input, select, textarea {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
                 "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
}
h1, h2, h3, h4 { letter-spacing: -0.01em; color: var(--text); }

/* ---------- Navbar ----------
 * Visual identity is "slate-900 with a thin underline." Replaces the
 * stock Bootstrap dark navbar so the brand wordmark + tabs feel
 * intentional rather than scaffolded. */
.navbar.app-nav {
    background: var(--brand);
    border-bottom: 1px solid #1f2a3a;
    padding-top: .6rem;
    padding-bottom: .6rem;
    box-shadow: 0 1px 0 rgba(15, 23, 42, .08);
}
.navbar.app-nav .navbar-brand {
    /* Compact form of the hero wordmark — same Fjalla One face, all
     * caps, generous tracking. Single-line so it fits the nav height.
     * The accent dot stays as a fingerprint mark next to the text. */
    font-family: 'Fjalla One', 'Arial Narrow', sans-serif;
    font-weight: 400;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    font-size: 1.1rem;
    color: #f8fafc;
}
.navbar.app-nav .navbar-brand::before {
    /* Subtle accent dot next to the wordmark — gives the brand a
     * fingerprint without committing to a real logo. */
    content: "";
    display: inline-block;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--accent);
    margin-right: .65rem;
    transform: translateY(-1px);
}
.navbar.app-nav .nav-link {
    color: #cbd5e1;
    padding: .35rem .7rem !important;
    border-radius: 6px;
    font-size: .92rem;
    transition: background 120ms ease, color 120ms ease;
}
.navbar.app-nav .nav-link:hover,
.navbar.app-nav .nav-link:focus { color: #f8fafc; background: #1c2738; }
.navbar.app-nav .nav-link.active { color: #f8fafc; background: #1c2738; }
.navbar.app-nav .dropdown-menu {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-card);
    padding: .35rem;
}
.navbar.app-nav .dropdown-item { border-radius: 6px; font-size: .92rem; }
.navbar.app-nav .dropdown-item:active { background: var(--brand); }

/* ---------- Page header ----------
 * Convention: pages can drop a {% block page_header %}...{% endblock %}
 * with a .page-header > .container > h1 + p. Gives every logged-in
 * page a quiet, consistent zone above the content rather than
 * starting with a raw h1 against the page background. */
.page-header {
    background: linear-gradient(180deg, var(--bg-card) 0%, var(--bg-page) 100%);
    border-bottom: 1px solid var(--line);
    padding: 1.5rem 0 1.25rem;
    margin-bottom: 1.5rem;
}
.page-header h1 {
    font-size: 1.5rem;
    font-weight: 700;
    margin: 0;
}
.page-header .page-subtitle {
    color: var(--text-muted);
    margin: .35rem 0 0;
    font-size: .95rem;
}
.page-header .page-actions {
    display: flex; gap: .5rem; flex-wrap: wrap;
    margin-top: .25rem;
}

/* ---------- Card surface ----------
 * Bootstrap's .card already exists, but defaults to a slightly
 * heavier border + no shadow. Override so any .card on a logged-in
 * page matches the landing's feature-card aesthetic. */
.card {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
}
.card {
    background: var(--bg-card);
    color: var(--text);
}
.card-header {
    background: var(--surface-elevated);
    border-bottom: 1px solid var(--line-soft);
    font-weight: 600;
    color: var(--text);
}

/* Generic "panel" container for pages that don't use Bootstrap .card.
 * Drop .panel onto any div for the same look. */
.panel {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: 1.25rem 1.25rem;
}
.panel + .panel { margin-top: 1rem; }

/* ---------- Buttons ----------
 * Bootstrap's defaults are fine; we sharpen the primary button to the
 * slate palette so it matches the navbar + landing CTAs. */
.btn-primary {
    background: var(--brand);
    border-color: var(--brand);
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
    background: var(--brand-soft);
    border-color: var(--brand-soft);
}
.btn-outline-primary {
    color: var(--brand); border-color: var(--brand);
}
.btn-outline-primary:hover,
.btn-outline-primary:focus {
    background: var(--brand); border-color: var(--brand); color: #fff;
}

/* ---------- Tables ----------
 * Tighter padding + lighter divider colour so dense data feels less
 * cramped. Only applies to .table.app-table to leave the matcher's
 * bespoke table styling alone. */
.table.app-table {
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.table.app-table thead th {
    background: var(--surface-elevated);
    border-bottom: 1px solid var(--line);
    color: var(--text-mid);
    font-weight: 600;
    font-size: .82rem;
    text-transform: uppercase;
    letter-spacing: .06em;
}
.table.app-table tbody td {
    border-color: var(--line-soft);
    color: var(--text);
}

/* ---------- Status pills ----------
 * The `_status_pill.html` include + Python `status_pill` helper both
 * emit `<span class="app-pill app-pill-{kind}">`. Reuse for any
 * tracked-bet status / offer progress / generic info chip. Adding a
 * new kind = adding two tokens (--status-X-bg, --status-X-fg) plus
 * an `.app-pill-X` rule below. The `-strong` opacity variant is for
 * "settled but not the headline" rows (e.g. half_won under a parent
 * win row). */
.app-pill {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    line-height: 1.4;
    white-space: nowrap;
}
.app-pill-won,
.app-pill-complete,
.app-pill-success { background: var(--status-win-bg); color: var(--status-win-fg); }
.app-pill-lost,
.app-pill-danger  { background: var(--status-loss-bg); color: var(--status-loss-fg); }
.app-pill-pending,
.app-pill-progress { background: var(--status-pending-bg); color: var(--status-pending-fg); }
.app-pill-void,
.app-pill-abandoned,
.app-pill-muted    { background: var(--status-void-bg); color: var(--status-void-fg); }
.app-pill-info     { background: var(--status-info-bg); color: var(--status-info-fg); }
.app-pill-warn     { background: var(--status-warn-bg); color: var(--status-warn-fg); }
/* Half-won / half-lost: same colour as their parent state, but the
   slightly lower opacity signals "settled but secondary outcome on a
   multi-leg bet." TrackedBet.status uses these literal slugs so the
   names match what the include receives. */
.app-pill-half_won  { background: var(--status-win-bg); color: var(--status-win-fg); opacity: .82; }
.app-pill-half_lost { background: var(--status-loss-bg); color: var(--status-loss-fg); opacity: .82; }
.app-pill.dim      { opacity: .8; }

/* ---------- Stat strip / stat card ----------
 * The recurring "row of summary tiles at the top of a page" pattern.
 * Used by /bets, /offer-tracker, /jobs and the matcher empty-state
 * card. Drop into a parent with `.app-stat-strip` and let the grid
 * auto-fit on smaller viewports. */
.app-stat-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin-bottom: 1.5rem;
}
.app-stat {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1rem 1.15rem;
    box-shadow: var(--shadow-card);
    transition: transform 120ms ease, border-color 120ms ease,
                box-shadow 120ms ease;
    color: var(--text);
}
.app-stat:hover {
    transform: translateY(-1px);
    border-color: var(--line-strong);
}
.app-stat .label {
    font-size: .72rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
    margin-bottom: .2rem;
}
.app-stat .value {
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.app-stat .value.win,
.app-stat .value.positive { color: var(--status-win-fg-strong); }
.app-stat .value.loss,
.app-stat .value.negative { color: var(--status-loss-fg-strong); }
.app-stat .value.muted    { color: var(--text-muted); }
.app-stat .sub {
    font-size: .72rem;
    color: var(--text-muted);
    margin-top: .15rem;
}

/* ---------- Forms ----------
 * Sharper focus ring — Bootstrap's default is fine but the blue
 * doesn't sit with the slate palette. */
.form-control:focus,
.form-select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(108, 140, 255, .18);
}

/* ---------- Misc ----------
 * Footer (defined in base.html) — sticky-footer pattern via flexbox.
 * Previous attempt used position:fixed which caused content to
 * scroll behind the footer (the "links overlap content" feedback);
 * this approach puts the footer in normal flow but uses
 * `margin-top: auto` inside a flex-column body so the footer is
 * always anchored to the bottom of the viewport on short pages and
 * sits at the end of content on long pages. No content ever
 * scrolls under it.
 *
 * body is a flex column with min-height:100vh; every direct child
 * stays in its natural flow except the footer, which margin-top:auto
 * pushes to the bottom.  */
body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}
footer.container.small {
    color: var(--text-muted);
    margin: auto 0 0 0;          /* push to bottom of flex body */
    max-width: none;
    padding: 12px 16px;
    background: var(--bg-page);
    border-top: 1px solid var(--border-soft, #e3e6ec);
}
footer.container.small hr { display: none; }
footer.container.small a { color: var(--text-mid); text-decoration: none; }
footer.container.small a:hover { color: var(--text); }

/* ---------- Dark theme ----------
 * Activated by `<html data-theme="dark">` (set inline in base.html
 * BEFORE first paint to avoid FOUC). The CSS-variable overrides
 * cover most of the site automatically — anything that already
 * reads --bg-card / --text / --line just inverts. Anything that
 * hard-coded a light hex needs an explicit override below.
 *
 * The navbar palette doesn't change (it was always slate-900); we
 * only adjust the dropdown surfaces + a couple of borders.
 */
[data-theme="dark"] {
    /* Surfaces — page < card < elevated < sunken in the same ladder
       as light so cards still feel raised above the page. */
    --bg-page: #0b1220;
    --bg-card: #131c2e;
    --surface-elevated: #182236;
    --surface-sunken:   #0e1626;
    --surface-hover:    #1f2b46;
    --surface-zebra:    #182236;
    --surface-muted:    #11192a;

    --line: #243049;
    --line-soft: #1a2336;
    --line-strong: #334155;
    --text: #e2e8f0;
    --text-mid: #b6c0d1;
    --text-muted: #94a3b8;
    /* --brand stays slate-900 — navbar background already reads as
       dark in both themes; keeping it constant avoids the navbar
       "switching colour" when toggling and matches the matcher
       table's existing dark header bar. */

    /* Status palette: deepen the bg, lift the fg. The chip surfaces
       are now intentionally close to --bg-card so a pill sits inside
       a card without the harsh light-on-dark contrast that the light
       theme uses (which would feel sticker-y in dark). */
    --status-win-bg:     #102a1c;
    --status-win-fg:     #6ee7a6;
    --status-win-fg-strong: #34d399;
    --status-loss-bg:    #2e1416;
    --status-loss-fg:    #f08a8c;
    --status-loss-fg-strong: #f87171;
    --status-pending-bg: #2a2110;
    --status-pending-fg: #f3c969;
    --status-void-bg:    #1a2336;
    --status-void-fg:    #94a3b8;
    --status-info-bg:    #102238;
    --status-info-fg:    #7eb4ff;
    --status-warn-bg:    #2a1c10;
    --status-warn-fg:    #f0a868;

    --shadow-card: 0 1px 2px rgba(0, 0, 0, .35),
                   0 8px 24px -18px rgba(0, 0, 0, .6);
}

/* Most card / panel / page-header / app-table styles already consume
 * the tokens redefined above, so the bulk of the dark theme falls
 * out for free. The overrides below are only for Bootstrap defaults
 * we never re-declared at the base level (.table without app-table,
 * forms, modals, alerts). */
[data-theme="dark"] body { background: var(--bg-page); color: var(--text); }

/* Bootstrap's bare `.table` (without our .app-table) ships its own
 * white background + black text — re-anchor so admin / settings
 * pages don't render as light slabs in dark layout. */
[data-theme="dark"] .table { color: var(--text); }
[data-theme="dark"] .table > :not(caption) > * > * {
    border-color: var(--line-soft);
    background-color: var(--bg-card);
    color: var(--text);
}
[data-theme="dark"] .table-striped > tbody > tr:nth-of-type(odd) > * {
    background-color: var(--surface-zebra);
}

/* Forms — input / select / textarea backgrounds invert too. */
[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select,
[data-theme="dark"] textarea {
    background: var(--bg-card);
    border-color: var(--line);
    color: var(--text);
}
[data-theme="dark"] .form-control::placeholder { color: var(--text-muted); }
[data-theme="dark"] .form-control:focus,
[data-theme="dark"] .form-select:focus {
    background: var(--bg-card);
    border-color: var(--accent);
    color: var(--text);
}
[data-theme="dark"] .form-label,
[data-theme="dark"] .form-text { color: var(--text-mid); }
[data-theme="dark"] .form-check-input {
    background-color: var(--bg-card);
    border-color: var(--line);
}

/* Bootstrap `.card-footer` — assumes a white surface like card-body
 * does. The light rule we redeclared above only covers .card-header,
 * so footers still rendered as white slabs in dark mode (forums
 * threads + new-thread form were the surfacings). */
[data-theme="dark"] .card-footer {
    background: var(--surface-elevated);
    border-top-color: var(--line-soft);
    color: var(--text);
}

/* Bootstrap list-group — used on /forums (section list + thread
 * list). Default surface is white + dark text, hover lightens.
 * Re-anchor to tokens. */
[data-theme="dark"] .list-group {
    background: transparent;
}
[data-theme="dark"] .list-group-item {
    background: var(--bg-card);
    border-color: var(--line);
    color: var(--text);
}
[data-theme="dark"] .list-group-item-action:hover,
[data-theme="dark"] .list-group-item-action:focus {
    background: var(--surface-hover);
    color: var(--text);
}

/* Breadcrumbs (forums threads + new-thread). Default Bootstrap text
 * + separator both go dark on a dark page. */
[data-theme="dark"] .breadcrumb { color: var(--text-mid); }
[data-theme="dark"] .breadcrumb-item a { color: var(--accent); }
[data-theme="dark"] .breadcrumb-item.active { color: var(--text); }
[data-theme="dark"] .breadcrumb-item + .breadcrumb-item::before {
    color: var(--text-muted);
}

/* Bootstrap's `.alert-secondary` and `.alert-light` are the two
 * variants we hadn't already covered — used for "thread locked" and
 * "no threads yet" messages on forums. */
[data-theme="dark"] .alert-secondary {
    background: var(--surface-elevated);
    border-color: var(--line);
    color: var(--text);
}
[data-theme="dark"] .alert-light {
    background: var(--surface-elevated);
    border-color: var(--line);
    color: var(--text);
}

/* Modals + offcanvas + dropdown menus. Bootstrap defaults assume a
 * white surface so the body looks bleached in dark mode without
 * these. */
[data-theme="dark"] .modal-content,
[data-theme="dark"] .offcanvas,
[data-theme="dark"] .dropdown-menu {
    background: var(--bg-card);
    border-color: var(--line);
    color: var(--text);
}
[data-theme="dark"] .modal-header,
[data-theme="dark"] .modal-footer { border-color: var(--line-soft); }
[data-theme="dark"] .dropdown-item { color: var(--text); }
[data-theme="dark"] .dropdown-item:hover,
[data-theme="dark"] .dropdown-item:focus {
    background: #1c2738; color: var(--text);
}
[data-theme="dark"] .dropdown-divider { border-color: var(--line-soft); }

/* Bootstrap alert backgrounds use light tints — adjust the common
 * cases so dismiss buttons and content stay legible. */
[data-theme="dark"] .alert { color: var(--text); }
[data-theme="dark"] .alert-info {
    background: #0f2638; border-color: #1a3a55; color: #cfe4f5;
}
[data-theme="dark"] .alert-success {
    background: #0f2e21; border-color: #1a4f3a; color: #d1f0df;
}
[data-theme="dark"] .alert-warning {
    background: #2e2410; border-color: #5a4421; color: #f5e1b8;
}
[data-theme="dark"] .alert-danger {
    background: #321618; border-color: #5a2226; color: #f5c4c7;
}

/* btn-close icon ships as a black-on-transparent SVG that vanishes
 * on dark surfaces — invert so close buttons stay visible. */
[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%); }

/* ---------- BookieOffer.staff_rating medal ----------
 * Editorial gold/silver/bronze tier shown next to each offer's kind
 * label on /offers/ and the per-bookie SEO pages. Gradients so the
 * medal reads as a medal at a glance — emoji-free so it prints +
 * reads back well via screen readers. */
.offer-medal {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.72rem;
    font-weight: 700;
    padding: 0.15rem 0.55rem 0.18rem;
    border-radius: 999px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1;
    border: 1px solid transparent;
    white-space: nowrap;
}
.offer-medal::before {
    content: "";
    width: 8px; height: 8px;
    border-radius: 50%;
    background: currentColor;
    opacity: .9;
}
.offer-medal-gold {
    background: linear-gradient(135deg, #fef3c7, #fbbf24);
    color: #78350f;
    border-color: #f59e0b;
}
.offer-medal-silver {
    background: linear-gradient(135deg, #f1f5f9, #cbd5e1);
    color: #334155;
    border-color: #94a3b8;
}
.offer-medal-bronze {
    background: linear-gradient(135deg, #fde68a, #b45309);
    color: #f8fafc;
    border-color: #92400e;
}
/* "None" tier — staff reviewed and decided not worth pursuing. Flat
   muted grey (no gradient) so it reads as the explicit-opt-out tier,
   not a metal-tier medal. The disc on the left flips to a hollow ring
   to reinforce the "no" signal. */
.offer-medal-none {
    background: #e5e7eb;
    color: #475569;
    border-color: #cbd5e1;
}
.offer-medal-none::before {
    background: transparent;
    border: 1.5px solid currentColor;
    opacity: .8;
}
[data-theme="dark"] .offer-medal-gold {
    background: linear-gradient(135deg, #92400e, #fbbf24);
    color: #fef3c7;
}
[data-theme="dark"] .offer-medal-silver {
    background: linear-gradient(135deg, #334155, #94a3b8);
    color: #f1f5f9;
}
[data-theme="dark"] .offer-medal-bronze {
    background: linear-gradient(135deg, #4b2410, #b45309);
    color: #fde68a;
}
[data-theme="dark"] .offer-medal-none {
    background: #1f2937;
    color: #94a3b8;
    border-color: #334155;
}
.offer-header-tags {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Bootstrap modal close-Xs: bump the icon weight + recolour to a
 * strong red so users don't have to hunt for them. Scoped to
 * `.modal-header` so dismiss-X on inline alerts keeps its neutral
 * styling. The filter chain is the standard "tint a black SVG" trick
 * — invert → sepia → saturate → hue-rotate lands close to #ef4444.
 * Dark mode has its own filter because the parent rule above
 * already inverts and would otherwise show as cyan. */
.modal-header .btn-close {
    --bs-btn-close-opacity: 1;
    --bs-btn-close-hover-opacity: 1;
    transform: scale(1.25);
    filter: invert(34%) sepia(78%) saturate(2000%) hue-rotate(345deg)
            brightness(95%) contrast(105%);
}
[data-theme="dark"] .modal-header .btn-close {
    filter: invert(48%) sepia(94%) saturate(2500%) hue-rotate(335deg)
            brightness(100%) contrast(95%);
}

/* Bookie logos sit on white in their source PNGs (they're optimised
 * for the bookies' own light sportsbook UI). On dark surfaces the
 * white-on-white logos disappear entirely, so we put every one of
 * them on a small near-white chip in dark mode. Covers:
 *   - matcher table cells + calc-modal bookie chips + mobile cards
 *   - jobs page bookie-card accordions
 *   - any future surface that uses .bookie-logo. */
[data-theme="dark"] .bookie-logo {
    background: #f1f5f9;
    border-radius: 3px;
    padding: 1px;
}

/* Outline buttons in dark mode. The light theme uses slate-900 for
 * btn-outline-primary's border + text, which against the
 * near-black dark page surface effectively disappears (the Filters
 * + Settings buttons on the matcher toolbar were the symptom).
 * Re-anchor to a bright readable foreground in dark. */
[data-theme="dark"] .btn-outline-primary {
    color: #e2e8f0;
    border-color: #475569;
    background: transparent;
}
[data-theme="dark"] .btn-outline-primary:hover,
[data-theme="dark"] .btn-outline-primary:focus,
[data-theme="dark"] .btn-outline-primary:active {
    background: var(--accent);
    border-color: var(--accent);
    color: #0f172a;
}
[data-theme="dark"] .btn-outline-secondary {
    color: #cbd5e1;
    border-color: #475569;
    background: transparent;
}
[data-theme="dark"] .btn-outline-secondary:hover,
[data-theme="dark"] .btn-outline-secondary:focus,
[data-theme="dark"] .btn-outline-secondary:active {
    background: #334155;
    border-color: #64748b;
    color: #f8fafc;
}
[data-theme="dark"] .btn-outline-secondary:disabled,
[data-theme="dark"] .btn-outline-primary:disabled {
    color: #64748b; border-color: #334155;
    opacity: .6;
}

/* Footer + horizontal rules — keep contrast against the darker page. */
[data-theme="dark"] footer.container.small {
    background: var(--bg-page); color: var(--text-muted);
    border-top-color: var(--line);
}
[data-theme="dark"] hr { border-color: var(--line); }

/* Misc utility classes commonly used across the site. */
[data-theme="dark"] .text-muted { color: var(--text-muted) !important; }
[data-theme="dark"] .text-body { color: var(--text) !important; }
[data-theme="dark"] .bg-light { background-color: #1a2336 !important; color: var(--text); }
[data-theme="dark"] .bg-white { background-color: var(--bg-card) !important; color: var(--text); }
[data-theme="dark"] .border { border-color: var(--line) !important; }

/* ---------- Theme toggle button ----------
 * Sits in the navbar; sun + moon SVGs both rendered but only one is
 * visible per theme. The button reuses .nav-link styling for hover
 * + focus so it visually matches the surrounding nav items. */
.theme-toggle {
    background: transparent;
    border: 0;
    color: #cbd5e1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: .35rem .55rem !important;
    border-radius: 6px;
    cursor: pointer;
    line-height: 1;
}
.theme-toggle:hover,
.theme-toggle:focus { color: #f8fafc; background: #1c2738; outline: none; }
.theme-toggle .theme-icon { display: none; }
/* Light theme: show moon (click -> dark). */
[data-theme="light"] .theme-toggle .theme-icon-moon,
:root:not([data-theme="dark"]) .theme-toggle .theme-icon-moon { display: inline-block; }
/* Dark theme: show sun (click -> light). */
[data-theme="dark"] .theme-toggle .theme-icon-sun { display: inline-block; }

/* ---------- Themed number-input spinner ----------
 * Replaces Windows/Chrome's default number-input up/down arrows
 * (which look like ugly system spinners against our slate UI) with
 * a small stacked +/- column matching the site palette. The wrapper
 * + buttons are injected by core/static/js/number-spinner.js so
 * existing <input type="number"> elements anywhere in the codebase
 * get the treatment without per-template changes.
 *
 * The buttons sit on the right edge of the input over the same
 * pixel area the native spinner used, so wrapping doesn't shift any
 * existing layout. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

.num-spinner {
    position: relative;
    /* inline-flex (instead of inline-block) so the wrapper shrinks
     * with its parent inside grid / flex cells. A bare inline-block
     * wrapper inherits the input's natural ~149px Chrome default
     * width, which overflowed constrained cells (most visibly the
     * Settings → Exchange commissions list, whose grid template is
     * `1fr 76px auto`). */
    display: inline-flex;
    align-items: stretch;
    max-width: 100%;
    /* Keep wrapping a standalone <input> visually neutral relative
     * to inline siblings (labels, "%" suffixes, etc.). */
    vertical-align: middle;
}
.num-spinner > input[type="number"] {
    /* Reserve room for the +/- column on the right. !important
     * because some per-page styles set their own padding-right
     * (e.g. user_settings .input-wrap input) which would let text
     * run under the buttons. */
    padding-right: 22px !important;
    /* Let the input shrink below its content / default size when
     * the parent column is narrower than ~149px. Without
     * `min-width: 0` flex items refuse to shrink past intrinsic
     * min and the wrapper still overflows. */
    flex: 1 1 auto;
    min-width: 0;
    box-sizing: border-box;
}
.num-spinner > .spin-btns {
    position: absolute;
    top: 2px; right: 2px; bottom: 2px;
    width: 18px;
    display: flex;
    flex-direction: column;
    border-left: 1px solid var(--line);
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
    overflow: hidden;
    pointer-events: auto;
}
.num-spinner > .spin-btns > button {
    flex: 1 1 50%;
    min-height: 0;
    padding: 0;
    border: 0;
    background: var(--surface-elevated);
    color: var(--text-muted);
    cursor: pointer;
    font-size: 8px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    transition: background 90ms, color 90ms;
}
.num-spinner > .spin-btns > button + button {
    border-top: 1px solid var(--line);
}
.num-spinner > .spin-btns > button:hover {
    background: var(--accent);
    color: #fff;
}
.num-spinner > .spin-btns > button:active {
    background: var(--accent-strong);
    color: #fff;
}
.num-spinner[data-disabled] > .spin-btns > button {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

/* ---------- "Find a bet" matcher iframe overlay ----------
 * Shared across /offers/ (grid + per-bookie SEO page) + /offer-tracker/.
 * Used to be inlined in each template — moved here so a single style
 * change (the close X going red + bigger; "Open full page" upgraded to a
 * proper button) applies everywhere without per-template edits.
 *
 * The iframe loads the matcher with `?embed=1` so base.html strips the
 * navbar / footer. Click-on-backdrop and ESC both close; the modal-open
 * class on <body> kills page scroll while the matcher is visible. */
.matcher-modal {
    position: fixed; inset: 0;
    background: rgba(15, 23, 42, 0.55);
    z-index: 1080;
    display: none;
    align-items: center; justify-content: center;
    padding: 2rem 1rem;
}
.matcher-modal.open { display: flex; }
.matcher-modal-dialog {
    background: var(--bg-card);
    width: min(1400px, 96vw);
    height: min(900px, 92vh);
    border-radius: 14px;
    box-shadow: 0 30px 60px -20px rgba(0, 0, 0, .45);
    display: flex; flex-direction: column;
    overflow: hidden;
    color: var(--text);
}
.matcher-modal-header {
    display: flex; align-items: center; gap: .6rem;
    padding: .55rem .8rem;
    border-bottom: 1px solid var(--line);
    background: var(--surface-elevated);
}
.matcher-modal-title {
    font-weight: 600; color: var(--text);
    flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: .95rem;
}
.matcher-modal-title .label {
    font-size: 0.66rem; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.06em;
    margin-right: .35rem;
}
/* "Open full page" used to be a quiet inline link; promote to a
 * proper outline button so the affordance is obvious next to the
 * close X. Borrows .btn-outline-primary's geometry without forcing
 * the whole Bootstrap utility chain on every consumer. */
.matcher-modal-open-full {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    padding: 0.32rem 0.75rem;
    border: 1.5px solid var(--accent-strong);
    border-radius: 6px;
    background: transparent;
    color: var(--accent-strong);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    line-height: 1.2;
    white-space: nowrap;
    transition: background 90ms, color 90ms;
}
.matcher-modal-open-full:hover,
.matcher-modal-open-full:focus {
    background: var(--accent-strong);
    color: #fff;
    text-decoration: none;
}
/* Close X — bolder, bigger, red. Mirrors the popup-X styling the
 * matcher's own modals already got (PR #173) so the close affordance
 * reads identically across every modal in the app. */
.matcher-modal-close {
    background: transparent;
    border: 1.5px solid #ef4444;
    color: #ef4444;
    font-size: 1.6rem;
    font-weight: 900;
    line-height: 1;
    width: 38px; height: 38px;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 90ms, color 90ms;
}
.matcher-modal-close:hover,
.matcher-modal-close:focus {
    background: #ef4444;
    color: #fff;
}
.matcher-modal-body {
    flex: 1; min-height: 0;
    position: relative;
    background: var(--surface-sunken);
}
.matcher-modal-body iframe {
    width: 100%; height: 100%;
    border: 0; display: block;
}

/* Spinner overlay shown while the iframe is loading the matcher /
   acca builder. Sits absolutely over the iframe so the user sees
   activity instead of a blank panel during the 1-2s first-paint;
   the per-page JS hides it when the iframe fires `load`. */
.matcher-modal-spinner {
    position: absolute; inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
    background: var(--surface-sunken);
    color: var(--text-mid);
    font-size: 0.92rem;
    z-index: 1;
}
.matcher-modal-spinner[hidden] { display: none; }
.matcher-modal-spinner::before {
    content: '';
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 3px solid var(--line);
    border-top-color: var(--accent-strong);
    animation: matcher-modal-spin 0.7s linear infinite;
}
@keyframes matcher-modal-spin {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    .matcher-modal-spinner::before { animation: none; }
}

body.matcher-modal-open { overflow: hidden; }
