/* Teal brand, three tiers. The split exists for contrast, not decoration:
   --accent is the default because most uses either put white text ON it
   (badges, pills, buttons) or use it AS text on white, and both need 4.5:1.
   --brand is the vivid logo-matching teal, reserved for the navbar, which is
   a large filled surface whose only text is the brand wordmark. */
:root {
    --brand: #00A0A2;        /* 3.21:1 on white — large text and UI only */
    --accent: #00767A;       /* 5.42:1 — safe for body text and small white-on-teal */
    --accent-dark: #005B5E;  /* 7.90:1 — hover, pressed, headings */
}
body { background-color: #ffffff; }
.navbar { background-color: var(--brand); }
.navbar-brand { color: #ffffff !important; font-weight: 600; }
.btn-primary, .nav-pills .nav-link.active, .nav-tabs .nav-link.active {
    background-color: var(--accent) !important;
    border-color: var(--accent) !important;
    color: #ffffff !important;
}
.btn-primary:hover { background-color: var(--accent-dark) !important; }
.nav-pills .nav-link, .nav-tabs .nav-link { color: var(--accent-dark); }

/* ---- Reading links on the tab bar (Gameweek briefings, Players A–Z) ----
   Same row as the four tool tabs, pushed to the far right, and deliberately
   quieter: lighter weight and a muted colour, so the eye still lands on the
   tools first. `margin-left: auto` on the first of them is what does the
   pushing - it's only applied when tool tabs precede them (see tabs.html), so
   on a prose page they sit at the left instead of floating on an empty bar. */
#mainTabs { align-items: flex-end; }
.content-link-first { margin-left: auto; }
.content-link .nav-link {
    color: #6b7280;
    font-weight: 400;
    border-color: transparent;
    white-space: nowrap;
}
.content-link .nav-link:hover { color: var(--accent-dark); }
/* The current page, when you're standing on one of them. Underlined rather
   than given the tabs' filled pill: these aren't panes of this document, and
   dressing them as the active tab would say they were. */
.content-link .nav-link.active {
    background-color: transparent !important;
    border-color: transparent !important;
    color: var(--accent-dark) !important;
    font-weight: 600;
    box-shadow: inset 0 -2px 0 var(--accent);
}
/* A hairline between the tools and the reading links. Only where they actually
   touch - with margin-left:auto there's a gap on desktop and the rule would be
   floating in it, but on a narrow screen the bar is a tight scroller and the
   two sets sit shoulder to shoulder. */
.content-link-first { border-left: 1px solid #e9e9ef; padding-left: 4px; }

/* The short label, for the widths where the full one doesn't fit. Both are in
   the markup and CSS picks; the alternative - swapping text in JavaScript on
   resize - would mean the label a crawler reads depends on a viewport it
   doesn't have. */
.cl-short { display: none; }

/* Below 992px six items stop fitting across one row. Two things happen, and
   the order matters: the labels shorten first, because that alone gets a
   tablet back to a single comfortable row, and only what still doesn't fit
   scrolls.

   Wrapping is what's being avoided. A wrapped tab bar puts a second row of
   chrome above every page and moves the content down with it, which is both
   the clutter this was meant to avoid and a layout shift on a slow font load.
   `nowrap` plus a scrolling bar means the row height is fixed at every width
   and the page below never moves. */
@media (max-width: 991.98px) {
    .cl-full { display: none; }
    .cl-short { display: inline; }
    #mainTabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;          /* Firefox */
        -webkit-overflow-scrolling: touch;
    }
    #mainTabs::-webkit-scrollbar { display: none; }   /* Safari / Chrome */
    #mainTabs .nav-item { flex: 0 0 auto; }
    /* An overflowing flex line has no free space, so the auto margin collapses
       to nothing and the reading links simply follow the tools. Stated rather
       than relied on, so the intent survives a refactor. */
    .content-link-first { margin-left: 0; }
}
/* Navbar action button (Change ID) — sits where the old mode toggle did */
#changeId {
    background-color: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.5);
    color: #ffffff;
}
#changeId:hover { background-color: rgba(255,255,255,0.3); }
.rating-badge {
    background-color: var(--accent);
    color: #ffffff;
    border-radius: 6px;
    padding: 2px 10px;
    font-weight: 600;
}
table th { color: var(--accent-dark); }
.form-select:focus, .form-control:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 0.25rem rgba(0, 172, 174, 0.25);
}
.rotation-table td, .rotation-table th {
    text-align: center;
    font-size: 0.82rem;
    white-space: nowrap;
}
.rotation-table td.team-col, .rotation-table th.team-col {
    text-align: left;
    font-weight: 600;
    position: sticky;
    left: 0;
    background: #fff;
    cursor: pointer;
}
.rotation-table tr.selected-team td {
    background-color: rgba(0, 172, 174, 0.15);
}
.rotation-table tr.selected-team td.team-col {
    background-color: rgba(0, 172, 174, 0.25);
}
.fixture-cell {
    border-radius: 4px;
    padding: 3px 6px;
    color: #1a1a1a;
    font-weight: 500;
}
.pair-card {
    border: 1px solid #eee;
    border-radius: 8px;
    padding: 10px 14px;
    margin-bottom: 12px;
    transition: all 0.3s ease;
    animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.btn-outline-primary {
    color: var(--accent);
    border-color: var(--accent);
}
.btn-outline-primary:hover {
    background-color: var(--accent);
    color: white;
}
.pair-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
}
.pair-team-label {
    width: 90px;
    font-weight: 600;
    font-size: 0.85rem;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
/* Team shirts — inline SVG from kits.js, keyed on team_code. Width is set
   explicitly rather than left to `auto`: these were <img> elements, which take
   their ratio from the file, but an <svg> takes it from the viewBox and some
   browsers collapse or stretch it under `width: auto`. The viewBox is square,
   so width always equals height. */
.shirt { height: 22px; width: 22px; vertical-align: middle; margin-right: 6px; }
.shirt-sm { height: 18px; width: 18px; vertical-align: middle; }
.player-name { font-weight: 500; }
/* Next-3-GW predicted points cells, coloured by fixture difficulty */
.gw-cell {
    display: inline-block;
    min-width: 42px;
    text-align: center;
    border-radius: 4px;
    padding: 2px 4px;
    margin-right: 3px;
    color: #1a1a1a;
    font-weight: 600;
    font-size: 0.82rem;
    line-height: 1.15;
}
.gw-cell small { display: block; font-weight: 400; font-size: 0.6rem; color: #333; }
/* Recommended rotation players under a pair, one row per position */
.rec-players { margin-top: 10px; padding-top: 8px; border-top: 1px dashed #e6e6e6; }
.pair-meta { font-size: 0.72rem; color: #888; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.03em; }
.rec-grid {
    display: grid;
    grid-template-columns: 40px auto 16px minmax(0, 1fr);
    gap: 6px 6px;
    align-items: center;
}
.rec-pos { font-weight: 700; font-size: 0.72rem; color: var(--accent-dark); }
.rec-slot { display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; min-width: 0; }
.rec-slot .player-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rec-plus { text-align: center; color: #b8b8b8; font-weight: 600; }
.rec-rating { background: var(--accent); color: #fff; border-radius: 4px; padding: 0 5px; font-weight: 600; font-size: 0.7rem; }
.rec-cost { color: #888; font-size: 0.72rem; white-space: nowrap; }
.rec-empty { color: #bbb; font-style: italic; font-size: 0.82rem; }
/* ---- Mobile: scale everything down for phones ---- */
@media (max-width: 575.98px) {
    body { font-size: 0.9rem; }
    .container { padding-left: 10px; padding-right: 10px; }

    .navbar-brand { font-size: 1.05rem; }
    #changeId { font-size: 0.72rem; padding: 2px 8px; }
    .nav-tabs .nav-link, .nav-pills .nav-link { font-size: 0.82rem; padding: 4px 8px; }

    /* Players table */
    .table { font-size: 0.78rem; }
    .table th, .table td { padding: 0.4rem 0.3rem; }
    .shirt { height: 18px; margin-right: 4px; }
    .shirt-sm { height: 15px; }
    .rating-badge { padding: 1px 7px; font-size: 0.76rem; }
    .gw-cell { min-width: 34px; font-size: 0.7rem; padding: 2px 3px; margin-right: 2px; }
    .gw-cell small { font-size: 0.5rem; }

    /* Rotation table */
    .rotation-table td, .rotation-table th { font-size: 0.7rem; }
    .fixture-cell { padding: 2px 4px; font-size: 0.7rem; }

    /* Rotation pair cards */
    .pair-card { padding: 8px 10px; margin-bottom: 10px; }
    .pair-team-label { width: 70px; font-size: 0.76rem; }
    .pair-row { gap: 4px; }
    .pair-row [style*="44px"] { width: 36px !important; }
    .section-heading { font-size: 0.95rem; }
    .pair-meta { font-size: 0.66rem; }
    .rec-grid { grid-template-columns: 34px auto 14px minmax(0, 1fr); gap: 5px 5px; }
    .rec-pos { font-size: 0.66rem; }
    .rec-slot { font-size: 0.78rem; gap: 4px; }
    .rec-rating { font-size: 0.64rem; padding: 0 4px; }
    .rec-cost { font-size: 0.66rem; }
    .rec-empty { font-size: 0.76rem; }
}

/* ============================================================
   MY TEAM
   ============================================================ */

/* First-run FPL ID prompt */
.id-prompt {
    max-width: 520px;
    margin: 1rem auto;
    padding: 24px;
    border: 1px solid #eee;
    border-radius: 12px;
    text-align: center;
    box-shadow: 0 2px 12px rgba(0,0,0,0.04);
}
.id-prompt .d-flex { justify-content: center; }

/* Info icon + hover/focus tooltip */
.info-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px; height: 20px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    font-style: italic;
    cursor: help;
    position: relative;
}
.info-tip {
    display: none;
    position: absolute;
    top: 130%;
    left: 50%;
    transform: translateX(-50%);
    width: 240px;
    background: #222;
    color: #fff;
    font-size: 0.72rem;
    font-style: normal;
    font-weight: 400;
    line-height: 1.35;
    text-align: left;
    padding: 8px 10px;
    border-radius: 6px;
    z-index: 20;
}
.info-icon:hover .info-tip, .info-icon:focus .info-tip { display: block; }

/* Header */
.team-header { border-bottom: 1px solid #eee; padding-bottom: 12px; }
.team-name { color: var(--accent-dark); font-weight: 700; }
.gw-nav { display: inline-flex; align-items: center; gap: 6px; }
.gw-label { font-weight: 600; min-width: 52px; text-align: center; }

/* align-items, and it is load-bearing. The AI tabs give this row a min-height
   to reserve its final size before the data lands (see the .stat-chips rule
   further down), and a flex container's default `align-items: stretch` handed
   that whole reserved height to the chips themselves - so three containers
   holding "33" and "37.4" were drawn 108px tall on a phone, most of it empty.
   Reserving space for a row and stretching the row's contents to fill it are
   different things; this asks for the first. align-content does the same job
   for the second line once the chips wrap. */
.stat-chips {
    display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px;
    align-items: flex-start; align-content: flex-start;
}
.stat-chip {
    display: flex; flex-direction: column;
    padding: 6px 12px;
    border-radius: 8px;
    background: #effafa;
    min-width: 78px;
}
.stat-chip.accent { background: var(--accent); }
.stat-chip.accent .stat-label, .stat-chip.accent .stat-value { color: #fff; }
.stat-label { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.03em; color: #888; }
.stat-value { font-size: 0.95rem; font-weight: 700; color: #222; }

/* Actions row above pitch */
.pitch-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rec-captain { font-size: 0.82rem; color: #555; }

/* Pitch — original drawing, Laws of the Game proportions. The solid green
   behind it is a fallback if the file ever fails to load. */

.pitch {
    background: #1f8a4c url('/static/pitch.svg') center/cover no-repeat;
    border-radius: 10px;
    padding: 18px 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.player { width: 76px; flex: 0 1 76px; min-width: 0; text-align: center; }
.player-kit { position: relative; display: inline-block; }
.kit { height: 44px; width: 44px; }
.cap-badge {
    position: absolute; top: -4px; right: 2px;
    width: 17px; height: 17px;
    background: #1a1a1a; color: #fff;
    border-radius: 50%;
    font-size: 0.62rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid #fff;
}
.cap-badge.vice { background: #6c757d; }

/* Automatic substitutions on the AI pitches. Top LEFT, because the armband
   badge already owns the top right and a card can carry both - a captain who
   didn't play is substituted and hands the armband on in the same breath. */
.auto-sub {
    position: absolute; top: -4px; left: 2px;
    width: 17px; height: 17px;
    border-radius: 50%; border: 1px solid #fff;
    font-size: 0.5rem; line-height: 1; color: #fff;
    display: flex; align-items: center; justify-content: center;
    z-index: 4;
}
.auto-sub.in { background: #1d8a4e; }
.auto-sub.out { background: #b02a37; }
/* Only a cursor. The AI cards are read-only, so anything more emphatic would
   promise an interaction the pop-up doesn't offer. */
.player-tappable { cursor: pointer; }
.injury-dot {
    position: absolute; bottom: 0; left: 4px;
    width: 11px; height: 11px;
    background: #e03131; border: 1px solid #fff; border-radius: 50%;
}
.player-name-pill {
    background: rgba(255,255,255,0.95);
    color: #1a1a1a;
    font-size: 0.68rem; font-weight: 600;
    border-radius: 3px;
    padding: 1px 3px;
    margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player-stat {
    background: var(--accent-dark);
    color: #fff;
    font-size: 0.62rem; font-weight: 600;
    border-radius: 0 0 3px 3px;
    padding: 1px 3px;
}

/* Bench */
.bench {
    margin-top: 10px;
    border: 1px dashed #ddd;
    border-radius: 10px;
    padding: 8px;
    background: #fafafa;
}
.bench-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.03em; color: #999; margin-bottom: 4px; }
.bench-row { display: flex; justify-content: center; gap: 4px; flex-wrap: nowrap; }

/* Transfer recommendations */
.transfer-rec { border: 1px solid #eee; border-radius: 8px; padding: 8px 10px; margin-bottom: 8px; }
.transfer-line { display: flex; align-items: center; gap: 6px; font-size: 0.85rem; font-weight: 500; }
.tr-arrow { color: var(--accent); font-weight: 700; }
.transfer-meta { display: flex; align-items: center; gap: 10px; font-size: 0.72rem; color: #777; margin-top: 4px; }
.ft-tag { border-radius: 4px; padding: 0 6px; font-weight: 600; color: #fff; }
.ft-tag.free { background: #2e9b57; }
.ft-tag.hit { background: #e07a1f; }

.news-feed-placeholder { border-top: 1px dashed #e6e6e6; padding-top: 10px; }

/* News feed — max-height is set from JS (syncNewsHeight) to match the
   leagues column's actual rendered height, so the two bottoms line up and
   the news list scrolls internally instead of trailing off or stretching
   the whole row taller. Bootstrap's .row stretches columns to equal height
   by default, which would inflate leaguesSection's measured height to match
   news's own uncapped content (circular) before JS ever gets to measure it -
   align-items:flex-start on this row lets each column size to its own
   content first, so the leagues measurement JS reads is its true height. */
.leagues-news-row { align-items: flex-start; }
.news-col { display: flex; flex-direction: column; }
.news-list { overflow-y: auto; padding-right: 2px; }
@media (max-width: 991.98px) {
    .news-list { max-height: 340px; }
}

.news-item {
    display: flex; align-items: flex-start; gap: 8px;
    border: 1px solid #eee; border-radius: 8px;
    padding: 8px 10px; margin-bottom: 6px;
}
.news-item .shirt-sm { flex: 0 0 auto; margin-top: 2px; }
.news-body { min-width: 0; flex: 1 1 auto; }
.news-head { display: flex; align-items: baseline; gap: 8px; }
.news-who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.news-player { font-weight: 600; font-size: 0.85rem; }
.news-team { font-size: 0.72rem; color: #999; }
.news-date { font-size: 0.68rem; color: #aaa; flex: 0 0 auto; margin-left: auto; }
.news-text { font-size: 0.78rem; color: #555; margin-top: 2px; }

/* Underperforming players table (Players tab) — same base look as the normal
   player search table (font size, name-with-shirt layout, row hover, rating
   badge) but its own table class rather than .ps-table, since .ps-table's
   nth-child column widths are tuned for that table's 7 columns and would
   misalign this table's 8. */
.underperf-table { font-size: 0.8rem; margin: 0; min-width: 680px; table-layout: fixed; width: 100%; }
.underperf-table th:nth-child(1), .underperf-table td:nth-child(1) { width: 18%; }   /* Player */
.underperf-table th:nth-child(2), .underperf-table td:nth-child(2) { width: 7%;  }   /* Pos */
.underperf-table th:nth-child(3), .underperf-table td:nth-child(3) { width: 10%; }   /* Team */
.underperf-table th:nth-child(4), .underperf-table td:nth-child(4) { width: 18%; }   /* Metric */
.underperf-table th:nth-child(5), .underperf-table td:nth-child(5) { width: 10%; }   /* Expected */
.underperf-table th:nth-child(6), .underperf-table td:nth-child(6) { width: 9%;  }   /* Actual */
.underperf-table th:nth-child(7), .underperf-table td:nth-child(7) { width: 7%;  }   /* Diff */
.underperf-table th:nth-child(8), .underperf-table td:nth-child(8) { width: 6%;  }   /* £m */
.underperf-table th:nth-child(9), .underperf-table td:nth-child(9) { width: 15%; }   /* Next 3 */
.underperf-table td:nth-child(9) .player-gws { display: flex; flex-wrap: nowrap; gap: 1px; justify-content: flex-start; margin-top: 0; }
.underperf-table td:nth-child(9) .mini-gw { flex: 1 1 0; min-width: 0; }
.underperf-table .underperf-diff { background-color: #e07a1f; }

/* Leagues */
.league-row {
    border: 1px solid #eee; border-radius: 8px;
    padding: 8px 12px; margin-bottom: 6px;
    display: grid; grid-template-columns: 1fr auto auto;
    gap: 10px; align-items: center;
}
.league-name { font-weight: 600; cursor: pointer; }
.league-rank { font-size: 0.8rem; color: #777; }
.league-caret { cursor: pointer; color: var(--accent); }
.league-standings { grid-column: 1 / -1; }
.league-table td { font-size: 0.8rem; vertical-align: middle; }
.ls-rank { width: 36px; font-weight: 700; color: var(--accent-dark); }
.ls-manager { font-size: 0.68rem; color: #999; }
.ls-total { text-align: right; font-weight: 600; }

/* Mobile scaling for the team view */
@media (max-width: 575.98px) {
    .player { width: 62px; }
    .kit { height: 36px; }
    .player-name-pill { font-size: 0.6rem; }
    .player-stat { font-size: 0.56rem; }
    .stat-chip { min-width: 64px; padding: 5px 9px; }
    .stat-value { font-size: 0.85rem; }
    .pitch { padding: 12px 4px 8px; }
    .pitch-row { gap: 2px; }
    .info-tip { width: 200px; }
}

/* ============================================================
   MY TEAM — v2 tweaks (gw nav, builder, grouped leagues)
   ============================================================ */

/* Gameweek nav: purple arrows, no box */
.gw-btn {
    background: none;
    border: none;
    color: var(--accent);
    font-size: 1rem;
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
}
.gw-btn:hover:not(:disabled) { color: var(--accent-dark); }
.gw-btn:disabled { color: #ccc; cursor: default; }

/* Drop the up/down spinner on the FPL ID field */
#idInput::-webkit-outer-spin-button,
#idInput::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#idInput { -moz-appearance: textfield; appearance: textfield; }

/* Grouped-league subheadings */
.league-group-label {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: #999; font-weight: 700; margin: 12px 0 4px;
}
.league-group-label:first-child { margin-top: 0; }

/* Mobile: keep tooltip on-screen, shrink gw nav */
@media (max-width: 575.98px) {
    .info-tip {
        position: fixed;
        left: 50%; right: auto; top: auto; bottom: 20px;
        transform: translateX(-50%);
        width: 90vw; max-width: 320px;
    }
    .gw-btn { font-size: 0.85rem; padding: 1px 4px; }
    .gw-label { min-width: 40px; font-size: 0.85rem; }
    #changeId { font-size: 0.72rem; padding: 2px 8px; }
}

/* ============================================================
   MY TEAM — v3 (layout, player modal, subs, filters)
   ============================================================ */

/* Thinner, centered pitch */
.pitch-wrap { max-width: 520px; margin: 0 auto; }

/* Clickable players + next-3 fixtures under each card */
.player { cursor: pointer; }
.mini-gw {
    font-size: 0.6rem; font-weight: 600; color: #1a1a1a;
    min-width: 15px; text-align: center; border-radius: 3px; padding: 0 2px; line-height: 1.4;
}

/* Substitution banner */
.sub-banner {
    background: #fff3cd; border: 1px solid #ffe69c; color: #664d03;
    border-radius: 8px; padding: 8px 12px; font-size: 0.85rem; margin-bottom: 10px;
}
.sub-mode .player { outline: 1px dashed var(--accent); outline-offset: 2px; border-radius: 8px; }

/* Search clear (x) button */
.search-clear-wrap { position: relative; display: inline-block; }
.search-clear {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    border: none; background: none; color: #999; font-size: 1.1rem; line-height: 1;
    cursor: pointer; padding: 0 2px; display: none;
}
.search-clear:hover { color: #555; }
.search-clear-wrap input { padding-right: 24px; }

/* Player modal */
.player-modal { position: fixed; inset: 0; z-index: 1050; display: flex; align-items: center; justify-content: center; }
.pm-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.45); }
.pm-card {
    position: relative; background: #fff; border-radius: 14px; padding: 18px;
    width: 92%; max-width: 420px; max-height: 86vh; overflow-y: auto;
    box-shadow: 0 12px 40px rgba(0,0,0,0.25);
}
.pm-close { position: absolute; top: 10px; right: 12px; border: none; background: none; font-size: 1.5rem; line-height: 1; color: #888; cursor: pointer; }
.pm-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.pm-name { font-weight: 700; font-size: 1.1rem; }
.pm-sub { color: #777; font-size: 0.8rem; }
.pm-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.pm-section-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: #999; font-weight: 700; margin: 12px 0 6px; }
.pm-fixtures { display: flex; gap: 6px; }
.pm-fix { flex: 1; border-radius: 6px; padding: 6px 4px; text-align: center; font-size: 0.72rem; display: flex; flex-direction: column; gap: 2px; }
.pm-fix span { font-weight: 700; }
.pm-form-table td { padding: 3px 6px; font-size: 0.8rem; }
.pm-pts { font-weight: 700; text-align: right; }
.pm-past { font-size: 0.78rem; color: #666; margin-top: 4px; }
.pm-transfer { border: 1px solid #eee; border-radius: 8px; padding: 8px; margin-bottom: 8px; max-height: 260px; overflow-y: auto; }
.pm-transfer-title { font-size: 0.8rem; font-weight: 600; margin-bottom: 6px; }
.pm-cand { display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-bottom: 1px solid #f2f2f2; font-size: 0.85rem; cursor: pointer; border-radius: 6px; }
.pm-cand:last-child { border-bottom: none; }
.pm-cand:hover { background: #effafa; }

@media (max-width: 575.98px) {
    .pitch-wrap { max-width: 100%; }
    .mini-gw { min-width: 13px; font-size: 0.55rem; }
}

/* ============================================================
   MY TEAM — v4 (two-col, sub states, dual slider, richer rows)
   ============================================================ */

/* Fixture cells under predicted points: opponent above the pts */
.mini-gw {
    display: inline-flex; flex-direction: column; align-items: center;
    line-height: 1.1; min-width: 20px; padding: 1px 3px;
}
.mini-gw b { font-size: 0.5rem; font-weight: 700; letter-spacing: -0.2px; }

/* Bench: position label above the kit */
.bench-pos { font-size: 0.6rem; font-weight: 700; color: #888; text-transform: uppercase; margin-bottom: 1px; text-align: center; }

/* Substitution highlighting */
.sub-mode .player { outline: none; }
.player.sub-ineligible { opacity: 0.3; pointer-events: none; }
.player.sub-eligible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
.player.sub-source { outline: 2px dashed #e03131; outline-offset: 2px; border-radius: 8px; }

/* Dual price slider */
.price-range { margin-top: 2px; }

/* Reset-filters button sizing */
#pickerReset, #builderReset { flex: 0 0 auto; }

/* Richer Find-players rows */
.picker-row { border-bottom: 1px solid #f2f2f2; cursor: pointer; padding: 6px 8px; }
.picker-row:last-child { border-bottom: none; }
.picker-row:hover { background: #effafa; }
.pr-top { display: flex; align-items: center; gap: 8px; }
.pr-gws { justify-content: flex-start; margin-top: 4px; }
.br-form { color: #666; font-size: 0.72rem; width: 46px; white-space: nowrap; }

@media (max-width: 575.98px) {
    .pitch-wrap { max-width: 100%; }
}

/* ============================================================
   MY TEAM — v5 (reusable search table, aligned slider, polish)
   ============================================================ */

/* --- Dual price slider: aligned track + purple fill --- */
.range-wrap { position: relative; height: 24px; }
.range-wrap::before {
    content: ''; position: absolute; left: 0; right: 0; top: 10px;
    height: 4px; background: #ddd; border-radius: 2px;
}
.range-fill { position: absolute; top: 10px; height: 4px; background: var(--accent); border-radius: 2px; }
.range-wrap input[type=range] {
    position: absolute; left: 0; top: 0; width: 100%; height: 24px; margin: 0;
    background: transparent; -webkit-appearance: none; appearance: none; pointer-events: none;
}
.range-wrap input[type=range]::-webkit-slider-runnable-track { background: transparent; height: 24px; border: none; }
.range-wrap input[type=range]::-moz-range-track { background: transparent; height: 24px; border: none; }
.range-wrap input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; pointer-events: auto; height: 16px; width: 16px; margin-top: 4px;
    border-radius: 50%; background: var(--accent); border: 2px solid #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3); cursor: pointer;
}
.range-wrap input[type=range]::-moz-range-thumb {
    pointer-events: auto; height: 16px; width: 16px; border: 2px solid #fff;
    border-radius: 50%; background: var(--accent); box-shadow: 0 1px 3px rgba(0,0,0,0.3); cursor: pointer;
}
.price-label { font-size: 0.72rem; color: #666; margin-bottom: 2px; }

/* --- Reusable player search --- */
.ps-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; }
.ps-controls .ps-search { flex: 1 1 150px; min-width: 130px; }
.ps-controls .ps-pos, .ps-controls .ps-team { flex: 0 0 auto; max-width: 130px; }
.ps-controls .ps-price { flex: 1 1 170px; min-width: 150px; }
.ps-list { max-height: 480px; overflow-y: auto; border: 1px solid #eee; border-radius: 8px; }
.ps-table { font-size: 0.8rem; margin: 0; }
.ps-table thead th {
    position: sticky; top: 0; background: #f6fcfc; z-index: 1;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.02em; color: #777;
    border-bottom: 2px solid #eee; white-space: nowrap; padding: 6px;
}
.ps-sortable { cursor: pointer; user-select: none; }
.ps-sortable:hover { color: var(--accent-dark); }
.ps-arrow { color: #c3c3c3; }
.ps-table td { vertical-align: middle; padding: 5px 6px; }
.ps-name { display: flex; align-items: center; gap: 6px; white-space: nowrap; font-weight: 600; }
.ps-table .player-gws { justify-content: flex-start; }
.ps-row:hover { background: #effafa; cursor: pointer; }
.ps-rec-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent-dark); font-weight: 700; margin: 2px 0 4px; }
.ps-rec .ps-table { border: 1px solid #d9f3f3; border-radius: 8px; }
/* The recommended-transfers block reuses .ps-list for its markup, and inherits
   a scroll pane sized for the full ratings table: a 260px floor, a 70vh cap and
   its own border. It is three rows. That left an eighth of the screen of empty
   bordered box sitting above the real table, inside a second border, every time
   a player was marked out.
   Sized to its contents instead. Nothing here needs the CLS reservation the
   other blocks carry (see the note further down): this one is built and
   destroyed by a click rather than arriving with a fetch, so there is no first
   paint for it to shift. Selector is deliberately two classes deep - it has to
   out-specify the later .ps-list rules rather than merely follow them. */
.ps-rec .ps-list {
    min-height: 0; max-height: none; overflow-y: visible; border: 0;
}
.ps-act { white-space: nowrap; padding: 2px 8px; font-size: 0.72rem; }

/* --- Card polish: fixtures under the name --- */
.player-name-pill { font-weight: 600; }
.player-stat { font-size: 0.72rem; color: #555; }
.player-gws { display: flex; gap: 3px; justify-content: center; margin-top: 3px; }
.mini-gw { min-width: 22px; padding: 2px 3px; border-radius: 4px; }
.mini-gw b { font-size: 0.52rem; }

.section-heading { font-weight: 700; color: var(--accent-dark); }

/* ============================================================
   MY TEAM — v6 (inline 3-col, aligned search table)
   ============================================================ */

/* Fixed layout so header + body + recommended table all align */
.ps-table { table-layout: fixed; width: 100%; }
.ps-table th, .ps-table td {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle;
}
/* Column widths are further down, in the v9 block, and are set by column class
   rather than by position - the Players tab carries an ownership column that My
   Team doesn't, so "the sixth cell" is not the same column on the two tables. */
.ps-table img.shirt-sm { height: 16px; width: 16px; object-fit: contain; vertical-align: middle; flex: 0 0 auto; }
.ps-table .player-gws { flex-wrap: nowrap; margin-top: 0; }
.ps-table .mini-gw { min-width: 27px; }
.ps-name span { overflow: hidden; text-overflow: ellipsis; }

/* Recommended-transfers cards fit a narrow column */
.transfer-meta { flex-wrap: wrap; gap: 4px 8px; }
.make-tr { padding: 2px 8px; font-size: 0.72rem; }

/* ============================================================
   MY TEAM — v7 (contained cards, roomier pitch, pending-out)
   ============================================================ */

.pitch-row { display: flex; justify-content: space-evenly; gap: 4px; margin-bottom: 10px; flex-wrap: nowrap; }
.bench-row { justify-content: space-evenly; gap: 4px; flex-wrap: nowrap; }

/* Each player is a tidy little contained card. flex-shrink (not width:auto)
   so 5-wide DEF/MID rows compress to fit one line instead of wrapping. */
.player {
    width: 76px; flex: 0 1 76px; min-width: 0; padding: 5px 4px 6px; border-radius: 10px;
    background: rgba(255,255,255,0.78); box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
.player-kit { position: relative; }
.player-name-pill { font-size: 0.72rem; margin-top: 2px; }
.player-gws { gap: 3px; margin-top: 4px; }
.mini-gw { min-width: 24px; }

/* Players marked for transfer out: greyed with a + to fill */
.player.pending-out .out-plus {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 1.5rem; font-weight: 800; color: #e03131;
}

/* Empty squad slot — tap to search for a player to fill it */
.player.empty-slot {
    border: 1px dashed #bbb; background: rgba(255,255,255,0.35); box-shadow: none;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.player.empty-slot .empty-slot-icon {
    font-size: 1.3rem; font-weight: 700; color: #999; line-height: 1;
    height: 44px; display: flex; align-items: center; justify-content: center;
}
.player.empty-slot .player-name-pill { background: transparent; color: #888; box-shadow: none; }
.player.empty-slot.empty-active { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }

/* ============================================================
   MY TEAM — v8 (transparency, taller table, mobile pitch)
   ============================================================ */

/* More transparent player cards */
.player { background: rgba(255,255,255,0.45); box-shadow: 0 1px 2px rgba(0,0,0,0.05); }

/* Taller search table (extends down toward the news feed) */
.ps-list { max-height: 70vh; }

/* Compact recommended-transfers cards for the narrow column */
.transfer-rec { font-size: 0.75rem; padding: 6px 8px; }
.transfer-line { gap: 4px; flex-wrap: wrap; }
.transfer-meta { font-size: 0.7rem; gap: 3px 6px; }
.tr-out, .tr-in { display: inline-flex; align-items: center; gap: 2px; }

/* Mobile: keep the formation on one line per row, shrink cards */
@media (max-width: 575.98px) {
    .pitch-row, .bench-row { flex-wrap: nowrap; gap: 3px; justify-content: space-between; }
    .player { width: auto; min-width: 0; flex: 1 1 0; padding: 2px 1px 3px; }
    .kit { height: 30px; }
    .player-name-pill {
        font-size: 0.56rem; line-height: 1.15;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .player-gws { gap: 1px; margin-top: 2px; }
    .mini-gw { min-width: 0; flex: 1 1 0; padding: 1px 0; }
    .mini-gw b { font-size: 0.42rem; }
    .bench-pos { font-size: 0.5rem; }
    .cap-badge { font-size: 0.55rem; padding: 0 3px; }
}

/* ============================================================
   MY TEAM — v9 (table column widths, mobile card size)
   ============================================================ */

/* Percentage widths → columns line up across header, body and the
   recommended table; Player column trimmed to free room for the rest.
   Sums to 100 in each set.

   Keyed on a class per column, not on nth-child. The Players tab shows an
   ownership column and My Team doesn't, so the two tables no longer have the
   same column in the same place - and both are rendered twice over, once by
   seo_tables.py on the server and again by createPlayerSearch() when the pool
   lands. Position-based widths mean four places to keep in step and a silent
   failure (every column off by one) when they drift. The class is written by
   both renderers from the same column key. */
.ps-table { table-layout: fixed; }

/* Without ownership - My Team's copy of the widget. Sums to 100. */
.ps-table .col-watch     { width: 5%;  }
.ps-table .col-web_name  { width: 25%; }
.ps-table .col-pos       { width: 8%;  }
.ps-table .col-team_name { width: 11%; }
.ps-table .col-form      { width: 11%; }
.ps-table .col-rating    { width: 13%; }
.ps-table .col-cost      { width: 9%;  }
.ps-table .col-fixtures  { width: 18%; }

/* With it - the Players tab. Every other column gives up a little rather than
   the new one coming out of the fixtures, which are the column that clips. */
.ps-table-own .col-watch     { width: 5%;  }
.ps-table-own .col-web_name  { width: 22%; }
.ps-table-own .col-pos       { width: 6%;  }
.ps-table-own .col-team_name { width: 10%; }
.ps-table-own .col-form      { width: 10%; }
.ps-table-own .col-rating    { width: 10%; }
.ps-table-own .col-cost      { width: 8%;  }
.ps-table-own .col-owned     { width: 11%; }
.ps-table-own .col-fixtures  { width: 18%; }

/* Fit all three fixture tiles inside the Next-3 column. */
.ps-table td.col-fixtures { overflow: visible; }
.ps-table td.col-fixtures .player-gws { display: flex; flex-wrap: nowrap; gap: 1px; justify-content: flex-start; }
.ps-table td.col-fixtures .mini-gw { flex: 1 1 0; min-width: 0; }
.ps-row { cursor: pointer; }
.ps-name { gap: 4px; }
.ps-name span { min-width: 0; }

/* Mobile: compact, non-stretched player cards */
@media (max-width: 575.98px) {
    .pitch-row, .bench-row { justify-content: center; gap: 3px; flex-wrap: nowrap; }
    .player { flex: 0 0 auto; width: 18.5%; max-width: 66px; min-width: 0; }
    .mini-gw { min-width: 0; flex: 1 1 0; }
}

/* ============================================================
   MY TEAM — v10 (name fix, widths, grey not red, mobile room)
   ============================================================ */

/* Robust name cell — always shows the name (truncated if needed) */

/* Column widths live in the v9 block above, keyed on .col-* rather than on
   position - there were four overlapping sets of nth-child widths here, each
   silently overriding the last, and none of them could survive a table with a
   different number of columns. */
.ps-table thead th { padding: 6px 4px; font-size: 0.66rem; }
.ps-table td { padding: 5px 4px; }
.ps-arrow { font-size: 0.85em; }
.ps-table .rating-badge { padding: 0 4px; }

/* Marked for transfer out: just greyed (with the +). Active one keeps the
   grey but gets a subtle purple ring — no red box. */
.player.pending-out { opacity: 0.4; filter: grayscale(1); }
.player.pending-out .out-plus { color: #444; }
.player.pending-active {
    opacity: 0.45; filter: grayscale(1); background: rgba(255,255,255,0.45);
    outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 10px;
}
.player.pending-active .out-plus { color: #444; }

/* Mobile: reclaim side space so the whole formation + bench fit */
@media (max-width: 575.98px) {
    .container { padding-left: 6px; padding-right: 6px; }
    #teamBody { --bs-gutter-x: 0.5rem; }
    .pitch-wrap { max-width: 100%; }
    .pitch-row, .bench-row { justify-content: space-between; gap: 2px; flex-wrap: nowrap; }
    .player { flex: 0 0 auto; width: 19%; max-width: none; min-width: 0; }
    .bench-row .player { width: 23%; }
    .ps-table thead th { font-size: 0.6rem; padding: 5px 2px; }
    .ps-table td { padding: 4px 2px; }
}

/* ============================================================
   MY TEAM — v11 (name cell fix, mobile GK centering)
   ============================================================ */

/* v10 set display:block on the <td>, which removed it from the table
   layout and hid the name. Keep it a real table cell that truncates. */
.ps-name {
    display: table-cell; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; font-weight: 600;
}
.ps-name img.shirt-sm { display: inline; vertical-align: middle; margin-right: 4px; }
.ps-name span { display: inline; color: inherit; }

/* Mobile: centre every pitch/bench row (so a lone GK is centred, not left) */
@media (max-width: 575.98px) {
    .pitch-row, .bench-row { justify-content: space-evenly; gap: 2px; flex-wrap: nowrap; }
    .player { width: 18%; }
    .bench-row .player { width: 20%; }
}

/* ============================================================
   MY TEAM — v12 (badge, fixtures, table scroll, info-tip, buttons)
   ============================================================ */

/* Captain/vice badge: top-right of the whole card */
.player { position: relative; }
.cap-badge { position: absolute; top: 2px; right: 2px; left: auto; z-index: 3; }

/* Upcoming-fixture cells: opponent + H/A always on one line, pts below */
.mini-gw { white-space: nowrap; overflow: hidden; }
.mini-gw b { white-space: nowrap; display: block; }

/* Search table: let it scroll sideways so fixtures are never cut off */
.ps-list { overflow: auto; }
.ps-table { min-width: 470px; }
.ps-table .player-gws { flex-wrap: nowrap; }

/* Player pop-up buttons: smaller, wrap neatly */
.pm-actions { gap: 5px; }
.pm-btn { font-size: 0.75rem; padding: 3px 9px; }

@media (max-width: 575.98px) {
    /* Info tooltip: centre it on screen, not pinned to the bottom */
    .info-tip {
        position: fixed; left: 50%; top: 50%; right: auto; bottom: auto;
        transform: translate(-50%, -50%);
        width: 88vw; max-width: 320px;
    }
    .pm-btn { font-size: 0.7rem; padding: 3px 7px; }
    .mini-gw b { font-size: 0.42rem; }
}

/* ============================================================
   MY TEAM — v13 (forceful badge + tooltip, disabled rows, fixtures)
   ============================================================ */

/* Captain/vice badge pinned to the card corner (high specificity) */
.pitch .player, .bench .player { position: relative; }
.pitch .cap-badge, .bench .cap-badge {
    position: absolute; top: 3px; right: 3px; left: auto; bottom: auto; z-index: 4;
}

/* Greyed, unclickable rows (e.g. 3-from-a-club already selected) */
.ps-disabled { opacity: 0.4; cursor: not-allowed; }
.ps-disabled:hover { background: transparent; }

@media (max-width: 575.98px) {
    /* Force the info tooltip to screen-centre, overriding earlier bottom rule */
    .info-icon .info-tip {
        position: fixed; left: 50%; right: auto; top: 50%; bottom: auto;
        transform: translate(-50%, -50%); width: 88vw; max-width: 320px;
    }
}

/* ============================================================
   MY TEAM — v14 (chips)
   ============================================================ */
.chip-row {
    display: flex; flex-direction: column; padding: 6px 10px; margin-bottom: 6px;
    border: 1px solid #eee; border-radius: 8px; background: #f6fcfc;
}
.chip-row.chip-hot { border-color: var(--accent); background: #e3f6f6; }
.chip-name { font-weight: 700; font-size: 0.85rem; color: var(--accent-dark); }
.chip-note { font-size: 0.72rem; color: #666; }
.chip-row.chip-hot .chip-note { color: var(--accent-dark); font-weight: 600; }
#refreshTransfersBtn { font-size: 1.1rem; line-height: 1; text-decoration: none; }

/* ============================================================
   MY TEAM — v15 (list view, chips bar)
   ============================================================ */

/* List view: drop the pitch graphic, give the table room */
.pitch.list-mode { background: none; padding: 0; }
.squad-list { width: 100%; font-size: 0.82rem; }
.squad-list td, .squad-list th { padding: 6px 8px; }

/* Chips strip above the pitch */
.chips-bar:empty { display: none; }
.chip-pill {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 12px; border-radius: 999px; cursor: pointer; user-select: none;
    background: #e3f6f6; color: var(--accent-dark); font-size: 0.8rem; font-weight: 600;
    border: 1px solid #c2eded;
}
.chip-pill:hover { background: #cff0f0; }
.chip-pill.chip-hot { background: var(--accent); color: #fff; border-color: var(--accent); }

/* ============================================================
   MY TEAM — v16 (chip cards, debt)
   ============================================================ */
.chip-card {
    width: 96px; text-align: center; padding: 8px 6px; border-radius: 12px;
    background: #fff; border: 1px solid #eee; cursor: pointer;
    box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
.chip-img { width: 44px; height: 44px; object-fit: contain; }
.chip-card-name { font-size: 0.7rem; font-weight: 600; margin-top: 4px; line-height: 1.1; }
.chip-status {
    font-size: 0.58rem; font-weight: 700; margin-top: 5px; padding: 2px 0;
    border-radius: 5px; text-transform: uppercase; letter-spacing: 0.03em;
}
.chip-card.chip-avail { border-color: #cde9d0; }
.chip-card.chip-avail .chip-status { background: #37b24d; color: #fff; }
.chip-card.chip-unavail { opacity: 0.5; filter: grayscale(1); }
.chip-card.chip-unavail .chip-status { background: #d5d5d5; color: #777; }

/* Over budget (in debt) */
.stat-neg .stat-value { color: #e03131; }

/* ============================================================
   MY TEAM — v17 (chips aligned to pitch, tighter, mobile)
   ============================================================ */

/* Chips span the pitch width and spread edge-to-edge */
.chips-bar { display: flex; flex-wrap: nowrap; justify-content: space-between; gap: 6px; width: 100%; }
.pitch-wrap { max-width: 100%; }

/* Tighten the gap between the header stat chips and the chips row */
#teamBody { margin-top: 4px !important; }
.stat-chips { margin-bottom: 6px; }

/* Chips scale to the pitch on every screen (share the row width) */
.chip-card {
    flex: 1 1 0; min-width: 0; width: auto; max-width: 110px;
    padding: 6px 4px; box-sizing: border-box;
}
.chip-img { width: 40px; height: 40px; }
.chip-status { font-size: 0.55rem; }

@media (max-width: 575.98px) {
    .chip-img { width: 34px; height: 34px; }
    .chip-card-name { font-size: 0.6rem; }
    .chip-status { font-size: 0.52rem; }
    .chip-card { padding: 4px 3px; }
}

/* ============================================================
   v18 (brand mark, footer, inline chip info, news stamps,
        tables that don't drag sideways on a phone)
   ============================================================ */

/* --- App icon in the navbar --- */
.navbar-brand.brand { display: inline-flex; align-items: center; gap: 8px; }
.brand-icon {
    width: 30px; height: 30px; border-radius: 7px; object-fit: cover;
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
}

/* --- Footer ---
   Sticky-footer layout: body is a full-height column, and the footer's auto
   top margin eats the leftover space, so on a short page (the FPL ID prompt,
   an error state) it sits on the bottom of the screen instead of floating
   mid-page with white space under it. On a long page the auto margin resolves
   to 0 and it just follows the content as normal.
   The player modal and tooltips are position:fixed, so they're out of flow and
   never become flex items here. */
body {
    min-height: 100vh;
    min-height: 100dvh;   /* ignores mobile browser chrome that comes and goes */
    display: flex;
    flex-direction: column;
}
.navbar, .site-footer { flex-shrink: 0; }

.site-footer {
    border-top: 1px solid #eee;
    background: #f6fcfc;
    padding: 18px 0 22px;
    margin-top: auto;
}
.footer-inner {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: space-between; gap: 10px 18px;
}
.footer-brand {
    display: inline-flex; align-items: center; gap: 8px;
    font-weight: 700; color: var(--accent-dark);
}
.footer-icon { width: 24px; height: 24px; border-radius: 6px; object-fit: cover; }
.footer-right {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px;
    justify-content: flex-end;
}
.footer-links { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.footer-links a {
    color: var(--accent-dark); font-size: 0.82rem; font-weight: 500; text-decoration: none;
}
.footer-links a:hover { color: var(--accent); text-decoration: underline; }
.footer-note {
    font-size: 0.68rem; color: #999; margin: 10px 0 0; line-height: 1.5; max-width: 720px;
}

/* --- Chip explanation, inline under the chips row (mobile) --- */
.chips-bar { margin-bottom: 8px; }
.chip-info {
    background: #effafa; border: 1px solid #c2eded; border-radius: 10px;
    padding: 8px 10px; margin-bottom: 8px;
    font-size: 0.78rem; line-height: 1.35; color: var(--accent-dark);
}
/* Wide screens keep the floating tooltip — there's room beside the chip there,
   and the panel would push the pitch down on every hover. */
@media (min-width: 768px) { .chip-info { display: none; } }

/* --- News feed heading row + refresh --- */
.news-head-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.news-updated { font-size: 0.66rem; color: #aaa; margin-left: auto; white-space: nowrap; }
.news-refresh {
    border: none; background: none; color: var(--accent);
    font-size: 1rem; line-height: 1; padding: 0 2px; cursor: pointer;
}
.news-refresh:hover { color: var(--accent-dark); }
.news-refresh.spinning { animation: news-spin 0.8s linear infinite; }
@keyframes news-spin { to { transform: rotate(360deg); } }

/* --- Scroll containers: no rubber-banding out into the page --- */
.ps-list, .table-responsive { overscroll-behavior-x: contain; }

@media (max-width: 575.98px) {
    /* Player tables fit the phone's width instead of scrolling sideways inside
       their own box — that inner horizontal drag is what makes the table feel
       loose while you're trying to scroll the page past it. Team drops out (the
       shirt already identifies the club) to buy back the room, and the
       remaining widths are re-shared so nothing is clipped. touch-action:pan-y
       makes the table ignore sideways gestures entirely. */
    .ps-list { overflow-x: hidden; touch-action: pan-y; }
    .ps-table, .underperf-table { min-width: 0; width: 100%; }

    .ps-table .col-team_name { display: none; }               /* Team */
    .ps-table .col-web_name  { width: 33%; }
    .ps-table .col-pos       { width: 10%; }
    .ps-table .col-form      { width: 13%; }
    .ps-table .col-rating    { width: 15%; }
    .ps-table .col-cost      { width: 12%; }
    .ps-table .col-fixtures  { width: 17%; }

    /* Ownership stays on the phone - it is the reason to be on that table -
       so the rest give up a few points each to fit it. */
    .ps-table-own .col-web_name { width: 28%; }
    .ps-table-own .col-pos      { width: 9%;  }
    .ps-table-own .col-form     { width: 11%; }
    .ps-table-own .col-rating   { width: 13%; }
    .ps-table-own .col-cost     { width: 10%; }
    .ps-table-own .col-owned    { width: 12%; }
    .ps-table-own .col-fixtures { width: 17%; }

    /* Same treatment for the underperforming table. Metric goes too: the
       Attackers/Defenders pill above it already says which metric it is. */
    .underperf-table th:nth-child(3), .underperf-table td:nth-child(3),
    .underperf-table th:nth-child(4), .underperf-table td:nth-child(4) { display: none; }
    .underperf-table th:nth-child(1), .underperf-table td:nth-child(1) { width: 27%; }   /* Player */
    .underperf-table th:nth-child(2), .underperf-table td:nth-child(2) { width: 10%; }   /* Pos */
    .underperf-table th:nth-child(5), .underperf-table td:nth-child(5) { width: 13%; }   /* Expected */
    .underperf-table th:nth-child(6), .underperf-table td:nth-child(6) { width: 12%; }   /* Actual */
    .underperf-table th:nth-child(7), .underperf-table td:nth-child(7) { width: 13%; }   /* Diff */
    .underperf-table th:nth-child(8), .underperf-table td:nth-child(8) { width: 11%; }   /* £m */
    .underperf-table th:nth-child(9), .underperf-table td:nth-child(9) { width: 14%; }   /* Next 3 */

    /* Nested rows (leagues + news) reset --bs-gutter-x to Bootstrap's 1.5rem
       default, so their negative margins overshot the 0.5rem padding on the
       column holding them and pushed the page a couple of px wider than the
       screen — enough to make the whole page jiggle sideways on a phone. */
    #teamBody .row, #teamBody, .ai-body { --bs-gutter-x: 0.5rem; }

    /* Footer stacks */
    .footer-inner { flex-direction: column; align-items: flex-start; gap: 8px; }
    .footer-right { justify-content: flex-start; gap: 10px 14px; }
    .footer-links { gap: 4px 14px; }
    .footer-links a { font-size: 0.78rem; }

    /* AI squad table: drop Team, same reasoning as the other player tables */
    .ai-squad-table th:nth-child(3), .ai-squad-table td:nth-child(3) { display: none; }
    .ai-head { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ============================================================
   AI BEST XI
   ============================================================ */
.ai-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.ai-gw-nav { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }

/* Points pill under each pitch card — the AI tab's equivalent of the
   next-3-fixtures strip on the My Team pitch. */
.ai-pts {
    margin-top: 3px; font-size: 0.68rem; font-weight: 700; color: #1a1a1a;
    background: rgba(255,255,255,0.9); border-radius: 3px; padding: 1px 3px;
}
.ai-actual { color: var(--accent-dark); }

/* Bench rows in the squad table are dimmed — they don't score. */
.ai-benched td { opacity: 0.55; }
.ai-arm {
    display: inline-block; background: #1a1a1a; color: #fff; border-radius: 3px;
    font-size: 0.58rem; font-weight: 700; padding: 0 4px; margin-left: 4px; vertical-align: middle;
}
.ai-arm.vice { background: #6c757d; }

/* Predicted-vs-actual delta */
.ai-over  { color: #2e9b57; font-weight: 700; }
.ai-under { color: #e03131; font-weight: 700; }

.ai-squad-table, .ai-history-table { min-width: 0; width: 100%; }
.ai-squad-table th:nth-child(1), .ai-squad-table td:nth-child(1) { width: 34%; }
.ai-squad-table th:nth-child(2), .ai-squad-table td:nth-child(2) { width: 11%; }
.ai-squad-table th:nth-child(3), .ai-squad-table td:nth-child(3) { width: 17%; }
.ai-squad-table th:nth-child(4), .ai-squad-table td:nth-child(4) { width: 13%; }
.ai-squad-table th:nth-child(5), .ai-squad-table td:nth-child(5) { width: 13%; }
.ai-squad-table th:nth-child(6), .ai-squad-table td:nth-child(6) { width: 12%; }
/* ============================================================
   AI MANAGER
   ============================================================ */
.mgr-why { font-size: 0.7rem; color: #777; margin-top: 4px; line-height: 1.35; }
.mgr-chip-play {
    background: var(--accent); color: #fff; border-radius: 8px;
    padding: 6px 10px; font-size: 0.8rem; margin-bottom: 8px;
}
.mgr-chip-note {
    display: flex; align-items: baseline; gap: 8px;
    border: 1px solid #eee; border-radius: 8px;
    padding: 6px 10px; margin-bottom: 5px; font-size: 0.76rem;
}
/* A chip that has met its threshold - the one thing worth spotting at a glance */
.mgr-chip-note.ready { border-color: #cde9d0; background: #f3fbf4; }
.mgr-chip-name {
    font-weight: 700; color: var(--accent-dark); text-transform: uppercase;
    font-size: 0.62rem; letter-spacing: 0.02em;
    /* Was a fixed 62px basis, which is narrower than "BENCH BOOST" needs, so
       it wrapped. Size to the content and refuse to break instead. */
    flex: 0 0 auto; white-space: nowrap;
}
.mgr-chip-detail { color: #666; min-width: 0; }
/* Where the chips it is NOT playing are going. Sits above "watching" because a
   plan with gameweeks on it says more than a list of weeks to keep an eye on. */
.mgr-chip-schedule {
    font-size: 0.72rem; color: #666; margin-top: 8px;
    border-top: 1px dashed #eee; padding-top: 6px;
}
.mgr-upcoming { font-size: 0.72rem; color: #888; margin-top: 6px; }

/* ============================================================
   AI pitches: fixture beside the projection; header nav right
   ============================================================ */

/* A projected score on its own is a bare number - the fixture next to it is
   what makes it judgeable. Same difficulty colour key as everywhere else. */
.ai-line { display: flex; align-items: center; justify-content: center; gap: 3px; margin-top: 3px; }
.ai-fix {
    font-size: 0.55rem; font-weight: 700; color: #1a1a1a;
    border-radius: 3px; padding: 1px 3px; line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ai-line .ai-pts { margin-top: 0; }

/* Heading takes the room; the gameweek arrows stay pinned to the right. */
.ai-head-text { flex: 1 1 240px; min-width: 0; }
.ai-head .ai-gw-nav { margin-left: auto; flex: 0 0 auto; }

@media (max-width: 575.98px) {
    .ai-fix { font-size: 0.46rem; padding: 0 2px; }
    .ai-line { gap: 2px; }
    /* Override the earlier column stack: keep it a wrapping row so the arrows
       can still sit hard right once the heading takes the full width. */
    .ai-head { flex-direction: row; flex-wrap: wrap; align-items: flex-start; }
    .ai-head-text { flex: 1 1 100%; }
    .ai-head .ai-gw-nav { margin-left: auto; margin-top: 4px; }
}

/* ---- AI pane headings match the My Team team-name scale ---- */
.ai-title { color: var(--accent-dark); font-weight: 700; }

/* ---- Recommended-transfers refresh: label stays, icon spins ---- */
.rt-icon { display: inline-block; }
.rt-icon.spinning { animation: news-spin 0.8s linear infinite; }

/* ---- AI player box: projection on top, fixture beneath, one tile ----
   Same shape and difficulty colouring as .mini-gw on the My Team pitch, so the
   two pitches read as the same component rather than two similar ones. */
.ai-mini {
    display: inline-flex; flex-direction: column; align-items: center;
    border-radius: 4px; padding: 1px 3px; line-height: 1.15;
    min-width: 40px; color: #1a1a1a; white-space: nowrap;
}
.ai-mini span { font-size: 0.5rem; font-weight: 600; letter-spacing: -0.2px; }
.ai-mini b.ai-actual { color: var(--accent-dark); }

@media (max-width: 575.98px) {
    .ai-mini { min-width: 0; padding: 1px 2px; }
    .ai-mini b { font-size: 0.62rem; }
    .ai-mini span { font-size: 0.42rem; }
}

/* ============================================================
   LIVE GAMEWEEK
   ============================================================ */
.live-banner {
    background: #eef7f0; border: 1px solid #cde9d0; color: #1f6b36;
    border-radius: 8px; padding: 8px 12px; font-size: 0.85rem;
}
.live-prov { color: #8a6d1f; font-size: 0.78rem; }
/* Quiet pulse: the number moves on its own, so say so without nagging. */
.live-dot {
    display: inline-block; width: 7px; height: 7px; border-radius: 50%;
    background: #2e9b57; margin: 0 5px 0 8px;
    animation: live-pulse 1.8s ease-in-out infinite;
}
@keyframes live-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

/* Actual points replace the fixture strip on a locked gameweek. */
.live-pts {
    display: inline-block; background: var(--accent); color: #fff;
    font-size: 0.78rem; font-weight: 700; border-radius: 4px; padding: 1px 7px;
}
.live-pts.live-cap { background: #1a1a1a; }

/* Nothing on a locked pitch is clickable-to-edit. */
.gw-locked .player { cursor: default; }

@media (max-width: 575.98px) {
    .live-pts { font-size: 0.66rem; padding: 0 5px; }
    .live-banner { font-size: 0.78rem; padding: 6px 9px; }
}


/* ============================================================
   Tile + badge polish
   ============================================================ */

/* `.ps-name span { color: inherit }` further up outranks `.ai-arm` on
   specificity, so the armband badge was rendering dark-on-dark. Qualify it to
   win rather than reaching for !important. */
.ps-name .ai-arm, .ai-squad-table .ai-arm { color: #fff; }

/* AI pitch tile: opponent above, score below, both a size down so a long
   "BOU (A)" fits the box instead of being clipped. */
.ai-mini b {
    font-size: 0.5rem; font-weight: 700; letter-spacing: -0.2px;
    display: block; white-space: nowrap;
}
.ai-mini span { font-size: 0.62rem; font-weight: 700; }
.ai-mini span.ai-actual { color: var(--accent-dark); }

/* The chip actually being played this week reads first. */
.chip-card.chip-playing { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(0, 172, 174, 0.25); }
.chip-card.chip-playing .chip-status { background: var(--accent); color: #fff; }

@media (max-width: 575.98px) {
    .ai-mini b { font-size: 0.42rem; }
    .ai-mini span { font-size: 0.55rem; }
}

/* ============================================================
   Fitting text into its box
   ============================================================ */

/* Upcoming-fixture cells on the My Team pitch. Bracketing the venue made the
   label longer ("ARS (H)" vs "ARS H"), so the opponent line drops a size and
   the tile uses the full width of its column instead of a fixed minimum. */
/* These tiles are ~21px wide on the pitch, and horizontal padding is 4px of
   that - the difference between the label fitting and being clipped. The
   background colour still reads as a box without it. */
.player-gws .mini-gw { min-width: 0; flex: 1 1 0; padding: 1px 0; font-size: 0.56rem; }
.player-gws .mini-gw b { font-size: 0.38rem; letter-spacing: -0.3px; }

@media (max-width: 575.98px) {
    /* All three fixtures show. A pitch card is ~64px on a phone, so each tile
       gets ~20px - which fits now that the label is "ARS(H)" rather than
       "ARS (H)" and the tiles carry no horizontal padding. */
    .player-gws .mini-gw { font-size: 0.5rem; padding: 1px 0; }
    .player-gws .mini-gw b { font-size: 0.36rem; }
}

/* Chip names on one line - "Triple Captain" wrapped and pushed its card taller
   than the others. Cards share the row evenly rather than being width-capped,
   so there's room to use. */
.chips-bar .chip-card { max-width: none; }
.chip-card-name {
    font-size: 0.62rem; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 575.98px) {
    .chip-card-name { font-size: 0.5rem; letter-spacing: -0.2px; }
    .chip-card { padding: 4px 2px; }
}


/* ============================================================
   AVAILABILITY BAND
   ============================================================ */

/* Sits directly above the name pill and spans the card, so a flagged player
   reads as a problem from across the pitch. Replaces the 11px corner dot,
   which carried the same information but only if you knew to hover it. */
.avail-band {
    display: block;
    margin-top: 2px;
    border-radius: 3px 3px 0 0;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1.25;
    text-align: center;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
}
/* Colour tracks likelihood, not just "flagged": 75% and 25% are very different
   decisions and shouldn't look the same. */
/* Every card carries a band now, so the fit state has to be quiet - otherwise
   the pitch becomes a wall of colour and the real warnings stop standing out. */
.avail-full     { background: #40916c; }   /* 100% - fit */
.avail-likely   { background: #f0a202; }   /* 75%  - probably fine */
.avail-doubt    { background: #e8710a; }   /* 50%  - real doubt */
.avail-unlikely { background: #d9480f; }   /* 25%  - probably not */
.avail-out      { background: #c92a2a; }   /* 0%   - injured/suspended/out */

/* The name pill squares off underneath so band and pill read as one label. */
.avail-band + .player-name-pill { border-radius: 0 0 3px 3px; margin-top: 0; }

@media (max-width: 575.98px) {
    .avail-band { font-size: 0.5rem; line-height: 1.2; }
}

/* Heading for the four tool pages. Deliberately smaller than the prose pages'
   h1: those open with a title and a lede, these open with a heading and then a
   dense table or pitch, and a 2rem heading above it just pushes the thing you
   came for below the fold on a phone. */
.tab-page-title {
    color: var(--accent-dark); font-weight: 700; font-size: 1.35rem;
    margin: 0 0 14px;
}

/* ============================================================
   CONTENT PAGES (about / privacy / contact)
   ============================================================ */

/* Prose, not a dashboard: a measure that's comfortable to read rather than the
   full container width the tool pages use. */
.content-page { max-width: 760px; margin: 0 auto; padding-top: 4px; }
.content-page h1.page-title {
    color: var(--accent-dark); font-weight: 700; margin-bottom: 10px;
}
.content-page .page-lede { font-size: 1.02rem; color: #444; line-height: 1.6; }
.content-page h2 {
    color: var(--accent-dark); font-weight: 700; font-size: 1.15rem;
    margin: 28px 0 8px;
}
.content-page h3 {
    color: #333; font-weight: 700; font-size: 0.98rem; margin: 18px 0 6px;
}
.content-page p, .content-page li { line-height: 1.65; color: #333; }
.content-page ul, .content-page ol { padding-left: 1.2rem; }
.content-page li { margin-bottom: 6px; }
.content-page a { color: var(--accent-dark); }
.content-page code {
    background: #f1f7f7; color: var(--accent-dark); padding: 1px 5px;
    border-radius: 4px; font-size: 0.88em; word-break: break-all;
}

/* FAQ. Each question is a card so the eye can find the one it wants without
   reading the answers - a plain run of h2/p is fine for an essay like /about
   but poor for a page people scan. Left rule rather than a full border keeps
   twelve of them from looking like twelve boxes. */
.faq-list { margin-top: 22px; }
.faq-item {
    border-left: 3px solid var(--accent);
    background: #fafcfc;
    border-radius: 0 8px 8px 0;
    padding: 12px 16px;
    margin-bottom: 12px;
}
.content-page .faq-item h2.faq-q {
    font-size: 1.02rem; margin: 0 0 6px; color: var(--accent-dark);
}
.content-page .faq-item p.faq-a { margin: 0; color: #333; }

/* ---- Injury news page ---------------------------------------------------
   Flag colours run worst-to-mildest, and they are the only colour on the page:
   the status word is what a reader scans for, so nothing else competes with
   it. Text stays dark on tinted backgrounds rather than white-on-colour, which
   fails contrast at this size. */
.injury-count { margin: 18px 0 10px; color: #555; }
.injury-table td { vertical-align: middle; }
.injury-name a { font-weight: 600; }
.injury-news { color: #444; font-size: 0.9rem; }
.injury-flag {
    display: inline-block; padding: 2px 8px; border-radius: 10px;
    font-size: 0.78rem; font-weight: 600; white-space: nowrap;
}
.injury-flag-i { background: #ffe3e3; color: #a02020; }   /* injured */
.injury-flag-s { background: #ffe9d6; color: #9a5410; }   /* suspended */
.injury-flag-u { background: #ececec; color: #4a4a4a; }   /* unavailable */
.injury-flag-n { background: #ececec; color: #4a4a4a; }   /* not in squad */
.injury-flag-d { background: #fff6cc; color: #8a6d0b; }   /* doubtful */
.injury-flag-a { background: #fff6cc; color: #8a6d0b; }   /* sub-100% with no status letter */
.injury-prose-heading { margin-top: 28px; }
.injury-prose p { margin: 0 0 8px; color: #333; }
.injury-none {
    border-left: 3px solid var(--accent);
    background: #fafcfc; border-radius: 0 8px 8px 0;
    padding: 14px 16px; margin: 20px 0;
}
.injury-none h2 { font-size: 1.05rem; margin: 0 0 6px; }
.injury-none p { margin: 0; }
/* The news panel's link through to the full page. */
.news-more { margin-top: 8px; font-size: 0.85rem; }

/* Ask-a-question box at the foot of the FAQ. Same left-rule card as an answer
   above it, because it belongs to the same list - it is where the next entry
   comes from. */
.ask-form {
    border-left: 3px solid var(--accent);
    background: #fafcfc;
    border-radius: 0 8px 8px 0;
    padding: 14px 16px;
    margin: 14px 0 8px;
}
.ask-form .form-label { font-weight: 600; font-size: 0.92rem; margin-bottom: 6px; }
.ask-form textarea { resize: vertical; font-size: 0.95rem; }
.ask-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; margin-top: 10px;
}
/* Reserves its line whether or not there is a message, so sending doesn't
   shunt the footer up the page under the reader's cursor. */
.ask-status { min-height: 1.2em; margin: 0; }

/* Landing page ID entry. Reuses .id-prompt so it's the same card the app
   shows, just given more room to breathe as the first thing on the page. */
.home-id-prompt { margin: 22px auto 34px; background: #fbfdfd; }
.home-id-title {
    color: var(--accent-dark); font-weight: 700; font-size: 1.15rem;
    margin: 0 0 6px;
}
.home-id-skip {
    font-size: 0.85rem; color: #666; margin: 16px 0 0;
}
/* The tool headings are links, and on a page that's mostly links they need to
   still read as headings rather than as a list of blue text. */
.content-page h3 a { text-decoration: none; }
.content-page h3 a:hover { text-decoration: underline; }

/* ---- Ko-fi ---- */
.icon-coffee { flex: none; }

.kofi-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 13px; border-radius: 999px;
    background: var(--accent); color: #fff;
    font-size: 0.82rem; font-weight: 700; text-decoration: none;
    border: 1px solid transparent; white-space: nowrap;
    transition: background 0.15s ease;
}
.kofi-btn:hover, .kofi-btn:focus { background: var(--accent-dark); color: #fff; }

/* "Team saved" toast. Fixed to the bottom of the viewport rather than placed in
   the page, because the Save button can be scrolled well out of sight by the
   time a save lands on a phone. */
.toast-pop {
    position: fixed; z-index: 1080;
    left: 50%; bottom: 22px; transform: translateX(-50%);
    width: min(340px, calc(100vw - 32px));
    background: #fff; border: 1px solid #e3eded; border-radius: 12px;
    box-shadow: 0 6px 26px rgba(0, 0, 0, 0.14);
    padding: 13px 34px 13px 15px;
}
.toast-pop.toast-in { animation: toastUp 0.26s ease-out; }
@keyframes toastUp {
    from { opacity: 0; transform: translate(-50%, 14px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
/* Respect a reduced-motion preference: the toast still appears, it just
   doesn't slide. */
@media (prefers-reduced-motion: reduce) {
    .toast-pop.toast-in { animation: none; }
}

.toast-title {
    font-weight: 700; color: var(--accent-dark); font-size: 0.95rem;
    line-height: 1.2;
}
.toast-title::before { content: "\2713\00a0"; }
.toast-msg { font-size: 0.8rem; color: #666; margin-top: 3px; line-height: 1.45; }

.toast-close {
    position: absolute; top: 6px; right: 8px;
    background: none; border: 0; padding: 2px 6px;
    font-size: 1.15rem; line-height: 1; color: #aaa; cursor: pointer;
}
.toast-close:hover { color: #555; }

.toast-kofi {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 11px; padding: 7px 12px; border-radius: 8px;
    background: #f1f7f7; color: var(--accent-dark);
    font-size: 0.78rem; font-weight: 600; text-decoration: none;
    line-height: 1.25;
}
.toast-kofi:hover { background: var(--accent); color: #fff; }

/* First-save dialog. Bigger and centred on purpose - it appears once a session
   and then never again, so it gets to be an interruption. */
.kofi-modal {
    position: fixed; inset: 0; z-index: 1090;
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
}
.kofi-backdrop { position: absolute; inset: 0; background: rgba(8, 38, 40, 0.55); }

.kofi-card {
    position: relative;
    width: 100%; max-width: 420px;
    background: #fff; border-radius: 18px;
    padding: 30px 30px 24px;
    text-align: center;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28);
    animation: kofiIn 0.22s ease-out;
    max-height: calc(100vh - 40px); overflow-y: auto;
}
@keyframes kofiIn {
    from { opacity: 0; transform: translateY(10px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .kofi-card { animation: none; }
}

/* Deliberately prominent, and a comfortable size on a phone: this is the only
   way out of the dialog, so it must be neither a hunt nor a fiddly tap. */
.kofi-close {
    position: absolute; top: 10px; right: 12px;
    width: 40px; height: 40px; border-radius: 50%;
    background: #f1f7f7; border: 0; color: #55706f;
    font-size: 1.4rem; line-height: 1; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.kofi-close:hover { background: var(--accent); color: #fff; }
.kofi-close:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: 2px; }

.kofi-cup {
    width: 68px; height: 68px; margin: 4px auto 14px;
    border-radius: 50%; background: #f1f7f7; color: var(--accent-dark);
    display: flex; align-items: center; justify-content: center;
}
.kofi-cup .icon-coffee { width: 34px; height: 34px; stroke-width: 1.6; }

.kofi-title {
    margin: 0 0 8px; font-size: 1.35rem; font-weight: 700;
    color: var(--accent-dark);
}
.kofi-lead { margin: 0 0 14px; font-size: 0.92rem; color: #444; line-height: 1.55; }
.kofi-body { margin: 0 0 20px; font-size: 0.86rem; color: #666; line-height: 1.6; }

.kofi-cta {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    width: 100%; padding: 12px 18px; border-radius: 10px;
    background: var(--accent); color: #fff;
    font-size: 0.95rem; font-weight: 700; text-decoration: none;
}
.kofi-cta:hover, .kofi-cta:focus { background: var(--accent-dark); color: #fff; }
.kofi-cta .icon-coffee { width: 18px; height: 18px; }

.kofi-foot { margin: 12px 0 0; font-size: 0.72rem; color: #9aa8a8; }

/* Set while the dialog is open. Without it the page scrolls behind the
   backdrop on a phone, which reads as the dialog being broken. */
body.modal-open { overflow: hidden; }

@media (max-width: 400px) {
    .kofi-card { padding: 30px 18px 20px; border-radius: 14px; }
    .kofi-title { font-size: 1.2rem; }
    .kofi-cup { width: 58px; height: 58px; }
    .kofi-cup .icon-coffee { width: 29px; height: 29px; }
}

.players-az-link {
    margin: 18px 0 0; font-size: 0.85rem; color: #666;
}
.players-az-link a { color: var(--accent-dark); font-weight: 600; }

.pm-profile {
    display: block; margin-top: 14px; text-align: center;
    font-size: 0.85rem; font-weight: 600; color: var(--accent-dark);
    text-decoration: none;
}
.pm-profile:hover { text-decoration: underline; }

/* ---- Player pages ---- */
.crumbs { font-size: 0.8rem; color: #888; margin-bottom: 10px; }
.crumbs a { color: var(--accent); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }

.player-sub { color: #555; font-size: 0.95rem; margin: -4px 0 18px; }

/* The four numbers someone came for, above the prose. */
.player-keys {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 10px; margin: 0 0 22px;
}
.player-key {
    background: #f1f7f7; border-radius: 10px; padding: 10px 12px; text-align: center;
}
.pk-val {
    display: block; font-size: 1.3rem; font-weight: 700; color: var(--accent-dark);
    line-height: 1.15;
}
.pk-lab {
    display: block; font-size: 0.66rem; text-transform: uppercase;
    letter-spacing: 0.04em; color: #777; margin-top: 3px;
}

/* Tables scroll inside their own box rather than pushing the page sideways. */
.table-scroll { overflow-x: auto; margin: 10px 0 6px; }
.stat-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.stat-table th, .stat-table td {
    padding: 7px 10px; border-bottom: 1px solid #eee; text-align: left;
}
.stat-table thead th {
    background: #f1f7f7; color: var(--accent-dark); font-weight: 700;
    font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em;
}
.stat-table tbody th { font-weight: 600; color: #555; width: 25%; }
.stat-table .num { text-align: right; font-variant-numeric: tabular-nums; }

.player-more { margin-top: 26px; font-size: 0.9rem; }

/* ---- A-Z index ---- */
.az-jump { display: flex; flex-wrap: wrap; gap: 6px; margin: 18px 0 8px; }
.az-jump a {
    display: inline-block; min-width: 30px; text-align: center;
    padding: 4px 7px; border-radius: 7px; background: #f1f7f7;
    color: var(--accent-dark); font-weight: 700; font-size: 0.85rem;
    text-decoration: none;
}
.az-jump a:hover { background: var(--accent); color: #fff; }
/* A letter with nothing left in it under the current filter. Dimmed and
   unclickable rather than removed - see the note in players_az.html on why the
   jump row has to keep its shape while you type. */
.az-jump a.az-jump-empty {
    opacity: 0.3; pointer-events: none; background: #f6f6f6; color: #aaa;
}

/* The filter box, inserted by the script at the foot of players_az.html. */
.az-search { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
.az-search-field { flex: 1 1 260px; max-width: 340px; }
.az-count { font-size: 0.8rem; color: #888; }
.az-nomatch { margin-top: 14px; color: #666; font-size: 0.9rem; }
.az-nomatch code { background: #f1f7f7; padding: 1px 5px; border-radius: 4px; color: var(--accent-dark); }

.az-list {
    list-style: none; padding: 0; margin: 6px 0 0;
    columns: 2; column-gap: 26px;
}
.az-list li {
    break-inside: avoid; margin-bottom: 5px; font-size: 0.9rem;
    line-height: 1.4;
}
.az-meta { color: #999; font-size: 0.76rem; white-space: nowrap; }

@media (max-width: 575.98px) {
    .az-list { columns: 1; }
    .stat-table tbody th { width: auto; }
}

/* Obfuscated addresses: styled as links even before the script swaps in the
   real mailto, so they don't look broken with JS disabled. */
.obf-mail { color: var(--accent-dark); font-weight: 600; word-break: break-word; }

.contact-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px; margin-top: 18px;
}
.contact-card {
    border: 1px solid #eee; border-radius: 10px; padding: 14px 16px;
    background: #faf9fe;
}
.contact-card h2 { margin: 0 0 6px; font-size: 1rem; }
.contact-card p { margin-bottom: 6px; font-size: 0.88rem; }
.contact-address { margin-bottom: 0 !important; }

@media (max-width: 575.98px) {
    .tab-page-title { font-size: 1.15rem; margin-bottom: 10px; }
    .content-page h1.page-title { font-size: 1.4rem; }
    .content-page .page-lede { font-size: 0.95rem; }
    .content-page h2 { font-size: 1.05rem; }
}

/* Brand is a home link on every page, so it needs link affordances the old
   <span> didn't - without underlining it into looking like body-copy text. */
a.navbar-brand.brand { text-decoration: none; }
a.navbar-brand.brand:hover .brand-icon { transform: scale(1.06); }
a.navbar-brand.brand:hover { color: #ffffff !important; opacity: 0.92; }
.brand-icon { transition: transform 0.15s ease; }
a.navbar-brand.brand:focus-visible {
    outline: 2px solid #ffffff; outline-offset: 3px; border-radius: 8px;
}

/* ============================================================
   RESERVED SPACE FOR JS-POPULATED CONTENT
   ============================================================

   Every block below starts empty in the HTML and is filled once its /api/
   response lands. Without a reserved height each one grows from nothing to
   several hundred pixels after first paint, shoving everything underneath it
   down the page - which is exactly what Cumulative Layout Shift measures, and
   what took the Players tab to 0.231 (the pass mark is 0.1).

   The values are the height each block settles at once full, so the box the
   reader sees before the data arrives is the same box they see after. Slightly
   too tall is harmless - a little empty space at the bottom of a scroll pane.
   Too short still shifts, so these round up rather than down. */

/* The whole player-search widget - controls, scroll pane and table - is built
   by createPlayerSearch() into an empty div, so the reserved height is the
   .ps-list max-height (70vh, set above) plus the controls row above it. */
#playersTabSearch, #playerSearch { min-height: calc(70vh + 46px); }

/* Tables whose <thead> is server-rendered but whose <tbody> starts empty: the
   underperforming list, the AI squad and both AI history tables. These collapse
   to the header row until data arrives rather than to nothing, so they need
   less than the full pane height.

   Exempt: the .ps-list inside .ps-rec, overridden up beside the other .ps-rec
   rules. That one holds three recommendations, appears on a click rather than
   on a fetch, and so has nothing to reserve against. */
.ps-list { min-height: 260px; }

/* The two track-record tables are exempt from all of it - the reservation
   above, the 70vh cap and the inner scroll. Those numbers describe the player
   search, which is always about that tall. A track record is one row in August
   and thirty-eight in May, so a fixed box is wrong at both ends: today it wraps
   a single 29px row in 260px of nothing, and by the end of the season it is a
   nested scroller inside a page that already scrolls.

   The row cap in loadAiHistory/loadMgrHistory is what bounds the height now, so
   nothing here needs to. Same shape as the .ps-rec .ps-list override further
   up, and for the same reason. */
.ps-list-history {
    min-height: 0;
    /* The vertical trap goes; the horizontal one stays. `overflow-y` is left
       alone deliberately - it cannot be set to `visible` while overflow-x is
       hidden (the spec computes it back to auto), and it does not need to be:
       with no max-height the box grows to its content and the scrollbar never
       appears. */
    max-height: none;
}

/* Rotation pairs are cards, not a table, and the row of them is about this
   tall on every breakpoint. */
#pairsContainer { min-height: 300px; }

/* One row per team, always 20 of them, drawn after /api/rotation returns. */
#rotationTable { min-height: 620px; }

/* Server-rendered tables (see seo_tables.py). These are replaced in place by
   the interactive versions, so the only styling here is what stops the two
   looking different in the moment before that happens.

   .ssr-controls stands in for the search-and-filter row app.js builds. It holds
   that row's height and nothing else - controls that did nothing without
   JavaScript would be worse than no controls. */
.ssr-controls { min-height: 46px; }
/* The next-three-fixtures column, as plain text rather than the coloured tiles
   the script draws. Small and muted so it reads as a footnote to the row. */
.ssr-fixtures { white-space: nowrap; }
.ssr-fix { font-size: 0.7rem; color: #6b7280; margin-right: 6px; }
.ssr-pair { padding: 8px 10px; }
.ssr-pair .pair-teams { font-size: 0.9rem; }

/* The My Team explainer. Prose on a pane that is otherwise entirely one
   person's data, shown until a squad is loaded. */
.tool-intro { max-width: 62ch; margin-bottom: 20px; }
.tool-intro ul { padding-left: 20px; }
.tool-intro li { margin-bottom: 6px; }

/* The AI tabs. Both squads are drawn as a skeleton pitch before either endpoint
   answers (see renderAiSkeletons in app.js), so the pitch itself needs no
   reservation - it is already the right size. These are the blocks around it
   that genuinely start empty: the stat chips above, the chip strip, and the two
   text panels beside the AI Manager's pitch. Left unreserved they each grew
   from nothing after their fetch and pushed the track-record tables down. */
/* Scoped to the two AI rows, not to every .stat-chips on the site. The
   reservation exists because those two are drawn from a fetch into an
   otherwise empty block - which is not true of My Team, where it was only
   ever reserving blank space under a row that had already rendered. */
#mgrChips, #aiChips { min-height: 58px; }
.chips-bar { min-height: 92px; }
#mgrMoves { min-height: 180px; }
#mgrChipPlan { min-height: 96px; }

/* `.chips-bar:empty { display: none }` further up is what My Team wants - no
   chip strip at all until a squad is loaded. The AI Manager's copy is
   different: it is always going to be filled, a moment later, with the same
   four cards, so hiding it in the meantime removes 92px from the layout and
   then puts it back. Reserved instead. */
#mgrChipsBar:empty { display: flex; }

/* The skeleton itself: real cards at real size, with the text and the coloured
   pills blanked out so nothing on screen can be read as a projection. Removed
   by clearAiSkeleton() the moment data lands. Deliberately not an animated
   shimmer - this block is typically filled within a few hundred milliseconds,
   and a pulse that brief reads as a flicker rather than as progress. */
.ai-skeleton .player-name-pill,
.ai-skeleton .ai-mini,
.ai-skeleton .avail-band,
.ai-skeleton .bench-pos,
.ai-skeleton .player-stat { color: transparent !important; }
.ai-skeleton .ai-mini,
.ai-skeleton .avail-band,
.ai-skeleton .player-stat { background: #e9ecef !important; }
.ai-skeleton .player-name-pill { background: rgba(255, 255, 255, 0.55); }
/* The shirt is the one part of a card that can't be faked: a placeholder has
   no team_code, so shirtImg() correctly draws nothing - and an empty .player-kit
   is 0px where a real one is 44, which is the single biggest height difference
   between a skeleton card and a filled one. Hold the space instead of inventing
   a club's kit to fill it. 44px and 30px are the two sizes .kit itself takes -
   there is a 36px rule under 576px too, but a later one at the same breakpoint
   overrides it to 30. These have to move if those do. */
.ai-skeleton .player-kit {
    display: inline-block;
    height: 44px;
    width: 44px;
    /* An empty inline-block sits on the text baseline and drags its parent's
       line box down by the descender - 6px taller than the real card, which
       would leave the skeleton overshooting instead of undershooting. Taking
       it off the baseline lands the two at exactly the same height. */
    vertical-align: top;
}
@media (max-width: 575.98px) { .ai-skeleton .player-kit { height: 30px; width: 30px; } }

@media (max-width: 575.98px) {
    /* Phones: the pane is already near-full-height and an over-reserved box
       means scrolling past emptiness on a small screen, which is a worse
       experience than the shift it prevents. Reserve enough to stop the jump
       being violent, not enough to be exact. */
    #playersTabSearch, #playerSearch { min-height: 420px; }
    .ps-list { min-height: 200px; }
    .ps-list-history { min-height: 0; }

    /* Six columns of numbers do not fit across 375px, and squeezing them
       wraps every cell onto two lines - which is most of why these blocks read
       as too long on a phone. Value/Bank and Cost are the ones a reader can
       lose: GW, Predicted and Actual are the whole point of the table, and the
       chip and formation change what those numbers mean. Same nth-child
       approach as .ai-squad-table above. */
    .mgr-history-table th:nth-child(2), .mgr-history-table td:nth-child(2),
    .mgr-history-table th:nth-child(3), .mgr-history-table td:nth-child(3),
    .xi-history-table th:nth-child(3), .xi-history-table td:nth-child(3) {
        display: none;
    }
    #pairsContainer { min-height: 240px; }
    #rotationTable { min-height: 480px; }
    /* The chips wrap to more rows on a narrow screen, and the two AI Manager
       text panels sit under the pitch rather than beside it, so both settle
       taller here than on a desktop. */
    #mgrChips, #aiChips { min-height: 108px; }
    .chips-bar { min-height: 80px; }
    #mgrChipPlan { min-height: 80px; }
    /* The one block here whose height genuinely varies with its content: the
       transfer list is 150px for the common nothing-worth-doing week and 187px
       for two moves. Reserved for two, per the "round up rather than down" rule
       above - a quiet week then carries 37px of white space, where the other
       way round every busy week would shift. Three moves in one week still
       overflows this, and is rare enough to accept.
       Only matters on a phone: above this breakpoint the list sits in a column
       beside the pitch, which is taller, so it can grow without moving
       anything. */
    #mgrMoves { min-height: 190px; }
}

/* ============================================================
   GAMEWEEK BRIEFING  (/gameweek/<n>)
   ============================================================

   Newspaper treatment, for a reason beyond decoration: this is the one part
   of the site that is prose rather than a tool, and it needs to look unlike
   the tables so a reader knows immediately which of the two they're on.

   A serif face for the headings against the sans body does most of that work.
   The stack is system serifs only - Georgia is on essentially every Windows
   and Mac, and the fallbacks cover Linux and Android. A web font would be a
   third-party connection and a render-blocking request on a page whose whole
   point is being fast and self-contained.

   Widths are wider than .content-page's 760px: the card grids need the room,
   and a masthead constrained to essay width doesn't read as a masthead. */

.gw-masthead, .gw-section, .gw-footer, .gw-empty { max-width: 980px; margin-inline: auto; }
.content-page:has(.gw-masthead) { max-width: 980px; }

/* --- Masthead --- */
.gw-masthead { margin-bottom: 32px; }
.gw-masthead-rule {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    border-top: 3px solid var(--accent-dark);
    border-bottom: 1px solid #ddd;
    padding: 8px 0; margin-bottom: 18px;
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em;
}
.gw-edition { font-weight: 700; color: var(--accent-dark); }
.gw-season { color: #777; }
.gw-status { margin-left: auto; padding: 2px 8px; border-radius: 3px; font-weight: 600; }
.gw-status.is-final { background: #eef6f6; color: var(--accent-dark); }
.gw-status.is-live { background: #fff4e5; color: #A85B00; }
/* Between the two, and deliberately so: a preview edition is settled enough to
   act on but not yet final. Green rather than the amber of "still moving
   nightly", because arriving here from a post the day before the deadline, the
   thing to know is that it's ready. */
.gw-status.is-preview { background: #eaf7ef; color: #1F7A47; }

.content-page h1.gw-title {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: clamp(1.9rem, 5vw, 3rem);
    line-height: 1.1; font-weight: 700; letter-spacing: -0.02em;
    color: #1a1a1a; margin: 0 0 12px;
}
.gw-standfirst {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 1.12rem; line-height: 1.55; color: #333; margin-bottom: 10px;
}
.gw-byline { font-size: 0.82rem; color: #777; line-height: 1.5; margin: 0; }
.gw-empty {
    font-size: 1rem; color: #555; background: #fafafa;
    border: 1px dashed #ddd; border-radius: 8px; padding: 20px; margin: 24px auto;
}

/* --- Section headings: rule through the middle, label knocked out of it --- */
.gw-section { margin-bottom: 40px; }
.content-page h2.gw-section-head {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 1.5rem; font-weight: 700; color: #1a1a1a;
    margin: 0 0 4px; position: relative; text-align: center;
}
.gw-section-head::before {
    content: ""; position: absolute; top: 50%; left: 0; right: 0;
    border-top: 1px solid #ddd; z-index: 0;
}
.gw-section-head span { position: relative; z-index: 1; background: #fff; padding: 0 16px; }
.gw-section-sub {
    text-align: center; font-size: 0.85rem; color: #777;
    margin: 0 auto 20px; max-width: 620px; line-height: 1.5;
}

/* --- Player cards --- */
.gw-cards { display: grid; gap: 16px; }
.gw-cards-3 { grid-template-columns: repeat(3, 1fr); }
.gw-card {
    border: 1px solid #e6e6e6; border-top: 3px solid var(--accent);
    border-radius: 0 0 8px 8px; padding: 14px 16px 16px; background: #fff;
}
.gw-card-diff { border-top-color: #A85B00; }
/* The armband section. A third colour rather than the accent, so the three
   card types on the page are told apart by more than their heading - 8.4:1 on
   white, so the figure text below is readable at the small size it's set in. */
.gw-card-captain { border-top-color: #5B3E8E; }
/* The roundup's blanks section. Red, because it is the one card type on any of
   these pages reporting something that went wrong. 5.9:1 on white. */
.gw-card-blank { border-top-color: #B3261E; }
.gw-card-top { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.gw-pos {
    margin-left: auto; font-size: 0.66rem; font-weight: 700; letter-spacing: 0.06em;
    background: #f1f1f1; color: #555; padding: 2px 6px; border-radius: 3px;
}
.content-page h3.gw-card-name {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 1.28rem; font-weight: 700; margin: 0 0 2px; color: #1a1a1a;
}
.gw-card-name a { color: inherit; text-decoration: none; }
.gw-card-name a:hover { color: var(--accent-dark); text-decoration: underline; }
.gw-card-figure {
    font-size: 0.8rem; font-weight: 700; color: var(--accent-dark);
    text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 8px;
}
.gw-card-diff .gw-card-figure { color: #A85B00; }
.gw-card-captain .gw-card-figure { color: #5B3E8E; }
.gw-card-blank .gw-card-figure { color: #B3261E; }
.content-page p.gw-card-why { font-size: 0.86rem; line-height: 1.5; color: #444; margin: 0 0 12px; }

/* --- The little stat strip on each card --- */
.gw-stats { display: flex; gap: 14px; margin: 0 0 10px; flex-wrap: wrap; }
.gw-stats div { display: flex; flex-direction: column; }
.gw-stats dt {
    font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.06em;
    color: #999; font-weight: 600;
}
.gw-stats dd { margin: 0; font-size: 0.94rem; font-weight: 700; color: #222; }
/* The roundup's scorecard sits on its own rather than inside a card, so it
   needs the room a card was providing. Centred, because there are only ever
   three figures and a left-aligned row of three looks like a truncated table. */
.gw-stats-wide { justify-content: center; gap: 40px; margin-bottom: 16px; }
.gw-stats-wide dt { font-size: 0.7rem; }
.gw-stats-wide dd { font-size: 1.6rem; }
.gw-scorecard { text-align: center; }
.gw-scorecard .gw-card-why { text-align: center; }

/* --- Fixture pills --- */
.gw-fixtures { display: flex; gap: 4px; flex-wrap: wrap; margin: 0; }
.gw-fixtures span {
    font-size: 0.68rem; background: #f4f8f8; color: #555;
    border: 1px solid #e4eded; border-radius: 3px; padding: 2px 6px; white-space: nowrap;
}

/* --- Club chip: two-tone, standing in for a badge we can't use --- */
.club-chip {
    display: inline-block; width: 14px; height: 14px; border-radius: 3px;
    background: linear-gradient(135deg, var(--club-a) 0%, var(--club-a) 50%,
                                        var(--club-b) 50%, var(--club-b) 100%);
    border: 1px solid rgba(0, 0, 0, 0.25);  /* or white kits vanish */
    flex: 0 0 auto; vertical-align: middle;
}
.club-name { font-size: 0.78rem; color: #666; font-weight: 600; }

/* --- Fixture section: two columns of team runs --- */
.gw-fixture-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }
.content-page h3.gw-sub-head {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 1.05rem; font-weight: 700; color: #1a1a1a;
    border-bottom: 2px solid var(--accent); padding-bottom: 5px; margin: 0 0 4px;
}
.gw-hint { font-size: 0.76rem; color: #888; margin: 0 0 12px; }
.gw-run { border-bottom: 1px solid #eee; padding: 10px 0; }
.gw-run:last-child { border-bottom: none; }
.gw-run-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.gw-run-score {
    margin-left: auto; font-size: 0.78rem; font-weight: 700; color: var(--accent-dark);
}
.gw-run-players { list-style: none; padding: 0; margin: 8px 0 0; }
.content-page .gw-run-players li { font-size: 0.82rem; margin-bottom: 3px; }
.gw-run-meta { color: #888; font-size: 0.74rem; }

/* --- Treatment table --- */
.gw-news-list { list-style: none; padding: 0; margin: 0; }
.gw-news-item {
    border-left: 3px solid #ccc; background: #fafafa;
    border-radius: 0 6px 6px 0; padding: 10px 14px; margin-bottom: 8px;
}
/* Status letters are FPL's own: i injured, s suspended, d doubtful,
   u/n unavailable or not in the squad. */
.gw-news-i, .gw-news-u, .gw-news-n { border-left-color: #C4122E; background: #fdf6f7; }
.gw-news-s { border-left-color: #A85B00; background: #fdfaf5; }
.gw-news-d { border-left-color: #E0A800; background: #fdfcf5; }
.gw-news-who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 3px; }
.gw-news-who a { color: #1a1a1a; text-decoration: none; }
.gw-news-who a:hover { color: var(--accent-dark); text-decoration: underline; }
.gw-news-owned { font-size: 0.72rem; color: #888; margin-left: auto; }
.content-page p.gw-news-why { font-size: 0.84rem; color: #555; margin: 0; line-height: 1.45; }

/* --- Footer --- */
.gw-footer { border-top: 1px solid #ddd; padding-top: 16px; margin-top: 8px; }
.content-page .gw-footer p { font-size: 0.82rem; color: #777; line-height: 1.55; }
.gw-footer-nav { display: flex; gap: 18px; }

/* --- The gameweek hub ---
   Two cards: the upcoming briefing and the last settled roundup. This replaced
   a list of every edition ever published, which was the right page in August
   and a wall of thirty dead links by November. The old .gw-archive-* rules went
   with it; the editions themselves are still served, just not listed. */
.gw-hub { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin: 24px 0 32px; }
.gw-hub-card {
    border: 1px solid #e6e6e6; border-top: 3px solid var(--accent);
    border-radius: 0 0 8px 8px; padding: 16px 18px 18px; background: #fff;
}
/* The roundup is the settled half of the week, and takes the same colour the
   frozen states use everywhere else on these pages. */
.gw-hub-roundup { border-top-color: #5B3E8E; }
.gw-hub-top { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.gw-hub-kicker {
    font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em;
    font-weight: 700; color: #777;
}
.content-page h2.gw-hub-title {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 1.32rem; font-weight: 700; margin: 0 0 6px; color: #1a1a1a;
}
.gw-hub-title a { color: inherit; text-decoration: none; }
.gw-hub-title a:hover { color: var(--accent-dark); text-decoration: underline; }
.gw-hub-deadline { font-size: 0.84rem; color: #444; margin: 0 0 8px; }
.content-page p.gw-hub-summary { font-size: 0.9rem; line-height: 1.55; color: #444; margin: 0 0 10px; }
.content-page p.gw-hub-lead {
    font-size: 0.88rem; margin: 0 0 12px; padding: 8px 10px;
    background: #fafafa; border-left: 3px solid #e0e0e0; border-radius: 0 3px 3px 0;
}
.gw-hub-go { margin: 0; font-size: 0.9rem; font-weight: 600; }

@media (max-width: 760px) {
    .gw-hub { grid-template-columns: 1fr; }
}

/* Tablet: three cards across is too tight, two columns of fixtures too narrow. */
@media (max-width: 900px) {
    .gw-cards-3 { grid-template-columns: repeat(2, 1fr); }
    .gw-fixture-cols { grid-template-columns: 1fr; gap: 24px; }
}

/* Phone: single column throughout, and the masthead loses its dramatic size
   because a 3rem headline on a 375px screen is four words per line. */
@media (max-width: 575.98px) {
    .gw-cards-3 { grid-template-columns: 1fr; }
    .content-page h1.gw-title { font-size: 1.65rem; }
    .gw-standfirst { font-size: 1rem; }
    .gw-status { margin-left: 0; }
    .gw-news-owned { margin-left: 0; }
}

/* =====================================================================
   New pages: accuracy, price changes, captain picks, compare, watchlist
   =====================================================================
   Every table below sits inside .table-responsive and carries a min-width
   rather than a fluid one. Same trick .underperf-table uses: below the floor
   the columns squeeze a sentence to one word per line, and scrolling the
   table sideways beats that. The page itself never scrolls horizontally. */
.accuracy-table { min-width: 620px; }
.accuracy-table th[scope="row"] { min-width: 190px; }
.accuracy-gw-table { min-width: 480px; }

.price-table { min-width: 400px; }
.price-table th[scope="row"] { min-width: 140px; }
.price-table th[scope="row"] .text-muted { margin-left: 4px; }
.price-recent-table { min-width: 420px; }

.captain-table { min-width: 720px; }
.captain-table th[scope="row"] { min-width: 130px; }
.captain-table .gw-fixtures { margin: 0; }

.wl-table { font-size: 0.82rem; min-width: 720px; }
.wl-table td, .wl-table th { vertical-align: middle; }
.wl-fixtures { white-space: nowrap; }
.wl-fixtures .mini-gw { margin-right: 2px; }
.wl-remove { color: #999; font-size: 1.1rem; line-height: 1; }
.wl-remove:hover { color: #e03131; }

/* The watchlist star, wherever it appears: player tables, the watchlist itself,
   and a player's own profile page.

   A button stripped back to its glyph rather than a styled <span>, because it
   has to be reachable by keyboard and announce whether it is pressed - see
   watchStar() in watchlist.js. The unfilled state is deliberately faint: a
   column of solid stars down a 100-row table would read as decoration and
   compete with the names, which are what the table is for.

   The hit area is padded out past the glyph. At 0.82rem a bare star is about
   11px across, which is half what a thumb needs, and this sits inside a row
   whose own tap does something else. */
.wl-star {
    background: none; border: 0; padding: 2px 4px; line-height: 1;
    font-size: 0.95rem; color: #c8ccd0; cursor: pointer;
    transition: color 0.12s ease, transform 0.12s ease;
}
.wl-star:hover { color: var(--accent-dark, #0b7285); transform: scale(1.15); }
.wl-star.is-on { color: #f5a524; }
.wl-star:focus-visible { outline: 2px solid var(--accent-dark, #0b7285); outline-offset: 1px; border-radius: 3px; }
td.col-watch, th.col-watch, .wl-starcell { text-align: center; padding-left: 0; padding-right: 0; }
.underperf-table .col-watch { width: 28px; }

/* On a profile the star sits beside the h1, so it is sized to the heading
   rather than to a table row. */
.player-title-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.player-title-row .page-title { margin-bottom: 0; }
.player-title-row .wl-star { font-size: 1.4rem; }

/* How close a watched player is to a price change. The bar is the share of the
   threshold his transfers have covered - see priceDrift() - so a full bar is a
   change tonight and a quarter bar is a drift. Rises and falls are coloured
   apart rather than only pointed apart, because the direction is the first
   thing read and an arrow at this size is nearly invisible. */
.wl-drift { white-space: nowrap; }
.wl-bar {
    display: inline-block; vertical-align: middle; width: 46px; height: 6px;
    background: #e9ecef; border-radius: 3px; overflow: hidden; margin-right: 6px;
}
.wl-bar-fill { display: block; height: 100%; border-radius: 3px; }
.wl-bar-fill.is-rise { background: #2f9e44; }
.wl-bar-fill.is-fall { background: #e03131; }
.wl-drift-label { font-size: 0.75rem; color: #666; }
.wl-drift-none { color: #adb5bd; }


/* Comparison: the players are the COLUMNS, so it reads down. */
.cmp-pickers { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; }
.cmp-picker { flex: 1 1 200px; min-width: 170px; }
.cmp-picker-label { display: block; font-size: 0.8rem; font-weight: 600;
                    color: var(--accent-dark); margin-bottom: 3px; }
.compare-table { min-width: 460px; }
.compare-table th[scope="row"] { width: 34%; font-weight: 600; color: #555; }
.compare-table thead th { color: var(--accent-dark); font-size: 1rem; }
.compare-table td { vertical-align: middle; }
/* Tinted rather than bolded: the claim is about the pair of cells, and colour
   says that where weight would only say "important". */
.compare-table td.cmp-best {
    background: #effafa;
    font-weight: 700;
    box-shadow: inset 2px 0 0 var(--accent);
}
.compare-table .mini-gw { margin-right: 2px; }

/* Status strip under a tool-page heading: when this locks, and whether the
   numbers are today's. Wraps rather than scrolls - two short items fit two
   lines, and a scroller for eight words would be worse. */
.status-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 18px;
    margin: -4px 0 14px;
    font-size: 0.82rem;
    color: #666;
}
.status-item { display: inline-flex; align-items: baseline; gap: 5px; }
.status-label { color: #888; }
.status-item time { font-weight: 600; color: var(--accent-dark); }
.status-countdown { color: #888; font-weight: 400; }
@media (max-width: 575.98px) {
    .status-strip { gap: 2px 14px; font-size: 0.78rem; }
}

/* ---- Captain-picks history & price-changes history (Part 4) ------------- */

/* Sub-tab panes toggled by the small page scripts share d-none with the rest
   of the site; these rules are only for the content inside the history views. */

/* The captain track record: the model's pick and the actual top scorer, side
   by side, one gameweek at a time. */
.cap-hist-nav { display: flex; align-items: center; gap: 12px; margin: 4px 0 16px; }
.cap-hist-title { font-weight: 600; font-size: 1.05rem; min-width: 8em; text-align: center; }
.cap-hist-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.cap-hist-card { border: 1px solid #e9e9ef; border-radius: 10px; padding: 14px 16px; }
.cap-hist-model { border-left: 4px solid var(--accent, #00acae); }
.cap-hist-actual { border-left: 4px solid #f5a524; }
.cap-hist-role { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
                 color: #888; margin-bottom: 4px; }
.cap-hist-card .gw-card-name { margin: 0 0 2px; font-size: 1.1rem; }
.cap-hist-card .gw-card-figure { font-size: 1.4rem; font-weight: 700; margin: 6px 0 4px;
                                 color: var(--accent-dark, #0b7285); }
.cap-hist-verdict { margin-top: 14px; padding: 10px 14px; border-radius: 8px; font-size: 0.9rem; }
.cap-hist-verdict.is-hit { background: #e6f7ed; color: #1a7a43; }
.cap-hist-verdict.is-miss { background: #f6f3ef; color: #555; }
@media (max-width: 575.98px) {
    .cap-hist-pair { grid-template-columns: 1fr; }
}

/* The price-change window: a scrollable list of everything since the deadline,
   so a busy gameweek doesn't run the page off the bottom. */
.pc-window-scroll { max-height: 60vh; overflow-y: auto; border: 1px solid #e9e9ef;
                    border-radius: 8px; }
.pc-window-scroll table { margin: 0; }
.pc-window-scroll thead th { position: sticky; top: 0; background: #fff; z-index: 1;
                             box-shadow: inset 0 -1px 0 #e9e9ef; }
.pc-dir { font-weight: 600; }
.pc-dir-rise { color: #2f9e44; }
.pc-dir-fall { color: #e03131; }
