/*
 * Small site-specific overrides that don't belong in the vendored style.css.
 */

/*
 * Collapsed-sidebar logo: the sidebar's own logo slides off-screen when the
 * sidebar is collapsed (.layout-wrap.full-width), so show it in the header
 * instead – same aspect ratio as the sidebar's own logo, sized to fit the
 * header row (the source is 104x104), with the collapse toggle sitting to
 * its right (swapped sides with it). Kept permanently `display: flex` (with
 * !important, since style.css's `.header-dashboard .wrap .header-left > a {
 * display: none }` outguns a plain class selector on specificity) and
 * animated via max-width/opacity instead, so the reveal can transition
 * smoothly rather than snapping with `display`, which can't be animated.
 */
.header-collapsed-logo {
    display: -webkit-box !important;
    display: -moz-box !important;
    display: -ms-flexbox !important;
    display: -webkit-flex !important;
    display: flex !important;
    align-items: center;
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    margin-right: 0;
    transition:
        max-width 0.35s cubic-bezier(0.4, 0, 0.2, 1),
        margin-right 0.35s cubic-bezier(0.4, 0, 0.2, 1),
        opacity 0.2s ease;
}

.layout-wrap.full-width .header-collapsed-logo {
    max-width: 56px;
    opacity: 1;
    margin-right: 15px;
    transition:
        max-width 0.35s cubic-bezier(0.4, 0, 0.2, 1),
        margin-right 0.35s cubic-bezier(0.4, 0, 0.2, 1),
        opacity 0.3s ease 0.1s;
}

.header-collapsed-logo img {
    height: 48px;
    width: 48px;
}

/*
 * Live match overlay on the Fixtures page (see fixtures.php's inline
 * script) – a small "LIVE" ribbon plus an in-page modal for score/odds/
 * timeline, fed by the tennis-api Socket.IO feed.
 */
.fixtures-b-card {
    position: relative;
}

.fixtures-b-live-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    background: var(--siq-bad);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 2px 8px;
    border-radius: 10px;
    z-index: 2;
}

.fixtures-b-card.is-live-now .fixtures-b-vs {
    color: var(--siq-bad);
    font-weight: 700;
}

.fixtures-live-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
}

.fixtures-live-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}

/*
 * --bg-card was never defined anywhere in the stylesheet, so this always fell
 * through to the hardcoded #1c1c26 – a dark panel, in BOTH themes. In light
 * mode that gave dark text (color: inherit) on a near-black card. --White is
 * the theme's own card surface: #fff in light, #1E293B under .dark-theme.
 */
.fixtures-live-modal-body {
    position: relative;
    background: var(--White);
    color: var(--Body-Text);
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    padding: 20px;
    width: 90%;
    max-width: 480px;
    max-height: 80vh;
    overflow-y: auto;
    /* Wide content (odds tables, timelines) scrolls INSIDE the dialog rather
       than widening it off-screen – auto, not hidden, so nothing is silently
       cut off on a phone. */
    overflow-x: auto;
    overscroll-behavior: contain;
}

/* A dialog on a phone should use the screen, not float in the middle of it. */

/* Tables inside a dialog get their own scroll rather than stretching it. */
.fixtures-live-modal-body table {
    width: 100%;
    max-width: 100%;
}

.fixtures-live-modal-body h5,
.fixtures-live-modal-body .stadium-style-title {
    color: var(--Heading);
}

/*
 * Shared live-match scoreboard building blocks – used by the standalone
 * Live Scores page, the dashboard tile (its own smaller "mini" variant),
 * and the Fixtures page's live-match modal. Server dot + per-set games +
 * current game points, driven by the tennis-api feed's own `indicator`
 * field (confirmed live by cross-referencing it against real matches' own
 * point-by-point timelines: "p1Serving,p2Serving" as 1/0 flags).
 */
.live-conn-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 4px 12px;
    border-radius: 20px;
    background: rgba(125, 125, 125, 0.15);
    color: var(--Note);
}
.live-conn-badge.is-live {
    background: rgba(224, 75, 75, 0.15);
    color: var(--siq-bad);
}

.live-scoreboard {
    /* style.css sets a BLANKET `table { table-layout: fixed; }` for every
       table on the site – fixed layout sizes columns only from the first
       row's own widths (or splits evenly with none given), which is what
       actually caused the set/points columns to collapse into each other.
       auto lets each column size from its own content/min-width again. */
    table-layout: auto;
    width: 100%;
    border-collapse: collapse;
}
/* ── The live board is fully ruled ──
   Cells carried no border of their own, so the grid appeared in some places
   and not others and the table looked unfinished down its edges. One rule for
   every cell, and the section-label rows opt out above so a heading does not
   get a line through it. */
.live-scoreboard { border: 1px solid var(--Stroke); }
.live-scoreboard td,
.live-scoreboard th {
    border-right: 1px solid var(--Stroke);
    border-bottom: 1px solid var(--Stroke);
}
.live-scoreboard td:last-child,
.live-scoreboard th:last-child { border-right: 0; }
.live-scoreboard tr:last-child td { border-bottom: 0; }

.live-scoreboard td {
    /* 16px/600 matches the body text size used elsewhere for this kind of
       prominent line – same as Next Fixtures' player names
       (.fixtures-a-players) and Upcoming Tournaments' title – this was
       14px, smaller than either, which read small next to the rest of the
       site rather than consistent with it. */
    padding: 7px 8px;
    font-size: 16px;
    color: var(--Body-Text);
    white-space: nowrap;
}
.live-scoreboard .live-serve-dot {
    /* Tennis ball emoji rather than a plain colored dot – no color tint
       needed (emoji renders its own color), just enough size/width to
       read clearly next to the player name. */
    width: 18px;
    padding-right: 0;
    font-size: 14px;
}
.live-scoreboard .live-player-name {
    /* NOT width:100% – inside a <table>, that forces this column to claim
       the entire table width regardless of the other columns' own
       min-width, squeezing the set/points columns down to nothing and
       making their digits overlap. max-width + the parent td's
       white-space:nowrap does the truncation job instead, letting
       table-layout:auto give the set/points columns their real min-width. */
    max-width: 220px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
}
.live-scoreboard tr.is-serving .live-player-name {
    color: var(--Heading);
    font-weight: 800;
}
.live-scoreboard .live-set {
    text-align: center;
    min-width: 28px;
    /* A set score is the score, not a caption about it - it was being drawn
       in the secondary ink, which read as greyed-out next to the player. */
    color: var(--Body-Text);
}
.live-scoreboard tr.is-serving .live-set:last-of-type {
    color: var(--Heading);
}
.live-scoreboard .live-points {
    text-align: center;
    min-width: 36px;
    font-size: 20px;
    font-weight: 700;
    /* Was a fixed #3d5afe, which is 2.85:1 on the dark scoreboard. The
       variable carries one value per theme. */
    color: var(--chip-points);
}

/*
 * Match stats dropdown (aces / double faults / 1st-serve % / break-point
 * conversion %) – collapsed by default, shown in the Live Scores detail
 * panel and the Fixtures page's live-match modal. Only ever available from
 * the Get Live Score snapshot / socket event-update (confirmed live), so
 * this whole block just doesn't render when a match hasn't got it yet.
 */
.live-stats-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    background: none;
    border: 1px solid var(--Stroke);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 700;
    color: var(--Heading);
    cursor: pointer;
    text-align: left;
}
.live-stats-chevron {
    transition: transform 0.15s ease;
    font-size: 11px;
}
.live-stats-toggle.is-open .live-stats-chevron {
    transform: rotate(180deg);
}
.live-stats-table {
    /* Same fixed-vs-auto issue as .live-scoreboard above – style.css's
       blanket `table { table-layout: fixed; }` needs overriding here too. */
    table-layout: auto;
    width: 100%;
    border-collapse: collapse;
    margin-top: 8px;
}
.live-stats-table thead th {
    font-size: 12px;
    font-weight: 700;
    color: var(--Heading);
    text-align: center;
    padding: 4px 6px;
    border-bottom: 1px solid var(--Stroke);
}
.live-stats-table td {
    padding: 6px;
    font-size: 14px;
    border-bottom: 1px solid var(--Stroke);
}
.live-stats-table tr:last-child td {
    border-bottom: none;
}
.live-stats-value {
    width: 60px;
    text-align: center;
    font-weight: 700;
    color: var(--Body-Text);
}
.live-stats-label {
    text-align: center;
    color: var(--Note);
    font-size: 12px;
}

/*
 * Pre-match favorite badge (fixture.php) – from Bet365/BetFair/Pinnacle's
 * own "Full Time Result" odds, majority consensus across whichever of the
 * three responded (panel's FixtureFavoriteService).
 */
.fixture-vs-favorite-badge {
    display: inline-block;
    margin-top: 4px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #f0ad4e;
    background: rgba(240, 173, 78, 0.15);
    padding: 3px 10px;
    border-radius: 10px;
}

/*
 * Odds Timeline (fixture.php) – point-by-point score/odds history captured
 * live by services/live-odds-listener.
 */
.odds-timeline-list {
    margin-top: 12px;
    max-height: 420px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.odds-timeline-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 6px 4px;
    border-bottom: 1px solid var(--Stroke);
    font-size: 13px;
}
.odds-timeline-time {
    flex-shrink: 0;
    width: 64px;
    color: var(--Note);
    font-size: 12px;
}
.odds-timeline-score {
    font-weight: 700;
    color: var(--Heading);
}
.odds-timeline-note {
    color: var(--Note);
    font-size: 12px;
    margin-left: 8px;
}
.odds-timeline-odds {
    color: var(--Body-Text);
}


/* ---------------------------------------------------------------
   Sports-IQ Tennis Intelligence – public fixture page.
   Uses the site's stadium-card tokens (--Stroke, the --siq-accent blue)
   rather than the demo's palette, so the section reads as part of
   the page rather than an embedded widget.
   --------------------------------------------------------------- */
.siqm-panel {
    margin-top: 14px;
    padding: 18px 20px;
    border-radius: 14px;
    border: 1px solid var(--Stroke);
    border-left: 5px solid var(--siqm-accent, var(--siq-accent));
}
.siqm-fav {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .08em;
    opacity: .7;
    margin-bottom: 6px;
}
.siqm-class {
    font-size: 26px;
    font-style: italic;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .5px;
    line-height: 1.15;
    color: var(--siqm-accent, var(--siq-accent));
}
.siqm-interp { margin-top: 8px; max-width: 74ch; }
.siqm-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.siqm-chip {
    padding: 5px 12px;
    border-radius: 999px;
    border: 1px solid var(--Stroke);
    font-size: 12px;
}
.siqm-chip-conflict {
    border-color: var(--siq-bad);
    background: rgba(239, 68, 68, .12);
    font-weight: 700;
}

.siqm-scroll { overflow-x: auto; }
.siqm-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.siqm-table th,
.siqm-table td { padding: 10px 12px; border-bottom: 1px solid var(--Stroke); text-align: left; vertical-align: top; }
.siqm-table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .07em;
    opacity: .7;
    font-weight: 700;
}
.siqm-table td.num,
.siqm-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.siqm-table tr:last-child td { border-bottom: 0; }
.siqm-sub { display: block; font-size: 11px; opacity: .6; margin-top: 2px; }

.siqm-oa { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 8px; }
.siqm-oa-card { border: 1px solid var(--Stroke); border-radius: 12px; padding: 14px 16px; }
.siqm-oa-name { font-weight: 600; margin-bottom: 4px; }
.siqm-oa-score { font-size: 30px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.siqm-oa-score.siqm-oa-na { opacity: .4; font-weight: 400; }
.siqm-oa-class { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; opacity: .8; }
.siqm-note { font-size: 12px; opacity: .65; }


/* Compact Match Intelligence badge on fixture cards (Part 2 UI spec §1) –
   directional class only, conflict marker when present. */
.fixtures-b-siq {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
    padding: 6px 12px 0;
    font-size: 11px;
}
.fixtures-b-siq-label {
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: 9px;
    opacity: .55;
}
.fixtures-b-siq-value {
    font-weight: 700;
    color: var(--siq-accent);
    letter-spacing: .02em;
}
.fixtures-b-siq-conflict { color: var(--siq-bad); font-weight: 700; }

/* ---- Sports-IQ on the player directory and profile ---------------------- */

.players-row-iq {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.siqp-pill {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 3px 8px;
    border: 1px solid var(--Stroke);
    border-radius: 6px;
    line-height: 1.2;
}

.siqp-pill em {
    font-style: normal;
    font-size: 10px;
    letter-spacing: .04em;
    color: var(--Note);
}

.siqp-pill b {
    font-size: 13px;
    font-weight: 700;
}

.siqp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    margin-top: 14px;
}

.siqp-card {
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    padding: 14px 16px;
    border-left: 5px solid var(--siqp-accent, var(--siq-accent));
}

.siqp-card-label {
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--Note);
}

.siqp-card-value {
    font-size: 30px;
    font-weight: 700;
    line-height: 1.1;
    margin-top: 4px;
}

.siqp-card-class {
    font-size: 12px;
    font-weight: 600;
    margin-top: 2px;
}

.siqp-card-note {
    font-size: 11px;
    color: var(--Note);
    margin-top: 6px;
}

/* ── Header cards: one per row on a phone ───────────────────────────────────
   The grid is auto-fit with a 180px minimum, so on a ~390px phone it still
   fits TWO columns - about 180px each once the card padding is taken off. A
   long category ("ATP Masters 1000", "Grand Slam") set at 24px does not fit
   that, so it ran outside the card and off the side of the page, which is the
   reported fault.

   Below 576px the cards therefore stack, which puts Category under Prize pot
   as asked and gives the value the full card width. Long values are also
   allowed to wrap rather than overflow, so this holds for a category longer
   than any currently in the data instead of only for the ones we have seen. */
@media (max-width: 575.98px) {
    .siqp-grid { grid-template-columns: 1fr; }
}
.siqp-card { min-width: 0; }
.siqp-card-value { overflow-wrap: anywhere; }

.siqp-foot {
    font-size: 11px;
    color: var(--Note);
    margin-top: 12px;
}


/* ---- Players directory on a phone --------------------------------------- */

.players-row-metas {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    /* Nothing here may push the row wider than its card. */
    min-width: 0;
    flex-wrap: wrap;
}

/* Wide screens show the full hand description; the compact form is for phones. */
.players-hand-short {
    display: none;
}

/*
 * At 320px the row had ~228px of content width to divide between a 40px rank,
 * an 18px flag, two nowrap meta chips and four 14px gaps – leaving the name
 * about 30px, which it cannot shrink into, so the row overflowed the screen.
 *
 * The row wraps, and age/hand move to a second line as a unit so the name gets
 * the full width of the first.
 */

.siqp-muted {
    color: var(--Note);
    font-size: 12px;
}

/* ---- "Under development" banner (tester/admin-only pages) -------------- */

/* Sits inside .main-content-inner, so the header/sidebar offsets are already
   applied by the layout – only its own spacing is set here. */
.under-dev-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    grid-column: 1 / -1;
    margin: 0 0 16px;
    padding: 12px 16px;
    border: 1px solid var(--siq-warn);
    border-left-width: 6px;
    border-radius: 10px;
    /* Repeating hazard stripe, kept faint so it reads as a status band rather
       than competing with the page content underneath it. */
    background:
        repeating-linear-gradient(
            135deg,
            rgba(209, 133, 43, .10) 0 10px,
            rgba(209, 133, 43, .04) 10px 20px
        );
    color: var(--Body-Text);
    font-size: 13px;
    line-height: 1.5;
}

.under-dev-icon {
    color: var(--siq-warn);
    font-size: 18px;
    line-height: 1;
    flex-shrink: 0;
}

.under-dev-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 8px;
    min-width: 0;
}

.under-dev-text strong {
    color: var(--siq-warn);
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: 12px;
}

.under-dev-detail {
    color: var(--Note);
    font-size: 12px;
}

.under-dev-tag {
    margin-left: auto;
    flex-shrink: 0;
    padding: 2px 8px;
    border: 1px solid var(--Stroke);
    border-radius: 6px;
    font-size: 11px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    color: var(--Note);
}


/* ---- Sports-IQ dials ---------------------------------------------------- */

.siqp-dials {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 8px;
    margin-top: 16px;
}

.siqp-dial {
    text-align: center;
    padding: 10px 8px 16px;
    border: 1px solid var(--Stroke);
    border-radius: 14px;
    background:
        radial-gradient(120% 80% at 50% 0%, rgba(78, 168, 255, .07), transparent 70%);
}

.siqp-dial-canvas {
    min-height: 190px;
}

.siqp-dial-empty {
    min-height: 190px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.siqp-dial-empty-mark {
    width: 92px;
    height: 92px;
    border-radius: 50%;
    border: 3px dashed var(--Stroke);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--Note);
    font-size: 26px;
}

.siqp-dial-empty-note {
    font-size: 11px;
    color: var(--Note);
}

.siqp-dial-title {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--Heading);
    margin-top: 2px;
}

.siqp-dial-class {
    font-size: 12px;
    font-weight: 600;
    margin-top: 3px;
}

/* ---- Opposed-measure split (bar grows out from the centre) ------------- */

.siq-split {
    display: grid;
    grid-template-columns: 62px 1fr 62px;
    grid-template-areas:
        "lv track rv"
        "name name name";
    align-items: center;
    gap: 4px 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--Stroke);
}

.siq-split:last-child { border-bottom: 0; }

.siq-split-value {
    font-size: 14px;
    font-weight: 600;
    color: var(--Note);
    font-variant-numeric: tabular-nums;
}

.siq-split-value:first-of-type { grid-area: lv; text-align: right; }
.siq-split-value:nth-of-type(2) { grid-area: rv; text-align: left; }

.siq-split-value.is-leading {
    color: var(--compare-strong);
    font-weight: 800;
}

/* Thicker than the 10px it was: these bars carry the comparison, and a thin
   rail made a small-but-real edge hard to see at a glance. */
.siq-split-track {
    grid-area: track;
    position: relative;
    display: block;
    height: 16px;
    border-radius: 999px;
    background: var(--Stroke);
}

/* The centre line is the reference the bar is measured from, so it stays
   visible above the fill. */
.siq-split-axis {
    position: absolute;
    left: 50%;
    top: -4px;
    bottom: -4px;
    width: 2px;
    margin-left: -1px;
    background: var(--Note);
    opacity: .55;
    z-index: 2;
}

.siq-split-bar {
    position: absolute;
    top: 0;
    bottom: 0;
    border-radius: 999px;
    transition: width .35s ease;
}

.siq-split-name {
    grid-area: name;
    text-align: center;
    font-size: 11px;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--Note);
}

.siq-split-edge {
    text-transform: none;
    letter-spacing: 0;
    opacity: .75;
}


/* ---- Surface radar ------------------------------------------------------ */

.surface-radar-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(220px, 320px);
    gap: 20px;
    align-items: center;
    margin-top: 12px;
}

/*
 * min-width: 0 is the whole fix for the radar spilling out of its card.
 *
 * A grid item defaults to min-width: auto, meaning it refuses to shrink below
 * its content's intrinsic size. ApexCharts measures the container once and
 * draws an SVG at that width, the item then refuses to shrink to the column,
 * and the chart pushes out past the card edge. Explicitly allowing the item to
 * shrink lets the column govern the chart instead of the other way round.
 */
.surface-radar,
.surface-legend {
    min-width: 0;
}

/* A radar is a shape, not a series over time – it gains nothing from being
   stretched across a 1500px card, and the label ring needs the room around it.
   Capped and centred within its column. */
.surface-radar {
    width: 100%;
    max-width: 420px;
    margin: 0 auto;
}

.siq-chart-wrap {
    width: 100%;
    max-width: 520px;
    margin: 12px auto 0;
}


.surface-legend-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--Stroke);
}

.surface-legend-row:last-child { border-bottom: 0; }

.surface-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    flex-shrink: 0;
}

.surface-dot-hard  { background: var(--siq-accent); }
.surface-dot-ihard { background: var(--siq-accent-alt); }
.surface-dot-clay  { background: var(--siq-warn); }
.surface-dot-grass { background: var(--siq-good); }

.surface-legend-name {
    flex: 1;
    font-size: 13px;
    font-weight: 600;
    color: var(--Body-Text);
}

.surface-legend-value {
    font-size: 15px;
    font-weight: 700;
    /* --Heading is a muted grey in dark mode; this is the headline number on
       the row, so it uses the same "strong" token as the comparison winners. */
    color: var(--compare-strong);
    font-variant-numeric: tabular-nums;
}

/* ---- Sports-IQ match panel: names, splits, dials ------------------------ */

.siqm-names {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    margin-top: 12px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--Heading);
}

.siqm-names span:last-child { text-align: right; }

.siqm-splits {
    margin-top: 4px;
}

.siqm-dials {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* ---- Opponent-Adjusted Performance ------------------------------------- */

.siqm-oa {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 12px;
    margin-top: 12px;
}

.siqm-oa-card {
    border: 1px solid var(--Stroke);
    border-radius: 14px;
    padding: 14px 14px 16px;
    text-align: center;
    background: radial-gradient(120% 80% at 50% 0%, rgba(78, 168, 255, .07), transparent 70%);
}

.siqm-oa-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--Heading);
}

.siqm-oa-class {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.siqm-oa-pp {
    margin-top: 12px;
}

/* Centred scale: zero (matching expectation) is the reference the reader
   needs, so the bar grows out from it rather than up from a floor. */
.siqm-oa-pp-track {
    position: relative;
    height: 8px;
    border-radius: 999px;
    background: var(--Stroke);
}

.siqm-oa-pp-axis {
    position: absolute;
    left: 50%;
    top: -3px;
    bottom: -3px;
    width: 2px;
    margin-left: -1px;
    background: var(--Note);
    opacity: .55;
    z-index: 2;
}

.siqm-oa-pp-bar {
    position: absolute;
    top: 0;
    bottom: 0;
    border-radius: 999px;
}

.siqm-oa-pp-label {
    margin-top: 7px;
    font-size: 11px;
    color: var(--Note);
}

.siqm-oa-pp-label strong {
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.siqm-oa-scale {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    margin-top: 10px;
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--Note);
}

.siqm-oa-scale span:nth-child(2) { text-align: center; }
.siqm-oa-scale span:last-child { text-align: right; }

/* ---- Sidebar "DEV" chip (tester/admin-only pages) ---------------------- */

.menu-dev-chip {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 5px;
    border: 1px solid var(--siq-warn);
    border-radius: 4px;
    color: var(--siq-warn);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: .06em;
    line-height: 1.5;
    vertical-align: middle;
}


/* ---- Chart overflow guard ---------------------------------------------- */

/*
 * ApexCharts writes explicit pixel width/height onto its wrapper and SVG. If
 * it ever measures a container wider than it ends up being – a sidebar
 * transition mid-render, a font landing late, a flex/grid item that shrinks
 * afterwards – the drawing stays at the old size and hangs outside its card.
 * Capping it here means the worst case is a slightly small chart rather than
 * one bleeding across the layout.
 */
.surface-radar .apexcharts-canvas,
.siq-chart-wrap .apexcharts-canvas,
.siqp-dial-canvas .apexcharts-canvas {
    max-width: 100% !important;
}

.surface-radar svg,
.siq-chart-wrap svg,
.siqp-dial-canvas svg {
    max-width: 100%;
}


/* ---- Mobile gutters ----------------------------------------------------- */

/*
 * The theme keeps a 30px gutter each side of the content at every width. On a
 * 360px phone that is 60px – a sixth of the screen – spent on whitespace while
 * the live scoreboard is fighting for room. Narrowed on small screens so the
 * content gets it back.
 */

/* ---- Device preview ----------------------------------------------------- */

.dev-preview-strip {
    display: flex;
    gap: 18px;
    align-items: flex-start;
    overflow-x: auto;
    padding: 16px 2px 6px;
}

.dev-preview-device {
    margin: 0;
    flex: 0 0 auto;
}

.dev-preview-device figcaption {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 700;
    color: var(--Heading);
}

.dev-preview-device figcaption span {
    font-size: 11px;
    font-weight: 400;
    color: var(--Note);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* The iframe is rendered at its true device width and then scaled down, so the
   page inside still sees a 320/360/390px viewport and fires the right media
   queries – scaling the frame itself would just shrink a desktop layout. */
.dev-preview-frame {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--Stroke);
    border-radius: 12px;
    background: var(--White);
}

.dev-preview-frame iframe {
    position: absolute;
    top: 0;
    left: 0;
    border: 0;
    transform-origin: top left;
}


/* ---- Winning value emphasis, in both themes ---------------------------- */

/*
 * The "leading" figure in a comparison was styled `color: var(--Heading)`
 * against a `var(--Body-Text)` base. That reads correctly in light mode
 * (#111 against #575864 – the winner is darker and stronger) but INVERTS in
 * dark mode, where --Heading is #94A3B8 and --Body-Text is #FFFFFF: the
 * winning number came out greyer than the losing one. On the Sports-IQ splits,
 * whose base is --Note (#95989D), the dark-mode "highlight" was #94A3B8 –
 * indistinguishable.
 *
 * One variable, defined per theme, so the winner is always the stronger of the
 * pair and the weight difference carries it even where colour is subtle.
 */
/*
 * ONE PALETTE, DEFINED ONCE.
 *
 * Roles rather than shades: what a colour MEANS is the thing worth keeping
 * consistent, and the site had drifted into four reds, three greens and three
 * ambers doing the same jobs in different files. Both chart scripts read these
 * same variables through cssVar(), so a graph, a badge and a chip cannot
 * disagree about what "bad" looks like.
 *
 * Amber is the only role that needs a per-theme value: the light-mode orange
 * is too dark to read against the dark background, so it lightens there. The
 * rest hold up in both themes (they were picked against both).
 *
 * Tour colours are ATP blue / WTA purple everywhere – the live scores page
 * used to carry its own blue/pink pair.
 */
:root {
    --siq-accent: #4EA8FF;
    --siq-accent-alt: #9B6BFF;
    --siq-good: #2FA36B;
    --siq-warn: #D1852B;
    --siq-bad: #E5484D;
    --tour-atp: #0057FF;
    --tour-wta: #9333EA;
}

.dark-theme {
    --siq-warn: #F0AE2F;
}
:root {
    --compare-strong: var(--Heading);
    --compare-weak: var(--Note);
}

.dark-theme {
    --compare-strong: #FFFFFF;
    --compare-weak: #8B94A5;
}

.compare-value,
.compare-simple-value {
    color: var(--compare-weak);
    font-weight: 500;
}

.compare-value.is-leading,
.compare-simple-value.is-leading {
    color: var(--compare-strong);
    font-weight: 800;
}

.siq-split-value {
    color: var(--compare-weak);
}



/* ---- Set-by-set guide ---------------------------------------------------
 * Lives above the responsive section: these rules were originally appended to
 * the end of the file, below the media queries, which is the source-order trap
 * the header down there warns about. */

.live-stats-tabs {
    display: flex;
    gap: 6px;
    margin-top: 8px;
}
.live-stats-tab {
    background: none;
    border: 1px solid var(--Stroke);
    border-radius: 999px;
    padding: 4px 12px;
    font-size: 12px;
    font-weight: 700;
    color: var(--Note);
    cursor: pointer;
}
.live-stats-tab.is-active {
    border-color: var(--compare-strong);
    color: var(--Heading);
}

.set-guide-table {
    /* style.css sets a blanket `table { table-layout: fixed }`; this table
       sizes to its content like the stats table beside it. */
    table-layout: auto;
    width: 100%;
    border-collapse: collapse;
    margin-top: 10px;
    font-size: 13px;
}
.set-guide-table th,
.set-guide-table td {
    padding: 6px 8px;
    text-align: center;
    white-space: nowrap;
    color: var(--Body-Text);
}
.set-guide-table thead th {
    font-size: 11px;
    color: var(--Note);
}
.set-guide-table .set-guide-label {
    text-align: left;
    color: var(--Heading);
    font-weight: 600;
    white-space: normal;
}
/* "in this set" / "match to date" – the distinction between a real per-set
   figure and a running one is the whole honesty of this table, so it sits on
   the row itself rather than in a footnote nobody reads. */
.set-guide-note {
    display: block;
    font-size: 10px;
    font-weight: 400;
    color: var(--Note);
}
.set-guide-table .set-guide-score {
    display: block;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    font-size: 12px;
    color: var(--Heading);
    font-weight: 700;
}
.set-guide-table .is-set-winner,
.set-guide-table .is-set-winner-1 .set-guide-score,
.set-guide-table .is-set-winner-2 .set-guide-score {
    color: var(--compare-strong);
    font-weight: 800;
}
.set-guide-value {
    font-variant-numeric: tabular-nums;
}
.set-guide-sep {
    color: var(--Note);
    padding: 0 3px;
}
.set-guide-missing {
    color: var(--Note);
}
.set-guide-key {
    margin: 6px 0 0;
    font-size: 11px;
    color: var(--Note);
}
/* Two shapes: a <tr> in the fixture page's component, a <p> under the table in
   the live popup. Both say the same thing – a set finished without a snapshot,
   so the counts either side of it can't be trusted. */
.set-guide-gap td {
    font-size: 11px;
    color: var(--siq-warn);
    background: rgba(209, 133, 43, .07);
}
p.set-guide-gap {
    margin: 6px 0 0;
    font-size: 11px;
    color: var(--siq-warn);
    background: rgba(209, 133, 43, .07);
    border-radius: 6px;
    padding: 6px 8px;
}


/* ---- First-Set Vulnerability (FSV-1.0) ----------------------------------
 * Above the responsive section on purpose – see the header below. */

.fsv-scenarios {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: 14px;
}
.fsv-card {
    border: 1px solid var(--Stroke);
    border-radius: 12px;
    padding: 14px;
}
/* Only the locked threshold gets a visual promotion. The display bands are UI
   interpretation and carry no betting meaning, so nothing but candidacy is
   allowed to change how a card reads. */
.fsv-card.is-candidate {
    border-color: var(--compare-strong);
}
.fsv-card.is-live {
    box-shadow: 0 0 0 2px var(--compare-strong) inset;
}
.fsv-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.fsv-if {
    font-size: 12px;
    font-weight: 700;
    color: var(--Note);
}
.fsv-live-chip {
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #fff;
    background: var(--compare-strong);
    border-radius: 999px;
    padding: 2px 8px;
}
.fsv-score-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 6px;
}
.fsv-score {
    font-size: 28px;
    font-weight: 800;
    color: var(--Heading);
    font-variant-numeric: tabular-nums;
}
.fsv-score small {
    font-size: 13px;
    font-weight: 600;
    color: var(--Note);
}
.fsv-score.is-null {
    color: var(--Note);
}
.fsv-band {
    font-size: 12px;
    font-weight: 700;
    color: var(--Note);
}
.fsv-meter {
    position: relative;
    height: 8px;
    margin-top: 10px;
    border-radius: 999px;
    background: var(--Stroke);
    overflow: hidden;
}
.fsv-meter-fill {
    height: 100%;
    background: var(--compare-strong);
}
.fsv-meter-threshold {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--Heading);
    opacity: .55;
}
.fsv-candidate-chip {
    margin-top: 10px;
    font-size: 11px;
    font-weight: 800;
    color: var(--compare-strong);
    text-transform: uppercase;
    letter-spacing: .03em;
}
.fsv-note,
.fsv-components-note,
.fsv-model-note {
    margin: 8px 0 0;
    font-size: 11px;
    color: var(--Note);
}
.fsv-components {
    margin-top: 10px;
}
.fsv-components summary {
    cursor: pointer;
    font-size: 12px;
    font-weight: 700;
    color: var(--Heading);
}
.fsv-component-table {
    table-layout: auto;
    width: 100%;
    border-collapse: collapse;
    margin-top: 8px;
}
.fsv-component-table td {
    padding: 4px 6px 4px 0;
    font-size: 12px;
    color: var(--Body-Text);
    border: 0;
}
.fsv-component-label {
    white-space: normal;
}
.fsv-component-bar {
    width: 40%;
    min-width: 70px;
}
.fsv-component-bar span {
    display: block;
    height: 6px;
    border-radius: 999px;
    background: var(--compare-strong);
}
.fsv-component-value {
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--Note);
    white-space: nowrap;
}
.fsv-signal {
    border-radius: 10px;
    padding: 10px 12px;
    font-size: 13px;
    color: var(--Body-Text);
    background: rgba(78, 168, 255, .09);
    border: 1px solid rgba(78, 168, 255, .35);
}
.fsv-signal-odds {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: var(--Note);
}


/* ---- Pre-Match Vulnerability Lay (PMVL-1.0) ---------------------------- */

.pmvl-box {
    border: 1px solid var(--compare-strong);
}
.pmvl-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
}
.pmvl-badge {
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #fff;
    background: var(--compare-strong);
    border-radius: 999px;
    padding: 4px 10px;
}
.pmvl-lead {
    font-size: 15px;
    color: var(--Body-Text);
}
.pmvl-reasons {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
    gap: 12px;
}
.pmvl-reason {
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    padding: 12px;
}
.pmvl-reason-title {
    font-size: 12px;
    font-weight: 800;
    color: var(--Heading);
    margin-bottom: 4px;
}
.pmvl-reason-body {
    font-size: 13px;
    color: var(--Body-Text);
    line-height: 1.5;
}
.pmvl-note {
    margin: 12px 0 0;
    font-size: 11px;
    color: var(--Note);
}


/* ---- Trading-model badges on fixture cards ------------------------------
 * Same shape as .fixture-vs-favorite-badge so they read as one family, with
 * a colour per signal. Colours are fixed rather than theme tokens because
 * each is a tint on its own translucent ground, which reads on both themes. */
.fixture-model-badge {
    display: inline-block;
    margin-top: 4px;
    margin-left: 4px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 3px 10px;
    border-radius: 10px;
    white-space: nowrap;
}
/* The strongest pre-match signal: a player flagged to lay outright. */
.fixture-model-badge.is-pmvl,
.fixture-model-badge.is-exposed {
    color: var(--siq-bad);
    background: rgba(229, 72, 77, 0.15);
}
/* Conditional: only becomes a signal if this player takes set 1. */
.fixture-model-badge.is-fsv {
    color: var(--siq-accent);
    background: rgba(78, 168, 255, 0.14);
}
/* Live: set 1 has been won and the signal is active. Solid, so it is the one
   that stands out while scrolling during play. */
.fixture-model-badge.is-fsv-live {
    color: #fff;
    background: var(--siq-accent);
}


/* ---- Recovery Candidate (RC-1.0) ---------------------------------------- */
.fixture-model-badge.is-rc {
    color: var(--siq-good);
    background: rgba(47, 163, 107, 0.15);
}
.fixture-model-badge.is-rc-live {
    color: #fff;
    background: var(--siq-good);
}
.rcand-box {
    border: 1px solid rgba(47, 163, 107, 0.55);
}
.rcand-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
}
.rcand-badge {
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    border-radius: 999px;
    padding: 4px 10px;
    color: var(--siq-good);
    background: rgba(47, 163, 107, 0.15);
}
.rcand-badge.is-live {
    color: #fff;
    background: var(--siq-good);
}
.rcand-badge.is-miss {
    color: var(--Note);
    background: var(--Stroke);
}
.rcand-lead {
    font-size: 15px;
    color: var(--Body-Text);
}


/* Past tab: played, result not in yet. Amber rather than the muted
   "Completed" so it reads as provisional, not final. */
.fixtures-b-countdown.fixtures-b-pending {
    color: var(--siq-warn);
    font-weight: 700;
}


/* ---- Match Filter -------------------------------------------------------- */
.mf-rule {
    display: grid;
    /* side · metric · window · comparison · value · second value · copy · remove */
    grid-template-columns: 120px minmax(0, 2fr) minmax(0, 1.2fr) minmax(0, 1.4fr) 92px 92px 36px 36px;
    gap: 8px;
    align-items: center;
    margin-bottom: 8px;
}

/* The second value box only appears for a range, so its column collapses. */
.mf-rule .mf-value2[hidden] { display: none; }

.mf-rule-actions { display: flex; flex-wrap: wrap; gap: 8px; }

.mf-logic {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--Note);
    white-space: nowrap;
}
.mf-logic .form-select { width: auto; }

.mf-scenarios {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 4px 0 14px;
}
.mf-scenarios-label { font-size: 12px; color: var(--Note); }
.mf-scenarios-note { flex-basis: 100%; font-size: 11px; color: var(--Note); }
.mf-scenario { cursor: pointer; }

.mf-minn { width: 70px; display: inline-block; }
.mf-minn[hidden] { display: none; }

@media (max-width: 900px) {
    .mf-rule { grid-template-columns: 1fr 1fr; }
}
.mf-remove {
    height: 36px;
    border: 1px solid var(--Stroke);
    border-radius: 8px;
    background: transparent;
    color: var(--Note);
    font-size: 18px;
    cursor: pointer;
}
.mf-controls, .mf-save {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
}
.mf-controls label {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--Body-Text);
    font-size: 13px;
}
.mf-save .form-control { max-width: 260px; }
.mf-check { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--Body-Text); }
.mf-saved { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.mf-saved-label { font-size: 12px; font-weight: 700; color: var(--Note); margin-right: 4px; }
.mf-delete { background: none; border: 0; padding: 0; font-size: 12px; color: var(--siq-bad); cursor: pointer; }
.mf-note { font-size: 13px; padding: 8px 12px; border-radius: 8px; }
.mf-note.is-ok { color: var(--siq-good); background: rgba(47, 163, 107, .1); }
.mf-note.is-error { color: var(--siq-bad); background: rgba(229, 72, 77, .1); }

.mf-table { table-layout: auto; width: 100%; border-collapse: collapse; font-size: 13px; }
.mf-table th, .mf-table td { padding: 8px 10px; color: var(--Body-Text); white-space: nowrap; border-width: 0 0 1px 0; }
.mf-table th { font-size: 11px; color: var(--Note); font-weight: 700; }
/* One weight for the whole heading: the figure's name used to drop to 400
   beside a bold side, which with a unit line under it as well made three
   different weights in one cell. The unit carries its own weight. */
.mf-table th span { font-weight: inherit; }
/* Tighter than a body cell, and sat on the bottom so a one-line heading
   lines up with the label line of a two-line one. Rows: 13px body text plus
   16px padding is 29px, and the unit line is 13px, so a heading must fit in
   42px - two lines at 1.25 plus 10px padding is 37px. */
.mf-table thead th { padding: 5px 10px; line-height: 1.25; vertical-align: bottom; }
.mf-table tbody tr { cursor: pointer; }
/* Folded into the one shared hover below; a tint on the td painted over it. */
.mf-table tbody tr:hover td { background: var(--hv-item); }
.mf-player { font-weight: 700; color: var(--Heading) !important; }
.mf-num { text-align: right; font-variant-numeric: tabular-nums; }
.mf-wl { display: inline-block; min-width: 22px; text-align: center; font-weight: 800; font-size: 11px; border-radius: 6px; padding: 2px 6px; color: #fff; }
.mf-wl.is-w { background: var(--siq-good); }
.mf-wl.is-l { background: var(--siq-bad); }

/* Each track was minmax(150px, 1fr) while .mf-tile-value is 24px/800 and the
   sub line under it runs to things like "on the 7,935 priced - 70.7% of them
   won". 150px minus 12px padding each side leaves 126px, so the value and the
   sub both had to wrap mid-figure. 210px is the width the widest value and a
   two-line sub actually need. (It used to match the .mf-cuts track exactly so
   the two sets of cards lined up; .mf-cuts is a fixed six-column grid now, so
   they no longer share an edge.) min() keeps it from overflowing a phone: below 210px the
   track simply becomes the full width instead of forcing a horizontal
   scrollbar. */
.mf-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: 12px; }
.mf-tile { border: 1px solid var(--Stroke); border-radius: 10px; padding: 12px; }
.mf-tile-value { font-size: 24px; font-weight: 800; color: var(--Heading); font-variant-numeric: tabular-nums;
    /* "+12.3 pts" and "140,532" are single figures - they read as two numbers
       if a line break lands inside them. */
    white-space: nowrap; }
.mf-tile-sub { overflow-wrap: anywhere; }
.mf-tile-value.is-up { color: var(--siq-good); }
.mf-tile-value.is-down { color: var(--siq-bad); }
.mf-tile-label { font-size: 11px; color: var(--Note); text-transform: uppercase; letter-spacing: .04em; }
.mf-caveat { font-size: 11px; color: var(--Note); margin: 8px 0 0; }

/* Monthly strike rate: one bar per month, height = win %, count beneath. */
.mf-months { display: flex; gap: 6px; align-items: flex-end; overflow-x: auto; padding-bottom: 4px; }
.mf-month { flex: 1 0 40px; text-align: center; }
.mf-month-bar { height: 90px; display: flex; align-items: flex-end; background: var(--Stroke); border-radius: 6px; overflow: hidden; }
.mf-month-bar span { display: block; width: 100%; background: var(--compare-strong); }
.mf-month-rate { font-size: 11px; font-weight: 700; color: var(--Heading); margin-top: 4px; }
.mf-month-label, .mf-month-n { font-size: 10px; color: var(--Note); }

/* Fixtures-list flag for a saved filter. */
.fixture-model-badge.is-mf {
    color: var(--siq-accent-alt);
    background: rgba(155, 107, 255, 0.15);
}


/* ---- Signals page -------------------------------------------------------- */
.sig-filter { display: flex; flex-wrap: wrap; gap: 6px; }
.sig-sub { font-size: 13px; color: var(--Note); margin-top: 4px; }
.sig-list { display: flex; flex-direction: column; gap: 8px; }

/*
 * One row = which signal, who, what to do, when. The action column is the
 * widest because it is the only part a reader has to act on; it keeps a
 * minimum so the instruction never collapses to one word per line.
 */
.sig-row {
    display: grid;
    grid-template-columns: minmax(190px, 1.1fr) minmax(150px, .8fr) minmax(260px, 1.3fr) auto;
    align-items: center; gap: 14px;
    padding: 12px 14px; border: 1px solid var(--Stroke); border-radius: 10px;
    color: var(--Body-Text); text-decoration: none;
}
.sig-row:hover { border-color: var(--compare-strong); }
.sig-row.is-live { border-color: var(--siq-good); box-shadow: 0 0 0 1px rgba(47, 163, 107, .35) inset; }
.sig-main { min-width: 0; }
.sig-chip-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sig-player { font-weight: 800; color: var(--Heading); font-size: 14px; margin-top: 6px; }
.sig-opp { font-size: 12px; color: var(--Note); margin-top: 2px; }
.sig-headline { font-weight: 700; color: var(--Heading); font-size: 14px; margin-top: 4px; }
.sig-meta { font-size: 12px; color: var(--Note); line-height: 1.5; min-width: 0; }
.sig-meta span { display: block; }
.sig-side { text-align: right; }
.sig-time { font-size: 12px; color: var(--Note); }
.sig-strength { font-size: 12px; font-weight: 700; color: var(--Heading); margin-top: 2px; }
.sig-score { font-size: 15px; font-weight: 800; color: var(--Heading); font-variant-numeric: tabular-nums; white-space: pre; }
.sig-day { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--Note); margin: 16px 0 8px; }

/* The instruction. Tone follows the trade: lay now, lay on trigger, back. */
.sig-action { padding: 9px 12px; border-radius: 9px; font-size: 12px; line-height: 1.45; border: 1px solid transparent; }
.sig-action b { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .02em; margin-bottom: 2px; }
.sig-action.is-red { background: rgba(229, 72, 77, .12); border-color: rgba(229, 72, 77, .3); }
.sig-action.is-amber { background: rgba(240, 174, 47, .13); border-color: rgba(240, 174, 47, .32); }
.sig-action.is-green { background: rgba(47, 163, 107, .13); border-color: rgba(47, 163, 107, .32); }
.sig-action.is-red b { color: var(--siq-bad); }
.sig-action.is-amber b { color: var(--siq-warn); }
.sig-action.is-green b { color: var(--siq-good); }
.sig-action.is-live { box-shadow: 0 0 0 1px rgba(47, 163, 107, .22) inset; }

.sig-live-flag { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 900; text-transform: uppercase; color: var(--siq-good-solid); }
.dark-theme .sig-live-flag { color: var(--siq-good); }
.sig-live-flag .sig-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--siq-good); animation: sig-pulse 1.4s ease-in-out infinite; }

/* One colour per model, matching the fixtures-list badges. */
.sig-chip { display: inline-block; font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; padding: 2px 8px; border-radius: 999px; }
.sig-chip.sig-lfsw { color: var(--siq-accent-solid); background: rgba(78, 168, 255, .14); }
.dark-theme .sig-chip.sig-lfsw { color: var(--siq-accent); }
.sig-chip.sig-pmvl, .sig-chip.sig-exposed { color: var(--siq-bad-solid); background: rgba(229, 72, 77, .14); }
.dark-theme .sig-chip.sig-pmvl, .dark-theme .sig-chip.sig-exposed { color: var(--siq-bad); }
.sig-chip.sig-rc { color: var(--siq-good-solid); background: rgba(47, 163, 107, .15); }
.dark-theme .sig-chip.sig-rc { color: var(--siq-good); }
/* The two overlays. Their own colours, so no tile reads as another model's. */
.sig-chip.sig-confluence { color: #6D3BC4; background: rgba(124, 77, 216, .15); }
.dark-theme .sig-chip.sig-confluence { color: #B79BF0; }
.sig-chip.sig-mismatch_v1 { color: #9A5A00; background: rgba(199, 128, 20, .16); }
.dark-theme .sig-chip.sig-mismatch_v1 { color: #E3A94A; }
.sig-live-dot { display: inline-block; width: 8px; height: 8px; margin-left: 6px; border-radius: 50%; background: var(--siq-bad); vertical-align: middle; animation: sig-pulse 1.4s ease-in-out infinite; }
@keyframes sig-pulse { 50% { opacity: .3; } }

.sig-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.sig-tile { border: 1px solid var(--Stroke); border-radius: 10px; padding: 12px; }
.sig-tile-lfsw { background: linear-gradient(135deg, rgba(78, 168, 255, .10), transparent 70%); }
.sig-tile-pmvl, .sig-tile-exposed { background: linear-gradient(135deg, rgba(229, 72, 77, .10), transparent 70%); }
.sig-tile-rc { background: linear-gradient(135deg, rgba(47, 163, 107, .10), transparent 70%); }
.sig-tile-confluence { background: linear-gradient(135deg, rgba(124, 77, 216, .10), transparent 70%); }
.sig-tile-mismatch_v1 { background: linear-gradient(135deg, rgba(199, 128, 20, .10), transparent 70%); }
.sig-tile-value { font-size: 26px; font-weight: 800; color: var(--Heading); margin-top: 6px; }
.sig-tile-label { font-size: 12px; color: var(--Note); }

/* Today & yesterday: two panels side by side, stacked on a phone. */
.sig-days { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.sig-dayset { border: 1px solid var(--Stroke); border-radius: 10px; padding: 14px; }
.sig-dayset-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;
                   padding-bottom: 10px; margin-bottom: 6px; border-bottom: 1px solid var(--Stroke); }
.sig-dayset-name { font-size: 16px; font-weight: 800; color: var(--Heading); }
.sig-dayset-date { font-size: 12px; color: var(--Note); }
.sig-dayset-total { text-align: right; }
.sig-dayset-total .sig-tile-value { margin-top: 0; line-height: 1.1; }
.sig-dayset-row { display: grid; grid-template-columns: 1fr auto 44px; align-items: center; gap: 10px; padding: 7px 0; font-size: 13px; }
.sig-dayset-row + .sig-dayset-row { border-top: 1px dashed var(--Stroke); }
.sig-dayset-row.is-empty { opacity: .55; }
.sig-dayset-count { color: var(--Heading); font-variant-numeric: tabular-nums; white-space: nowrap; }
.sig-dayset-count small { color: var(--Note); margin-left: 4px; }
.sig-pager { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 14px; }
.sig-pager-at { font-size: 12px; color: var(--Note); }
.sig-dayset-pct { font-weight: 800; color: var(--Heading); text-align: right; font-variant-numeric: tabular-nums; }

.sig-outcome-cell { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--Note); }
.sig-outcome { font-size: 10px; font-weight: 900; border-radius: 999px; padding: 3px 8px; }
.sig-outcome.is-hit { color: var(--siq-good); background: rgba(47, 163, 107, .15); }
.sig-outcome.is-miss { color: var(--siq-bad); background: rgba(229, 72, 77, .13); }
.sig-outcome.is-void { color: var(--Note); background: rgba(149, 152, 157, .16); }
.sig-caveat { font-size: 11px; color: var(--Note); margin: 8px 0 0; }
.sig-outcome { display: inline-block; font-size: 11px; font-weight: 800; padding: 2px 8px; border-radius: 6px; color: #fff; }
.sig-outcome.is-hit { background: var(--siq-good); }
.sig-outcome.is-miss { background: #95989d; }
.sig-outcome.is-void { background: transparent; color: var(--Note); border: 1px solid var(--Stroke); }
/* The player a signal is about: bold, with the model's colour as an underline
   so it survives both themes and reads even where colour alone would not. */
.sig-player { font-weight: 800; color: var(--Heading); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.sig-player-lfsw { text-decoration-color: var(--siq-accent); }
.sig-player-pmvl { text-decoration-color: var(--siq-bad); }
.sig-player-rc { text-decoration-color: var(--siq-good); }


/* ---- Fixture page: in-play panel ----------------------------------------- */
.fixture-live-box { border: 1px solid var(--siq-bad); }
.fixture-live-box.is-ended { border-color: var(--Stroke); }
.fixture-live-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; }
.fixture-live-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--siq-bad); margin-right: 4px; vertical-align: middle; animation: sig-pulse 1.4s ease-in-out infinite; }
.fixture-live-box.is-ended .fixture-live-dot { animation: none; background: var(--Note); }
.fixture-live-meta { font-size: 12px; color: var(--Note); }
.fixture-live-board { width: 100%; }
/* A completed set's winning game count, so the set history reads at a glance. */
.live-scoreboard .live-set.is-won { color: var(--siq-good); background: rgba(47, 163, 107, .14); font-weight: 800; }
.live-scoreboard .live-set.is-lost { color: var(--siq-bad); background: rgba(229, 72, 77, .12); font-weight: 700; }
/* Kept: older markup still emits this class for a set the player won. */
.live-scoreboard .fixture-live-setwon { color: var(--Heading); font-weight: 800; }
.fixture-live-subtitle { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--Note); margin-bottom: 6px; }
/* Stats share the scoreboard's columns: per-set figures under S1, S2…, the
   match total under Pts. Lighter than the score rows so the score reads first. */
.live-scoreboard .fixture-live-statlabel td { padding-top: 12px; padding-bottom: 2px; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--Heading); border-width: 0; }
.live-scoreboard .fixture-live-statrow td { padding-top: 3px; padding-bottom: 3px; font-size: 13px; color: var(--Body-Text); }
.live-scoreboard .fixture-live-statrow .live-player-name-text { font-weight: 400; color: var(--Body-Text); }
.live-scoreboard .fixture-live-statrow .live-points.fixture-live-total { font-size: 14px; font-weight: 800; color: var(--Heading); }
.fixture-live-note { font-size: 11px; color: var(--Note); margin: 6px 0 0; }
/*
 * Serve marker. Live Scores styles .live-serve-inline inside its own page
 * stylesheet, so on the fixture page the marker was an empty zero-size span
 * and the server was never shown. Scoped to this panel so Live Scores' own
 * rules stay untouched. The marker keeps its space when not serving, so the
 * two names always line up.
 */
.fixture-live-board .live-player-name-inner {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.fixture-live-board .live-serve-inline {
    display: inline-block;
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: transparent;
}
.fixture-live-board .live-serve-inline.is-on {
    background: #d6e04b;
    box-shadow: 0 0 0 3px rgba(214, 224, 75, 0.25);
}
.fixture-live-board .live-player-name-text {
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Scoreboard and match odds side by side; the odds column is only as wide
   as it needs to be, and the scoreboard takes the rest. */
.fixture-live-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(200px, 260px);
    gap: 20px;
    align-items: start;
}
.fixture-live-main { min-width: 0; }
.odds-chart-title { font-size: 12px; font-weight: 700; color: var(--Heading); }
.fixture-live-odds { display: flex; flex-direction: column; gap: 6px; }
.fixture-live-odds div { display: flex; justify-content: space-between; gap: 12px; padding: 6px 10px; border: 1px solid var(--Stroke); border-radius: 8px; font-size: 13px; color: var(--Body-Text); }
.fixture-live-odds strong { color: var(--Heading); font-variant-numeric: tabular-nums; }


/* ---- Fixture page: Your filters ----------------------------------------- */
.fx-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 12px; }
.fx-filter { border: 1px solid rgba(155, 107, 255, .45); border-radius: 10px; padding: 12px; }
.fx-filter-head { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.fx-filter-name { font-weight: 800; color: var(--siq-accent-alt); }
.fx-filter-player { font-size: 12px; color: var(--Body-Text); }
.sig-player-mf { text-decoration-color: var(--siq-accent-alt); }
.fx-filter-rules { table-layout: auto; width: 100%; border-collapse: collapse; margin-top: 8px; }
.fx-filter-rules td { padding: 4px 0; font-size: 12px; color: var(--Body-Text); border-width: 0 0 1px 0; }
.fx-filter-value { text-align: right; font-weight: 700; color: var(--Heading) !important; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fx-filter-tick { color: var(--siq-good); margin-left: 4px; }


/* ---- Fixture page header: photo on the OUTSIDE of each name -------------- */
.fixture-vs-player.is-left,
.fixture-vs-player.is-right {
    display: flex;
    align-items: center;
    gap: 16px;
    max-width: 360px;
    flex-basis: 280px;
}
/* Mirrored, so player two's photo sits on the far right. */
.fixture-vs-player.is-right { flex-direction: row-reverse; }
.fixture-vs-player.is-left .fixture-vs-info { text-align: left; }
.fixture-vs-player.is-right .fixture-vs-info { text-align: right; }
.fixture-vs-player.is-left .fixture-vs-bio { justify-content: flex-start; }
.fixture-vs-player.is-right .fixture-vs-bio { justify-content: flex-end; }
.fixture-vs-info { min-width: 0; }
/* Flag and the Favorite tag share one line above the name. */
.fixture-vs-flagrow { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.fixture-vs-flagrow .fixture-vs-flag { margin-bottom: 0; }
.fixture-vs-flagrow .fixture-vs-favorite-badge { margin-top: 0; }
.fixture-vs-player.is-right .fixture-vs-flagrow { justify-content: flex-end; }
.fixture-vs-info .fixture-vs-name { overflow-wrap: anywhere; }
.fixture-vs-photo {
    width: 84px;
    height: 84px;
    flex: 0 0 auto;
    border-radius: 50%;
    object-fit: cover;
    object-position: top;
    background: var(--Stroke);
    border: 2px solid var(--Stroke);
}


/* ---- Header: notifications + messages ----------------------------------- */
.hdr-badge[hidden] { display: none !important; }
.hdr-dropdown { width: min(380px, 92vw); max-height: 70vh; overflow-y: auto; }
.hdr-items { display: block; }
.hdr-empty { font-size: 12px; color: var(--Note); padding: 8px 4px; }
.hdr-noti { display: flex; gap: 10px; align-items: flex-start; padding: 8px 6px; border-radius: 8px; text-decoration: none; color: var(--Body-Text); }
.hdr-noti:hover { background: var(--hv-item); }
.hdr-noti.is-unread { background: rgba(78, 168, 255, .06); }
.hdr-noti.is-unread .hdr-noti-title::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: var(--siq-accent); vertical-align: middle; }
.hdr-noti-icon { flex: 0 0 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--Stroke); color: var(--Heading); font-size: 13px; }
.hdr-noti.is-warning .hdr-noti-icon { background: rgba(209, 133, 43, .18); color: var(--siq-warn); }
.hdr-noti.is-live .hdr-noti-icon { background: rgba(229, 72, 77, .15); color: var(--siq-bad); }
.hdr-noti.is-upcoming .hdr-noti-icon { background: rgba(78, 168, 255, .15); color: var(--siq-accent); }
.hdr-noti.is-filter .hdr-noti-icon { background: rgba(155, 107, 255, .15); color: var(--siq-accent-alt); }
.hdr-noti.is-good .hdr-noti-icon { background: rgba(47, 163, 107, .15); color: var(--siq-good); }
.hdr-noti-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hdr-noti-title { font-size: 13px; font-weight: 700; color: var(--Heading); }
.hdr-noti-body { font-size: 12px; color: var(--Body-Text); margin-top: 2px; }
.hdr-noti-time { flex-shrink: 0; font-size: 11px; color: var(--Note); }
/* A short ring on the bell when a new notification lands. */
@keyframes hdr-ring { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(-14deg); } 40% { transform: rotate(12deg); } 60% { transform: rotate(-8deg); } 80% { transform: rotate(5deg); } }
.hdr-ring .icon-bell { display: inline-block; animation: hdr-ring .9s ease-in-out 2; }

/* ---- Notifications page -------------------------------------------------- */
.notif-list { display: flex; flex-direction: column; gap: 8px; }
.notif-row { display: flex; gap: 12px; align-items: flex-start; padding: 12px; border: 1px solid var(--Stroke); border-radius: 10px; text-decoration: none; color: var(--Body-Text); }
.notif-row:hover { border-color: var(--compare-strong); }
.notif-row.is-unread { border-left: 3px solid var(--siq-accent); background: rgba(78, 168, 255, .04); }
.notif-icon { flex: 0 0 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--Stroke); color: var(--Heading); }
.notif-row.is-warning .notif-icon { background: rgba(209, 133, 43, .18); color: var(--siq-warn); }
.notif-row.is-live .notif-icon { background: rgba(229, 72, 77, .15); color: var(--siq-bad); }
.notif-row.is-upcoming .notif-icon { background: rgba(78, 168, 255, .15); color: var(--siq-accent); }
.notif-row.is-filter .notif-icon { background: rgba(155, 107, 255, .15); color: var(--siq-accent-alt); }
.notif-row.is-good .notif-icon { background: rgba(47, 163, 107, .15); color: var(--siq-good); }
.notif-main { display: flex; flex-direction: column; min-width: 0; }
.notif-title { font-weight: 700; color: var(--Heading); font-size: 14px; }
.notif-body { font-size: 13px; margin-top: 2px; }
.notif-meta { font-size: 11px; color: var(--Note); margin-top: 4px; }

/* ---- Messages page ------------------------------------------------------- */
.msg-layout { display: grid; grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); gap: 16px; }
.msg-list { display: flex; flex-direction: column; gap: 6px; }
.msg-conv { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--Stroke); border-radius: 10px; text-decoration: none; color: var(--Body-Text); }
.msg-conv:hover, .msg-conv.is-active { border-color: var(--compare-strong); }
.msg-conv.is-unread .msg-conv-with { font-weight: 800; }
.msg-conv-top { display: flex; justify-content: space-between; gap: 6px; }
.msg-conv-with { color: var(--Heading); font-size: 13px; font-weight: 600; }
.msg-conv-subject { font-size: 12px; color: var(--Heading); }
.msg-conv-snippet { font-size: 12px; color: var(--Note); }
.msg-badge { font-size: 10px; font-weight: 800; color: #fff; background: var(--siq-accent); border-radius: 999px; padding: 1px 7px; }
.msg-pane { border: 1px solid var(--Stroke); border-radius: 10px; padding: 14px; min-width: 0; }
.msg-thread-subject { font-weight: 800; color: var(--Heading); margin-bottom: 10px; }
.msg-thread { display: flex; flex-direction: column; gap: 10px; }
.msg-bubble { max-width: 80%; padding: 10px 12px; border-radius: 12px; background: var(--Stroke); color: var(--Body-Text); align-self: flex-start; }
.msg-bubble.is-mine { align-self: flex-end; background: rgba(78, 168, 255, .15); }
.msg-bubble-meta { font-size: 11px; color: var(--Note); margin-bottom: 4px; }
.msg-bubble-body { font-size: 14px; overflow-wrap: anywhere; }
.msg-form { display: flex; flex-direction: column; gap: 10px; }
.msg-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--Body-Text); }
.msg-form .tf-button { align-self: flex-start; }
.msg-list-heading { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--Note); margin: 4px 0 2px; }
.msg-broadcast { border-style: dashed; cursor: default; }
.msg-check { flex-direction: row !important; align-items: center; gap: 8px !important; }
.msg-check[hidden] { display: none !important; }
/* Contact form. The honeypot is moved off-screen rather than display:none,
   which some bots detect and skip. */
/* max-width alone shrinks the box but leaves it flush to the grid's start
   edge - the theme's grid items stretch to fill by default, and nothing
   here asked for the leftover space either side to be shared. */
.contact-box { max-width: 760px; margin-left: auto; margin-right: auto; }
.contact-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 10px; }
.contact-hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }

/* A long champions history scrolls inside its card rather than pushing the
   rest of the page down — Wimbledon carries 40 years of it. */
.tourn-hub-champions {
    max-height: 520px;
    overflow-y: auto;
}

/* ── Past tournaments: one box per tournament, listing its years ── */
/* The whole box is a link to the most recent edition, laid over the card as
   a transparent cover rather than wrapping it: the individual year chips are
   links too, and an anchor inside an anchor is not valid HTML. The chips sit
   above the cover, so clicking a year still takes you to that year and
   clicking anywhere else takes you to the latest one. */
.tourn-past-card { position: relative; cursor: pointer; }
.tourn-past-cover { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.tourn-past-cover:focus-visible { outline: 2px solid var(--siq-accent); outline-offset: 2px; }
.tourn-past-card .fixtures-b-body { position: relative; }
.tourn-past-years { position: relative; z-index: 2; }
.tourn-past-years {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
    /* A long-running event has decades of them: keep the box a sane height. */
    max-height: 96px;
    overflow-y: auto;
}
.tourn-past-count { margin-top: 8px; font-size: 11px; color: var(--Note); }

/* ── Tournament hub: seeds, titles and season chips ──
   These three carried no styles at all: a seeds list was a run of numbers and
   names in a plain div, the season chips were bare links, and Most Titles was
   a two-column table of small numbers. They are the cards people linger on,
   so they are drawn properly here. */

.tourn-hub-seeds {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
    gap: 8px;
    margin-top: 12px;
}
.tourn-hub-seed {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    min-width: 0;
}
/* The number is the point of a seeding list, so it reads as a badge rather
   than as part of the sentence. */
.tourn-hub-seed-no {
    flex: 0 0 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-weight: 800;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    background: rgba(149, 152, 157, .16);
    color: var(--Heading);
}
.tourn-hub-seed.is-high .tourn-hub-seed-no { background: rgba(47, 163, 107, .16); color: var(--siq-good); }
.tourn-hub-seed.is-top { border-color: var(--siq-good); }
.tourn-hub-seed.is-top .tourn-hub-seed-no { background: var(--siq-good); color: #fff; }
.tourn-hub-seed-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tourn-hub-seed-name { font-weight: 600; overflow-wrap: anywhere; }
.tourn-hub-seed-rank { font-size: 11px; color: var(--Note); }

/* Bands, so the list is read by where a player sits in the draw rather than
   as thirty-two identical pills. Each band carries a coloured rail on its
   heading; the rail is the only colour, so the names stay the loudest thing. */
.tourn-hub-seed-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    margin-top: 10px;
    font-size: 12px;
    color: var(--Note);
}
.tourn-hub-seed-summary strong { color: var(--Heading); font-weight: 700; }
.tourn-hub-seed-band { margin-top: 16px; }
.tourn-hub-seed-band-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
    padding-left: 10px;
    border-left: 3px solid var(--Stroke);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--Heading);
}
.tourn-hub-seed-band-head span {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--Note);
}
.tourn-hub-seed-band.is-q .tourn-hub-seed-band-head { border-left-color: var(--siq-good); }
.tourn-hub-seed-band.is-h .tourn-hub-seed-band-head { border-left-color: var(--siq-accent-alt); }
.tourn-hub-seed-band.is-m .tourn-hub-seed-band-head { border-left-color: var(--siq-accent); }
.tourn-hub-seed-band .tourn-hub-seeds { margin-top: 8px; }
/* Seeds 9 and beyond are the field: the same information, drawn quieter, so
   the eye goes to the names that shape the draw. */
.tourn-hub-seed-band.is-m .tourn-hub-seed,
.tourn-hub-seed-band.is-l .tourn-hub-seed { padding: 6px 10px; }
.tourn-hub-seed-band.is-m .tourn-hub-seed-no,
.tourn-hub-seed-band.is-l .tourn-hub-seed-no { flex-basis: 26px; height: 26px; font-size: 12px; }

/* Most titles: a bar against the leader, so one dominant name is obvious. */
.tourn-hub-titles { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.tourn-hub-title-row {
    display: grid;
    /* The name takes the room, and the bar is a fixed 200px sitting beside
       the count it belongs to. Sharing the width as fractions left a wide
       card with a lake of nothing between the name and the bar. */
    grid-template-columns: 22px minmax(0, 1fr) 200px auto;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border: 1px solid var(--Stroke);
    border-radius: 10px;
}
.tourn-hub-title-row.is-leader { border-color: var(--siq-accent-alt); }
/* A podium: first, second and third get their own colour on the position
   number, the way a leaderboard is read. */
.tourn-hub-title-pos {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 7px;
    font-size: 12px; font-weight: 700; color: var(--Note);
    background: rgba(149, 152, 157, .14);
    font-variant-numeric: tabular-nums;
}
.tourn-hub-title-row.is-first .tourn-hub-title-pos { background: #C9A227; color: #fff; }
.tourn-hub-title-row.is-second .tourn-hub-title-pos { background: #9AA3AE; color: #fff; }
.tourn-hub-title-row.is-third .tourn-hub-title-pos { background: #B0764A; color: #fff; }
.tourn-hub-title-who { min-width: 0; font-weight: 600; overflow-wrap: anywhere; display: flex; flex-direction: column; gap: 4px; }
.tourn-hub-title-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* The years behind the number. "Five titles" is a figure; the years are what
   people actually want to know, and they are already on the page. */
.tourn-hub-title-years { display: flex; flex-wrap: wrap; gap: 4px; }
.tourn-hub-title-year {
    font-size: 10px; font-weight: 600; line-height: 1;
    padding: 3px 6px; border-radius: 5px;
    color: var(--Note); background: rgba(149, 152, 157, .14);
    font-variant-numeric: tabular-nums;
}
.tourn-hub-title-row.is-first .tourn-hub-title-year { color: #8A6E11; background: rgba(201, 162, 39, .16); }
.tourn-hub-title-year.is-more { background: none; }
.tourn-hub-title-bar { height: 8px; border-radius: 999px; background: rgba(149, 152, 157, .16); overflow: hidden; }
.tourn-hub-title-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--siq-accent-alt); }
.tourn-hub-title-count { white-space: nowrap; font-variant-numeric: tabular-nums; }
.tourn-hub-title-count strong { font-size: 15px; color: var(--Heading); }
.tourn-hub-title-count span { font-size: 11px; color: var(--Note); margin-left: 3px; }

/* Year chips, on the hub and on the Past tournaments cards. */
.tourn-hub-season {
    display: inline-block;
    padding: 3px 9px;
    border: 1px solid var(--Stroke);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--Body-Text);
    text-decoration: none;
}
a.tourn-hub-season:hover { border-color: var(--siq-accent-alt); color: var(--siq-accent-alt); }
.tourn-hub-season.is-current { background: var(--siq-accent-alt); border-color: var(--siq-accent-alt); color: #fff; }
/* On record, but with no draw behind it: visibly not a link. */
.tourn-hub-season.is-missing { color: var(--Note); border-style: dashed; cursor: default; }

/* At phone width the bar has nowhere useful to go; the count still does. */
@media (max-width: 575px) {
    .tourn-hub-title-row { grid-template-columns: 22px minmax(0, 1fr) auto; }
    .tourn-hub-title-bar { display: none; }
}

/* ── Tournament hub: the identity card stays at the top ──
   Which tournament and which year you are looking at is the context for
   every card below it, and the page is long. The site header is fixed at
   80px, so this sits just under it and the rest scrolls behind.
   Left static on small screens, where it would eat too much of the view. */
.tourn-hub-sticky {
    position: sticky;
    top: 92px;
    z-index: 5;
    /* Opaque: cards passing underneath must not show through. */
    background: var(--White);
}

/* ── Dashboard cards: keep every card's content inside its card ──
   The dashboard grid is already minmax(0, 1fr) and most cards set
   min-width: 0 on their flexible column. These are the ones that did not:
   a flex child defaults to min-width: auto, which is as wide as its longest
   unbroken word, so one long tournament or player name pushed past the card
   edge at narrow widths instead of wrapping or truncating. */
.wg-box { min-width: 0; }
.tourn-b-row, .tourn-b-left { min-width: 0; }
.tourn-b-left { flex: 1; }
.tourn-b-title, .tourn-b-sub { overflow-wrap: anywhere; }
.live-scores-tile .live-row-top { min-width: 0; gap: 8px; }
/* Tournament name shortens; the time beside it always stays whole. */
.live-scores-tile .live-row-top > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-scores-tile .live-row-top > span:last-child { flex-shrink: 0; }
.live-scores-tile .live-row-names { overflow-wrap: anywhere; }
.rankings-card .rankings-a-name, .spotlight-card .stadium-player-name { overflow-wrap: anywhere; }

/* ── Set scores: green for a set won, red for one lost ──
   Written from one player's point of view (see ScoreHelper), so the same
   string reads correctly in a list that belongs to a player and in one that
   is winner-first. A set still in play stays neutral rather than being
   called early; 'ret.' and the like are notes, not sets. */
.score-set {
    display: inline-block; padding: 1px 6px; margin-right: 4px; border-radius: 5px;
    font-variant-numeric: tabular-nums; font-weight: 700; line-height: 1.5;
}
.score-set.is-won { color: var(--siq-good-solid); background: rgba(47, 163, 107, .14); }
.dark-theme .score-set.is-won { color: var(--siq-good); }
.score-set.is-lost { color: var(--siq-bad); background: rgba(229, 72, 77, .13); }
.score-set.is-open { color: var(--Note); background: rgba(149, 152, 157, .14); }
.score-set.is-note { color: var(--Note); background: none; font-weight: 600; font-style: italic; }
.score-set:last-child { margin-right: 0; }

/* ── Signal flags on a live score card, beside the player name ── */
.live-sig-flag {
    display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
    font-size: 9px; font-weight: 800; letter-spacing: .03em; white-space: nowrap; vertical-align: middle;
}
.live-sig-flag.is-red { color: var(--siq-bad); background: rgba(229, 72, 77, .14); }
.live-sig-flag.is-amber { color: var(--siq-warn); background: rgba(240, 174, 47, .16); }
.live-sig-flag.is-green { color: var(--siq-good); background: rgba(47, 163, 107, .15); }
.live-sig-flag.is-live { box-shadow: 0 0 0 1px currentColor inset; }

/* ── Notification rows: signal colour down the edge, instruction as a CTA ── */
.hdr-noti.sig-tone-red, .notif-row.sig-tone-red { border-left: 3px solid var(--siq-bad); }
.hdr-noti.sig-tone-amber, .notif-row.sig-tone-amber { border-left: 3px solid var(--siq-warn); }
.hdr-noti.sig-tone-green, .notif-row.sig-tone-green { border-left: 3px solid var(--siq-good); }
.hdr-noti-cta, .notif-cta {
    display: block; margin-top: 6px; padding: 5px 8px; border-radius: 6px;
    font-size: 10px; font-weight: 900; text-transform: uppercase; letter-spacing: .03em; text-align: center;
}
.sig-tone-red .hdr-noti-cta, .sig-tone-red .notif-cta { color: var(--siq-bad); background: rgba(229, 72, 77, .14); }
.sig-tone-amber .hdr-noti-cta, .sig-tone-amber .notif-cta { color: var(--siq-warn); background: rgba(240, 174, 47, .16); }
.sig-tone-green .hdr-noti-cta, .sig-tone-green .notif-cta { color: var(--siq-good); background: rgba(47, 163, 107, .15); }
/* ── Signal branding: how-to-trade blocks and the Signals page legend ── */
.sig-trade { margin-top: 14px; padding: 12px 14px; border-radius: 10px; border: 1px solid var(--preset-color, rgba(127,127,127,.25)); background: rgba(127, 127, 127, .06); }
.sig-trade-title { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; opacity: .75; }
.sig-trade-body { margin: 6px 0 0; font-size: 13px; line-height: 1.55; }
.sig-trade-simple { margin: 8px 0 0; font-size: 12px; font-weight: 800; letter-spacing: .03em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.sig-howto { display: flex; flex-direction: column; gap: 8px; }
.sig-howto-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sig-howto-type { font-size: 11px; font-weight: 700; opacity: .7; }
.sig-howto-instruction { font-size: 11px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; opacity: .85; }
/* Report a bug – header button and pop-up. Uses the header icons' theme
   (same background, text colour and dark-theme variables), not a colour of its own. */
.header-dashboard .wrap .hdr-bug-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; flex-shrink: 0; white-space: nowrap; height: 40px; padding: 0 18px; border: 0; border-radius: 999px; background: rgba(203, 213, 225, 0.3); color: var(--Heading); font-size: 14px; font-weight: 600; line-height: 1; cursor: pointer; transition: background .15s, color .15s; }
.header-dashboard .wrap .hdr-bug-btn i { font-size: 18px; color: var(--Heading); transition: color .15s; }
.header-dashboard .wrap .hdr-bug-btn:hover, .header-dashboard .wrap .hdr-bug-btn:focus-visible { background: var(--Main); color: #fff; }
.header-dashboard .wrap .hdr-bug-btn:hover i, .header-dashboard .wrap .hdr-bug-btn:focus-visible i { color: #fff; }
.dark-theme .header-dashboard .wrap .hdr-bug-btn { background: rgba(203, 213, 225, 0.1019607843); }
#hdr-bug-modal .modal-content { background: var(--White); color: var(--Body-Text); border: 1px solid var(--Stroke); border-radius: 14px; }
#hdr-bug-modal .modal-header, #hdr-bug-modal .modal-footer { border-color: var(--Stroke); }
#hdr-bug-modal .modal-title { color: var(--Heading); font-weight: 700; }
#hdr-bug-modal .form-label { color: var(--Heading); font-weight: 600; font-size: 13px; }
#hdr-bug-modal .form-control { background: var(--White); color: var(--Body-Text); border: 1px solid var(--Input); border-radius: 8px; font-size: 14px; }
#hdr-bug-modal .form-control::placeholder { color: var(--Text-Holder); }
#hdr-bug-modal .form-control:focus { border-color: var(--Main); box-shadow: none; }
#hdr-bug-modal .btn-primary { background: var(--Main); border-color: var(--Main); }
#hdr-bug-modal .btn-secondary { background: transparent; color: var(--Heading); border: 1px solid var(--Input); }
.dark-theme #hdr-bug-modal .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
.hdr-bug-page { font-size: 12px; color: var(--Note); word-break: break-all; margin-bottom: 12px; }
.hdr-bug-msg { margin-top: 12px; font-size: 13px; font-weight: 600; }
.hdr-bug-msg.is-ok { color: var(--siq-good); }
.hdr-bug-msg.is-error { color: var(--siq-bad); }
/* Full match statistics under the live panel (fixture page, live scores popup). */
/*
 * style.css sets a blanket `table { table-layout: fixed }`. Under fixed
 * layout a column's width comes from the first row and the table's own width,
 * never from the content, so on a phone every column was forced to about
 * 44px while holding 80-125px of unbreakable text - "Service games won" and
 * "25/45 (55%)" simply drew straight over their neighbours. The wrapper
 * already scrolls; the table just was not allowed to become wide enough to
 * need it.
 *
 * auto layout plus max-content does that: the table takes the width its
 * figures actually need and .table-responsive scrolls, which is the
 * behaviour a wide statistics table wants on a narrow screen. min-width
 * keeps it filling the card on a desktop.
 */
.fixture-full-stats {
    table-layout: auto;
    width: max-content;
    min-width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.fixture-full-stats th, .fixture-full-stats td { padding: 5px 8px; text-align: center; border-bottom: 1px solid var(--Stroke); white-space: nowrap; }
.fixture-full-stats thead th { font-size: 12px; font-weight: 700; color: var(--Heading); }
.fixture-full-stats .fixture-full-p { font-weight: 500; font-size: 11px; color: var(--Body-Text); max-width: 110px; overflow: hidden; text-overflow: ellipsis; }
.fixture-full-stats .fixture-full-label {
    text-align: left;
    color: var(--Heading);
    /* The label stays put while the figures scroll under it - otherwise you
       scroll right to reach the Match column and can no longer see which row
       you are reading. */
    position: sticky;
    left: 0;
    background: var(--White);
    z-index: 1;
    /* Wrapping beats a 230px column on a 390px screen: "1st serve return
       points won" on two lines leaves room for the numbers people came for. */
    white-space: normal;
    max-width: 130px;
    line-height: 1.25;
}
/* The label column stays put while the figures scroll under it - otherwise
   you scroll right to reach the Match column and can no longer see which row
   you are reading. */
.fixture-full-stats .fixture-full-group td { position: sticky; left: 0; background: var(--White); }
.fixture-full-stats .is-match { background: rgba(78, 168, 255, .07); font-weight: 600; }
.fixture-full-stats .fixture-full-group td { text-align: left; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--Note); padding-top: 10px; }
.h2h-surface-filter { display: flex; flex-wrap: wrap; gap: 6px; }
/* ---- Player page: career overview + surface profile (player-surface-profile.php) ---- */
.surface-dot-carpet { background: var(--Note); }
.psp-tile-sub { font-size: 11px; color: var(--Note); margin-top: 2px; }
.psp-levels { display: flex; flex-wrap: wrap; gap: 8px; }
.psp-level { display: flex; flex-direction: column; padding: 8px 12px; border: 1px solid var(--Stroke); border-radius: 10px; min-width: 140px; }
.psp-level-name { font-size: 11px; color: var(--Note); text-transform: uppercase; letter-spacing: .03em; }
.psp-level-val { font-size: 13px; color: var(--Heading); }

.psp-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px; }
.psp-card { position: relative; border: 1px solid var(--Stroke); border-radius: 14px; padding: 14px 16px 12px; overflow: hidden; background: var(--White); }
.psp-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--Note); }
.psp-card-hard::before { background: var(--siq-accent); }
.psp-card-ihard::before { background: var(--siq-accent-alt); }
.psp-card-clay::before { background: var(--siq-warn); }
.psp-card-grass::before { background: var(--siq-good); }
.psp-card.is-best { box-shadow: 0 0 0 2px var(--siq-good) inset; }
.psp-card-head { display: flex; justify-content: space-between; align-items: center; gap: 6px; }
.psp-card-name { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; color: var(--Heading); }
.psp-best { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--siq-good); background: rgba(47, 163, 107, .14); padding: 2px 8px; border-radius: 999px; }
.psp-card-main { display: flex; align-items: baseline; gap: 10px; margin-top: 8px; }
.psp-card-pct { font-size: 34px; font-weight: 800; line-height: 1; color: var(--Heading); }
.psp-card-wl { font-size: 14px; color: var(--Note); }
.psp-meter { position: relative; height: 10px; border-radius: 999px; background: var(--Input); margin-top: 10px; }
.psp-meter-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 999px; background: var(--Note); }
.psp-card-hard .psp-meter-fill { background: var(--siq-accent); }
.psp-card-ihard .psp-meter-fill { background: var(--siq-accent-alt); }
.psp-card-clay .psp-meter-fill { background: var(--siq-warn); }
.psp-card-grass .psp-meter-fill { background: var(--siq-good); }
.psp-meter-mark { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--Heading); border-radius: 2px; }
.psp-card-diff { font-size: 12px; margin-top: 6px; min-height: 16px; color: var(--Note); }
.psp-card-diff.is-up { color: var(--siq-good); }
.psp-card-diff.is-down { color: var(--siq-bad); }
.psp-card-facts { margin: 10px 0 0; display: grid; gap: 4px; }
.psp-card-facts > div { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; }
.psp-card-facts dt { font-weight: 400; color: var(--Note); }
.psp-card-facts dd { margin: 0; font-weight: 600; color: var(--Heading); text-align: right; }
.psp-card-facts dd span { font-weight: 400; color: var(--Note); }
.psp-form { display: flex; gap: 3px; margin-top: 10px; }
.psp-form .streak-block { width: 14px; height: 8px; border-radius: 2px; background: var(--Stroke); }
.psp-form .streak-block.streak-win { background: var(--siq-good); }
.psp-form .streak-block.streak-loss { background: var(--siq-bad); }
[data-psp] .stadium-section-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--Note); margin-bottom: 6px; }

.psp-toggle { display: flex; gap: 6px; }
.psp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.psp-table th, .psp-table td { padding: 7px 10px; text-align: center; border-bottom: 1px solid var(--Stroke); white-space: nowrap; }
.psp-table thead th { font-weight: 700; color: var(--Heading); }
.psp-table thead th .surface-legend-dot { margin-right: 5px; vertical-align: middle; }
.psp-table .psp-label { text-align: left; color: var(--Heading); white-space: normal; min-width: 170px; }
.psp-table .is-all { background: rgba(78, 168, 255, .06); font-weight: 600; }
.psp-table td.is-best { color: var(--siq-good); font-weight: 700; }
.psp-table .psp-group td { text-align: left; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--Note); padding-top: 14px; background: transparent; }
.psp-sub { font-size: 11px; color: var(--Note); font-weight: 400; }
.psp-years td { color: var(--Heading); }

/* ── Year-by-surface grid: the percentage sits UNDER the record, not beside it ─
   Each cell carries two things - "12-5" and the win percentage. Written inline
   they read as one run of characters ("12-5 70%"), which is hard to scan down a
   column and makes every cell wide enough that four surfaces plus All push the
   grid into a sideways scroll on a phone. Stacking the percentage puts the
   record on the line the eye follows and roughly halves the cell width.

   Done here rather than in the markup because the two cells that need it - the
   All column and the per-surface columns - build their contents separately, so
   one rule keeps them agreeing where two edits could drift apart. */
.psp-years .psp-sub { display: block; margin-top: 1px; line-height: 1.25; }
/* A cell with no matches carries a dash and nothing else, and must NOT become a
   stacked block - there is no record above it to sit under. It gets its own
   class rather than reusing .psp-sub, because the obvious selector for it,
   td > .psp-sub:only-child, silently matches every cell in the table: CSS
   :only-child counts ELEMENT children and ignores text nodes, so the span in
   "42-10 <span>81%</span>" is an only child too. That cancelled the rule above
   everywhere and is only visible by measuring the rendered page. */
.psp-none { font-size: 11px; color: var(--Note); font-weight: 400; }
/* Player page – Vs the Tour */
.vt-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12px; color: var(--Note); align-items: center; }
.vt-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.vt-legend-bar { display: inline-block; width: 28px; height: 8px; border-radius: 4px; background: var(--siq-accent); }
.vt-legend-mark { display: inline-block; width: 2px; height: 14px; background: var(--Heading); border-radius: 2px; }
.tour-compare-bar.vt-bar { overflow: visible; height: 10px; }
.vt-mid { position: absolute; left: 50%; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--Heading); opacity: .55; border-radius: 2px; }
.vt-badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; white-space: nowrap; margin-left: auto; }
.vt-badge.is-good { color: var(--siq-good); background: rgba(47, 163, 107, .14); }
.vt-badge.is-mid { color: var(--siq-accent); background: rgba(78, 168, 255, .14); }
.vt-badge.is-low { color: var(--siq-bad); background: rgba(229, 72, 77, .14); }
.vt-legend .vt-badge { margin-left: 0; }
.yoy-full .yoy-career td { border-top: 2px solid var(--Stroke); font-weight: 700; background: rgba(78, 168, 255, .06); }
.yoy-full .yoy-delta { display: inline-block; margin: 0 0 0 4px; font-size: 11px; }

/* ── Year on Year, on a phone: the column titles wrap instead of overlapping ──
   Every cell in .psp-table is white-space: nowrap, which is right for the
   figures - "12-5" and "68.4%" should never break. But the Year on Year head
   carries real phrases ("Won after winning set 1", "Break chances / match",
   "Straight-set wins"), and a nowrap heading that will not fit its column
   spills out of its own cell and draws over the heading next to it. Nothing
   clips it, so the two titles sit on top of each other and neither is
   readable - which is the fault as reported from a phone.

   So on a narrow screen the HEAD alone is allowed to wrap, to roughly two
   lines: min-width sets how wide a column gets before the text breaks, and
   without it the longest phrases would wrap to one word per line and make the
   head absurdly tall. The body keeps nowrap, so no figure breaks, and the
   table still scrolls inside .table-responsive rather than squeezing.

   Head cells align to the bottom so that a heading which takes one line still
   sits on the same baseline as one that takes two. */
@media (max-width: 767.98px) {
    .yoy-full thead th {
        white-space: normal;
        min-width: 116px;
        vertical-align: bottom;
        line-height: 1.25;
    }
    /* Year and Matches are short and must not be given the same floor, or the
       two columns the eye anchors on become the widest things in the table. */
    .yoy-full thead th:first-child,
    .yoy-full thead th:nth-child(2) { min-width: 0; }
}
/* Player main card – Profile-IQ (player-profile-iq.php): the Sports-IQ
   Ratings dial (siqp-dial) plus a compact breakdown. */
.piq-card { flex-shrink: 0; margin-left: auto; display: flex; align-items: center; gap: 18px; cursor: help; }
.piq-dial { margin: 0; width: 210px; }
.piq-parts { display: grid; gap: 7px; width: 200px; }
.piq-part { display: grid; grid-template-columns: 66px 1fr 52px; align-items: center; gap: 8px; font-size: 12px; }
.piq-part-label { color: var(--Heading); font-weight: 600; }
.piq-bar { height: 6px; border-radius: 999px; background: var(--Input); overflow: hidden; }
.piq-bar-fill { display: block; height: 100%; border-radius: 999px; }
.piq-part-value { text-align: right; color: var(--Note); font-variant-numeric: tabular-nums; }
/* Player page – Career Overview (player-surface-profile.php) */
.co-hero { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
.co-hero-item { position: relative; overflow: hidden; padding: 16px 18px 14px; border: 1px solid var(--Stroke); border-radius: 14px;
    background: linear-gradient(135deg, var(--co-tint), transparent 65%); --co-tint: rgba(78, 168, 255, .10); --co-accent: var(--siq-accent); }
.co-hero-item::before { content: ""; position: absolute; left: 0; top: 0; right: 0; height: 3px; background: var(--co-accent); }
.co-accent-blue   { --co-tint: rgba(78, 168, 255, .12); --co-accent: var(--siq-accent); }
.co-accent-gold   { --co-tint: rgba(240, 174, 47, .14); --co-accent: #F0AE2F; }
.co-accent-purple { --co-tint: rgba(155, 107, 255, .12); --co-accent: var(--siq-accent-alt); }
.co-accent-green  { --co-tint: rgba(47, 163, 107, .12); --co-accent: var(--siq-good); }
.co-hero-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--Note); }
.co-hero-value { font-size: 32px; font-weight: 800; line-height: 1.1; margin-top: 6px; color: var(--Heading); font-variant-numeric: tabular-nums; }
.co-hero-value.co-money { font-size: 24px; margin-top: 12px; }
.co-hero-bar { height: 5px; border-radius: 999px; background: var(--Input); margin-top: 10px; overflow: hidden; }
.co-hero-bar span { display: block; height: 100%; border-radius: 999px; background: var(--co-accent); }
.co-hero-sub { font-size: 12px; color: var(--Note); margin-top: 6px; }

.co-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.co-panel { padding: 14px 18px; border: 1px solid var(--Stroke); border-radius: 14px; display: grid; gap: 12px; align-content: start; }
.co-panel-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--Note); }
.co-rate-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.co-rate-label { font-size: 13px; font-weight: 600; color: var(--Heading); }
.co-rate-value { font-size: 15px; font-weight: 800; color: var(--Heading); font-variant-numeric: tabular-nums; }
.co-rate-bar { height: 6px; border-radius: 999px; background: var(--Input); margin-top: 5px; overflow: hidden; }
.co-rate-bar span { display: block; height: 100%; border-radius: 999px; background: var(--Note); }
.co-rate-bar span.is-good { background: var(--siq-good); }
.co-rate-bar span.is-mid  { background: var(--siq-accent); }
.co-rate-bar span.is-low  { background: var(--siq-warn); }
.co-rate-detail { font-size: 11px; color: var(--Note); margin-top: 3px; }

.co-trophies { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.co-trophy { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border: 1px solid var(--Stroke); border-radius: 12px; min-width: 170px; }
.co-trophy.is-major { border-color: rgba(240, 174, 47, .55); background: linear-gradient(135deg, rgba(240, 174, 47, .14), transparent 70%); }
.co-trophy-icon { font-size: 18px; filter: grayscale(1); opacity: .6; }
.co-trophy.is-major .co-trophy-icon { filter: none; opacity: 1; }
.co-trophy-count { font-size: 24px; font-weight: 800; color: var(--Heading); line-height: 1; min-width: 22px; text-align: center; }
.co-trophy-text { display: flex; flex-direction: column; }
.co-trophy-name { font-size: 13px; font-weight: 600; color: var(--Heading); }
.co-trophy-finals { font-size: 11px; color: var(--Note); }
/* Carpet had no bar colour anywhere (only hard / indoor / clay / grass did). */
.tourn-b-bar.surface-carpet, .fixtures-a-item.surface-carpet .fixtures-a-bar, .fixtures-b-card.surface-carpet .fixtures-b-bar { background: #8d6e63; }
/* Dashboard top row: Live Scores, Next Fixtures, Upcoming Tournaments.
   One shared layout so the three cards are the same size and their rows line
   up across the row: same title height, same gap to the content, identical
   ATP / WTA headings, 8px between rows, every row --dash-row-h tall, and the
   paging / "View all" controls pinned to the very bottom. */
.tf-section-3 { align-items: stretch; }
.live-scores-tile, .next-fixtures, .upcoming-tournaments { --dash-row-h: 112px; display: flex !important; flex-direction: column; height: 100%; }
.live-scores-tile > .d-flex:first-child { min-height: 34px; margin: 0 !important; }
.live-scores-tile .stadium-style-title, .next-fixtures > .stadium-style-title, .upcoming-tournaments > .stadium-style-title { margin: 0 !important; height: 34px; line-height: 34px; }
/* ATP / WTA badge on every row in all three cards (live, fixtures). */
.tf-section-3 .tour-badge { display: inline-block; font-size: 10px; font-weight: 700; padding: 1px 6px; border-radius: 4px; color: #fff; margin-right: 6px; vertical-align: 1px; }
.tf-section-3 .tour-badge.tour-atp { background: var(--tour-atp); }
/* White text sits on this, and --tour-wta measures 5.38 under it before the
   card tints it further. The same hue, dark enough to carry white outright. */
.tf-section-3 .tour-badge.tour-wta { background: #6B21A8; }
.live-scores-tile .dash-live-list, .wg-box.next-fixtures > .fixture-slider-wrap, .upcoming-tournaments .tournament-slider-outer { margin-top: 12px !important; flex: 0 0 auto !important; }
.dash-row-title, .next-fixtures .fixture-section-title, .upcoming-tournaments .fixture-section-title { margin: 0 0 6px !important; line-height: 16px; min-height: 16px; }
.next-fixtures .fixture-section-title[style*="margin-top"], .upcoming-tournaments .fixture-section-title[style*="margin-top"],
.dash-live-list-inner + .dash-row-title, .dash-live-empty + .dash-row-title { margin-top: 12px !important; }
.dash-live-list-inner, .next-fixtures .fixtures-a-list, .upcoming-tournaments .tourn-list { display: flex; flex-direction: column; gap: 8px !important; }
/* A floor, not a ceiling.
   This was a fixed height with the overflow hidden, so the three cards lined
   up row for row - until the content outgrew it, and then a live card was
   cutting its second player clean in half and a fixture card was losing its
   time. Alignment is a nicety; a row sliced through the middle is a fault.
   The height is now the height every ordinary row settles at, so they still
   line up, and the rare long one grows instead of being cut. */
.live-scores-tile .dash-live-card, .next-fixtures .fixtures-a-item, .upcoming-tournaments .tourn-b-item {
    min-height: var(--dash-row-h);
    box-sizing: border-box;
    overflow: visible;
}
.next-fixtures .fixtures-a-body { padding: 10px 14px; display: flex; flex-direction: column; justify-content: center; }
.upcoming-tournaments .tourn-b-item { padding: 10px 14px; display: flex; flex-direction: column; justify-content: space-between; }
.live-scores-tile .dash-live-card { display: flex; flex-direction: column; justify-content: center; }
.dash-live-empty { height: calc(var(--dash-row-h) * 2 + 8px); display: flex; align-items: center; justify-content: center; border: 1px dashed var(--Stroke); border-radius: 12px; font-size: 12px; color: var(--Note); }
.dash-live-rank { color: var(--Note); font-weight: 600; margin-left: 4px; }
.live-scores-tile > a:last-child, .next-fixtures .fixture-pagination, .upcoming-tournaments .tournament-pagination { margin-top: auto !important; padding-top: 12px; }
/* ========================================================================
 * RESPONSIVE OVERRIDES – keep these last.
 *
 * CSS breaks a specificity tie by source order, so a media-query rule placed
 * above the base rule it means to override is simply undone by it. That had
 * already happened once here (.siqm-oa) and repeatedly in the Live Scores
 * stylesheet, where five mobile rules were being silently reverted – including
 * the one that showed the serve marker on phones.
 *
 * New rules go ABOVE this line; new breakpoints go below it.
 * ======================================================================== */

@media (max-width: 700px) {
    .fixtures-live-modal-body {
        width: 95%;
        max-width: 95%;
        max-height: 88vh;
        padding: 16px;
    }
}
@media (max-width: 700px) {
    .siqm-class { font-size: 19px; }
    .siqm-oa { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
    .players-row-iq { display: none; }
}
@media (max-width: 700px) {
    .players-directory-row {
        flex-wrap: wrap;
        gap: 4px 10px;
        padding: 10px 12px;
    }
    .players-row-rank {
        width: 26px;
        font-size: 14px;
    }
    .players-row-name {
        font-size: 14px;
        /* Wraps to a second line rather than truncating – a player's name is
           the one thing on this row that must stay readable. */
        white-space: normal;
        overflow-wrap: anywhere;
    }
    .players-row-metas {
        flex-basis: 100%;
        padding-left: 36px;
        gap: 8px;
    }
    .players-row-meta {
        font-size: 11px;
        /* Safety net: even the compact form must never widen the row. */
        white-space: normal;
        overflow-wrap: anywhere;
    }
    /*
     * "Right-Handed, Two-Handed Backhand" is 33 characters – about 180px at
     * 11px, which with the age chip and the 36px indent overran a 320px screen
     * even on its own line. The compact form ("RH * 2H") keeps the same
     * information in a fifth of the width; the full text stays in the title.
     */
    .players-hand-full {
        display: none;
    }
    .players-hand-short {
        display: inline;
    }
}
@media (max-width: 767px) {
    .under-dev-tag { display: none; }
}
@media (max-width: 820px) {
    .surface-radar-layout { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 520px) {
    .layout-wrap .section-content-right .main-content .main-content-inner {
        padding-left: 14px;
        padding-right: 14px;
    }
}

/* Match Filter: the six-column rule row stacks on a phone. */
@media (max-width: 767px) {
    .mf-rule {
        grid-template-columns: 1fr 1fr;
    }
    .mf-rule select:nth-child(2) {
        grid-column: 1 / -1;
    }
}

/* Signals: the right-hand column drops underneath on a phone. */
@media (max-width: 600px) {
    .sig-row { flex-direction: column; align-items: flex-start; }
    .sig-side { text-align: left; }
}

/* Fixture live panel: no room for the odds beside the scoreboard on a phone,
   so they drop underneath it. */
@media (max-width: 767px) {
    .fixture-live-top {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Fixture header: smaller photos on a phone; the two players still sit
   either side of VS, photo outermost. */
@media (max-width: 600px) {
    .fixture-vs-photo { width: 56px; height: 56px; }
    .fixture-vs-player.is-left, .fixture-vs-player.is-right { gap: 10px; flex-basis: 100%; }
    .fixture-vs-info .fixture-vs-name { font-size: 18px; }
}

/* Messages: conversation list above the open thread on a phone. */
@media (max-width: 767px) {
    .msg-layout { grid-template-columns: minmax(0, 1fr); }
    .msg-bubble { max-width: 92%; }
}

/* Header on tablets and phones: bring back the profile, messages and
   notification icons. The template hides .user, .message and .noti (with the
   theme toggle, maximise and apps) at 991px and below, so none of the three
   were reachable on a phone. Same selector as the template's rule, placed
   later, so it wins without !important. The other items stay hidden to keep
   the bar from crowding. */
@media (max-width: 991px) {
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid > .user,
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid > .message,
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid > .noti {
        display: block;
    }
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid {
        gap: 10px;
    }
    /* Picture only: the name and role beside it would crowd a phone's bar.
       (The template hides this only when it is a div; here it is a span.) */
    .header-dashboard .popup-wrap.user .header-user > .flex-column,
    .header-dashboard .popup-wrap.user .header-user > div:last-child {
        display: none;
    }
    /* Keep a dropdown inside the screen: anchored to the right edge of the
       viewport rather than to its (now small) icon. */
    .header-dashboard .popup-wrap .dropdown-menu.hdr-dropdown,
    .header-dashboard .popup-wrap.user .dropdown-menu {
        position: fixed !important;
        top: 64px !important;
        right: 8px !important;
        left: auto !important;
        transform: none !important;
        width: min(380px, calc(100vw - 16px));
        max-height: calc(100vh - 80px);
        overflow-y: auto;
    }
}
@media (max-width: 575px) {
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid .header-item {
        width: 36px;
        height: 36px;
    }
}

/* Signals rows: four columns is a desktop luxury. Below 991px the action box
   drops to its own full-width line (it is the part that must stay readable),
   and on a phone every column stacks. Placed here, after the base rules, per
   the source-order note at the top of this section. */
@media (max-width: 991px) {
    .sig-row {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
    }
    .sig-action {
        grid-column: 1 / -1;
    }
}
@media (max-width: 600px) {
    .sig-row {
        grid-template-columns: 1fr;
        gap: 10px;
    }
    .sig-side {
        text-align: left;
    }
    .sig-howto-row {
        align-items: flex-start;
    }
}

/* A live card is narrow on a phone: the signal flag drops to its own line
   under the player name rather than squeezing the score columns. */
@media (max-width: 600px) {
    .live-scoreboard .live-sig-flag {
        display: block;
        margin: 3px 0 0;
        width: fit-content;
    }
}

/* The tournament identity card is only pinned where there is room for it:
   on a phone it would cover most of the screen. */
@media (max-width: 991px) {
    .tourn-hub-sticky {
        position: static;
    }
}
@media (max-width: 767px) {
    .hdr-bug-label { display: none; }
    .header-dashboard .wrap .hdr-bug-btn { width: 36px; height: 36px; padding: 0; }
}
@media (max-width: 991px) {
    .piq-card { margin-left: 0; width: 100%; justify-content: center; flex-wrap: wrap; }
}
@media (max-width: 991px) {
    .co-hero { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .co-panels { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
    .co-hero { gap: 10px; }
    .co-hero-item { padding: 12px 12px 10px; }
    .co-hero-value { font-size: 24px; }
    .co-hero-value.co-money { font-size: 18px; }
    .co-trophy { min-width: 0; flex: 1 1 45%; }
}


/* =====================================================================
 * Account page (pages/account.php) + notification settings panel
 * (components/notification-settings.php, also on TradeSense / Match Filter)
 * ===================================================================== */
.acc-page { display: flex; flex-direction: column; gap: 24px; }
.acc-page .wg-box { margin: 0; }
.acc-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; align-items: start; }
.acc-col { display: flex; flex-direction: column; gap: 24px; min-width: 0; }
@media (max-width: 1100px) { .acc-grid { grid-template-columns: 1fr; } }

/* Profile header */
.acc-hero { display: flex; flex-direction: row !important; flex-wrap: wrap; align-items: center; gap: 24px; padding: 28px !important;
    background: linear-gradient(120deg, rgba(78, 168, 255, .10), rgba(155, 107, 255, .08) 60%, transparent), var(--White) !important; }
.acc-avatar-wrap { position: relative; flex: 0 0 auto; margin: 0; }
.acc-avatar { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; display: block;
    border: 3px solid var(--White); box-shadow: 0 0 0 2px var(--siq-accent), 0 6px 18px rgba(0, 0, 0, .12); }
.acc-avatar-edit { position: absolute; right: -2px; bottom: -2px; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; margin: 0;
    display: flex; align-items: center; justify-content: center; background: var(--siq-accent); color: #fff; font-size: 15px;
    border: 3px solid var(--White); transition: transform .15s ease; }
.acc-avatar-edit:hover { transform: scale(1.08); }
.acc-hero-main { flex: 1 1 260px; min-width: 0; }
.acc-name { margin: 0 0 4px; color: var(--Heading); font-weight: 700; }
.acc-meta { color: var(--Body-Text); font-size: 14px; overflow-wrap: anywhere; }
.acc-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.acc-chip { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
    background: var(--Input); color: var(--Heading); white-space: nowrap; }
.acc-chip.is-accent { background: rgba(78, 168, 255, .15); color: var(--siq-accent); }
.acc-chip.is-good { background: rgba(47, 163, 107, .15); color: var(--siq-good); }
.acc-chip.is-warn { background: rgba(209, 133, 43, .16); color: var(--siq-warn); }
.acc-chip.is-bad { background: rgba(229, 72, 77, .15); color: var(--siq-bad); }
.acc-hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; flex: 0 1 auto; }
.acc-hero-actions .acc-help { flex-basis: 100%; margin: 0; }
.acc-hero-msg { flex-basis: 100%; margin: 0 !important; }
.acc-btn-ghost { height: 40px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--Stroke); background: var(--White);
    color: var(--Heading); font-weight: 600; font-size: 14px; cursor: pointer; transition: border-color .15s, color .15s; }
.acc-btn-ghost:hover { border-color: var(--siq-accent); color: var(--siq-accent); }
.acc-btn-link { background: none; border: 0; padding: 0; color: var(--siq-bad); font-size: 14px; font-weight: 600; cursor: pointer; }
.acc-btn-link:hover { text-decoration: underline; }

/* Cards and forms */
.acc-card { gap: 0 !important; }
.acc-card .stadium-style-title { margin-bottom: 18px; }
.acc-form { display: flex; flex-direction: column; gap: 18px; }
.acc-field { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.acc-field > label { margin: 0; font-size: 13px; font-weight: 600; color: var(--Heading); }
.acc-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.acc-field-row.is-tight { align-items: center; }
@media (max-width: 575px) { .acc-field-row { grid-template-columns: 1fr; } }
.acc-page .acc-field input[type=password],
.acc-page .acc-field select { width: 100%; height: 44px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--Stroke);
    background-color: var(--Input); color: var(--Heading); font-size: 14px; box-shadow: none; transition: border-color .15s, box-shadow .15s; }
.acc-page .acc-field select { padding-right: 36px; }
.acc-page .acc-field input:focus,
.acc-page .acc-field select:focus { outline: none; border-color: var(--siq-accent); box-shadow: 0 0 0 3px rgba(78, 168, 255, .18); }
.acc-help { font-size: 12px; color: var(--Note); line-height: 1.5; }
.acc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 4px; }
.acc-actions form { margin: 0; }
.acc-actions .tf-button { width: auto; padding: 0 22px; }
.acc-msg { padding: 10px 14px; border-radius: 10px; font-size: 13px; font-weight: 500; margin-bottom: 16px; }
.acc-msg.is-ok { background: rgba(47, 163, 107, .12); color: var(--siq-good); }
.acc-msg.is-error { background: rgba(229, 72, 77, .12); color: var(--siq-bad); }
.acc-preview { height: 44px; display: flex; align-items: center; padding: 0 14px; border: 1px dashed var(--Stroke); border-radius: 12px;
    color: var(--Heading); font-weight: 700; white-space: nowrap; overflow: hidden; transition: font-size .15s ease; }

/* Theme segmented control */
.acc-segment { display: grid; grid-template-columns: repeat(3, 1fr); padding: 4px; gap: 4px; border-radius: 12px; background: var(--Input); }
.acc-segment label { margin: 0; cursor: pointer; }
.acc-segment input { position: absolute; opacity: 0; pointer-events: none; }
.acc-segment span { display: flex; align-items: center; justify-content: center; height: 36px; border-radius: 9px; font-size: 13px; font-weight: 600;
    color: var(--Body-Text); transition: background .15s, color .15s; }
.acc-segment input:checked + span { background: var(--White); color: var(--siq-accent); box-shadow: 0 1px 4px rgba(0, 0, 0, .10); }
.acc-segment input:focus-visible + span { outline: 2px solid var(--siq-accent); }

/* Subscription */
.acc-sub { border: 1px solid var(--Stroke); border-radius: 12px; padding: 16px 18px; margin-bottom: 16px; }
.acc-sub-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.acc-sub-plan { font-size: 18px; font-weight: 700; color: var(--Heading); }
.acc-dl { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; margin: 0; font-size: 14px; }
.acc-dl dt { color: var(--Note); font-weight: 500; }
.acc-dl dd { margin: 0; color: var(--Heading); font-weight: 600; text-align: right; }

/* Notification settings panel */
.notif-settings .notif-settings-toggle { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px;
    background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
.notif-settings-hint { font-size: 13px; color: var(--Note); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.notif-settings-hint i { transition: transform .2s ease; }
.notif-settings.is-open .notif-settings-hint i { transform: rotate(180deg); }
.notif-settings .notif-settings-body > p { color: var(--Body-Text); }
.notif-settings .table-responsive { border: 1px solid var(--Stroke); border-radius: 12px; }
/*
 * style.css sets a blanket `table { table-layout: fixed }`. Under fixed
 * layout a column's width comes from the table's own width divided up, never
 * from the content, so on a phone the eight columns here - a 150-250px name,
 * three 92px switches, two 196px dropdowns - were squeezed to fit whatever
 * width the card had, however unreadable, instead of the wrapper's own
 * horizontal scroll ever kicking in. Same fix as .fixture-full-stats: auto
 * layout plus max-content lets the table take the width its columns actually
 * need, so .table-responsive scrolls on a phone and min-width keeps it
 * filling the card on a desktop.
 */
.notif-grid { table-layout: auto; width: max-content; min-width: 100%; border-collapse: collapse; margin: 0 !important; }
.notif-grid th { padding: 12px 18px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
    color: var(--Note); background: var(--bg-table-1); text-align: center; vertical-align: middle; border-bottom: 1px solid var(--Stroke); }
/* The row's own name stays left - "EXPOSED", "OUTMATCHED" reads naturally
   flush to the edge, not centred in a 190px column. The HEADER above it is
   a column heading like every other, so it is deliberately left off this
   rule: "Alert" was the one heading not centred with its neighbours, sitting
   left while every other heading (On the site, Push, How early...) sat in
   the middle of its own column. */
.notif-grid td:first-child { text-align: left; }
.notif-grid td { padding: 14px 18px; border-bottom: 1px solid var(--Stroke); text-align: center; vertical-align: middle; }
.notif-grid tr:last-child td { border-bottom: 0; }
.notif-grid-name { font-weight: 700; color: var(--Heading); white-space: nowrap; }
.notif-grid-filters td { background: var(--bg-table-1); }
.notif-na { color: var(--Icon); }
.notif-switch { position: relative; display: inline-block; width: 42px; height: 24px; margin: 0; cursor: pointer; vertical-align: middle; }
.notif-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.notif-switch span { position: absolute; inset: 0; border-radius: 999px; background: var(--Palette-Blue-Gray-300); transition: background .2s; }
.notif-switch span::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .2); transition: transform .2s; }
.notif-switch input:checked + span { background: var(--siq-good); }
.notif-switch input:checked + span::after { transform: translateX(18px); }
.notif-switch input:focus-visible + span { outline: 2px solid var(--siq-accent); outline-offset: 2px; }
.notif-settings-msg { min-height: 18px; margin-top: 10px; font-size: 13px; font-weight: 600; }
.notif-settings-msg.is-ok { color: var(--siq-good); }
.notif-settings-msg.is-error { color: var(--siq-bad); }
.dark-theme .notif-switch span { background: #475569; }

/* ---- Match Filter: steps, conditions, outcome ---------------------------- */
.mf-step { border: 1px solid var(--Stroke); border-radius: 12px; padding: 18px 20px; margin-bottom: 14px; }
.mf-step-head { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 14px; }
.mf-step-no { flex: 0 0 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    background: rgba(78, 168, 255, .15); color: var(--siq-accent); font-weight: 800; font-size: 13px; }
.mf-step-title { font-weight: 700; color: var(--Heading); font-size: 15px; line-height: 26px; }
.mf-step-sub { font-size: 12px; color: var(--Note); line-height: 1.5; }
.mf-conds { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.mf-conds label { display: flex; flex-direction: column; gap: 6px; margin: 0; font-size: 12px; font-weight: 600; color: var(--Heading); }
.mf-conds label[hidden] { display: none; }
.mf-conds .mf-outcome { grid-column: span 2; }
@media (max-width: 575px) { .mf-conds .mf-outcome { grid-column: auto; } }
.mf-span:disabled { opacity: .55; cursor: not-allowed; }
.mf-outcome-banner { display: inline-block; padding: 8px 14px; border-radius: 10px; font-size: 13px; color: var(--Body-Text);
    background: rgba(78, 168, 255, .10); }
.mf-outcome-banner strong { color: var(--Heading); }
.mf-tile-main { border-color: var(--siq-accent); background: rgba(78, 168, 255, .06); }
.mf-tile-sub { font-size: 11px; color: var(--Note); margin-top: 2px; }
.mf-month-bar { position: relative; }
.mf-month-bar i { position: absolute; left: 0; right: 0; height: 2px; background: var(--siq-warn); z-index: 1; }

/* Match Filter: Find matches / Backtest tabs */
.mf-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.mf-tab { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 14px 18px; text-align: left; cursor: pointer;
    border: 1px solid var(--Stroke); border-radius: 12px; background: var(--White); transition: border-color .15s, background .15s; }
.mf-tab:hover { border-color: var(--siq-accent); }
.mf-tab.active { border-color: var(--siq-accent); background: rgba(78, 168, 255, .08); box-shadow: inset 0 -3px 0 var(--siq-accent); }
.mf-tab-title { font-size: 15px; font-weight: 700; color: var(--Heading); }
.mf-tab.active .mf-tab-title { color: var(--siq-accent); }
.mf-tab-sub { font-size: 12px; color: var(--Note); }
@media (max-width: 575px) { .mf-tabs { grid-template-columns: 1fr; } }

/* Fixture cards: the live score in the site's set style, one set under
   another down the middle of the card. */
.fixtures-b-vs.is-live-score { display: flex; flex-direction: column; align-items: center; gap: 3px; padding-top: 8px; }
.fixtures-b-vs.is-live-score .score-set { margin: 0; min-width: 40px; text-align: center; }
.fixtures-b-card.is-live-now .fixtures-b-vs.is-live-score { color: inherit; }
.fixtures-b-card.is-live-now .fixtures-b-countdown { color: var(--siq-bad); font-weight: 600; }

/* Match Intelligence on fixture cards: plain-English line under the label. */
.fixtures-b-siq-text { flex-basis: 100%; font-size: 11px; line-height: 1.4; color: var(--Body-Text); opacity: .85; }
/* Live card whose score has stopped moving (see fixtures.php). */
.fixtures-b-card.is-interrupted .fixtures-b-live-badge { background: var(--siq-warn); }
.fixtures-b-card.is-interrupted .fixtures-b-countdown { color: var(--siq-warn); }

/* Fixture page header: Match Intelligence summary, as on the fixtures list. */
.fixture-siq-strip { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-top: 18px; padding: 12px 16px;
    border-radius: 12px; border-left: 4px solid var(--siq-accent); background: var(--bg-table-1); }
.fixture-siq-strip-label { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--Note); font-weight: 600; }
.fixture-siq-strip-value { font-size: 15px; font-weight: 700; color: var(--siq-accent); }
.fixture-siq-strip-text { flex-basis: 100%; font-size: 13px; line-height: 1.5; color: var(--Body-Text); }

/* Fixture page: Match Intelligence explained in plain English. */
.siqm-takeaway { margin-top: 12px; padding: 10px 14px; border-radius: 10px; font-size: 14px; line-height: 1.5; color: var(--Heading);
    background: color-mix(in srgb, var(--siqm-accent) 10%, transparent); }
.siqm-explain { margin-top: 16px; border: 1px solid var(--Stroke); border-radius: 12px; overflow: hidden; }
.siqm-explain-head { padding: 10px 16px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--Note); background: var(--bg-table-1); border-bottom: 1px solid var(--Stroke); }
.siqm-explain-row { padding: 12px 16px; border-bottom: 1px solid var(--Stroke); border-left: 3px solid var(--Stroke); }
.siqm-explain-row.is-good { border-left-color: var(--siq-good); }
.siqm-explain-row.is-bad { border-left-color: var(--siq-bad); }
.siqm-explain-q { display: flex; justify-content: space-between; gap: 12px; font-weight: 700; color: var(--Heading); font-size: 14px; }
.siqm-explain-v { font-variant-numeric: tabular-nums; white-space: nowrap; }
.siqm-explain-row.is-good .siqm-explain-v { color: var(--siq-good); }
.siqm-explain-row.is-bad .siqm-explain-v { color: var(--siq-bad); }
.siqm-explain-a { margin-top: 4px; font-size: 13px; line-height: 1.5; color: var(--Body-Text); }
.siqm-explain-note { padding: 10px 16px; font-size: 12px; line-height: 1.5; color: var(--Note); background: var(--bg-table-1); }

/* =====================================================================
 * Fixture page – player-profile look: tale of the tape + duel panels.
 * Player one is blue, player two purple, everywhere on the page.
 * ===================================================================== */
:root { --duel-p1: var(--siq-accent); --duel-p2: var(--siq-accent-alt); }
.tape-head { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; margin-bottom: 10px; }
.tape-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--Note); }
.tape-name { font-size: 13px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tape-name.is-p1 { color: var(--duel-p1); }
.tape-name.is-p2 { color: var(--duel-p2); text-align: right; }
.co-hero.tape-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1100px) { .co-hero.tape-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 600px) { .co-hero.tape-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tape-item { background: linear-gradient(160deg, var(--co-tint), transparent 70%); }
.tape-values { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; margin-top: 8px; }
.tape-v { font-size: 22px; font-weight: 800; font-variant-numeric: tabular-nums; opacity: .55; }
.tape-v.is-p1 { color: var(--duel-p1); }
.tape-v.is-p2 { color: var(--duel-p2); }
.tape-v.is-lead { opacity: 1; }
.tape-vs { font-size: 11px; color: var(--Note); font-weight: 600; }

.duel-bar { display: flex; height: 6px; border-radius: 999px; overflow: hidden; margin-top: 8px; background: var(--Input); gap: 2px; }
.duel-bar span { display: block; height: 100%; transition: width .4s ease; }
.duel-bar .is-p1 { background: var(--duel-p1); border-radius: 999px 0 0 999px; }
.duel-bar .is-p2 { background: var(--duel-p2); border-radius: 0 999px 999px 0; }

.duel-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.duel-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: 999px; font-size: 12px; font-weight: 700; }
.duel-chip::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.duel-chip.is-p1 { color: var(--duel-p1); background: rgba(78, 168, 255, .12); }
.duel-chip.is-p2 { color: var(--duel-p2); background: rgba(155, 107, 255, .12); }
.duel-legend-note { font-size: 12px; color: var(--Note); }

.duel-panel { gap: 14px; }
.duel-icon { display: inline-flex; width: 22px; height: 22px; align-items: center; justify-content: center; border-radius: 6px;
    background: var(--Input); font-size: 12px; margin-right: 6px; vertical-align: middle; filter: saturate(.8); }
.duel-top { display: grid; grid-template-columns: 70px 1fr 70px; align-items: baseline; gap: 8px; }
.duel-label { text-align: center; font-size: 13px; font-weight: 600; color: var(--Heading); }
.duel-v { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; opacity: .6; }
.duel-v.is-p1 { color: var(--duel-p1); }
.duel-v.is-p2 { color: var(--duel-p2); text-align: right; }
.duel-v.is-lead { opacity: 1; }
.duel-v.is-lead::after { content: " ▲"; font-size: 9px; vertical-align: 2px; }
.duel-v.is-p2.is-lead::after { content: ""; }
.duel-v.is-p2.is-lead::before { content: "▲ "; font-size: 9px; vertical-align: 2px; }
.duel-detail { font-size: 11px; color: var(--Note); text-align: center; margin-top: 3px; }
/* Split bars: a centre mark so "level" is visible and the lean is obvious. */
.duel-bar { position: relative; }
.duel-bar::after { content: ""; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 2px; margin-left: -1px;
    background: var(--Heading); opacity: .35; border-radius: 2px; }

/* Fixture page: previous meetings expand into full match detail. */
details.h2h-meeting-item { display: block; padding: 0; }
.h2h-meeting-summary { display: flex; align-items: center; gap: 12px; cursor: pointer; list-style: none; padding: 12px 4px; }
.h2h-meeting-summary::-webkit-details-marker { display: none; }
.h2h-meeting-summary .h2h-meeting-body { flex: 1; min-width: 0; }
.h2h-meeting-toggle { flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--siq-accent); display: inline-flex; align-items: center; gap: 4px; }
.h2h-meeting-toggle::after { content: "▾"; transition: transform .2s ease; }
details[open] > .h2h-meeting-summary .h2h-meeting-toggle::after { transform: rotate(180deg); }
.h2h-meeting-summary:hover .h2h-meeting-toggle { text-decoration: underline; }
.meeting-detail { padding: 4px 4px 16px; }
.meeting-facts { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.meeting-facts > span { font-size: 12px; padding: 5px 10px; border-radius: 999px; background: var(--Input); color: var(--Body-Text); }
.meeting-facts strong { color: var(--Note); font-weight: 600; margin-right: 4px; }
.duel-ink-p1 { color: var(--duel-p1); font-weight: 700; }
.duel-ink-p2 { color: var(--duel-p2); font-weight: 700; }
.meeting-market-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; font-size: 14px; color: var(--Heading); }
.mm-label { display: block; font-size: 11px; color: var(--Note); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 2px; }
.mk-chart { width: 100%; height: 130px; display: block; margin-top: 12px; border-radius: 10px; background: var(--bg-table-1); }
.mk-line { fill: none; stroke: var(--duel-p1); stroke-width: 2; vector-effect: non-scaling-stroke; }
.mk-area { fill: var(--duel-p1); opacity: .12; }
.mk-mid { stroke: var(--Note); stroke-dasharray: 4 4; opacity: .5; vector-effect: non-scaling-stroke; }
.mk-set { stroke: var(--duel-p2); opacity: .45; vector-effect: non-scaling-stroke; }
.mk-set-label { font-size: 10px; fill: var(--Note); }
.mm-caption { font-size: 12px; color: var(--Note); margin-top: 6px; line-height: 1.5; }
.meeting-link { display: inline-block; margin-top: 10px; font-size: 13px; font-weight: 600; color: var(--siq-accent); }

/* Match result page – fixture-page look for a finished match. */
.fixture-vs-divider.result-score { display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 14px; }
.fixture-vs-divider.result-score .score-set { margin: 0; min-width: 46px; text-align: center; font-size: 14px; }
.result-final-label { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--Note); margin-top: 2px; }
.result-winner-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 10px; font-weight: 800; letter-spacing: .05em;
    text-transform: uppercase; color: #fff; background: var(--siq-good); }
.fixture-vs-player.is-loser .fixture-vs-photo { filter: grayscale(.6); opacity: .8; }
.result-outcome-chip { font-size: 11px; font-weight: 700; padding: 2px 10px; border-radius: 999px; }
.result-outcome-chip.is-won { color: var(--siq-good); background: rgba(47, 163, 107, .14); }
.result-outcome-chip.is-lost { color: var(--siq-bad); background: rgba(229, 72, 77, .13); }
a.result-meeting-link { display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
a.result-meeting-link .h2h-meeting-body { flex: 1; min-width: 0; }
.h2h-meeting-toggle.is-link::after { content: "→"; }
.fixture-header-meta .stadium-tour-badge { margin-left: 4px; }
/* One centre line for the whole row: the LIVE/FINISHED pills, the surface
   icon and text, the separators and the Betfair pill are different heights,
   and a flex row without align-items left each on its own baseline. */
.fixture-header-meta { align-items: center; line-height: 1.4; }
.fixture-header-meta > span { display: inline-flex; align-items: center; gap: 5px; }
.fixture-header-meta > span svg, .fixture-header-meta > span img, .fixture-header-meta > span i { display: block; flex-shrink: 0; }
.fixture-full-note { font-size: 11px; color: var(--Note); margin-top: 8px; line-height: 1.5; }

/* Fixture page: LIVE flag at the top of the match header. */
/* White text on a fill, so it takes the solid red: --siq-bad is tuned to be
   read AS text on a dark card, which leaves it too pale underneath. */
.fixture-live-flag { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; background: var(--siq-bad-solid);
    color: #fff; font-size: 11px; font-weight: 800; letter-spacing: .06em; }
.fixture-live-flag[hidden] { display: none; }
.fixture-live-flag .fixture-live-dot { background: #fff; }

/* Set by Set: one card per set above the full statistics. */
.sbs-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
.sbs-card { border: 1px solid var(--Stroke); border-radius: 14px; padding: 12px 16px; background: linear-gradient(160deg, rgba(78, 168, 255, .06), transparent 70%); }
.sbs-card.is-open { border-style: dashed; }
.sbs-card-head { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--Note); }
.sbs-card-score { font-size: 28px; font-weight: 800; margin-top: 4px; font-variant-numeric: tabular-nums; }
.sbs-card-score span { opacity: .55; }
.sbs-card-score .is-set-winner, .sbs-card-score .sbs-dash { opacity: 1; }
.sbs-dash { color: var(--Note); margin: 0 4px; font-weight: 400; }
.sbs-card-sub { font-size: 12px; color: var(--Body-Text); }
.sbs-card-odds { font-size: 12px; color: var(--Note); margin-top: 6px; }
.sbs-card-note { font-size: 10px; opacity: .7; }

/* Player page: Surface Breakdown as topic tabs + one card per figure. */
.psp-groups { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 14px; }
.psp-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.psp-cards-grid[hidden] { display: none; }
.psp-mcard { border: 1px solid var(--Stroke); border-radius: 14px; padding: 12px 14px; display: grid; gap: 7px; align-content: start; }
.psp-mcard-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 2px; }
.psp-mcard-label { font-size: 13px; font-weight: 700; color: var(--Heading); }
.psp-mcard-all { font-size: 13px; font-weight: 800; color: var(--Heading); white-space: nowrap; font-variant-numeric: tabular-nums; }
.psp-mcard-all .psp-sub { font-weight: 600; }
.psp-bar-row { display: grid; grid-template-columns: 70px 1fr auto; align-items: center; gap: 8px; font-size: 12px; }
.psp-bar-name { color: var(--Body-Text); display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.psp-bar-track { height: 8px; border-radius: 999px; background: var(--Input); overflow: hidden; }
.psp-bar-fill { display: block; height: 100%; border-radius: 999px; opacity: .75; }
.psp-bar-val { font-weight: 700; color: var(--Heading); white-space: nowrap; font-variant-numeric: tabular-nums; min-width: 52px; text-align: right; }
.psp-bar-row.is-best .psp-bar-fill { opacity: 1; }
.psp-bar-row.is-best .psp-bar-name { color: var(--Heading); font-weight: 700; }
.psp-best { color: #F0AE2F; }

/* Set by Set: the standout figure in each pair, in the player's colour. */
.fixture-full-stats td.is-lead-p1, .fixture-full-stats td.is-lead-p2 { font-weight: 800; }
.fixture-full-stats td.is-lead-p1 { color: var(--duel-p1); background: rgba(78, 168, 255, .10); }
.fixture-full-stats td.is-lead-p2 { color: var(--duel-p2); background: rgba(155, 107, 255, .10); }
.sbs-lead-p1 { color: var(--duel-p1); font-weight: 800; }
.sbs-lead-p2 { color: var(--duel-p2); font-weight: 800; }
.set-guide-table .siqp-muted .sbs-lead-p1, .set-guide-table .siqp-muted .sbs-lead-p2 { opacity: 1; }

/* Header Match Intelligence: the summary opens the full breakdown in place. */
details.fixture-siq-drop { margin-top: 18px; }
details.fixture-siq-drop > summary.fixture-siq-strip { margin-top: 0; cursor: pointer; list-style: none; position: relative; padding-right: 150px; }
details.fixture-siq-drop > summary::-webkit-details-marker { display: none; }
.fixture-siq-more { position: absolute; right: 16px; top: 12px; font-size: 12px; font-weight: 700; color: var(--siq-accent);
    display: inline-flex; align-items: center; gap: 4px; }
.fixture-siq-more::after { content: "▾"; transition: transform .2s ease; }
details.fixture-siq-drop[open] .fixture-siq-more::after { transform: rotate(180deg); }
details.fixture-siq-drop[open] > summary.fixture-siq-strip { border-radius: 12px 12px 0 0; }
.fixture-siq-body { border: 1px solid var(--Stroke); border-top: 0; border-radius: 0 0 12px 12px; padding: 16px; }
@media (max-width: 575px) {
    details.fixture-siq-drop > summary.fixture-siq-strip { padding-right: 16px; padding-bottom: 34px; }
    .fixture-siq-more { top: auto; bottom: 10px; }
}

/* Tale of the tape: wrap to the space the card actually has (the sidebar
   narrows it well before the viewport breakpoints kick in), so figures are
   never cut off. */
.co-hero.tape-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)) !important; }
.tape-item { min-width: 0; }
.tape-values { flex-wrap: wrap; row-gap: 2px; }
.tape-v { font-size: clamp(16px, 2.2vw, 22px); }
.tape-item .co-hero-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Odds movement chart: our own hover (see attachHover in odds-movement-chart.js). */
.odds-hover-line { position: absolute; width: 1px; background: var(--Note); opacity: .6; pointer-events: none; z-index: 3; }
.odds-hover-dot { position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%; border: 2px solid #fff;
    pointer-events: none; z-index: 4; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
.odds-hover-dot.is-p1, .odds-hover-key.is-p1 { background: var(--siq-accent); }
.odds-hover-dot.is-p2, .odds-hover-key.is-p2 { background: var(--siq-accent-alt); }
.odds-hover-tip { position: absolute; width: 190px; padding: 8px 10px; border-radius: 8px; font-size: 12px; line-height: 1.6; pointer-events: none; z-index: 5;
    background: var(--White); color: var(--Heading); border: 1px solid var(--Stroke); box-shadow: 0 4px 14px rgba(0,0,0,.15); }
.odds-hover-tip-head { font-weight: 700; border-bottom: 1px solid var(--Stroke); margin-bottom: 4px; padding-bottom: 2px; }
.odds-hover-key { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
.odds-hover-line[hidden], .odds-hover-dot[hidden], .odds-hover-tip[hidden] { display: none; }

/* Signal panels: how the signal turned out (components/signal-outcome.php). */
.sig-result { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; margin-top: 12px; padding: 10px 14px; border-radius: 12px;
    border: 1px solid var(--Stroke); font-size: 13px; color: var(--Body-Text); }
.sig-result-icon { width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
    font-weight: 800; color: #fff; background: var(--Note); flex: 0 0 auto; }
.sig-result-label { font-weight: 800; text-transform: uppercase; letter-spacing: .06em; font-size: 12px; }
.sig-result-text { flex: 1 1 240px; }
.sig-result.is-won { background: rgba(47, 163, 107, .12); border-color: rgba(47, 163, 107, .45); }
.sig-result.is-won .sig-result-icon { background: var(--siq-good); }
.sig-result.is-won .sig-result-label { color: var(--siq-good); }
.sig-result.is-lost { background: rgba(229, 72, 77, .10); border-color: rgba(229, 72, 77, .40); }
.sig-result.is-lost .sig-result-icon { background: var(--siq-bad); }
.sig-result.is-lost .sig-result-label { color: var(--siq-bad); }
.sig-result.is-open .sig-result-icon { background: var(--siq-accent); }
.sig-result.is-open .sig-result-label { color: var(--siq-accent); }


/* TradeSense results: one square per result – green success, red not, grey void. */
.sig-square { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 6px;
    font-weight: 800; font-size: 15px; color: #fff; flex: 0 0 auto; }
.sig-square.is-hit { background: var(--siq-good-solid); }
.sig-square.is-miss { background: var(--siq-bad-solid); }
.sig-square.is-void { background: #5B6270; }
.sig-outcome-cell.is-square { display: flex; align-items: center; justify-content: center; }

/* "View as" (admin preview of other user levels). */
.hdr-viewas { display: inline-flex; align-items: center; gap: 6px; margin: 0 6px 0 0; }
.hdr-viewas-label { display: inline-flex; align-items: center; gap: 4px; margin: 0; font-size: 12px; font-weight: 600; color: var(--Note); white-space: nowrap; }
.hdr-viewas-select { height: 34px; width: auto !important; padding: 0 28px 0 10px !important; border-radius: 10px; font-size: 13px; font-weight: 600;
    border: 1px solid var(--Stroke) !important; background-color: var(--Input) !important; color: var(--Heading); cursor: pointer; }
.hdr-viewas-select.is-on { border-color: #F0AE2F !important; background-color: rgba(240, 174, 47, .15) !important; color: #b7791f; }
@media (max-width: 767px) { .hdr-viewas-label span { display: none; } }
.viewas-strip { position: sticky; top: 0; z-index: 1100; padding: 7px 16px; text-align: center; font-size: 13px; color: #1f1400;
    background: #F0AE2F; }
.viewas-strip a { margin-left: 10px; font-weight: 700; color: #1f1400; text-decoration: underline; }

/* Preview pages (Page Access → Preview): locked sections and chips. */
.preview-lock { position: relative; overflow: hidden; min-height: 190px; display: flex !important; align-items: center; justify-content: center; }
.preview-lock-ghost { position: absolute; inset: 20px; display: grid; gap: 12px; filter: blur(3px); opacity: .5; pointer-events: none; }
.preview-lock-ghost span { display: block; height: 22px; border-radius: 8px; background: linear-gradient(90deg, var(--Input), var(--Stroke), var(--Input)); }
.preview-lock-ghost span:nth-child(2) { width: 80%; } .preview-lock-ghost span:nth-child(3) { width: 90%; } .preview-lock-ghost span:nth-child(4) { width: 60%; }
.preview-lock-body { position: relative; text-align: center; max-width: 460px; padding: 18px 22px; border-radius: 16px;
    background: color-mix(in srgb, var(--White) 88%, transparent); box-shadow: 0 6px 24px rgba(0,0,0,.12); }
.preview-lock-icon { font-size: 26px; }
.preview-lock-text { font-size: 13px; color: var(--Body-Text); margin: 4px 0 12px; }
.preview-lock-cta { display: inline-flex; width: auto !important; padding: 0 22px; }
.preview-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px; font-size: 10px; font-weight: 700;
    letter-spacing: .03em; color: var(--Note); background: var(--Input); text-decoration: none; white-space: nowrap; }
.preview-chip:hover { color: var(--siq-accent); }
.menu-preview-chip { font-size: 10px; margin-left: 6px; opacity: .8; }
.preview-banner { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 18px; border-radius: 14px;
    background: linear-gradient(120deg, rgba(78, 168, 255, .14), rgba(155, 107, 255, .12)); border: 1px solid rgba(78, 168, 255, .35); }
.preview-banner strong { color: var(--Heading); }
.preview-banner .tf-button { width: auto !important; padding: 0 18px; height: 38px; }
.preview-piq { margin-left: auto; align-self: center; }
.preview-header { display: flex !important; flex-direction: row !important; align-items: center; gap: 12px; flex-wrap: wrap; padding-top: 18px !important; padding-bottom: 18px !important; }
.preview-header .stadium-style-title { flex: 1 1 auto; }
.preview-header-lock { font-size: 12px; color: var(--Note); }
.preview-header-cta { font-size: 12px; font-weight: 700; color: var(--siq-accent); }
.preview-piq { margin-left: auto; align-self: center; }
.preview-piq .preview-chip { padding: 14px 22px; border-radius: 16px; font-size: 13px; filter: none; background: linear-gradient(135deg, var(--Input), var(--Stroke)); }
/* Preview "Blur": the section's real headings, then blurred placeholder lines. */
.preview-blur { position: relative; }
.preview-blur-body { display: grid; gap: 10px; margin-top: 14px; filter: blur(4px); opacity: .7; }
.preview-blur-body span { display: block; height: 12px; border-radius: 6px; background: linear-gradient(90deg, var(--Body-Text), var(--Note)); opacity: .45; }
.preview-blur-cta { position: absolute; right: 24px; bottom: 22px; padding: 6px 14px; border-radius: 999px; font-size: 12px; font-weight: 700;
    background: var(--White); color: var(--siq-accent); box-shadow: 0 2px 10px rgba(0,0,0,.15); }
.preview-inline-blur { display: inline-block; height: 1em; min-width: 1.5em; vertical-align: middle; border-radius: 4px;
    background: linear-gradient(90deg, var(--Note), var(--Body-Text), var(--Note)); opacity: .35; filter: blur(2.5px); }
.players-row-points { font-size: 12px; color: var(--Note); white-space: nowrap; font-variant-numeric: tabular-nums; }
.live-detail-lock { position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 40px); opacity: 0; transition: all .25s; z-index: 1200;
    padding: 12px 18px; border-radius: 12px; background: var(--White); color: var(--Heading); box-shadow: 0 8px 28px rgba(0,0,0,.25); font-size: 13px; }
.live-detail-lock.is-on { transform: translate(-50%, 0); opacity: 1; }
.live-detail-lock a { margin-left: 8px; font-weight: 700; color: var(--siq-accent); }
.live-sig-flag.is-locked { color: var(--Note); background: var(--Input); }
.sig-action.is-locked { opacity: .85; }

/* Dashboard: Live Scores tile in the same type scale as Next Fixtures and
   Upcoming Tournaments (names 16px bold, secondary lines 12px). */
.live-scores-tile .dash-live-event,
.live-scores-tile .live-row-names,
.live-scores-tile .body-text,
.live-scores-tile .dash-live-list span:not([class]) { font-size: 12px; }
.live-scores-tile a.body-text { font-size: 12px; font-weight: 600; }

/* Serve and return rings, one pair per player, under the five dimensions. */
.siqm-sr {
    /* Four rings, one pair per player, on one row where there is room. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 720px) {
    .siqm-sr { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.siqm-sr-name {
    font-size: 11px;
    font-weight: 600;
    margin-top: 2px;
}

.siqm-sr-name.is-p1 { color: var(--duel-p1, #5b9dff); }
.siqm-sr-name.is-p2 { color: var(--duel-p2, #b07cff); }

/* ---- Trading calculators ------------------------------------------------ */

.calc-panel { display: none; }
.calc-panel.is-active { display: block; }

.calc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 16px;
}

.calc-card {
    border: 1px solid var(--Stroke);
    border-radius: 14px;
    padding: 16px 18px;
}

.calc-card-title {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--Heading);
    margin-bottom: 4px;
}

.calc-label {
    display: block;
    font-size: 12px;
    color: var(--Note);
    margin: 12px 0 5px;
}

.calc-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

@media (max-width: 520px) {
    .calc-row { grid-template-columns: 1fr; }
}

.calc-line {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--Stroke);
    font-size: 13px;
    color: var(--Note);
}

.calc-line b {
    color: var(--Heading);
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.calc-verdict {
    margin-top: 14px;
    padding: 14px;
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    font-size: 14px;
    color: var(--Heading);
}

.calc-good { color: #32d583; }

.calc-note {
    font-size: 12px;
    color: var(--Note);
    line-height: 1.5;
}

.calc-err {
    font-size: 13px;
    color: #ff647c;
    min-height: 18px;
    margin-top: 10px;
}

/* The tab row reuses the TradeSense filter chips, which are anchors there.
   A button carries the browser's own control colours, so they are reset to
   match the anchor version - otherwise an inactive tab renders unreadably
   pale on pale. */
.sig-filter button.players-tab {
    background: transparent;
    color: var(--Body-Text);
    border: 1px solid var(--Stroke);
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    font-size: 14px;
}

.sig-filter button.players-tab.active {
    background: var(--siq-accent);
    border-color: var(--siq-accent);
    color: #fff;
}

/* Match Filter: what each rule did, and the same result cut several ways. */
.mf-funnel { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.mf-funnel-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(120px, 3fr) 150px; gap: 10px; align-items: center; font-size: 12px; }
.mf-funnel-rule { color: var(--Heading); }
.mf-funnel-bar { height: 8px; background: var(--Stroke); border-radius: 5px; overflow: hidden; }
.mf-funnel-bar i { display: block; height: 100%; background: var(--siq-accent); }
.mf-funnel-n { color: var(--Note); text-align: right; font-variant-numeric: tabular-nums; }

/* The five cut cards over two rows rather than one. An auto-fit track of
   minmax(260px, 1fr) put all five side by side on a desktop, which is about
   260px a card - and four headed columns (a label, a count, a rate and a
   signed P/L, each header carrying its unit) do not fit in 260px minus the
   card's padding, so the header row needed the .table-responsive scrollbar
   before a member could even read it.

   Six columns, because it divides by both three and two: the cards on the
   first row span 2 (a third of the width each) and the cards on the second
   span 3 (a half each), which is the widest a five-card set can be laid out
   in two rows. match-filter.php works out which card gets which span from
   how many cards it is actually drawing, so a cut with no rows dropping out
   does not shift the widths onto the wrong cards.

   The .table-responsive wrappers stay in place underneath as a backstop: a
   half-width card is wide enough for these tables at normal text size, and a
   member who has scaled text up still gets a scroller rather than a table
   running out over the card's edge. */
.mf-cuts { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-top: 10px; }
.mf-cut { border: 1px solid var(--Stroke); border-radius: 12px; padding: 12px 14px; grid-column: span 6; min-width: 0; }
.mf-cut--3up { grid-column: span 2; }
.mf-cut--2up { grid-column: span 3; }
.mf-cut--1up { grid-column: span 6; }
@media (max-width: 991.98px) {
    /* A third of a tablet is back to the width that caused the problem, so
       below desktop the rows halve: two cards abreast, then one column on a
       phone. */
    .mf-cut--3up { grid-column: span 3; }
}
@media (max-width: 575.98px) {
    .mf-cut, .mf-cut--3up, .mf-cut--2up, .mf-cut--1up { grid-column: span 6; }
}
.mf-cut-title { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--Note); margin-bottom: 6px; }
.mf-cut-table {
    width: 100%;
    font-size: 12px;
    /* style.css sets a blanket `table { table-layout: fixed }` for every table
       on the site. .mf-table opts out; this one did not, so its four columns
       were split into four equal quarters whatever they held - the label
       column wrapped "5.00 and above" onto three lines while the number
       columns sat half empty. That is the actual reason the numbers did not
       fit. auto sizes each column from its own content, as .mf-table does. */
    table-layout: auto;
    /* And the same blanket rule gives every table `margin: 0 0 30px`, which
       was a block of empty space between the last row and the bottom of the
       card it sits in. */
    margin: 0;
}
/* Cells had NO horizontal padding at all, so a label ran straight into the
   count beside it and the last column sat hard against the table border. */
.mf-cut-table th, .mf-cut-table td { padding: 4px 8px; color: var(--Body-Text); }
/* The numbers never wrap; the label column is the one allowed to, and only if
   it has to. */
.mf-cut-table .mf-num { white-space: nowrap; width: 1%; font-variant-numeric: tabular-nums; }
.mf-cut-table tbody th { font-weight: 400; text-align: left; }
.mf-cut-table thead th {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--Note);
    white-space: nowrap;
    /* A body row here is 20px (12px text, 8px padding) and the unit line
       13px, so the heading has 33px: a 10px label at 1.25 plus the unit plus
       6px of padding is 32px. The 4px the body cells get would go over. */
    padding: 3px 8px;
    line-height: 1.25;
    vertical-align: bottom;
}
/* The shared rule above zeroes the last row's bottom border on td only. The
   label cell is a <th scope="row">, so without this it keeps a stub of rule
   under it while the cells beside it have none - the same half-a-line the
   dashboard live tile had. */
.mf-cut-table tbody tr:last-child th { border-bottom: 0; }
.mf-cut-table .is-up { color: #32d583; }
.mf-cut-table .is-down { color: #ff647c; }

/* The backtest bio: what was actually run, above the figures. */
.mf-bio { font-size: 13px; color: var(--Body-Text); max-width: 70ch; }
.mf-bio p { margin: 0 0 6px; }
.mf-bio p:last-child { margin-bottom: 0; }
.mf-bio strong { color: var(--Heading); }
.mf-bio-rules { margin: 0 0 6px; padding-left: 18px; }
.mf-bio-rules li { margin: 0 0 2px; }

@media (max-width: 720px) {
    .mf-funnel-row { grid-template-columns: 1fr; }
}

/* Match Filter help chips: a plain-English note, on hover or focus. */
.mf-help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    border: 1px solid var(--Stroke);
    border-radius: 50%;
    color: var(--siq-accent);
    font-size: 11px;
    font-weight: 700;
    cursor: help;
    vertical-align: middle;
    position: relative;
}
.mf-help::after {
    content: attr(data-help);
    position: absolute;
    left: 0;
    top: 130%;
    z-index: 40;
    width: max(240px, 22vw);
    max-width: 360px;
    padding: 10px 12px;
    border: 1px solid var(--Stroke);
    border-radius: 9px;
    background: var(--Bg-Page, #0d1320);
    color: var(--Body-Text);
    font-size: 12px;
    font-weight: 400;
    line-height: 1.5;
    text-align: left;
    box-shadow: 0 12px 30px rgba(0, 0, 0, .45);
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
}
.mf-help:hover::after, .mf-help:focus::after { opacity: 1; }

/* The sample-size line reads as one sentence, with the box inline. */
.mf-check {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--Note);
}
.mf-check .mf-minn { margin: 0; }
.mf-remove svg { display: block; margin: 0 auto; }
/* Beats the site-wide full-width rule on .form-control. */
.mf-check input.mf-minn { width: 76px; flex: 0 0 76px; height: 34px; padding: 4px 8px; }

/* Saved filters: the name opens one, the arrow runs it straight away. */
.mf-saved-item { display: inline-flex; align-items: stretch; }
.mf-saved-item .mf-saved-name { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.mf-saved-item .mf-saved-run { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: 0; padding-inline: 10px; }
.mf-saved-item.is-active .players-tab { background: var(--siq-accent); border-color: var(--siq-accent); color: #fff; }

.mf-testing { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--Note); }
.mf-testing strong { color: var(--Heading); }
.mf-testing-note, .mf-testing-link { font-size: 12px; }

/* ---- Header bar: let it shrink to the screen --------------------------- */
/*
 * The template sets flex-shrink: 0 on the bar, so it kept its natural width
 * whatever the screen and simply ran off the right edge - 28px past on a
 * phone, 37px at 1024 where the sidebar takes 264px. Nothing scrolled
 * sideways, so the last item was quietly clipped on every page.
 *
 * The bar may now shrink, and so may its items; the two that can afford to
 * lose width (the view-as picker and the bug button) give it up first, and
 * the icon buttons keep their size.
 */
.layout-wrap .section-content-right .header-dashboard .wrap .header-grid {
    flex-shrink: 1;
    min-width: 0;
}

.layout-wrap .section-content-right .header-dashboard .wrap .header-grid > * {
    min-width: 0;
}

.header-dashboard .wrap .header-grid .hdr-viewas {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
}

.header-dashboard .wrap .header-grid .hdr-viewas-select {
    min-width: 0;
    max-width: 100%;
    text-overflow: ellipsis;
}

/* Below this the bug button is its icon alone: the label is the first thing
   that can go, and the button keeps its meaning through its title. */
@media (max-width: 1100px) {
    .header-dashboard .wrap .header-grid .hdr-bug-btn span,
    .header-dashboard .wrap .header-grid .hdr-bug-btn .hdr-bug-label {
        display: none;
    }
    .header-dashboard .wrap .header-grid .hdr-bug-btn {
        padding-inline: 9px;
    }
}

/*
 * Signal rows in the awkward band: from 992px to about 1250px the sidebar
 * still takes its full 264px, leaving roughly 700-950px of content - not
 * enough for four columns with their minimum widths, so the score column was
 * clipped by a few pixels at 1024. The action box drops to its own line here,
 * exactly as it does below 992px, and every column may shrink.
 */
@media (max-width: 1250px) {
    .sig-row {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
    }
    .sig-action {
        grid-column: 1 / -1;
    }
}

.sig-side { min-width: 0; }

/*
 * The notification-settings toggle is a title and a hint on one row. The hint
 * was set never to wrap, so in the middle widths - where the title is still
 * long and the content column has lost the sidebar's 264px - it pushed past
 * the edge and its chevron was clipped. It may wrap now, and drops away
 * entirely on a phone where the chevron alone says the same thing.
 */
.notif-settings .notif-settings-toggle { flex-wrap: wrap; }
.notif-settings-hint { white-space: normal; min-width: 0; text-align: right; }

@media (max-width: 575px) {
    .notif-settings-hint { font-size: 0; gap: 0; }
    .notif-settings-hint i { font-size: 15px; }
}

/*
 * Dashboard tiles on a phone: let the rows grow.
 *
 * The three tiles are pinned to an 86px row height so they line up side by
 * side on a desktop, with the overflow hidden. Stacked on a phone there is
 * nothing left to line up with, and a wrapped doubles name needs more than
 * 86px - so the fixed height only clipped the time and the tournament off a
 * card. The height becomes a minimum here, and nothing is cut.
 */
/* The phone rule that used to undo the fixed height is no longer needed:
   the height above is already a minimum at every width. */

/* Account: the browsers a member is signed in on. */
.acc-devices { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.acc-device { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6px; padding: 10px 12px; border: 1px solid var(--Stroke); border-radius: 10px; }
.acc-device-name { font-size: 13px; font-weight: 600; color: var(--Heading); }
.acc-device-this { font-size: 11px; font-weight: 700; color: var(--siq-good); margin-left: 6px; }
.acc-device-meta { font-size: 12px; color: var(--Note); }
.acc-subtitle { font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--Note); }
.acc-signout-all { background: transparent; border: 1px solid var(--Stroke); color: var(--Note); border-radius: 8px; padding: 7px 14px; font-size: 13px; font-weight: 600; cursor: pointer; }
.acc-signout-all:hover { color: #ff647c; border-color: #ff647c; }

/* Notification settings: one row per saved Match Filter, under the master switch. */
.notif-grid-filter .notif-grid-sub { padding-left: 22px; font-weight: 500; color: var(--Body-Text); }
.notif-grid-filter .notif-grid-sub::before { content: "\21B3"; margin-right: 6px; color: var(--Note); }
.notif-grid-filter.is-muted { opacity: .5; }

/* ── Bracket zoom toolbar ──
   These buttons carried no styling of their own, so they fell back to the
   browser's own button face - a pale grey box - while inheriting the page's
   text colour. In dark mode that was white on light grey and in light mode
   grey on grey; either way the labels could not be read. Given the site's own
   surface, stroke and text colours, they now follow the theme like everything
   else. */
.pd-bracket-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.pd-bracket-zoom-label,
.pd-bracket-zoom-value {
    font-size: 12px;
    color: var(--Note);
}
.pd-bracket-zoom-value { min-width: 44px; text-align: center; font-variant-numeric: tabular-nums; }
.pd-bracket-zoom-btn {
    min-width: 34px;
    height: 32px;
    padding: 0 10px;
    border: 1px solid var(--Stroke) !important;
    border-radius: 8px;
    background: var(--White);
    color: var(--Body-Text);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
}
.pd-bracket-zoom-btn:hover {
    border-color: var(--siq-accent) !important;
    color: var(--siq-accent);
}
.pd-bracket-zoom-btn:focus-visible {
    outline: 2px solid var(--siq-accent);
    outline-offset: 2px;
}

/* A note above a draw saying what kind of draw it is. Qualifying is played
   before the tournament proper and is easily mistaken for round 1 of it. */
.draw-note {
    padding: 10px 12px;
    border: 1px solid var(--Stroke);
    border-left: 3px solid var(--siq-accent);
    border-radius: 8px;
    font-size: 13px;
    color: var(--Body-Text);
    background: rgba(149, 152, 157, .06);
}
.draw-note strong { color: var(--Heading); }

/* ── Tournament hub: order of play ──
   One row per match, grouped by day: time, the two names, and the score once
   there is one. A row is a link to the fixture, so the card is the way into
   a tournament that is under way. */
.tourn-hub-oop-day { margin-top: 14px; }
.tourn-hub-oop-date {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--Heading);
    padding-left: 10px;
    border-left: 3px solid var(--siq-accent);
    margin-bottom: 8px;
}
.tourn-hub-oop-row {
    display: grid;
    grid-template-columns: 74px minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    padding: 8px 10px;
    border: 1px solid var(--Stroke);
    border-radius: 10px;
    margin-bottom: 6px;
    color: var(--Body-Text);
    text-decoration: none;
}
.tourn-hub-oop-row:hover { border-color: var(--siq-accent); }
.tourn-hub-oop-row.is-live { border-color: var(--siq-good); }
.tourn-hub-oop-time {
    display: flex; flex-direction: column; gap: 3px;
    font-size: 13px; font-weight: 700; color: var(--Heading);
    font-variant-numeric: tabular-nums;
}
.tourn-hub-oop-live {
    align-self: flex-start;
    font-size: 9px; font-weight: 800; letter-spacing: .06em;
    padding: 2px 5px; border-radius: 4px;
    background: var(--siq-good); color: #fff;
}
.tourn-hub-oop-names { display: flex; flex-direction: column; gap: 3px; min-width: 0; overflow-wrap: anywhere; }
.tourn-hub-oop-names > span { display: flex; align-items: center; gap: 6px; }
.tourn-hub-oop-score { white-space: nowrap; }

/* On a phone the time moves above the names rather than squeezing them. */
@media (max-width: 575px) {
    .tourn-hub-oop-row { grid-template-columns: 1fr; gap: 6px; }
    .tourn-hub-oop-time { flex-direction: row; align-items: center; gap: 8px; }
}

/* ── The sidebar casts no shadow ──
   The left menu carried a 24px shadow on every side, so it bled a grey haze
   onto the white header and footer beside it - visible as a smudge along
   their left edge rather than as depth. A single hairline does the job of
   separating the two without the smear. */
.layout-wrap .section-menu-left {
    box-shadow: none;
    border-right: 1px solid var(--Stroke);
}

/* ── Flags need an edge of their own ──
   Japan, Switzerland's white cross, Poland's lower half, Nigeria's centre:
   plenty of flags are white at the border, so on a white card they lost their
   shape entirely, and the mostly-black ones did the same in dark mode. A
   hairline in the theme's own stroke colour, plus a faint inner edge, gives
   every flag a boundary without tinting it. Applied to the image so it hugs
   the flag rather than the box around it. */
.flag-box-small img,
.flag-box img {
    border: 1px solid var(--Stroke);
    border-radius: 2px;
    box-shadow: inset 0 0 0 1px rgba(20, 25, 38, .08);
}
.dark-theme .flag-box-small img,
.dark-theme .flag-box img {
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);
}
/* The border adds to the fixed 18x12, which would crop the flag; box-sizing
   keeps the drawn size exactly what it was. */
.flag-box-small img { box-sizing: border-box; }

/* ══ Colour contrast: WCAG 2.2 AA ══
   Measured every visible run of text on every page against the background
   actually behind it, in both themes, and adjusted the palette where it fell
   short of 4.5:1 (3:1 for large text). The hues are the same; several were
   simply too light to read.

   The worst offenders were the muted grey used for notes and captions, which
   sat at 2.89:1 on a white card and is used on nearly every page, and the
   active tab, which was white text on a pale blue at 2.51:1.

   Each value below is the same colour walked toward black or white until it
   just clears the threshold, so nothing changes character. */
:root {
    /* Kept in step with theme-tokens.css, which is the palette of record and
       is byte-identical in the admin panel. Both were a rounding error under
       AA as ink on white - #73777D at 4.50 and #0075E5 at 4.49 - so both are
       one step darker. tools/palette-check.mjs fails if these two drift. */
    --Note: #6B6F75;          /* was #73777D - 4.50:1 on white */
    --siq-accent: #006BCF;    /* was #0075E5 - 4.49:1 as ink on white */
    --siq-accent-alt: #8950FF;/* was #9B6BFF - 3.53:1 on white */
    --siq-good: #278758;      /* was #2FA36B - 3.20:1 either way */
    --siq-bad: #E12F34;       /* was #E5484D - 3.91:1 on white */
    --siq-warn: #A56922;      /* was #D1852B - 2.96:1 on white */
}
.dark-theme {
    /* On the dark card surface the greys, greens and amber already pass, so
       they are restated at their original values: this block sits after the
       ones above, and :root and .dark-theme carry the same specificity, so
       anything not repeated here would take the light-mode value. */
    --Note: #95989D;
    --siq-warn: #F0AE2F;
    --siq-good: #2FA36B;
    --siq-accent: #4EA8FF;
    /* These two did fall short against the dark card. */
    --siq-bad: #E96368;
    --siq-accent-alt: #A174FF;
}

/* Amber is the one colour no amount of darkening makes safe under white text
   without turning brown, so the badges that sit on it take dark text instead -
   8.25:1, and amber still reads as amber. */
.fixtures-b-live-badge,
.live-badge.is-interrupted,
.badge-warn {
    color: #2B2000;
}


/* Chips draw their ink on a tinted version of their own colour, which lifts
   the background and costs about half a point of contrast. In dark mode the
   inks are lifted to match, so a chip passes on its tint as well as on the
   plain card. */
.dark-theme {
    --Note: #A7AAAE;
    --siq-good: #34B376;
    --siq-bad: #EB7377;
    --siq-accent-alt: #AF89FF;
}

/* A filled accent surface keeps one colour in both themes, because white text
   on it has to pass in both: the light blue that reads well AS text on a dark
   card is far too pale UNDER white text. */
:root, .dark-theme { --siq-accent-solid: #0071DB; }
.players-tab.active,
.sig-filter button.players-tab.active,
.mf-saved-item.is-active .players-tab,
.live-filter-btn.is-active {
    background: var(--siq-accent-solid);
    border-color: var(--siq-accent-solid);
    color: #fff;
}
/* The notification count: white on the brand orange was 3.25:1. */
.hdr-badge { background: #D54400; }
/* Solid fills need the deeper shades whatever the theme, for the white text
   sitting on them - the lifted inks above are for text, not for backgrounds. */
:root, .dark-theme { --siq-good-solid: #278758; --siq-bad-solid: #C4282C; }
.lm-set.set-win { background: var(--siq-good-solid); }
.lm-set.set-loss { background: var(--siq-bad-solid); }

/* The duel colours are derived - "--duel-p1: var(--siq-accent)" - and a
   derived custom property resolves where it is DECLARED, not where it is
   used. Declared only on :root, they kept the light-mode ink even though the
   theme class sits on <body>, which is why the duel names and chips were the
   pale light-mode blue on a dark card. Re-declared here so they re-resolve
   against the dark palette. */
.dark-theme { --duel-p1: var(--siq-accent); --duel-p2: var(--siq-accent-alt); }

/* Remaining one-off fills whose own rules are more specific than the palette:
   each was white text on a colour too light to carry it. */
.header-dashboard .hdr-badge,
span.text-tiny.hdr-badge { background: #D54400; }
.live-filter-btn.is-active {
    background: var(--siq-accent-solid);
    border-color: var(--siq-accent-solid);
}
.stadium-tour-badge.tour-atp { background: #12893E; }

/* A value at 55% of its colour is a design choice, not a reading one: the
   trailing side of a duel was down at 3.2:1. Still visibly the quieter of
   the two, now legible. */
.tape-v { opacity: .75; }

/* ── The Sports-IQ rating ramp ──
   Six steps used for every rating chip and directional label, written into
   inline styles by SportsIqPresenter. As fixed hex they had to serve a white
   card and a dark one and could not: the gold sat at 2.18:1 on white, the red
   at 3.34:1 on the dark surface. Same six steps, taken per theme to where
   they can be read. */
:root {
    --siq-ramp-gold: #9D6E19;
    --siq-ramp-good: #2C8659;
    --siq-ramp-info: #2C7DB0;
    --siq-ramp-mid:  #6E7788;
    --siq-ramp-warn: #A56922;
    --siq-ramp-bad:  #CE4852;
}
.dark-theme {
    --siq-ramp-gold: #E0A53A;
    --siq-ramp-good: #35A16B;
    --siq-ramp-info: #3D96CF;
    --siq-ramp-mid:  #878F9E;
    --siq-ramp-warn: #D1852B;
    --siq-ramp-bad:  #D96F77;
}

/* The last few one-off fills, each more specific than the palette it should
   have been following. */
.header-dashboard .hdr-badge { background: #D54400 !important; }
.stadium-tour-badge.tour-atp { color: #fff; }
.result-winner-badge { background: var(--siq-good-solid); }
.tf-button { background: #176EFC; }

/* Final margin: several of the fills above landed at 4.47-4.49, which is the
   right side of nothing. Taken a shade further so they clear 4.5 outright. */
:root, .dark-theme {
    --siq-accent-solid: #006BCF;
    --siq-good-solid: #23794E;
    --siq-bad-solid: #C4282C;
}
.tf-button { background: #1266F0; }
/* Set pills are filled with the ramp green and red, which are text shades on
   this theme and far too light under white. */
.lm-set.set-win { background: var(--siq-good-solid) !important; }
.lm-set.set-loss { background: var(--siq-bad-solid) !important; }
.stadium-tour-badge.tour-atp { background: #107835 !important; color: #fff !important; }
/* Tour badges tint their own colour as a background and then write that same
   colour on top of it, which cannot work. The ink is lifted; the tint stays. */
/* Grey on a grey pill, in both themes: 4.46:1 in dark and worse on the white
   header. One value per theme. */
.hdr-bug-label { color: #5A6779; }
.dark-theme .hdr-bug-label { color: #A3B0C2; }

/* The last band sat between 3.9 and 4.5 - chips on a tint of their own colour,
   where the tint lifts the background under them. Each ink lifted a further
   step so the whole set clears 4.5:1 on the tint as well as on the card. */
.dark-theme {
    --Note: #C8CDD4;
    --siq-good: #38BC7C;
    --siq-bad: #EE8286;
    --siq-accent: #5CB0FF;
    --siq-accent-alt: #B795FF;
    --siq-ramp-mid: #909AA8;
}
.dark-theme { --siq-accent-alt: #BE9FFF; }

/* ══ Tables: a visible grid, all the way round ══
   Tables were drawn with hairline row separators in the stroke colour and no
   outer edge at all, and several had nothing under the header row - so a
   header ran into its first row and the last row ran off into the card.

   A near-black line in light mode, as asked. Dark mode gets its mirror
   image - a near-white line - because a black line on a dark card is
   invisible, which would put the problem back in the other theme. */
:root { --table-line: #1B2130; }
.dark-theme { --table-line: #C8CEDA; }

/* Separate rather than collapsed, purely so the corners can be round:
   border-radius has no effect on a collapsed table, which is why these had
   square corners inside cards that are rounded everywhere else. With
   separate borders the cells would double every internal line, so each cell
   draws only its right and bottom edge and the table itself draws the
   outside. */
.siq-table, .yoy-table, .psp-table, .mf-table, .siqm-table,
.set-guide-table, .mf-cut-table, .live-stats-table, .fsv-component-table,
.fixture-full-stats, .notif-grid {
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--table-line);
    border-radius: 12px;
    overflow: hidden;
}
.siq-table th, .siq-table td,
.yoy-table th, .yoy-table td,
.psp-table th, .psp-table td,
.mf-table th, .mf-table td,
.siqm-table th, .siqm-table td,
.set-guide-table th, .set-guide-table td,
.mf-cut-table th, .mf-cut-table td,
.live-stats-table th, .live-stats-table td,
.fsv-component-table th, .fsv-component-table td,
.fixture-full-stats th, .fixture-full-stats td,
.notif-grid th, .notif-grid td {
    border-right: 1px solid var(--table-line);
    border-bottom: 1px solid var(--table-line);
}
/* The table's own border is the outside edge, so the last cell in a row and
   the last row do not draw one of their own - that would sit a line directly
   on top of the border and thicken two sides out of four. */
.siq-table th:last-child, .siq-table td:last-child,
.yoy-table th:last-child, .yoy-table td:last-child,
.psp-table th:last-child, .psp-table td:last-child,
.mf-table th:last-child, .mf-table td:last-child,
.siqm-table th:last-child, .siqm-table td:last-child,
.set-guide-table th:last-child, .set-guide-table td:last-child,
.mf-cut-table th:last-child, .mf-cut-table td:last-child,
.live-stats-table th:last-child, .live-stats-table td:last-child,
.fsv-component-table th:last-child, .fsv-component-table td:last-child,
.fixture-full-stats th:last-child, .fixture-full-stats td:last-child,
.notif-grid th:last-child, .notif-grid td:last-child {
    border-right: 0;
}
/* The header is the line people said was missing: heavier, so the head reads
   as separate from the body rather than as its first row. */
.siq-table thead th, .yoy-table thead th, .psp-table thead th,
.mf-table thead th, .siqm-table thead th, .set-guide-table thead th,
.mf-cut-table thead th, .live-stats-table thead th,
.fsv-component-table thead th, .fixture-full-stats thead th,
.notif-grid thead th {
    border-bottom: 2px solid var(--table-line);
}
/* Rules elsewhere zero the last row's border to avoid a double line with a
   card edge; with a real table border there is no double line, and losing it
   is exactly the missing bottom edge. */
.siq-table tbody tr:last-child td, .yoy-table tbody tr:last-child td,
.psp-table tbody tr:last-child td, .mf-table tbody tr:last-child td,
.siqm-table tbody tr:last-child td, .set-guide-table tbody tr:last-child td,
.mf-cut-table tbody tr:last-child td, .live-stats-table tbody tr:last-child td,
.fsv-component-table tbody tr:last-child td, .fixture-full-stats tbody tr:last-child td,
.notif-grid tbody tr:last-child td { border-bottom: 0; }

/* ── Elo on a player profile ── */
.elo-move.is-up strong { color: var(--siq-good); }
.elo-move.is-down strong { color: var(--siq-bad); }
.elo-surfaces {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
    gap: 10px;
    margin-top: 12px;
}
.elo-surface {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    border: 1px solid var(--table-line);
    border-radius: 10px;
    /* The surface colours the site already uses, as a rail rather than a
       wash, so clay and grass are told apart at a glance. */
    border-left-width: 4px;
}
.elo-surface.surface-hard { border-left-color: #2C7DB0; }
.elo-surface.surface-clay { border-left-color: #C1622F; }
.elo-surface.surface-grass { border-left-color: #2C8659; }
.elo-surface-name { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--Note); }
.elo-surface-value { font-size: 20px; font-weight: 800; color: var(--Heading); font-variant-numeric: tabular-nums; }
.elo-surface-count { font-size: 11px; color: var(--Note); }

/* ── Odds legend on a result page ──
   The chart shows both prices; the legend says whose is whose and carries the
   four numbers people actually quote: where each price opened, where it
   closed, and how short and long it went. */
.odds-legend { display: flex; flex-direction: column; gap: 8px; }
.odds-legend-item {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 8px 10px; border: 1px solid var(--Stroke); border-radius: 10px;
}
.odds-legend-key { width: 14px; height: 3px; border-radius: 2px; flex: 0 0 14px; }
.odds-legend-item.is-p1 .odds-legend-key { background: var(--duel-p1); }
.odds-legend-item.is-p2 .odds-legend-key { background: var(--duel-p2); }
.odds-legend-item.is-p1 .odds-legend-name { color: var(--duel-p1); }
.odds-legend-item.is-p2 .odds-legend-name { color: var(--duel-p2); }
.odds-legend-name { font-weight: 700; }
.odds-legend-nums { font-size: 12px; color: var(--Note); font-variant-numeric: tabular-nums; }
.odds-legend-nums strong { color: var(--Heading); }

/* The set-by-set table holds two figures per cell, so it needs room to
   breathe more than it needs to be narrow. */
.set-by-set-table th, .set-by-set-table td { padding: 8px 10px; white-space: nowrap; }

/* ── Heat-map cells ──
   The surface table tints a cell by how well the season went, up to 38% of
   the colour. That tint lifts the background under the text, and the muted
   grey used for the percentage was left behind at 3.2:1. Inside a tinted
   cell the text takes the full body colour and the percentage a readable
   grey; the tint still carries the meaning. */
.psp-table td[style*="background"] { color: var(--Body-Text); }
.psp-table td[style*="background"] .psp-sub { color: var(--Body-Text); opacity: .85; }
.dark-theme .psp-table td[style*="background"],
.dark-theme .psp-table td[style*="background"] .psp-sub { color: #E8ECF3; }

/* A call to action with no tone class had neither colour nor background of
   its own, so it inherited whatever was around it - white on white in the
   notifications list. */
.hdr-noti-cta, .notif-cta { color: var(--Body-Text); background: rgba(127, 127, 127, .12); }

/* A tab that is a <button> fell back to the browser's button face while
   inheriting the page's text colour: white on light grey. */
.players-tab { background: transparent; color: var(--Body-Text); }
.players-tab.active { color: #fff; }

/* Bootstrap's muted grey is fixed and does not know about the dark card. */
.text-muted { color: var(--Note) !important; }

/* A tinted call to action sits inside rows that sometimes have a solid
   background of their own, and a translucent tint over a blue row is just
   blue - 1.11:1. Each tone gets an opaque background so it reads the same
   wherever it lands. */
.hdr-noti-cta, .notif-cta { background: var(--White); }
.sig-tone-red .hdr-noti-cta, .sig-tone-red .notif-cta { background: #FDECEC; color: #A3181C; }
.sig-tone-amber .hdr-noti-cta, .sig-tone-amber .notif-cta { background: #FBF0DC; color: #7A4E0E; }
.sig-tone-green .hdr-noti-cta, .sig-tone-green .notif-cta { background: #E4F4EC; color: #1D6742; }
.dark-theme .sig-tone-red .hdr-noti-cta, .dark-theme .sig-tone-red .notif-cta { background: #3B2A2C; color: #F0999C; }
.dark-theme .sig-tone-amber .hdr-noti-cta, .dark-theme .sig-tone-amber .notif-cta { background: #39301C; color: #F0C570; }
.dark-theme .sig-tone-green .hdr-noti-cta, .dark-theme .sig-tone-green .notif-cta { background: #1E3A2D; color: #6FD3A2; }

/* The body link colour is fixed and fails on the dark card. */
.tf-color, a.tf-color { color: var(--siq-accent); }
/* A chip on the slightly lighter panel inside a card. */
.acc-chip { color: var(--Note); }
/* The gauge label is drawn into SVG with the palette captured at render
   time, so a theme flip afterwards leaves it behind. Set in CSS, where the
   theme always wins. */
.siq-gauge-label { fill: var(--Note); color: var(--Note); }
/* style.css sets .tf-color to a fixed blue with more specificity than the
   palette rule above it. */
a.tf-color, .tf-color { color: var(--siq-accent) !important; }

/* ── Skip link and the hidden label class ──
   Off-screen until focused, then it sits over the header where it can be
   seen. Not display:none, which would take it out of the tab order and
   defeat the point. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1000;
    padding: 10px 16px;
    border-radius: 0 0 8px 0;
    background: var(--siq-accent-solid);
    color: #fff;
    font-weight: 700;
    text-decoration: none;
}
.skip-link:focus {
    left: 0;
    outline: 3px solid var(--siq-warn);
    outline-offset: 2px;
}
/* Read by a screen reader, seen by nobody. */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
/* main is a landmark, not a layout box. */
main#main-content { display: contents; }
main#main-content:focus { outline: none; }

/* Two chips that fill with a colour lifted for TEXT and then write white on
   it. The solid shades exist for exactly this. */
.tourn-hub-season.is-current {
    background: var(--siq-accent-solid);
    border-color: var(--siq-accent-solid);
}
/* The leader's year chips on a gold tint: one ink per theme. */
.tourn-hub-title-row.is-first .tourn-hub-title-year { color: #7A6210; }
.dark-theme .tourn-hub-title-row.is-first .tourn-hub-title-year { color: #E3B84A; background: rgba(224, 165, 58, .18); }
/* The Match Intelligence strip tints itself with the directional colour and
   writes that colour on top; on the dark card it came out at 4.28:1. */
.dark-theme .fixture-siq-strip-value,
.dark-theme .fixture-siq-more { color: #E8ECF3; }

/* The last five, all chips writing their own colour on a tint of itself. The
   tint lightens the background under the ink, so each one goes a step darker
   in light mode and a step lighter in dark.

   Done as variables rather than as ":root:not(.dark-theme)" selectors: the
   theme class lands on <body> when the server renders it and on <html> when
   the inline script applies it before paint, so a selector naming one of them
   is wrong half the time. A custom property with a .dark-theme override
   cascades correctly from wherever the class happens to be. */
:root {
    --chip-live: #CC1D22;
    --chip-points: #0073E2;
    --chip-countdown: #006DD5;
    --chip-atp: #0050EA;
    --chip-wta: #871DE8;
}
.dark-theme {
    --chip-live: #F0999C;
    --chip-points: #6F85FE;
    --chip-countdown: #8FC4FF;
    --chip-atp: #679BFF;
    --chip-wta: #C087F3;
}
.live-conn-badge.is-live { color: var(--chip-live); }
.live-points, .live-points.live-set-header { color: var(--chip-points); }
.fixtures-b-countdown { color: var(--chip-countdown); }
.tourn-b-badge.tour-atp { color: var(--chip-atp) !important; }
.tourn-b-badge.tour-wta { color: var(--chip-wta) !important; }

/* ── The masthead, matching the admin panel's ──
   The site showed a bare bitmap while the panel showed a mark with SportsIQ
   and ADMIN beside it, so the two halves of the same product did not look
   related. This is the panel's lockup with Tennis in place of Admin, and the
   wordmark is markup rather than baked into an image so it takes the theme's
   own text colour instead of needing a second file for dark mode. */
.siq-logo {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    min-width: 0;
}
.siq-logo img { flex: 0 0 auto; }
/*
 * The wordmark and the product name, stacked beside the mark.
 *
 * Both are text so they take the theme's colour and spell the hyphen in
 * Sports-IQ rather than having it drawn into an image that would then need a
 * second file for dark mode.
 */
.siq-logo-lockup { display: inline-flex; flex-direction: column; justify-content: center; min-width: 0; gap: 2px; }
.siq-logo-mark {
    font-size: 20px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.2px;
    color: var(--Heading);
    white-space: nowrap;
}
/* The mark's green is #3CD2A5, which sits at 9.3 on the dark sidebar but
   only 1.9 on a white one - under even the large-text bar. Dark mode keeps
   the brand green; light mode takes a deeper one of the same hue (4.1). */
.siq-logo-iq { color: #0E8F6B; }
.dark-theme .siq-logo-iq { color: #3CD2A5; }
/* --Heading is a muted blue-grey in dark mode, which is right for a
   heading and wrong for a wordmark; the mark is white, as it is drawn. */
.dark-theme .siq-logo-mark { color: #FFFFFF; }
.dark-theme .siq-logo-text { color: var(--Note); }
.siq-logo-text {
    font-size: 11px;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: .18em;
    text-transform: uppercase;
    /* Pinned rather than taking --Note, which measures 4.13 on white and is
   a decision still open elsewhere; a wordmark should not wait on it. */
    color: #5B6270;
    white-space: nowrap;
}

/* Collapsed to the icon rail, the words have nowhere to go. */
.layout-wrap.menu-style-icon .section-menu-left:not(:hover) .siq-logo-lockup { display: none; }

/* ── The logo box is the height the layout reserves for it ──
   .section-menu-left reserves exactly 81px of padding-top for the fixed logo
   box. The box itself had no height of its own, so it took the logo's natural
   104px and stood 131px tall - overhanging the menu by 50px, which is why the
   first menu items sat across the line under the logo. Capping the mark and
   pinning the box to the height the layout already assumes puts the rule back
   where the menu starts. */
.layout-wrap .section-menu-left > .box-logo {
    min-height: 81px;
    box-sizing: border-box;
}
.layout-wrap .section-menu-left > .box-logo a > img,
#logo_header {
    /* 52px is what the 81px box leaves after its 14/13 padding. The mark
       carries a vertical SPORTS wordmark, so it needs every one of them -
       at 40px that wordmark was an illegible green smudge. */
    max-height: 52px;
    width: auto;
}

/* ── Initials instead of a stranger's face ──
   Members without a photo used to get the theme's stock portrait. These are
   the circles AvatarHelper draws: same box as the <img> they replace, so the
   surrounding layout does not know the difference. The colour comes from the
   helper (derived from the name, and dark enough for white text). */
.siq-avatar,
.siq-initials {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    color: #FFFFFF;
    font-weight: 700;
    /* Letters scale with the circle, so the same rule serves the 40px header
       and the 96px account page without a second font-size. */
    font-size: .42em;
    line-height: 1;
    letter-spacing: .02em;
    text-transform: uppercase;
    user-select: none;
}
.siq-avatar.is-initials,
.siq-initials.is-initials { font-size: 15px; }
.acc-avatar.is-initials {
    /* .acc-avatar is display:block for the <img> case; the initials need to
       centre their letters, so the span switches to flex. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #FFFFFF;
    font-weight: 700;
    font-size: 34px;
    line-height: 1;
    letter-spacing: .02em;
    user-select: none;
}

/* ── A face on each row of the players directory ──
   The listing showed a rank, a flag and a name, which is the same row for
   everyone. The photo is the fastest way to recognise a player, and it costs
   nothing above the fold now that the images resolve from an index rather
   than from a pair of globs each. Players with no photo on file get their
   initials, so the column never becomes a run of identical silhouettes. */
.players-row-photo {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    /* The photos are portraits with the head near the top, so anchoring the
       crop there keeps the face in the circle instead of the chest. */
    object-position: top center;
    flex: 0 0 auto;
    background: var(--Stroke);
    font-size: 13px;
}
.players-row-photo.is-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #FFFFFF;
    font-weight: 700;
    line-height: 1;
    user-select: none;
}

/* On a phone the row already wraps; a smaller circle keeps the name on the
   first line with it rather than pushing it onto the second. */
@media (max-width: 700px) {
    .players-row-photo { width: 28px; height: 28px; font-size: 11px; }
}

/* ── Match Intelligence: room between the parts ──
   The panel ran the summary, the three split bars, the two dials and the four
   serve/return rings together with 4-14px between them, so it read as one
   undifferentiated block of graphics and you could not tell which label went
   with which chart. The heading that used to sit above them ("The Five
   Dimensions") only counted our own products at the reader - every chart in
   here already carries its own name - so it has gone, and the space it was
   providing has to come from the layout instead.

   Each group gets air above it and a hairline where a real change of subject
   happens, which is what the heading was actually doing. */
.siqm-section { margin-top: 26px; }

.siqm-section .siqm-names { margin-top: 0; }

.siqm-section .siqm-splits {
    margin-top: 10px;
    /* The three split bars are one comparison, so they sit closer to each
       other than the group does to what follows it. */
    display: grid;
    gap: 10px;
}

.siqm-section .siqm-dials {
    margin-top: 24px;
    padding-top: 22px;
    border-top: 1px solid var(--Stroke);
}

/* The four serve/return rings are the same two measures as the first split
   bars, shown per player - a continuation, not a new subject, so they are
   spaced apart but not ruled off. */
.siqm-section .siqm-dials.siqm-sr {
    margin-top: 22px;
    padding-top: 0;
    border-top: 0;
}

.siqm-section .siqm-sr .siqp-dial-title {
    /* Was inheriting the dial default and reading louder than the player name
       under it, which is the thing that tells you whose ring this is. */
    color: var(--Note);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* The summary panel and the charts under it are two different kinds of thing;
   the panel was sitting straight on top of the first chart group. */
.siqm-panel { margin-bottom: 4px; }

/* The example picker: a named control, not a chip that could be mistaken for
   one of the filter flags on the fixtures list. The list carries each
   example's explanation, so it needs more room than a normal select. */
.mf-scenario-select {
    max-width: 420px;
    width: auto;
    display: inline-block;
}
@media (max-width: 575px) {
    .mf-scenario-select { max-width: 100%; width: 100%; }
}

/* ── Footer ──
   It used to be one line of copyright. It now carries the pages people read
   rather than use - the guide, what this is, what it costs, the legal pages -
   because those had nowhere else to live and every link to them pointed at a
   different product's website.

   Four centred rows, in the order they matter: where to go, the legal pages,
   whose site this is, and the gambling-help mark under all of it.

   Everything here is the site's own body text - var(--Body-Text), 14px, 400,
   and the inherited family. Every one of these links had been rendering in
   the theme blue, because style.css carries

       .layout-wrap .section-content-right .bottom-page a { color: var(--Main); }

   which is three classes to .site-footer-links' one and won on specificity.
   Matching that depth is what actually changes the colour; the rules below
   are written long for that reason rather than by preference. */
.site-footer-inner {
    width: 100%;
    padding: 0 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    text-align: center;
}

.site-footer-links,
.site-footer-legal {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px 22px;
}

.layout-wrap .section-content-right .bottom-page .site-footer-links a,
.layout-wrap .section-content-right .bottom-page .site-footer-legal a {
    color: var(--Body-Text);
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
    text-decoration: none;
}

.layout-wrap .section-content-right .bottom-page .site-footer-links a:hover,
.layout-wrap .section-content-right .bottom-page .site-footer-legal a:hover {
    color: var(--Main);
    text-decoration: underline;
}

.site-footer-copy {
    color: var(--Body-Text);
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
}

.site-footer-aware {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    /* GambleAware's own file, with the "Gamble" letterforms taking
       currentColor so the word is legible in the light theme as well as the
       dark one it was drawn for. The "Aware" half stays brand orange. */
    color: var(--Body-Text);
}
.layout-wrap .section-content-right .bottom-page .site-footer-aware a {
    display: inline-flex;
    align-items: center;
    color: inherit;
}
.layout-wrap .section-content-right .bottom-page .site-footer-aware a:hover { color: var(--Main); }
.site-footer-aware svg { height: 21px; width: auto; display: block; }

.site-footer-18 {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    border: 1.5px solid currentColor;
    border-radius: 999px;
    padding: 1px 8px;
    line-height: 1.5;
}

@media (max-width: 700px) {
    .site-footer-links { gap: 6px 16px; }
    .site-footer-aware svg { height: 18px; }
}

/* ── The prose pages: About, FAQ, Terms, Privacy, Subscriptions, Guide ──
   Long-form reading, which the rest of the site does not do anywhere else, so
   these want a measure and some rhythm rather than the dense card layout. */
/* px, not rem: the theme's text-size control changes the root font size, so a
   rem max-width shrank this card to about 660px at the smaller settings.

   Centred, which it was not. The card was capped at a width and then left
   sitting against the left edge of its grid area, so on a 1920 screen it ran
   295-1359 inside a column running 295-1877 and the right third of the page
   was empty. */
.cp-page {
    max-width: 980px;
    margin-inline: auto;
}
.cp-lead { font-size: 15px; line-height: 1.7; }
.cp-section { margin-top: 26px; }
.cp-section:first-of-type { margin-top: 20px; }
.cp-heading {
    font-size: 17px;
    font-weight: 700;
    color: var(--Heading);
    margin: 0 0 8px;
}
.cp-body { line-height: 1.7; }
.cp-body p { margin: 0 0 10px; }
.cp-body p:last-child { margin-bottom: 0; }
.cp-body ul { margin: 0 0 10px 20px; }
.cp-body li { margin-bottom: 6px; }
/* Links in the prose take the accent colour - but a .tf-button IS a link
   here, and this rule was repainting its white label the same blue as its
   own background: rgb(34,117,252) text on rgb(18,102,240). The button is
   excluded so it keeps the styling every other button on the site has. */
.cp-body a:not(.tf-button) { color: var(--Main); }
.cp-foot {
    margin-top: 28px;
    padding-top: 16px;
    border-top: 1px solid var(--Stroke);
    color: var(--Note);
    font-size: 13px;
    line-height: 1.6;
}
.cp-foot a { color: var(--Main); }

.cp-price { font-size: 34px; font-weight: 800; color: var(--Heading); margin: 4px 0 12px; line-height: 1.1; }
.cp-price span { font-size: 15px; font-weight: 500; color: var(--Note); }
.cp-cta { margin-top: 14px; }

/* The guide is a reference, so its definitions read as a two-column list
   rather than as a data table. */
/* Six section tabs kept on one line push 596px off the side of a 390px phone,
   because .players-directory-tabs is a flex row that does not wrap. Each tab
   stays on its own line of text; the row wraps instead. */
.guide-tabs {
    margin-top: 14px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.guide-tabs .players-tab { white-space: nowrap; }

/* style.css sets table-layout: fixed on every table, which held these two
   columns at a width that cut the definitions off on the right. */
.guide-defs {
    table-layout: auto;
    width: 100%;
}
.guide-defs td { vertical-align: top; line-height: 1.6; white-space: normal; }
.guide-term { width: 190px; color: var(--Heading); }
@media (max-width: 700px) {
    .guide-defs td { display: block; }
    .guide-term { width: auto; padding-bottom: 2px; border-bottom: 0; }
    .guide-def { padding-top: 0; }
}

/* ── A full-width card stays full width between 768px and 1440px ──
   style.css guards its base reset with :not([style*="grid-column"]), so a card
   that asks for `grid-column: 1 / -1` inline keeps it. Its 1440px media query
   has no such guard:

       .tf-section-3 > div:nth-child(1) { grid-column: span 2/span 2 !important; }

   and an !important external rule beats a plain inline style. So on any laptop
   between 768 and 1440 the first card in a section was pinned to two of four
   columns - 702px of an available 1062 - whatever it asked for. Every one of
   the 74 inline grid-columns in this codebase is `1 / -1`, so restoring that
   intent is the whole fix. */
@media (max-width: 1440px) {
    .layout-wrap .tf-section-3 > div[style*="grid-column"],
    .layout-wrap .tf-section-4 > div[style*="grid-column"],
    .layout-wrap .tf-section-5 > div[style*="grid-column"] {
        grid-column: 1 / -1 !important;
    }
}

/* ── The filter bar sits on one line ──
   Fixtures and Tournaments share a control bar: view tabs and tour tabs on the
   left, then Year, Sort by and Filter pushed right. Those last three were
   sitting at three different heights - the sort list 6px below the others -
   because .players-directory-sort carries `margin-top: 12px`, written for the
   stacked layout it used to live in. Inside a flex row that margin is simply a
   nudge downwards.

   Everything in the bar is given the same line box so the labels, the selects
   and the sort links share a centre line. */
.siq-controlbar { row-gap: 10px; }

.siq-controlbar .players-directory-sort {
    margin-top: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0 5px;
    line-height: 30px;
}

.siq-controlbar-right > div,
.siq-controlbar-right .players-directory-sort {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 30px;
}

/* The "Year:" and "Filter:" captions were 12px against 14px controls and sat
   on their own baseline; same size and line box as everything beside them. */
.siq-controlbar label.body-text {
    margin: 0;
    font-size: 12px;
    line-height: 30px;
    color: var(--Note);
    white-space: nowrap;
}

/* The selects were 23px tall against 30px tabs, so they floated inside the
   row. Matching the tab height lines the whole bar up. */
.siq-controlbar .form-select {
    height: 30px;
    padding-top: 0;
    padding-bottom: 0;
    line-height: 28px;
}

@media (max-width: 767px) {
    /* Stacked on a phone, where a single line was never going to hold. */
    .siq-controlbar-right { width: 100%; margin-left: 0 !important; }
    .siq-controlbar .form-select { width: 100%; max-width: none; }
    .siq-controlbar-right > div { width: 100%; }
}

/* "plus VAT" belongs with the figure, not in a footnote - it is part of the
   price. Sized down so it reads as a qualifier rather than competing with it. */
.cp-price-vat { font-size: 15px; font-weight: 500; color: var(--Note); }

/* ── Titles by level: the titles sit under their own chip ──
   The chips said how many a player had won at each level and never which
   tournaments. Each one opens now.

   The chip is the <details>; the panel drops out of it, anchored to the chip
   on a desktop so the row of chips does not jump about as one opens, and
   in the flow on a phone where a floating panel has nowhere to float. */
.co-trophies { position: relative; }

details.co-trophy { position: relative; }
details.co-trophy > .co-trophy-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
}
details.co-trophy > .co-trophy-summary::-webkit-details-marker { display: none; }

/* A chip with titles behind it has to look like it opens. */
.co-trophy.has-titles { transition: border-color .15s ease; }
.co-trophy.has-titles:hover,
.co-trophy.has-titles[open] { border-color: var(--siq-accent); }

.co-trophy-panel {
    /* Hidden explicitly: Chrome renders a closed <details>'s content at full
       height here, so leaving it to the browser left every panel expanded. */
    display: none;
    margin-top: 10px;
    padding: 12px 14px;
    border: 1px solid var(--Stroke);
    border-radius: 12px;
    /* --bg-table-1 is #28354852 - an eight-digit hex whose last byte is 32%
       alpha - so the panel was see-through and the cards underneath read
       straight through the titles. The card's own background is opaque. */
    background: var(--White);
    max-height: 320px;
    overflow-y: auto;
}
.co-trophy[open] > .co-trophy-panel { display: block; }

.ptit-list { list-style: none; margin: 0; padding: 0; }
.ptit-item {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 10px;
    padding: 4px 0;
    line-height: 1.5;
    border-bottom: 1px solid var(--Stroke);
}
.ptit-item:last-child { border-bottom: 0; }
.ptit-year-inline {
    flex: 0 0 42px;
    font-weight: 800;
    color: var(--Heading);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
}
.ptit-name { font-weight: 600; color: var(--Body-Text); font-size: 13px; }
.ptit-beat { font-size: 12px; color: var(--Note); }
.ptit-score { font-variant-numeric: tabular-nums; }
.ptit-note { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--Note); }
.ptit-hint { margin: 0; font-size: 12px; color: var(--Note); }

/* Desktop: the panel hangs off its chip rather than pushing the row apart. */
@media (min-width: 768px) {
    .co-trophy[open] > .co-trophy-panel {
        position: absolute;
        top: calc(100% + 6px);
        left: 0;
        z-index: 20;
        width: max-content;
        min-width: 320px;
        max-width: min(560px, 90vw);
        box-shadow: 0 10px 28px rgba(20, 25, 38, .18);
    }
    /* A chip near the right-hand edge would otherwise open off the card. */
    .co-trophies > .co-trophy:nth-last-child(-n+2)[open] > .co-trophy-panel {
        left: auto;
        right: 0;
    }
}

/* ── ATP and WTA are a pill, everywhere ──
   .stadium-tour-badge got its pill shape from
   `.spotlight-card.spotlight-stadium .stadium-tour-badge`, so it only looked
   like one inside the dashboard spotlight. Anywhere else - the player profile
   among them - it inherited the background colour and nothing else: measured
   padding 0, border-radius 0, font-weight 400. A green highlight behind plain
   text rather than a badge.

   One definition, unscoped, shared by every inline tour label on the site.
   (The coloured strip across the top of a fixture or tournament card is
   deliberately not included: that is the card's own tour stripe, not a label
   sitting in a line of text.) */
.stadium-tour-badge,
.tour-badge,
.tourn-b-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #FFFFFF !important;
    white-space: nowrap;
    vertical-align: middle;
}

.stadium-tour-badge.tour-atp,
.tour-badge.tour-atp,
.tourn-b-badge.tour-atp { background: var(--tour-atp); }

.stadium-tour-badge.tour-wta,
.tour-badge.tour-wta,
.tourn-b-badge.tour-wta { background: var(--tour-wta); }

/* The dark theme softens the WTA purple so it does not glare; kept, and now
   applied to every one of these rather than just the spotlight's. */
.dark-theme .stadium-tour-badge.tour-wta,
.dark-theme .tour-badge.tour-wta,
.dark-theme .tourn-b-badge.tour-wta { background: rgba(147, 51, 234, .8); }

/* ── The gap under a card heading ──
   Measured across the player page, every card left 36px between its heading
   and the first thing under it: the heading's own bottom margin, plus the
   .mt-2/.mt-3 on whatever followed, stacked. Two separate decisions adding up
   to one that nobody made. Set once, as a single deliberate step. */
.wg-box .stadium-style-title {
    margin-bottom: 0;
    /* The theme gives an h2 a 79px line box. On a 22px italic heading that is
       nearly three times the type size, so most of the space under a card
       heading was empty leading inside the heading itself rather than a gap
       anybody had chosen. */
    line-height: 1.25;
}
/* .wg-box is a flex column with `gap: 24px`, which already separates the
   heading from the body. The .mt-2/.mt-3 on whatever followed was being added
   on top of that gap, not instead of it - 24 + 14, measured as 36px under
   every heading on the page. The gap alone is the spacing. */
.wg-box .stadium-style-title + * { margin-top: 0 !important; }

/* A heading sharing a row with controls keeps its own spacing - the gap there
   is between the row and the body, not between the heading and its neighbour. */
.wg-box .stadium-style-title.mb-0 + * { margin-top: 0 !important; }

/* Same double-spacing, one level further down: .wg-box is a flex column with a
   24px gap, and its direct children still carry the .mt-2 / .mt-3 they needed
   back when the card was a normal block. Those margins are added to the gap,
   not instead of it - the Rank History note and the chart under it sat 36px
   apart for that reason. The gap is the spacing; the margins go. */
.wg-box.stadium-style > .mt-1,
.wg-box.stadium-style > .mt-2,
.wg-box.stadium-style > .mt-3,
.wg-box.stadium-style > .mt-4 { margin-top: 0 !important; }

/* ── Sign Up ran off the side of a phone ──
   Logged out, the header's Log In / Sign Up pair sat 28px past the right edge
   of a 390px screen (measured right=418), so every page a visitor landed on
   scrolled sideways. The search box beside them takes all the width it is
   given and the buttons are flex-shrink: 0, so there was nothing left for
   them. Below 576px they lose some padding and the search box gives up the
   room instead. */
@media (max-width: 575px) {
    .header-dashboard .wrap .header-item.auth-links { gap: 4px; }
    .header-dashboard .wrap .header-item.auth-links .auth-link {
        padding: 0 10px;
        font-size: 12px;
    }
    /* The container was being squeezed to 34px while its two nowrap buttons
       needed ~78px, so they simply spilled out of it. It keeps its own width;
       the search box to its left is the thing that gives way. */
    .header-dashboard .wrap .header-item.auth-links {
        flex: 0 0 auto !important;
        width: auto !important;
        min-width: max-content;
    }
    .layout-wrap .section-content-right .header-dashboard .wrap .header-grid { flex-shrink: 0; }
    .header-dashboard .wrap > form,
    .header-dashboard .wrap .form-search,
    .header-dashboard .wrap .header-search { min-width: 0; flex: 1 1 auto; }
}

/* ── Trading Desk ──
   A member's own record of what they are trading. Built from the same tiles,
   pills and inputs the rest of the site uses rather than a private vocabulary,
   so it reads as part of the product. */
.td-intro { font-size: 14px; color: var(--Body-Text); margin: 0 0 4px; }
.td-note { font-size: 13px; color: #5B6270; margin: 10px 0 0; }
.dark-theme .td-note { color: var(--Note); }
.td-muted { color: #5B6270; }
.dark-theme .td-muted { color: var(--Note); }
.td-pos { color: var(--siq-good-solid); }
.td-neg { color: var(--siq-bad-solid); }
.dark-theme .td-pos { color: var(--siq-good); }
.dark-theme .td-neg { color: var(--siq-bad); }

.td-alert { font-size: 13px; border-radius: 10px; padding: 9px 12px; margin-bottom: 12px; border: 1px solid var(--Stroke); }
.td-alert.is-good { border-color: var(--siq-good-solid); }
.td-alert.is-bad { border-color: var(--siq-bad-solid); }

.td-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px; }
.td-tile { border: 1px solid var(--Stroke); border-radius: 12px; padding: 12px 14px; }
.td-tile-value { font-size: 22px; font-weight: 800; color: var(--Heading); }
.td-tile-label { font-size: 12px; color: #5B6270; }
.dark-theme .td-tile-label { color: var(--Note); }

.td-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; }
.td-tabs { display: flex; flex-wrap: wrap; gap: 6px; }
.td-tab {
    display: inline-block; padding: 5px 13px; border: 1px solid var(--Stroke); border-radius: 999px;
    font-size: 13px; font-weight: 600; text-decoration: none; color: var(--Body-Text);
}
.td-tab.is-on { background: var(--siq-accent-solid); border-color: var(--siq-accent-solid); color: #fff; }

.td-label {
    display: block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    color: #5B6270; margin: 14px 0 5px;
}
.dark-theme .td-label { color: var(--Note); }
.td-select, .td-input, .td-textarea {
    border: 1px solid var(--Stroke); border-radius: 8px; padding: 8px 10px;
    background: var(--White); color: var(--Body-Text); font-size: 14px; width: 100%; max-width: 100%;
}
.td-textarea { resize: vertical; }
/* One height for every control in an entry. The selects and the number
   inputs share the rule above, yet rendered ten pixels apart - a select is
   sized by its own text metrics and a number field by the theme's input
   rules, so "the same padding" did not mean the same box. Stated outright,
   and with !important because the theme's input rules are what it has to
   beat, and matching their specificity by hand is a rule that breaks the
   next time the theme changes. */
.td-entry .td-select, .td-entry .td-input {
    height: 44px !important; padding-top: 0 !important; padding-bottom: 0 !important;
    box-sizing: border-box; line-height: 42px;
}
.td-select:focus-visible, .td-input:focus-visible, .td-textarea:focus-visible {
    outline: 2px solid var(--siq-accent-solid); outline-offset: 1px;
}
.td-inline { display: flex; flex-wrap: wrap; gap: 8px; }
.td-inline > * { flex: 1 1 130px; min-width: 0; }

.td-add { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.td-add .td-select { flex: 1 1 320px; }
.td-add .tf-button { white-space: nowrap; }

.td-entry { border: 1px solid var(--Stroke); border-radius: 12px; padding: 14px 16px; margin-top: 14px; }
.td-entry-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; justify-content: space-between; }
.td-fixture { font-weight: 700; color: var(--Heading); font-size: 15px; }
/* The right-hand side of an entry's headline: the Betfair chip where the market
   is still open, the outcome, and the fold. */
.td-head-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.td-exchange { cursor: pointer; }
.td-fold { width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--Stroke); background: transparent;
           color: var(--Sub-Text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.td-fold:hover { border-color: var(--siq-accent); color: var(--Heading); }
.td-fold i { transition: transform .15s ease; }
.td-fold[aria-expanded="false"] i { transform: rotate(-90deg); }
.td-entry-head { cursor: pointer; }
.td-entry.is-folded { padding-bottom: 12px; }
.td-body { margin-top: 12px; }
.td-exit { margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--Stroke); }
.td-figure { font-size: 15px; font-weight: 700; color: var(--Heading); padding: 7px 0; }
.td-scenarios { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: baseline; margin-top: 10px; font-size: 13px; color: var(--Sub-Text); }
.td-scenario b { font-size: 14px; margin-left: 4px; }
.td-scenario-real { padding-left: 12px; border-left: 1px solid var(--Stroke); color: var(--Heading); }
.td-autosaved { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--siq-good); }

/* The "add to my desk" dialog. */
.desk-dialog { border: 1px solid var(--Stroke); border-radius: 14px; padding: 0; max-width: 460px; width: calc(100% - 32px);
               background: var(--White); color: var(--Body-Text); }
.desk-dialog::backdrop { background: rgba(10, 14, 24, .55); }
.desk-dialog-form { padding: 18px 20px 16px; }
.desk-dialog-title { font-size: 16px; font-weight: 700; color: var(--Heading); }
.desk-dialog-match { font-size: 13px; color: var(--Sub-Text); margin-top: 2px; }
.desk-dialog-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; margin-top: 14px; }
.desk-dialog-grid label, .desk-dialog-notes { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.desk-dialog-grid label > span, .desk-dialog-notes > span { font-size: 11px; font-weight: 700; letter-spacing: .04em;
                                                            text-transform: uppercase; color: var(--Note); }
.desk-dialog select, .desk-dialog input, .desk-dialog textarea { width: 100%; font-size: 13.5px; padding: 7px 9px;
        border: 1px solid var(--Stroke); border-radius: 8px; background: var(--Input-BG, transparent); color: inherit; }
.desk-dialog-signals { margin: 12px 0 0; padding: 10px 12px; border: 1px solid var(--Stroke); border-radius: 10px; }
.desk-dialog-signals legend { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
                              color: var(--Note); padding: 0 4px; }
.desk-dialog-signal-list { display: flex; flex-wrap: wrap; gap: 8px 14px; }
/* Signals the models fired on this match: first, ticked, and labelled. */
.desk-dialog-here { display: inline-block; margin-left: 4px; padding: 0 6px; border-radius: 999px; font-size: 10px; font-weight: 700; letter-spacing: .03em; color: var(--siq-good); border: 1px solid currentColor; vertical-align: middle; }
.desk-dialog-signal { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--Heading); cursor: pointer; }
.desk-dialog-signal input { width: auto; margin: 0; }
.desk-dialog-notes { margin-top: 10px; }
.desk-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.desk-dialog-cancel { background: transparent; border: 1px solid var(--Stroke); border-radius: 999px; padding: 7px 16px;
                      color: var(--Sub-Text); cursor: pointer; }
.desk-dialog-add { width: auto !important; padding: 0 20px !important; }
@media (max-width: 420px) { .desk-dialog-grid { grid-template-columns: 1fr; } }

/* The site's confirm box - same dress as the desk dialog, one question, two verbs. */
.siq-confirm { border: 1px solid var(--Stroke); border-radius: 14px; padding: 0; max-width: 420px; width: calc(100% - 32px);
               background: var(--White); color: var(--Body-Text); }
.siq-confirm::backdrop { background: rgba(10, 14, 24, .55); }
.siq-confirm-form { padding: 20px 20px 16px; }
.siq-confirm-text { font-size: 14.5px; line-height: 1.5; color: var(--Heading); margin: 0; }
.siq-confirm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.siq-confirm-cancel { background: transparent; border: 1px solid var(--Stroke); border-radius: 999px; padding: 7px 16px;
                      color: var(--Sub-Text); cursor: pointer; }
.siq-confirm-ok { width: auto !important; padding: 0 20px !important; }
.td-meta { font-size: 12.5px; color: #5B6270; margin-top: 2px; }
.dark-theme .td-meta { color: var(--Note); }

/* Trading desk: a fixture's countdown before it starts, and its live score
   while it is on. Both sit under .td-meta in the entry head. */
.td-countdown { font-size: 12.5px; color: var(--Sub-Text); margin-top: 4px; }
.td-countdown-clock { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--Heading); }

.td-live { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; font-size: 12.5px; }
.td-live-flag { font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
                color: #fff; background: #D8362A; border-radius: 4px; padding: 2px 6px; }
.td-live-score { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--Heading); }
.td-live-points { font-variant-numeric: tabular-nums; color: var(--Sub-Text); }
.td-live-serve { color: #3CB371; }
.td-live-stats { display: flex; flex-wrap: wrap; gap: 10px; color: var(--Sub-Text); }
.td-live-stat b { color: var(--Heading); font-variant-numeric: tabular-nums; }

/* Betfair market link on the fixture header. Deliberately quiet: it is a
   way out to the exchange, not a call to action. */

/* How often Telegram sends. Sits under the switch grid in notification
   settings, so it borrows that block's spacing rather than inventing its own. */
.notif-cadence { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--Stroke); }
.notif-cadence label { display: block; margin-bottom: 6px; font-size: 13.5px; }
.notif-cadence-select { width: 100%; max-width: 340px; padding: 7px 10px; border-radius: 8px;
                        border: 1px solid var(--Stroke); background: var(--Bg); color: var(--Main-Text); }
.notif-cadence-note { margin: 8px 0 0; font-size: 12px; line-height: 1.55; color: var(--Sub-Text); max-width: 560px; }


@media (max-width: 560px) { .td-live-stats { width: 100%; } }

.td-traders { font-weight: 600; }

.td-outcome {
    display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
    text-transform: uppercase; padding: 4px 10px; border-radius: 999px; color: #fff; white-space: nowrap;
}
.td-outcome.is-open { background: #5B6270; }
.td-outcome.is-won { background: var(--siq-good-solid); }
.td-outcome.is-lost { background: var(--siq-bad-solid); }
.td-outcome.is-void { background: #7A4E0E; }

.td-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; }
.td-checks { display: flex; flex-wrap: wrap; gap: 8px; }
.td-check {
    display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
    border: 1px solid var(--Stroke); border-radius: 999px; padding: 5px 12px; font-size: 13px;
}
.td-check input { margin: 0; }

.td-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 14px; }
.td-remove {
    background: none; border: 1px solid var(--Stroke); border-radius: 8px; padding: 8px 14px;
    font-size: 13px; font-weight: 600; color: var(--siq-bad-solid); cursor: pointer; white-space: nowrap;
}
.dark-theme .td-remove { color: var(--siq-bad); }

/* The count of other members on a fixture, wherever a fixture is listed. */
.td-trading-badge {
    display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700;
    padding: 2px 8px; border-radius: 999px; border: 1px solid var(--Stroke); color: #5B6270; white-space: nowrap;
}
.dark-theme .td-trading-badge { color: var(--Note); }

/* How many members have a fixture on their desk. A count only - never who. */
.fixtures-b-trading {
    display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
    padding: 2px 8px; border-radius: 999px;
    border: 1px solid var(--Stroke); color: #5B6270; white-space: nowrap;
}
.dark-theme .fixtures-b-trading { color: var(--Note); }

/* ── The signal overview ──
   What each signal IS, next to how it is traded. Shown on TradeSense and, as
   the Guide's own definition table, in the Guide. Built on the chips and
   labels the signals page already uses. */
.sig-overview-title {
    font-size: 15px; font-weight: 800; letter-spacing: .02em; color: var(--Heading);
    margin: 18px 0 0; text-transform: uppercase;
}
.sig-overview-title span { color: var(--siq-accent-solid); }
.dark-theme .sig-overview-title span { color: var(--siq-accent); }
/* Collapsible, same interaction as the notification settings panel just above
   it on this page - a hint that rotates its chevron rather than a second
   control language to learn. */
.sig-overview-toggle { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px;
    background: none; border: 0; padding: 0; margin-top: 18px; cursor: pointer; text-align: left; }
.sig-overview-toggle .sig-overview-title { margin-top: 0; }
.sig-overview-hint { font-size: 13px; font-weight: 600; color: var(--Note); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.sig-overview-hint i { transition: transform .2s ease; }
.sig-overview-box.is-open .sig-overview-hint i { transform: rotate(180deg); }
.sig-overview { display: grid; gap: 12px; margin-top: 14px; }
.sig-overview-row { border: 1px solid var(--Stroke); border-radius: 12px; padding: 12px 14px; }
.sig-overview-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sig-overview-summary { margin: 10px 0 0; font-size: 14px; line-height: 1.55; color: var(--Body-Text); }
.sig-overview-trade { margin: 6px 0 0; font-size: 13px; line-height: 1.55; color: #5B6270; }
.dark-theme .sig-overview-trade { color: var(--Note); }
.sig-overview-type { margin: 0 0 6px; font-size: 13px; }
/* The instruction is a <code>, which inherits a pale background from the
   theme - in dark mode that left white text on near-white, at 1.09. Both the
   ink and the fill are stated here so neither theme can inherit its way into
   an unreadable pair. */
.guide-def .sig-overview-type code,
.sig-overview-type code {
    font-size: 12px;
    color: #1F2430;
    background: #EEF1F6;
    padding: 1px 6px;
    border-radius: 5px;
}
.dark-theme .guide-def .sig-overview-type code,
.dark-theme .sig-overview-type code { color: #E8ECF3; background: #2B3648; }

@media (max-width: 575.98px) {
    /* The instruction is the long part, so it gets its own line rather than
       squeezing the chip and the type into nothing. */
    .sig-overview-head { gap: 6px; }
    .sig-overview-head .sig-howto-instruction { flex: 1 0 100%; }
}


/* Links inside prose. The theme's anchor ink measures 4.18-4.49 on a white
   card - a hair under - and prose is exactly where a link has to be readable. */
.body-text a:not([class]),
.cp-body a:not([class]) { color: var(--siq-accent-solid); }
.dark-theme .body-text a:not([class]),
.dark-theme .cp-body a:not([class]) { color: var(--siq-accent); }

/* The accent word in a card title, and links in a page's closing note, both
   measure just under AA as ink on a white card (4.49 and 4.18). The solid
   variant is the same hue a step darker and clears it; dark mode keeps the
   lighter one, which is what reads there. Scoped rather than changing the
   shared token, which is a site-wide decision still open. */
.stadium-style-title span { color: var(--siq-accent-solid); }
.dark-theme .stadium-style-title span { color: var(--siq-accent); }
.cp-foot a:not([class]) { color: var(--siq-accent-solid); }
.dark-theme .cp-foot a:not([class]) { color: var(--siq-accent); }

/* ── Add to my trading desk ──
   The same control in three places. It has to read as an ACTION, not as the
   quiet count beside it - an outline pill in the site's grey disappeared into
   the card, which is what it was doing before. Filled and in the brand accent
   when it is an invitation, filled green once the match is on the desk. */
.fixtures-b-desk,
.live-match-desk {
    display: inline-block; font-size: 10.5px; font-weight: 800; letter-spacing: .04em;
    text-transform: uppercase; padding: 3px 10px; border-radius: 999px;
    text-decoration: none; white-space: nowrap; cursor: pointer;
    background: var(--siq-accent-solid); border: 1px solid var(--siq-accent-solid); color: #fff;
}
.fixtures-b-desk:hover,
.live-match-desk:hover { background: #0B5FB8; border-color: #0B5FB8; color: #fff; }
.fixtures-b-desk.is-on,
.live-match-desk.is-on {
    background: var(--siq-good-solid); border-color: var(--siq-good-solid); color: #fff;
}
.fixtures-b-desk.is-busy,
.live-match-desk.is-busy { opacity: .55; pointer-events: none; }

.live-match-desk { margin-left: auto; }

/* The fixture page's own control: the card's top-right corner. */
.fixture-header-card { position: relative; }
.fixture-desk-wrap {
    position: absolute; top: 14px; right: 16px; z-index: 2;
    display: flex; align-items: center; gap: 10px;
}
.fixture-desk-form { margin: 0; }
.fixture-desk-count { font-size: 12px; font-weight: 700; color: var(--Note); white-space: nowrap; }
.fixture-desk-btn {
    /* .tf-button goes full width in most contexts; this one is a corner chip. */
    width: auto; padding: 7px 16px; border-radius: 999px;
    font-size: 12px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
    white-space: nowrap; cursor: pointer;
    background: var(--siq-accent-solid); border: 1px solid var(--siq-accent-solid); color: #fff;
}
.fixture-desk-btn:hover { background: #0B5FB8; border-color: #0B5FB8; }
.fixture-desk-btn.is-on { background: var(--siq-good-solid); border-color: var(--siq-good-solid); }
.fixture-desk-btn:focus-visible,
.fixtures-b-desk:focus-visible,
.live-match-desk:focus-visible { outline: 2px solid var(--Heading); outline-offset: 2px; }

@media (max-width: 575.98px) {
    /* The corner is tight on a phone; the control drops under the meta row. */
    .fixture-desk-wrap { position: static; margin-top: 10px; }
}

/* ── A last pass of near-miss inks on the fixtures page ──
   All three are the same story: a colour picked to read on a dark card, used
   as ink on a white one. #f0ad4e is 1.95 against white; the accent is 4.49,
   just under. Dark mode keeps the lighter values, which is where they work. */
.fixture-vs-favorite-badge { color: #8A5A00; }
.dark-theme .fixture-vs-favorite-badge { color: #f0ad4e; }

.fixtures-b-siq-value { color: var(--siq-accent-solid); }
.dark-theme .fixtures-b-siq-value { color: var(--siq-accent); }

.players-directory-sort a.active { color: var(--siq-accent-solid); }
.dark-theme .players-directory-sort a.active { color: var(--siq-accent); }

/* The same control on the Match Filter table and on TradeSense rows. Both put
   it inside something already clickable, so it inherits the pill's look and
   the shared script stops the row's own navigation. */
.mf-desk, .sig-desk {
    display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .04em;
    text-transform: uppercase; padding: 2px 9px; border-radius: 999px;
    white-space: nowrap; cursor: pointer; vertical-align: middle;
    background: var(--siq-accent-solid); border: 1px solid var(--siq-accent-solid); color: #fff;
}
.mf-desk { margin-left: 8px; }
.sig-desk { margin-left: 8px; }
.mf-desk:hover, .sig-desk:hover { background: #0B5FB8; border-color: #0B5FB8; }
.mf-desk.is-on, .sig-desk.is-on { background: var(--siq-good-solid); border-color: var(--siq-good-solid); }
.mf-desk.is-busy, .sig-desk.is-busy { opacity: .55; pointer-events: none; }
.mf-desk:focus-visible, .sig-desk:focus-visible { outline: 2px solid var(--Heading); outline-offset: 2px; }

/* The desk entry's title links back to the match. It is a heading first and a
   link second, so it takes the heading's colour and only underlines on hover. */
.td-fixture-link { color: inherit; text-decoration: none; }
.td-fixture-link:hover, .td-fixture-link:focus-visible { text-decoration: underline; }
.td-fixture-link:focus-visible { outline: 2px solid var(--siq-accent); outline-offset: 2px; }

/* The public trader count, wherever a fixture is listed. Quiet next to the
   action beside it - it is information, not a button. */
.sig-trading, .mf-trading, .live-match-trading {
    display: inline-block; font-size: 10.5px; font-weight: 700;
    color: var(--Note); white-space: nowrap; margin-left: 8px;
}
.live-match-trading { margin-left: auto; margin-right: 6px; }

/* ── One hover, everywhere ──
   Three different treatments had grown up: a fill in --hv-item (the theme's
   own hover token, used by style.css for its tables), a fill in --Stroke
   (Next Fixtures, the players list) and a border colour change (signal rows,
   notifications, order of play). Two of them sat on the dashboard together.

   One answer for every row and card a member can click: the themed fill, plus
   the accent on the border so it still reads on a bordered row where a fill
   alone is easy to miss. --hv-item is defined for both themes, so this does
   not need a dark-mode twin. */
.fixtures-a-item:hover,
.fixtures-b-card:hover,
.players-directory-row:hover,
.sig-row:hover,
.notif-row:hover,
.tourn-hub-oop-row:hover,
.live-match-card:hover,
.dash-live-card:hover,
.mf-table tbody tr[onclick]:hover,
.td-entry:hover {
    background: var(--hv-item);
    border-color: var(--siq-accent);
}

/* The same easing on all of them, so one does not snap while its neighbour
   fades. */
.fixtures-a-item,
.fixtures-b-card,
.players-directory-row,
.sig-row,
.notif-row,
.tourn-hub-oop-row,
.live-match-card,
.dash-live-card,
.mf-table tbody tr[onclick],
.td-entry {
    transition: background-color .15s ease, border-color .15s ease;
}

/* ── Dashboard badges carry white text, so they take the solid fills ──
   --siq-good, --siq-bad and --tour-wta are tuned to be read AS colour on a
   card; under white text they measured 2.43 to 3.30. The solid pair is the
   same hue dark enough to carry it, in both themes. */
.spotlight-card.spotlight-stadium .stadium-form-badge.form-win { background: var(--siq-good-solid); }
.spotlight-card.spotlight-stadium .stadium-form-badge.form-loss { background: var(--siq-bad-solid); }
.stadium-tour-badge.tour-wta,
.dark-theme .stadium-tour-badge.tour-wta { background: #6B21A8; }

/* The spotlight card paints its tour badge a fixed #16A34A, which carries
   white at only 3.30. Same green, dark enough to hold the text. (That it is
   green rather than the tour colour is the card's own design, left alone.) */
.spotlight-card.spotlight-stadium .stadium-tour-badge { background: var(--siq-good-solid); }

/* ── Form controls follow the theme ──
   style.css gives every input, select and textarea `color: var(--Body-Text)`
   and no background at all, so in dark mode they were white text on the
   browser's own white. The dropdown list was worse: an <option> is painted by
   the platform and inherits nothing, so it came out white on #F2F7FB -
   invisible.

   Bare element selectors, so any component with a class of its own still wins;
   this only supplies the background that was never set. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
select,
textarea {
    background-color: var(--White);
    color: var(--Body-Text);
}

/* The dropdown list itself. Stated on both sides because the platform draws
   this menu outside the page and inherits neither. */
select option,
select optgroup {
    background-color: var(--White);
    color: var(--Body-Text);
}

/* Bootstrap's own form classes set a white background of their own, which a
   bare element selector cannot reach. Same treatment, so a select on Fixtures
   looks like a select on the Trading Desk. */
.form-control,
.form-select,
.form-control:focus,
.form-select:focus {
    background-color: var(--White);
    color: var(--Body-Text);
    border-color: var(--Stroke);
}
.form-select option,
.form-control option { background-color: var(--White); color: var(--Body-Text); }

/* Placeholder text is secondary, not invisible. */
input::placeholder,
textarea::placeholder { color: var(--Note); opacity: 1; }

/* Exit question - shown once, only while a subscription is winding down. */
.acc-exit-lead { color: var(--Sub-Text); margin: 0 0 16px; line-height: 1.6; }
.acc-exit-reasons { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px; margin-bottom: 16px; }
.acc-exit-reason { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-radius: 12px;
    border: 1px solid var(--Stroke); background: var(--Input-BG, transparent); color: var(--Main-Text);
    cursor: pointer; transition: border-color .15s ease, background .15s ease; }
.acc-exit-reason:hover { border-color: var(--Main); }
.acc-exit-reason input { accent-color: var(--Main); flex: 0 0 auto; width: 16px; height: 16px; margin: 0; }
.acc-exit-reason:has(input:checked) { border-color: var(--Main); background: rgba(18, 102, 240, .08); }
.acc-exit-label { display: block; font-size: 13px; color: var(--Sub-Text); margin-bottom: 6px; }
.acc-exit-detail { width: 100%; padding: 12px 14px; border-radius: 12px; border: 1px solid var(--Stroke);
    background: var(--Input-BG, transparent); color: var(--Main-Text); font: inherit; resize: vertical; min-height: 76px; }
.acc-exit-detail:focus { outline: none; border-color: var(--Main); }
.acc-exit-actions { display: flex; align-items: center; gap: 16px; margin-top: 16px; flex-wrap: wrap; }
.acc-exit-actions .tf-button { width: auto; padding: 0 28px; }
.acc-exit-skip { background: none; border: 0; padding: 0; font: inherit; color: var(--Sub-Text);
    text-decoration: underline; cursor: pointer; }
.acc-exit-skip:hover { color: var(--Main-Text); }

/* Live Scores: the country flag and the favourite badge, which the fixtures
   list has always had and this page did not. */
/* Always 20px wide, flag or no flag. The box lives inside the name cell, so
   a card whose players we have no country for would otherwise start its names
   20px to the left of the card above it - and the set and points columns with
   them. See flagHtml() in live-scores.php. */
.live-scoreboard .live-flag { flex: 0 0 auto; width: 20px; margin-right: 2px; }
.live-scoreboard .live-flag.is-empty { height: 14px; }
.live-scoreboard .live-flag img { display: block; width: 20px; height: 14px; object-fit: cover; border-radius: 2px; }
.live-fav-badge { display: inline-block; flex: 0 0 auto; font-size: 9px; font-weight: 800;
    letter-spacing: .04em; padding: 2px 5px; border-radius: 4px; white-space: nowrap;
    color: #fff; background: var(--siq-accent-solid); }

/* ── Surfaces and tours, one rule each ───────────────────────────────────
   Both were defined in several stylesheets with different colours and
   different shapes, so the same thing looked different depending which page
   you were on - ATP was green on a tournament card and blue on a fixture,
   and Indoor had no colour at all on the tournaments page. These come last
   in the file deliberately, so they settle it. */

.tourn-b-bar.surface-hard,   .fixtures-a-item.surface-hard .fixtures-a-bar,   .fixtures-b-card.surface-hard .fixtures-b-bar,
.elo-surface.surface-hard    { background: var(--surface-hard); }
.tourn-b-bar.surface-ihard,  .fixtures-a-item.surface-ihard .fixtures-a-bar,  .fixtures-b-card.surface-ihard .fixtures-b-bar,
.elo-surface.surface-ihard   { background: var(--surface-ihard); }
.tourn-b-bar.surface-clay,   .fixtures-a-item.surface-clay .fixtures-a-bar,   .fixtures-b-card.surface-clay .fixtures-b-bar,
.elo-surface.surface-clay    { background: var(--surface-clay); }
.tourn-b-bar.surface-grass,  .fixtures-a-item.surface-grass .fixtures-a-bar,  .fixtures-b-card.surface-grass .fixtures-b-bar,
.elo-surface.surface-grass   { background: var(--surface-grass); }
.tourn-b-bar.surface-carpet, .fixtures-a-item.surface-carpet .fixtures-a-bar, .fixtures-b-card.surface-carpet .fixtures-b-bar,
.elo-surface.surface-carpet  { background: var(--surface-carpet); }
.tourn-b-bar.surface-unknown, .fixtures-a-item.surface-unknown .fixtures-a-bar, .fixtures-b-card.surface-unknown .fixtures-b-bar,
.elo-surface.surface-unknown { background: var(--surface-unknown); }

/* The Elo panel uses a left edge rather than a bar, so it takes the colour
   there instead - same token, so the two can never disagree. */
.elo-surface.surface-hard, .elo-surface.surface-ihard, .elo-surface.surface-clay,
.elo-surface.surface-grass, .elo-surface.surface-carpet, .elo-surface.surface-unknown {
    background: none;
}
.elo-surface.surface-hard   { border-left-color: var(--surface-hard); }
.elo-surface.surface-ihard  { border-left-color: var(--surface-ihard); }
.elo-surface.surface-clay   { border-left-color: var(--surface-clay); }
.elo-surface.surface-grass  { border-left-color: var(--surface-grass); }
.elo-surface.surface-carpet { border-left-color: var(--surface-carpet); }
.elo-surface.surface-unknown{ border-left-color: var(--surface-unknown); }

/* The little dots, from the same tokens. */
.surface-dot-hard   { background: var(--surface-hard); }
.surface-dot-ihard  { background: var(--surface-ihard); }
.surface-dot-clay   { background: var(--surface-clay); }
.surface-dot-grass  { background: var(--surface-grass); }
.surface-dot-carpet { background: var(--surface-carpet); }
.surface-dot-unknown{ background: var(--surface-unknown); }

/* ── Tour pills ──
   One shape and one colour wherever ATP or WTA is named. The green ATP badge
   on tournament cards was the odd one out and is gone. */
/* The shape is forced, like the colour. Three older rules across two
   stylesheets set their own radius, size and padding, and they are more
   specific than a bare class selector - which is how the same pill came to
   be 4px on a fixture, 6px on a stadium card and 8px on a tournament. */
.tour-badge, .stadium-tour-badge, .tourn-b-badge, .tf-section-3 .tour-badge {
    display: inline-block !important;
    font-size: 10px !important;
    font-weight: 800 !important;
    letter-spacing: .05em !important;
    text-transform: uppercase !important;
    line-height: 1.6 !important;
    padding: 2px 7px !important;
    border-radius: 4px !important;
    color: #fff !important;
    vertical-align: 1px;
}
.tour-badge.tour-atp, .stadium-tour-badge.tour-atp, .tourn-b-badge.tour-atp,
.tf-section-3 .tour-badge.tour-atp,
.dark-theme .tour-badge.tour-atp, .dark-theme .stadium-tour-badge.tour-atp,
.dark-theme .tourn-b-badge.tour-atp {
    background: var(--tour-atp) !important;
    color: #fff !important;
}
.tour-badge.tour-wta, .stadium-tour-badge.tour-wta, .tourn-b-badge.tour-wta,
.tf-section-3 .tour-badge.tour-wta,
.dark-theme .tour-badge.tour-wta, .dark-theme .stadium-tour-badge.tour-wta,
.dark-theme .tourn-b-badge.tour-wta {
    background: var(--tour-wta) !important;
    color: #fff !important;
}

/* The tour band down the side of a fixture card is the same two colours as
   the pill, at full strength. The dark-theme rule dimmed WTA to 80% opacity
   so purple and blue no longer matched each other in weight. */
.fixtures-b-card.tour-atp .fixtures-b-tour-band,
.dark-theme .fixtures-b-card.tour-atp .fixtures-b-tour-band { background: var(--tour-atp) !important; }
.fixtures-b-card.tour-wta .fixtures-b-tour-band,
.dark-theme .fixtures-b-card.tour-wta .fixtures-b-tour-band { background: var(--tour-wta) !important; }

/* The running total's window picker. */
.sig-rt-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.sig-rt-windows { display: flex; gap: 6px; flex-wrap: wrap; }
.sig-rt-win { display: inline-block; font-size: 12px; font-weight: 600; padding: 4px 11px; border-radius: 999px;
    border: 1px solid var(--Stroke); color: var(--Sub-Text); text-decoration: none; white-space: nowrap; }
.sig-rt-win:hover { border-color: var(--Main); color: var(--Main-Text); }
.sig-rt-win.is-on { background: var(--siq-accent-solid); border-color: var(--siq-accent-solid); color: #fff; }

/* Password strength, on signup and reset. */
.pw-meter { margin-top: 8px; }
.pw-meter-bar { height: 5px; border-radius: 999px; background: var(--Stroke); overflow: hidden; }
.pw-meter-bar span { display: block; height: 100%; width: 0; border-radius: 999px; transition: width .2s ease, background .2s ease; }
.pw-meter-text { font-size: 11.5px; margin-top: 4px; color: var(--Sub-Text); }
.pw-meter.s0 .pw-meter-bar span { width: 8%;   background: #C4282C; }
.pw-meter.s1 .pw-meter-bar span { width: 30%;  background: #C4282C; }
.pw-meter.s2 .pw-meter-bar span { width: 55%;  background: #9A5A00; }
.pw-meter.s3 .pw-meter-bar span { width: 80%;  background: #23794E; }
.pw-meter.s4 .pw-meter-bar span { width: 100%; background: #23794E; }
.pw-hint { font-size: 12px; color: var(--Sub-Text); margin-top: 6px; line-height: 1.5; }
.acc-device { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.acc-device-out { margin: 0 0 0 auto; }
.acc-device-signout { background: none; border: 0; padding: 0; font: inherit; font-size: 12px;
    color: var(--Sub-Text); text-decoration: underline; cursor: pointer; }
.acc-device-signout:hover { color: var(--siq-bad-solid); }

/* Closing an account: findable, but never the first thing you meet. */
.acc-del-list { margin: 6px 0 14px 18px; padding: 0; font-size: 13px; color: var(--Sub-Text); line-height: 1.7; }
.acc-del-list.is-kept { color: var(--Note); }
.acc-del-details { border-top: 1px solid var(--Stroke); padding-top: 12px; margin-top: 4px; }
.acc-del-details summary { cursor: pointer; font-size: 13px; color: var(--Sub-Text); }
.acc-del-details summary:hover { color: var(--Main-Text); }
.acc-del-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 10px; }
.acc-del-input { max-width: 170px; padding: 0 14px; height: 40px; border-radius: 10px;
    border: 1px solid var(--Stroke); background: var(--Input-BG, transparent); color: var(--Main-Text);
    letter-spacing: .12em; text-transform: uppercase; }
.acc-del-btn { height: 40px; padding: 0 20px; border-radius: 10px; border: 1px solid var(--siq-bad-solid);
    background: none; color: var(--siq-bad-solid); font-weight: 700; font-size: 13px; cursor: pointer; }
.acc-del-btn:hover { background: var(--siq-bad-solid); color: #fff; }

/* Live Scores keyboard shortcuts. */
/*
 * --Sub-Text and --Main-Text are named nowhere in this site's palette, so
 * every declaration below that used them was invalid at computed-value time
 * and the property silently fell back to the inherited value - which, with
 * nothing setting a colour on the card or on body in either theme, is
 * Bootstrap's own #212529. On a kbd that landed #212529 ink on the #212529
 * background Bootstrap also hardcodes, and on the bar around it #212529 ink
 * on a #1E293B dark card. Hence one report of unreadable keys and another of
 * unreadable labels: they are the same fault in two places. Both halves of
 * every pair are now tokens that exist in both themes.
 */
.live-keys-toggle { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--Stroke);
    background: none; color: var(--Note); font-weight: 700; font-size: 12px; cursor: pointer; }
.live-keys-toggle:hover { border-color: var(--Main); color: var(--Heading); }
.live-keys { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; font-size: 12px;
    color: var(--Note); border: 1px solid var(--Stroke); border-radius: 10px; padding: 8px 12px; }
.live-keys kbd { display: inline-block; min-width: 18px; text-align: center; padding: 1px 5px;
    border: 1px solid var(--Stroke); border-bottom-width: 2px; border-radius: 4px;
    font-family: ui-monospace, monospace; font-size: 11px;
    background: var(--Input); color: var(--Heading); margin-right: 4px; }

/* ── Telegram, in the notification settings panel ───────────────────────────
   Set off from the switch grid above it with a border and a tint rather than
   its own card: it is another way to receive the same alerts, not a separate
   setting, and a second card would read as one. */
.notif-tg { margin-top: 18px; padding: 16px 18px; border: 1px solid var(--Stroke); border-radius: 12px; }
.notif-tg-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.notif-tg-title { font-size: 15px; font-weight: 700; color: var(--Heading); }
.notif-tg-title span { color: var(--siq-accent, var(--Heading)); }
.notif-tg-state { font-size: 12px; font-weight: 600; color: var(--Note); }
.notif-tg[data-state="on"] .notif-tg-state { color: var(--siq-good); }
.notif-tg[data-state="paused"] .notif-tg-state { color: var(--siq-warn, var(--Note)); }
.notif-tg-text { font-size: 13px; line-height: 1.6; color: var(--Sub-Text); margin: 8px 0 0; }
.notif-tg-text code { font-size: 12.5px; padding: 1px 5px; border-radius: 4px;
                      background: var(--Input-BG, rgba(0,0,0,.05)); }
.notif-tg-why { font-size: 12px; color: var(--Note); margin: 6px 0 0; }
.notif-tg-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 14px; }
.notif-tg-btn { width: auto !important; padding: 0 22px !important; }

/* Quiet hours. Deliberately the same box as the Telegram block above it -
   both are settings that belong to the whole panel rather than to one alert,
   so they read as a pair rather than as two unrelated ideas. */
.notif-quiet { margin-top: 18px; padding: 16px 18px; border: 1px solid var(--Stroke); border-radius: 12px; }
.notif-quiet-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.notif-quiet-title { font-size: 15px; font-weight: 700; color: var(--Heading); }
.notif-quiet-state { font-size: 12px; font-weight: 600; color: var(--Note); }
.notif-quiet[data-on="1"] .notif-quiet-state { color: var(--siq-good); }
.notif-quiet-text { font-size: 13px; line-height: 1.6; color: var(--Sub-Text); margin: 8px 0 0; }
.notif-quiet-row { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.notif-quiet-field { display: flex; flex-direction: column; gap: 5px; }
.notif-quiet-field > span { font-size: 11px; font-weight: 700; letter-spacing: .04em;
                            text-transform: uppercase; color: var(--Note); }
/* Its own width rather than the form default: a full-width select for "23:00"
   is four characters in a field the width of the card. */
.notif-quiet-field select { width: auto; min-width: 104px; padding: 6px 24px 6px 10px; font-size: 13px; }
.notif-quiet-hint { font-size: 12px; color: var(--Note); padding-bottom: 8px; }

/* "Betfair only" on the fixtures toolbar. Sits with the sort links rather
   than in the Filter dropdown, so it is styled to read as one of them until
   it is on, at which point it has to look switched ON rather than merely
   selected - it removes cards from the page, and a control that hides things
   should not be easy to leave on by accident. */
.fixtures-bf-only { display: inline-flex; align-items: center; }
.fixtures-bf-toggle {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 4px 12px; border: 1px solid var(--Stroke); border-radius: 999px;
    font-size: 12px; font-weight: 600; color: var(--Sub-Text); text-decoration: none;
    white-space: nowrap;
}
.fixtures-bf-toggle:hover { border-color: var(--siq-accent); color: var(--Heading); }
.fixtures-bf-dot {
    width: 8px; height: 8px; border-radius: 50%;
    border: 1px solid currentColor; background: transparent;
}
.fixtures-bf-toggle.is-on {
    background: var(--siq-accent-solid); border-color: var(--siq-accent-solid); color: #fff;
}
.fixtures-bf-toggle.is-on .fixtures-bf-dot { background: currentColor; border-color: currentColor; }
.fixtures-bf-toggle.is-on:hover { color: #fff; }
.notif-tg-ghost { background: none; border: 1px solid var(--Stroke); border-radius: 8px;
                  padding: 8px 16px; font-size: 13px; font-weight: 600; color: var(--Main-Text); cursor: pointer; }
.notif-tg-ghost:hover { border-color: var(--Main-Text); }
.notif-tg-ghost.is-danger { color: var(--siq-bad); }
.notif-tg-ghost.is-danger:hover { border-color: var(--siq-bad); }
.notif-tg-ghost:disabled { opacity: .55; cursor: default; }
.notif-tg-msg { font-size: 12.5px; margin-top: 10px; min-height: 1em; color: var(--Note); }
.notif-tg-msg.is-ok { color: var(--siq-good); }
.notif-tg-msg.is-error { color: var(--siq-bad); }

/* ── Notification settings: one column per channel ──────────────────────────
   The columns are narrow and fixed so the three switch columns line up with
   their headings whatever the alert names do, and the name column takes the
   rest. Below 640px the table scrolls inside .table-responsive rather than
   squeezing the switches to nothing. */
.notif-grid-ch th.notif-ch, .notif-grid-ch td.notif-ch { width: 92px; text-align: center; }
.notif-grid-ch th.notif-ch { vertical-align: middle; }
.notif-ch-name { display: block; font-size: 12.5px; font-weight: 700; color: var(--Heading); }
.notif-ch-note { display: block; font-size: 10.5px; font-weight: 500; color: var(--Note); margin-top: 2px; }
.notif-grid-ch th.notif-ch[data-state="on"] .notif-ch-note { color: var(--siq-good); }
.notif-grid-ch th.notif-ch[data-state="off"] .notif-ch-note,
.notif-grid-ch th.notif-ch[data-state="paused"] .notif-ch-note { color: var(--siq-warn, var(--Note)); }
.notif-grid-ch th.notif-ch[data-state="unavailable"] { opacity: .55; }
.notif-grid-when { display: block; font-size: 11.5px; font-weight: 500; color: var(--Note); margin-top: 1px; }
.notif-grid-ch .notif-grid-name { min-width: 190px; }
.notif-grid-ch input:disabled + span { opacity: .4; cursor: not-allowed; }
.notif-grid-foot { font-size: 12px; color: var(--Note); line-height: 1.6; margin: 12px 0 0; }
.notif-push .notif-tg-title span { color: var(--siq-accent, var(--Heading)); }
@media (max-width: 640px) {
    .notif-grid-ch th.notif-ch, .notif-grid-ch td.notif-ch { width: 76px; }
    .notif-grid-ch .notif-grid-name { min-width: 150px; }
}

/* ── Tournament bracket: flags, if the library's stylesheet does not arrive ──
   brackets-viewer sizes participant flags in its own CSS, loaded from a CDN,
   and that single rule is the only thing holding them down:

       .brackets-viewer .participant .name > img { width: var(--participant-image-size) }

   With it missing the bracket still renders, so nothing looks broken - but every
   flag falls back to the natural size of the SVG, which is about a thousand
   pixels wide. That is exactly what happened when the CDN was blocked, and a
   CDN being unreachable would do it again.

   So the same size is declared locally as a floor. It carries no !important and
   sits before nothing, so the library's own rule still wins when it loads; this
   only decides what happens when it does not. */
.brackets-viewer .participant .name > img {
    width: var(--participant-image-size, 18px);
    height: var(--participant-image-size, 18px);
    object-fit: cover;
    vertical-align: middle;
}

/* An admin's own email, hidden for the camera (see App\Helpers\PrivacyMask).
   The reveal reads as a quiet aside, not a call to action - it is a convenience
   for whoever is signed in, not something anybody needs to be pointed at. */
.acc-email-masked { letter-spacing: .04em; }
.acc-email-reveal { background: none; border: 0; padding: 0 0 0 6px; font: inherit;
                    font-size: 11.5px; color: var(--Note); text-decoration: underline;
                    cursor: pointer; }
.acc-email-reveal:hover { color: var(--Main-Text); }

/* ── Choosing a profile photo ───────────────────────────────────────────────
   The preview is deliberately the same shape and size as the avatar it will
   become, so it reads as "this is the result" rather than as a thumbnail of the
   file. Circular for the same reason - that is how it is displayed everywhere
   else on the site. */
.acc-avatar-prep { display: flex; gap: 16px; align-items: center; flex-wrap: wrap;
                   margin-top: 16px; padding: 14px 16px;
                   border: 1px solid var(--Stroke); border-radius: 12px; }
.acc-avatar-prep img { width: 72px; height: 72px; border-radius: 50%; object-fit: cover;
                       border: 1px solid var(--Stroke); background: var(--Input-BG, #f3f4f6); flex-shrink: 0; }
.acc-avatar-prep-main { flex: 1 1 220px; min-width: 0; }
.acc-avatar-prep-title { font-size: 14px; font-weight: 700; color: var(--Heading); margin-bottom: 2px; }
.acc-avatar-prep-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.acc-avatar-save { width: auto !important; padding: 0 22px !important; }
.acc-avatar-save:disabled { opacity: .55; cursor: default; }

/* The controls under a live card's scoreboard: trader count, desk button and
   the Betfair market. On their own line so they stop crowding the meta row -
   the desk button in particular had no room there. Wraps rather than clips. */
/* One line, with no rule of its own. The rule sat directly under the
   scoreboard's own bottom border and the two together read as an empty row. */
.live-match-actions {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
    gap: 8px; margin-top: 6px;
}
/* The count pushes nothing around in here - the row lays itself out. */
.live-match-actions .live-match-trading { margin-left: 0; margin-right: 0; }

/* How much warning a trading-desk reminder gives. Sits under the switch grid,
   borrowing the Telegram cadence block's spacing rather than inventing its own. */
.notif-desk-lead { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--Stroke); }
.notif-desk-lead label { display: block; margin-bottom: 6px; font-size: 13.5px; }

/* The yearly option under the monthly one on the Pro page. Quieter than the
   main call to action: it is an alternative, not a second decision. */
.cp-plan-alt { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--Stroke); }
.cp-plan-alt-note { margin: 4px 0 10px; font-size: 13.5px; color: var(--Sub-Text); }
.tf-button-quiet {
    background: transparent; color: var(--Main-Text);
    border: 1px solid var(--Stroke);
}
.tf-button-quiet:hover { border-color: var(--siq-accent); color: var(--Heading); }

/* ── Changing plan on the account page ──────────────────────────────────
   Monthly <-> yearly. The block lives inside .acc-actions, which is a flex
   row of buttons, so it claims a full row of its own rather than trying to
   sit beside "Manage subscription" - what it has to say is several lines
   long and must not be squeezed into a column.

   Closed by default and opened by the member, because it is a decision, not
   an announcement: the summary is the question and everything inside it is
   the answer, spelled out before there is anything to press. */
.acc-plan-switch { flex: 1 1 100%; margin: 4px 0 0; }
.acc-actions > .acc-msg { flex: 1 1 100%; margin-bottom: 0; }

/* The disclosure line. Reads as a link rather than a button - pressing it
   costs nothing, and it should not look like the thing that spends money. */
.acc-plan-summary {
    display: inline-block; cursor: pointer; font-size: 13.5px; font-weight: 700;
    color: var(--siq-accent); text-decoration: underline; text-underline-offset: 3px;
    list-style: none;
}
.acc-plan-summary::-webkit-details-marker { display: none; }
.acc-plan-summary::before { content: '\203A'; display: inline-block; margin-right: 7px; transition: transform .15s ease; }
details[open] > .acc-plan-summary::before { transform: rotate(90deg); }
.acc-plan-summary:hover, .acc-plan-summary:focus-visible { color: var(--Heading); }
.acc-plan-summary:focus-visible { outline: 2px solid var(--siq-accent); outline-offset: 3px; }

/* What will happen. Boxed and set off from the rest of the card, so the
   amount and the dates are read rather than skimmed past. */
.acc-plan-switch .acc-plan-lead,
.acc-plan-switch .acc-plan-what,
.acc-plan-switch .acc-plan-form { margin-left: 18px; }
.acc-plan-lead { margin: 12px 0 8px; font-size: 13.5px; color: var(--Main-Text); }
.acc-plan-what {
    margin: 0 0 14px; padding: 12px 16px 12px 32px; border-radius: 10px;
    border: 1px solid var(--Stroke); border-left: 4px solid var(--siq-accent);
    font-size: 13.5px; line-height: 1.65; color: var(--Main-Text);
}
.acc-plan-what li + li { margin-top: 6px; }
.acc-plan-what strong { color: var(--Heading); }
.acc-plan-form { margin: 0; }
.acc-plan-form .tf-button { width: auto; padding: 0 22px; }

/* "including VAT" beside the plan in the summary list: the same quiet note
   the Pro page uses, because the two must not appear to disagree. */
.acc-plan-vat { font-size: 11.5px; color: var(--Note); white-space: nowrap; }

@media (max-width: 480px) {
    .acc-plan-switch .acc-plan-lead,
    .acc-plan-switch .acc-plan-what,
    .acc-plan-switch .acc-plan-form { margin-left: 0; }
    .acc-plan-form .tf-button { width: 100%; }
}

/* Two ways on from the payment-successful page. Side by side on a desktop,
   stacked on a phone, both full width so neither reads as the lesser one. */
.cs-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.cs-actions .tf-button { flex: 1 1 200px; text-align: center; }

/* ── The Betfair market link, everywhere it is offered ──────────────────
   It was an underlined text link in three places and it is an action in all
   three: it leaves the site and opens a market. One chip, so it reads the
   same on a fixtures card, on a live card and on the fixture page.

   Why a class and not an element: on a fixtures card the whole card is an
   <a>, so the control there has to stay a <span> - an anchor nested in an
   anchor is invalid and browsers unnest it, which breaks the card. The look
   therefore has to be carried entirely by the class. The two places whose
   markup this batch could touch take .tf-button .tf-button-chip; the fixture
   page's anchor cannot be edited tonight, so it is named here and gets the
   same declarations.

   Quiet rather than filled, deliberately: the "+ Desk" chip beside it is the
   primary action on the card, and two filled pills competing in one corner is
   what made the foot of the card look busy. Every value is a token, so both
   themes follow. */
.tf-button-chip,
.fixture-exchange-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: max-content;
    height: auto;
    padding: 3px 10px;
    border: 1px solid var(--Stroke);
    border-radius: 999px;
    /* Filled, not outlined. --Stroke against a card is all but invisible in
       light mode, and a button nobody can see the edge of is the text link
       this was meant to stop being. The grey fill reads as a control in both
       themes and still leaves the filled-accent desk chip beside it as the
       louder of the two. */
    background: var(--Input);
    color: var(--Heading);
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: .02em;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
}
.tf-button-chip:hover,
.fixture-exchange-link:hover {
    border-color: var(--Main);
    background: var(--Input);
    color: var(--Main);
}
.tf-button-chip:focus-visible,
.fixture-exchange-link:focus-visible {
    outline: 2px solid var(--siq-accent);
    outline-offset: 2px;
}

/* ── The foot of a fixtures-list card ───────────────────────────────────
   The shape the owner drew: Betfair chip -- tournament over round -- Desk
   chip, one row, everything centred on it. Before this the tournament and
   round sat on the left of a flex row with both chips pushed to the right by
   `justify-content: space-between`.

   A grid rather than flex, because the middle column is the one that has to
   absorb a long tournament name and the two side columns must not move when
   it does. `auto minmax(0, 1fr) auto` says exactly that in one line: each
   chip is as wide as its own text, the middle takes every pixel left over,
   and the minmax(0, …) is what lets it shrink below the width of its longest
   word instead of pushing a chip off the card. Flex would need a flex triple
   plus a min-width on the middle and would still let the chips shrink.

   The middle is centred in the space between the chips, which is not quite
   the centre of the card whenever the two chips are different widths -
   "Betfair ↗" and "+ Desk" always are. Truly optical centring would mean
   pinning both side columns to one fixed width, which would either clip
   "On my desk" or leave a gap; between the two, matching the drawing beats
   matching the pixel.

   margin-top: auto moves here from .fixtures-b-tournament, which still needs
   its own copy: the tournaments page reuses this card shell without a foot. */
.fixtures-b-foot {
    margin-top: auto;
    padding-top: 10px;
    border-top: 1px solid var(--Stroke);
    display: grid;
    /* Equal side columns, so the middle sits on the CARD's centre line.
       With `auto 1fr auto` the sides took whatever their chips needed and
       the tournament was centred in the gap BETWEEN them - which drifts
       left on a card with no Betfair chip and right when the desk chip is
       hidden, so no two cards agreed. Two 1fr tracks are the same width
       whatever is in them, and the middle is `auto`, so it is centred on
       the card and stays put from card to card. The chips do not wrap
       because they are nowrap; below 400px the whole foot stacks anyway. */
    /* The side tracks are floored at their own content; only the middle may
       shrink. Getting this wrong is what put the desk chip on top of the
       tournament name: with `1fr auto 1fr` the middle `auto` took its
       min-content as a floor, so a long name ("Japan Open Tennis
       Championships - Tokyo") held the track wide and squeezed the 1fr
       sides to ZERO - and the chip, which is nowrap and so keeps its 63px
       whatever its cell says, then overflowed a 0px cell leftward, across
       the name. Measured: 63px of overlap at a four-across grid.

       Dropping the sides' floor to 0 (minmax(0, 1fr)) does not help - it
       licenses exactly the same collapse. The floor has to be min-content,
       so a side track is never narrower than the chip standing in it, while
       minmax(0, auto) in the middle lets the NAME give way instead and wrap
       onto a second line, which is what overflow-wrap below is for. Both
       sides stay 1fr, so with a chip on each they are still equal and the
       middle still sits on the card's centre line. */
    grid-template-columns: minmax(min-content, 1fr) minmax(0, auto) minmax(min-content, 1fr);
    align-items: center;
}

/* The chips' space is RESERVED on the tabs that have chips, whether or not
   this particular card shows one.

   Without it the middle column is a different width on every card: a card
   with no Betfair market gave that space to the tournament name, the card
   beside it did not, and the two names sat at two different widths on two
   different centre lines. Reserving the same width on both sides makes the
   middle identical on every card in the grid, which is the point - the foot
   should read as one row across the page, not as a per-card accident.

   --fx-chip is the widest chip that can appear: "ON MY DESK" at 93px, which
   is wider than "+ DESK" (63px) and "Betfair" (69px). Both sides reserve the
   SAME width so the middle stays on the card's centre line rather than being
   pushed off it by a wider chip on one side.

   Only on Upcoming and In play. Results has no chips at all now, so a reserve
   there would be two columns of empty air squeezing the name for nothing. */
.fixtures-b-foot.has-chips {
    /* The chip's own width, and the gutter between it and the name. The
       gutter is part of the reserve rather than a margin on the chip: a
       margin inside a track this tight has nowhere to go and the name ends
       up touching the chip. */
    --fx-chip: 93px;
    --fx-reserve: 103px;
    /* FIXED sides, not 1fr. With 1fr the sides absorbed whatever the name
       did not want, so a short name ("M15 Luan") left 146px either side and
       a long one left 93px - the middle was a different width on every card
       and no two names shared a centre line. Fixed sides with a 1fr middle
       invert that: the middle is the card's width less the two reserves, so
       it is identical on every card in the grid, and the name is centred in
       the same box whether or not this card has a Betfair market. */
    grid-template-columns: var(--fx-reserve) minmax(0, 1fr) var(--fx-reserve);
}

/* The desk chip keeps one width in both states. It says "+ Desk" at 63px and
   "On my desk" at 93px, so adding a match to the desk used to widen the chip
   and shove the tournament name sideways - the card moved under the click
   that changed it. Pinned to the wider of the two and centred, so the toggle
   changes the colour and the word and nothing else; it is also what makes
   the reserve above a number that can be relied on. */
.fixtures-b-foot.has-chips .fixtures-b-desk {
    min-width: var(--fx-chip);
    justify-content: center;
}

/* The reserve already carries the gutter, so the chips' own margins would
   double it and push them out of their track. */
.fixtures-b-foot.has-chips .fixtures-b-foot-left > * { margin-right: 0; }
.fixtures-b-foot.has-chips .fixtures-b-foot-right > * { margin-left: 0; }
/* The two side cells. They are still flex boxes inside their own column, so
   the chip sits centred in it, and they are still .fixtures-b-actions so the
   24px chip rule and the [hidden] rule further down keep their hold. An empty
   left cell (no Betfair market for this match) collapses to nothing but keeps
   its column, so the middle stays where it is from card to card. */
.fixtures-b-foot-left,
.fixtures-b-foot-right {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
.fixtures-b-foot-left { justify-content: flex-start; }
.fixtures-b-foot-right { justify-content: flex-end; }
/* Chips keep their own width so an equal-track foot cannot squash one. */
.fixtures-b-foot-left > *, .fixtures-b-foot-right > * { white-space: nowrap; }

/* The gutter between a chip and the tournament is the chip's own margin, not
   the grid's column-gap. A gap is charged between the tracks whether or not
   anything is in them, so a card with no Betfair market, or one whose desk
   chip is still [hidden], would carry 10px of empty air on that side and the
   middle would sit off-centre. A margin on a display: none chip costs
   nothing, so the gutter appears exactly when the chip does. */
.fixtures-b-foot-left > * { margin-right: 10px; }
.fixtures-b-foot-right > * { margin-left: 10px; }

/* The middle column. min-width: 0 comes from the track above; break-anywhere
   wrapping is what stops a single long tournament name (or a word longer than
   the column) forcing the row wider than a 320px phone. */
.fixtures-b-where {
    min-width: 0;
    text-align: center;
}
.fixtures-b-foot .fixtures-b-tournament,
.fixtures-b-foot .fixtures-b-round {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
    text-align: center;
    overflow-wrap: anywhere;
}
.fixtures-b-foot .fixtures-b-round { margin-top: 2px; }
/* .fixtures-b-actions is now the class both side cells share. It is kept as a
   class of its own rather than folded into the two cell rules because the
   chip-height rule and the [hidden] desk rule at the bottom of this file are
   written against it; deleting it would silently unhide a desk chip. */
.fixtures-b-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}
@media (max-width: 400px) {
    /* At the narrowest the three columns stay - the chips are 24px pills and
       the middle wraps - but the gutters close up so the tournament keeps as
       many characters as it can. */
    .fixtures-b-foot-left > * { margin-right: 6px; }
    .fixtures-b-foot-right > * { margin-left: 6px; }
}

/* ── Player Spotlight, clickable ────────────────────────────────────────
   The card carries data-href (behaviours.js), which is how every other
   clickable card on the dashboard does it - the Content-Security-Policy
   rules out an inline handler, and the card cannot be one big <a> because
   the player's name inside it already is. That anchor is what makes middle
   click, open-in-new-tab and the keyboard work; the handler stands aside
   for a real link, so clicking the name does not fire both. */
.spotlight-card[data-href] { cursor: pointer; }
a.stadium-player-name { color: inherit; text-decoration: none; }
a.stadium-player-name:hover { text-decoration: underline; }
.spotlight-card[data-href]:hover { border-color: var(--siq-accent); }

/* Names in the two ranking tiles are real links; they should look like the
   text they replaced until somebody points at them. */
.rankings-a-name a { color: inherit; text-decoration: none; }
.rankings-a-name a:hover { color: var(--siq-accent); text-decoration: underline; }

/* The dark theme reaches into .tf-button with a descendant selector (border: 0,
   and a blue fill on hover), which outranks the chip's own flat rules and
   would give it a border in one theme and not the other. Restated at the same
   weight so the chip looks like itself in both. */
.dark-theme .tf-button-chip {
    border: 1px solid var(--Stroke);
    background: var(--Input);
    color: var(--Heading);
}
.dark-theme .tf-button-chip:hover {
    border-color: var(--siq-accent);
    background: var(--Input);
    color: var(--siq-accent);
}

/* The two chips in a card's foot are one height, stated outright rather than
   left to add up: the Betfair chip is an inline-flex box and the desk chip an
   inline-block, and with the same padding, border and line-height they still
   round differently, which is a chip a pixel taller than its neighbour. Both
   become inline-flex here, 24px tall, text centred, so they are the same box
   whatever each one's own rule says. Scoped to the foot so the live-scores
   copy of the desk chip is left as it is. */
.fixtures-b-actions .fixtures-b-desk,
.fixtures-b-actions .tf-button-chip {
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    height: 24px;
    padding: 0 10px;
    line-height: 1;
}

/* The desk chip is rendered with `hidden` so that, with its script absent,
   there is no control rather than a dead one - but the rules that give it its
   shape set `display`, which outranks the UA rule behind that attribute and
   showed it anyway. After the foot rule above on purpose, and with a
   three-class copy of it, so that rule cannot win the same argument back. A
   display: none item takes no flex gap, so a hidden chip leaves no hole. */
.fixtures-b-desk[hidden],
.fixtures-b-actions .fixtures-b-desk[hidden],
.live-match-desk[hidden] { display: none; }

/* The fixture page's own link carries no .tf-button, so the dark-theme pair
   above misses it; --Main is the light-mode blue and sits at about 3:1 on a
   dark chip, where the lifted accent is the readable one. */
.dark-theme .fixture-exchange-link:hover { border-color: var(--siq-accent); color: var(--siq-accent); }

/* ─────────────────────────────────────────────────────────────────────────
   THE DASHBOARD'S SIX CARDS, SET TO ONE RULER

   Six cards in one grid, each built at a different time, each with its own
   padding and its own idea of how tall a heading is. The result was that no
   two titles began on the same line and the rows beneath them drifted apart
   card by card. Rather than nudge each one, every card is given the same
   box here: the same padding, a heading of a known height, and a body that
   begins at the same offset in all six.

   This sits at the end of the file deliberately - it has to beat the
   per-component style blocks, several of which set their own padding.
   ───────────────────────────────────────────────────────────────────────── */
.tf-section-3 > div > .wg-box,
.tf-section-3 .wg-box.live-scores-tile,
.tf-section-3 .wg-box.next-fixtures,
.tf-section-3 .wg-box.upcoming-tournaments,
.tf-section-3 .wg-box.rankings-card,
.tf-section-3 .wg-box.spotlight-card {
    padding: 24px;
    display: flex;
    flex-direction: column;
    height: 100%;
    box-sizing: border-box;
}

/* Every heading, whichever class it happens to carry, occupies the same
   34px line. The global h2 line-height is 79px, so without this each title
   sat in a box two and a half times its own size and any difference in the
   markup around it moved the card's whole contents. */
.tf-section-3 .stadium-style-title,
.tf-section-3 .stadium-title {
    margin: 0 !important;
    height: 34px;
    line-height: 34px;
    flex: 0 0 auto;
}

/* The two cards that wrap their heading in something - Live Scores in a
   flex row so the LIVE badge can sit beside it, the Spotlight in a header
   block - must not let the wrapper add height of its own. */
.tf-section-3 .live-scores-tile > .d-flex:first-child,
.tf-section-3 .spotlight-card .stadium-header {
    min-height: 34px;
    margin: 0 !important;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}

/* The body of every card starts exactly 12px under the heading. */
.tf-section-3 .live-scores-tile .dash-live-list,
.tf-section-3 .wg-box.next-fixtures > .fixture-slider-wrap,
.tf-section-3 .upcoming-tournaments .tournament-slider-outer,
.tf-section-3 .rankings-card > .body-text,
.tf-section-3 .spotlight-card .stadium-main {
    margin-top: 12px !important;
}

/* BOTTOM ROW: centred in the height the row gives them.
   These three stretch to the tallest card in the row, so a ten-row list and
   a spotlight both hung from the top with the spare height pooled beneath.
   The body takes what the heading leaves and sits in the middle of it. */
.tf-section-3 .rankings-card > .body-text { flex: 0 0 auto; }
.tf-section-3 .rankings-card .rankings-a-list,
.tf-section-3 .spotlight-card .stadium-main {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
/* The spotlight's body is a photo beside its details, so it stays a row and
   centres the pair of them instead. */
.tf-section-3 .spotlight-card .stadium-main {
    flex-direction: row;
    align-items: center;
}


/* The surface bar has to carry the card's curve itself.
   .fixtures-a-item is border-radius 12px with overflow hidden, which used to
   clip the bar's square end into the corner - but the row rule above sets
   overflow: visible so a long name is never sliced, and that switched the
   clipping off. The bar kept its square corners and stuck out past the
   rounded edge. Rounding the bar's own left side gives the same result
   without asking for the clipping back. Same for the fixtures list's
   coloured edge and the tournament rows, which are built the same way. */
.tf-section-3 .fixtures-a-item > .fixtures-a-bar:first-child {
    border-radius: 12px 0 0 12px;
}

/* Header search: each result is one link across the whole row (30 Sep 2026).
   Only the name used to be clickable; the line under it and the space beside
   it did nothing. */
.site-search-row {
    display: flex; flex-direction: column; gap: 2px;
    width: 100%;
    padding: 8px 10px; margin: 0 -10px;
    border-radius: 10px;
    text-decoration: none; color: inherit;
    cursor: pointer;
    transition: background .15s;
}
.site-search-row:hover, .site-search-row:focus-visible { background: var(--Input, rgba(127,127,127,.12)); text-decoration: none; }
.site-search-row:focus-visible { outline: 2px solid var(--siq-accent); outline-offset: 1px; }
.site-search-row .site-search-row-title { color: var(--Heading); font-weight: 600; }
.site-search-row:hover .site-search-row-title { color: var(--siq-accent); }
.site-search-row .site-search-row-sub { color: var(--Note); }

/* Results: the actual start under the scheduled one (30 Sep 2026). */
.fixtures-b-started { display: block; font-size: 11px; font-weight: 600; color: var(--Note); margin-top: 1px; }

/* Header notifications: a little air between items (1 Oct 2026). They sat
   edge to edge, so each card's tinted background ran into the next. */
.hdr-items { display: flex; flex-direction: column; gap: 8px; padding: 2px 0; }
.hdr-noti { padding: 10px 8px; }
.hdr-noti-body { margin-top: 3px; line-height: 1.4; }
.hdr-noti-cta { margin-top: 6px; }
