/* ==========================================================================
   Live tips — the in-play badge, score and price shared by every surface that
   lists a tip (Tips Board, Tips List, Tips Page Browser, Acca, Double, single
   tip page).

   These styles deliberately live in ONE file rather than in each block's
   stylesheet: the markup is injected by assets/js/live.js, which is identical
   everywhere, so the look has to be too. Each block keeps ownership of WHERE
   the badge sits — the script only ever writes into an element the block
   already positioned (its kickoff time, its "vs", its odds), so nothing here
   needs to know about any block's layout.
   ========================================================================== */

.tp-live {
    display: inline-flex;
    align-items: center;
    gap: 0.28rem;
    font-size: 0.66rem;
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--tp-danger, #ef4444);
    white-space: nowrap;
}

.tp-live__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
    animation: tp-live-pulse 1.6s ease-in-out infinite;
}

/* The minute is the part that actually changes, so it gets the tabular figures
   to stop the badge jittering as it ticks 9' -> 10'. */
.tp-live__min {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
}

/* Full time: same shape, but drained of urgency — the match is over, it just
   hasn't been settled and moved to the results archive yet. */
.tp-live--ft {
    color: var(--tp-muted, #94a3b8);
}

.tp-live--ft .tp-live__dot {
    animation: none;
    opacity: 0.6;
}

/* Settled: the verdict, arriving without a reload. No dot — the match is over
   and there is nothing left to pulse about; the word is the whole signal. */
.tp-live--won,
.tp-live--lost {
    padding: 0.12rem 0.42rem;
    border-radius: 4px;
    letter-spacing: 0.06em;
}

.tp-live--won {
    color: var(--tp-win, #10b981);
    background: var(--tp-win-bg, rgba(16, 185, 129, 0.12));
}

.tp-live--lost {
    color: var(--tp-loss, #ef4444);
    background: var(--tp-loss-bg, rgba(239, 68, 68, 0.12));
}

@keyframes tp-live-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.35; transform: scale(0.8); }
}

@media ( prefers-reduced-motion: reduce ) {
    .tp-live__dot { animation: none; }
}

/* Score, in place of the "vs" separator — red, like the badge, so the two live
   signals on a row read as one thing. Tabular figures keep the teams either
   side from shifting on a goal.
   ---------------------------------------------------------------------------
   Sizing is a token each surface sets, NOT a value inherited from the element
   being replaced and NOT one absolute size for everything. Inheriting fails
   because the separators are deliberately tiny (0.65–0.8em of an already small
   row), which made the score the least legible thing on it. One absolute size
   fails the other way: these lists each run on their own type scale, so a size
   that sits right on a card is oversized in a compact row. The rule below sets
   each score just above its own surface's team name. */
.tp-live-score {
    font-weight: 800;
    font-size: var(--tp-live-score-size, 0.9rem);
    line-height: 1.15;
    letter-spacing: 0;
    color: var(--tp-danger, #ef4444);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Team names run 0.8rem here, so the score sits a touch above them. */
.tp-tips-list__row,
.tp-page-browser__row {
    --tp-live-score-size: 0.85rem;
}

/* Board rows: names are clamp(0.8 → 0.88rem). */
.tp-tips-board__row {
    --tp-live-score-size: 0.9rem;
}

/* Ticket legs: names are 0.9rem. */
.tp-acca__leg,
.tp-double__leg {
    --tp-live-score-size: 0.95rem;
}

/* Grid cards: names are 0.9375rem, and a card has room to breathe. */
.tp-tip-card {
    --tp-live-score-size: 1.05rem;
}

/* Single tip page: names are 1.15rem and the score is the whole point. */
.tp-hero {
    --tp-live-score-size: 1.45rem;
}

/* In-play price. Drifting out (longer odds) is green with an up arrow;
   shortening is red with a down arrow. The arrow carries the direction as well
   as the colour — colour alone would leave colour-blind users with nothing. */
.tp-live-odds {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.tp-live-odds__arrow {
    font-size: 0.72em;
    margin-left: 0.15em;
}

.tp-live-odds--up   { color: var(--tp-primary, #10b981); }
.tp-live-odds--down { color: var(--tp-danger, #ef4444); }

/* A started match can't be backed at the posted price, so its add-to-slip
   button is taken out of service rather than hidden — a missing button reads
   as a bug, a disabled one reads as a reason. */
.tp-slip-add[data-tp-live-locked] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

.tp-slip-add[data-tp-live-locked] .tp-slip-add__ico {
    display: none;
}

/* Once every leg of an Acca/Double ticket has kicked off there is nothing left
   to add, so its "Add all" button says "Ticket in play" and stops working. It
   keeps its place rather than disappearing, for the same reason as above. */
.tp-acca__add-all[disabled],
.tp-double__add-all[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
    pointer-events: none;
}

/* A goal just landed. The score is the only thing on the page worth
   interrupting someone for, and a number that changes silently between polls
   reads as a rendering glitch rather than an event. Two and a half seconds,
   then it settles back to being a score. */
.tp-live-goal {
    animation: tp-goal-flash 2.4s ease-out 1;
}

@keyframes tp-goal-flash {
    0%      { transform: scale(1);    filter: brightness(1); }
    8%      { transform: scale(1.28); filter: brightness(1.9); }
    22%     { transform: scale(1);    filter: brightness(1.35); }
    40%     { transform: scale(1.12); filter: brightness(1.7); }
    100%    { transform: scale(1);    filter: brightness(1); }
}

/* Someone who has asked for less motion still needs to know a goal went in —
   they get the brightness lift without anything moving. */
@media ( prefers-reduced-motion: reduce ) {
    .tp-live-goal { animation: tp-goal-fade 2.4s ease-out 1; }
    @keyframes tp-goal-fade {
        0%, 40% { filter: brightness(1.8); }
        100%    { filter: brightness(1); }
    }
}

/* ---- Live match stats (single tip page) ------------------------------
   Five rows, each a pair of numbers with a single split bar between them.
   Two bare numbers make the reader do the comparison; the bar does it for
   them, and shares of the row's own total mean an 8-2 shot count reads the
   same way a 62-38 possession split does. */

.tp-mstats-card__teams {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 0.5rem;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--tp-muted, #94a3b8);
}
.tp-mstats-card__teams span:last-child { text-align: right; }

.tp-mstats {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    display: grid;
    row-gap: 0.7rem;
}

.tp-mstat {
    display: grid;
    grid-template-columns: 2.6rem minmax(0, 1fr) 2.6rem;
    align-items: center;
    column-gap: 0.6rem;
    row-gap: 0.25rem;
}

.tp-mstat__h,
.tp-mstat__a {
    grid-row: 1;
    font-size: 0.9rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}
.tp-mstat__h { grid-column: 1; }
.tp-mstat__a { grid-column: 3; text-align: right; }

.tp-mstat__label {
    grid-row: 1;
    grid-column: 2;
    text-align: center;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--tp-muted, #94a3b8);
}

/* The bar spans all three columns so its ends line up with the numbers. */
.tp-mstat__bar {
    grid-row: 2;
    grid-column: 1 / -1;
    display: block;
    height: 4px;
    border-radius: 999px;
    /* The away share is the track showing through behind the home fill. */
    background: var(--tp-muted, #94a3b8);
    overflow: hidden;
}

.tp-mstat__fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--tp-primary, #10b981);
    transition: width 0.4s ease;
}

@media ( prefers-reduced-motion: reduce ) {
    .tp-mstat__fill { transition: none; }
}

/* ---- Match timeline (single tip page) --------------------------------
   Two lanes with the clock running between them: home down the left, away
   down the right. A flat list made the reader check a team label on every
   row to work out who did what — here the side answers that before the name
   is read, the way a stadium scoreboard does it. */

.tp-tl {
    list-style: none;
    margin: 0.6rem 0 0;
    padding: 0;
    display: grid;
    row-gap: 0.1rem;
    position: relative;
}

/* The spine, drawn behind the rows. Every row uses the same three columns with
   equal 1fr sides, so the middle column's centre is exactly 50%. */
.tp-tl::before {
    content: "";
    position: absolute;
    top: 0.35rem;
    bottom: 0.35rem;
    left: 50%;
    width: 1px;
    transform: translateX(-0.5px);
    background: var(--tp-border-color, #1f2937);
}

/* Each row is its OWN grid, and both of its children are pinned to row 1.
   Both halves of that matter, because grid placement is *sparse*: it never
   moves backwards. On an away row the DOM order is entry (column 3) then
   minute (column 2), so the minute can never be placed after the cursor —
   without `grid-row: 1` it wraps onto a line of its own and the clock reads
   one event late all the way down. The mobile layout below reverses the two
   columns, which trips the same rule, so the pin is what fixes both. */
.tp-tl__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 2.9rem minmax(0, 1fr);
    align-items: center;
}

.tp-tl__row > * { grid-row: 1; }

.tp-tl__min {
    grid-column: 2;
    justify-self: center;
    z-index: 1;
    padding: 0.1rem 0.3rem;
    border-radius: 4px;
    background: var(--tp-card, #141b24);
    font-size: 0.72rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--tp-muted, #94a3b8);
    white-space: nowrap;
}

/* Half separator — the structural break a reader expects between 45' and 46'.
   Sits above the spine (z-index on a grid item works without positioning, the
   same trick .tp-tl__min uses) with a card-coloured band, so the vertical line
   stops at the label instead of striking through it. */
.tp-tl__half {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    z-index: 1;
    margin: 0.45rem 0 0.3rem;
    padding: 0.15rem 0;
    background: var(--tp-card, #141b24);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--tp-muted, #94a3b8);
}

.tp-tl__half::before,
.tp-tl__half::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--tp-border-color, #1f2937);
}

.tp-tl__half:first-child { margin-top: 0; }

.tp-tl__tick { opacity: 0.6; }

.tp-tl__entry {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    padding: 0.34rem 0.1rem;
}

/* Home reads outward from the spine, away mirrors it — so both lanes lead
   with their icon against the clock and run their names away from it. */
.tp-tl__row--home .tp-tl__entry {
    grid-column: 1;
    flex-direction: row-reverse;
    text-align: right;
}

.tp-tl__row--away .tp-tl__entry {
    grid-column: 3;
    text-align: left;
}

.tp-tl__text { min-width: 0; }

.tp-tl__who {
    display: block;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--tp-foreground, #e8eaf0);
    overflow-wrap: anywhere;
}

.tp-tl__note {
    display: block;
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1.3;
    color: var(--tp-muted, #94a3b8);
}

/* ---- Icons ------------------------------------------------------------
   Drawn here rather than emitted as markup, so the PHP template and the
   live script only ever have to agree on a class name. Cards are real
   rectangles — an emoji card renders differently on every platform and at
   this size reads as a coloured smudge. */

.tp-tl__icon {
    flex: none;
    width: 18px;
    height: 18px;
    display: grid;
    place-items: center;
    font-size: 0.95rem;
    line-height: 1;
}

.tp-tl__entry--goal .tp-tl__icon::before,
.tp-tl__entry--pen-goal .tp-tl__icon::before,
.tp-tl__entry--own-goal .tp-tl__icon::before { content: "⚽"; }

/* An own goal is still a goal on the board, but it belongs to the other
   team's story — desaturated and tinted so it never reads as a celebration. */
.tp-tl__entry--own-goal .tp-tl__icon::before {
    filter: grayscale(1);
    opacity: 0.75;
}
.tp-tl__entry--own-goal .tp-tl__who { color: var(--tp-muted, #94a3b8); }

/* A missed penalty arrives from the feed as a "Goal" event that isn't one.
   It gets a crossed ball so it can never be mistaken for a score. */
.tp-tl__entry--pen-miss .tp-tl__icon::before {
    content: "⊘";
    color: var(--tp-danger, #ef4444);
    font-size: 1.05rem;
    font-weight: 700;
}
.tp-tl__entry--pen-miss .tp-tl__who { color: var(--tp-muted, #94a3b8); }

.tp-tl__entry--yellow .tp-tl__icon::before,
.tp-tl__entry--red .tp-tl__icon::before,
.tp-tl__entry--yellow2 .tp-tl__icon::before {
    content: "";
    width: 10px;
    height: 14px;
    border-radius: 2px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

.tp-tl__entry--yellow .tp-tl__icon::before { background: #facc15; }
.tp-tl__entry--red .tp-tl__icon::before    { background: var(--tp-danger, #ef4444); }

/* Second yellow: one card showing both, split on the diagonal. */
.tp-tl__entry--yellow2 .tp-tl__icon::before {
    background: linear-gradient(115deg, #facc15 0 48%, var(--tp-danger, #ef4444) 52% 100%);
}

.tp-tl__entry--sub .tp-tl__icon::before {
    content: "⇄";
    color: var(--tp-primary, #10b981);
    font-weight: 700;
}
.tp-tl__entry--sub .tp-tl__who { font-weight: 600; }

.tp-tl__entry--other .tp-tl__icon::before {
    content: "•";
    color: var(--tp-muted, #94a3b8);
}

/* Goals earn a tinted lane so the two or three moments that decided the match
   stand out from the bookings and substitutions around them. */
.tp-tl__row--home .tp-tl__entry--goal,
.tp-tl__row--home .tp-tl__entry--pen-goal,
.tp-tl__row--away .tp-tl__entry--goal,
.tp-tl__row--away .tp-tl__entry--pen-goal {
    background: rgba(16, 185, 129, 0.07);
    border-radius: 8px;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}

/* Narrow screens: the two lanes collapse to one, and the clock moves to the
   front of each row — a 2.9rem spine between two columns of names is unusable
   below about 380px. */
@media ( max-width: 460px ) {
    .tp-tl { row-gap: 0.15rem; }
    .tp-tl::before { display: none; }
    .tp-tl__row {
        grid-template-columns: 2.6rem minmax(0, 1fr);
    }
    .tp-tl__min {
        grid-column: 1;
        justify-self: start;
        background: none;
        padding-left: 0;
    }
    .tp-tl__row--home .tp-tl__entry,
    .tp-tl__row--away .tp-tl__entry {
        grid-column: 2;
        flex-direction: row;
        text-align: left;
    }
    /* With both teams in one column the side has to be said again — a rule on
       the away lane's leading edge, rather than a label on every row. */
    .tp-tl__row--away .tp-tl__entry {
        border-left: 2px solid var(--tp-border-color, #1f2937);
        padding-left: 0.55rem;
    }
}

/* `.tp-is-live` is set on the row/card root by live.js and carries NO styling of
   its own — the pulsing badge is already the signal, and a second marker on the
   row read as noise. It stays because it is the only "this row is live" hook;
   the Tips Page Browser stylesheet uses it to undo the two rules that would
   otherwise hide the badge it puts in the confidence slot. Those live in that
   block's own SCSS rather than here, so they can out-specify the rules they
   need to beat without depending on which stylesheet loads first. */
