/* Premier Padel hub — brand palette: navy #0a1020, cyan #52e1fe, amber #ffb547 */

/* Desktop: fill the rail's content column. The base .app-main uses
   `margin:0 auto`, which in the app-shell grid cell suppresses `stretch` and
   shrink-wraps the hub off-centre; drop the cap + auto-margins so the hero
   spans the column and the card grid goes multi-column. Mobile keeps the base
   .app-main rule (capped column + bottom padding that clears the floating nav). */
@media (min-width:1024px){
  /* min-width:0 is load-bearing: .app-main is a 1fr grid item, whose default
     min-width:auto lets the wide carousel rail blow the track out and scroll
     the whole page. Zeroing it keeps the column at the available width so the
     rail scrolls internally instead. */
  .premier-padel-page .app-main { max-width:none; margin:0; min-width:0; }
}

/* ── Editorial hero (Om-style) ── prominent display headline; the cinematic
   banner is a supporting side visual, not a full-bleed block. Stacks on
   mobile (banner on top, reduced height), two columns on wider screens. */
.pp-hero {
  position: relative; overflow: hidden; isolation: isolate;
  border-radius: 16px; border: 1px solid rgba(255,255,255,.08);
  background: #0a1020;
  margin: 4px 0 34px; padding: clamp(24px, 4.5vw, 52px);
  min-height: clamp(220px, 30vw, 360px);
  display: flex; flex-direction: column; justify-content: center;
}
/* Full-bleed banner, faded into the navy so the text reads on top. */
.pp-hero__visual {
  position: absolute; inset: 0; z-index: -1;
  background: var(--pp-banner) center / cover no-repeat;
  opacity: .30;
}
.pp-hero__visual::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(10,16,32,.86) 0%, rgba(10,16,32,.42) 62%, rgba(10,16,32,.12) 100%),
    linear-gradient(0deg, rgba(10,16,32,.5) 0%, transparent 46%);
}
.pp-hero__body { position: relative; display: flex; flex-direction: column; align-items: flex-start; max-width: 640px; }

.pp-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono), monospace;
  color: #52e1fe; letter-spacing: .22em; font-size: 10px; font-weight: 700;
  text-transform: uppercase; margin: 0 0 14px;
}
.pp-eyebrow-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #52e1fe; box-shadow: 0 0 10px #52e1fe;
  animation: pp-blink 1.6s ease-in-out infinite;
}
@keyframes pp-blink { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.pp-headline {
  font-family: var(--font-display), 'Manrope', sans-serif;
  font-weight: 800; font-size: clamp(30px, 5vw, 52px); line-height: 1.02;
  letter-spacing: -0.035em; color: #f3f6fb; margin: 0 0 14px;
}
.pp-headline em { font-style: normal; color: #ffb547; }
.pp-lede { font-size: 15px; line-height: 1.6; color: #9aa6bc; max-width: 54ch; margin: 0 0 20px; }

.pp-numbers {
  display: grid; grid-template-columns: repeat(3, auto); margin: 4px 0 0;
  border-top: 1px solid rgba(255,255,255,.08); border-bottom: 1px solid rgba(255,255,255,.08);
}
.pp-numbers > div {
  padding: 12px 20px; display: flex; flex-direction: column; gap: 4px;
  border-left: 1px solid rgba(255,255,255,.08);
}
.pp-numbers > div:first-child { border-left: none; padding-left: 0; }
.pp-numbers dt { font-family: var(--font-mono), monospace; font-size: 9px; letter-spacing: .2em; text-transform: uppercase; color: #5d6a82; order: 1; }
.pp-numbers dd { font-family: var(--font-mono), monospace; font-size: 22px; font-weight: 700; letter-spacing: -.02em; color: #52e1fe; line-height: 1; margin: 0; order: 2; }
.pp-numbers > div:nth-child(2) dd { color: #f3f6fb; }
.pp-numbers > div:nth-child(3) dd { color: #ffb547; }
.pp-numbers span { font-family: var(--font-mono), monospace; font-size: 9px; letter-spacing: .14em; text-transform: uppercase; color: #5d6a82; order: 3; }


.pp-band { margin: 0 0 32px; }
.pp-band__title { color: #f3f6fb; font-size: 18px; font-weight: 700; margin: 0 0 12px; }
.pp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }

/* ── Upcoming carousel ── the "Kommende" band is a horizontal scroll-snap rail
   instead of a wrapping grid, so 12 upcoming events don't flood the page.
   ‹ › arrows page through it on desktop; touch/narrow screens swipe natively. */
.pp-band__bar { display: flex; align-items: center; justify-content: space-between; margin: 0 0 12px; }
.pp-band__bar .pp-band__title { margin: 0; }
.pp-rail__nav { display: flex; gap: 8px; }
.pp-rail__arrow {
  width: 32px; height: 32px; border-radius: 999px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1; padding-bottom: 2px;
  color: #cdd6e6; background: #0e1626; border: 1px solid rgba(255,255,255,.12);
  transition: color .15s ease, border-color .15s ease, opacity .15s ease;
}
.pp-rail__arrow:hover:not(:disabled) { color: #52e1fe; border-color: rgba(82,225,254,.45); }
.pp-rail__arrow:disabled { opacity: .35; cursor: default; }

.pp-rail {
  display: flex; gap: 14px; overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-behavior: smooth; padding: 2px 0 10px; min-width: 0;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.pp-rail::-webkit-scrollbar { display: none; }
/* Show exactly four cards per view — 1 live + the next 3 kommende — with the
   14px gaps accounted for; narrower viewports drop to the 200px floor and the
   rest scroll. */
.pp-rail .pp-card { flex: 0 0 calc((100% - 42px) / 4); min-width: 200px; scroll-snap-align: start; }

/* Touch / narrow viewports: hide the arrows, native swipe takes over. */
@media (hover: none), (max-width: 640px) {
  .pp-rail__nav { display: none !important; }
}

.pp-card {
  position: relative; overflow: hidden; isolation: isolate;
  background: #0e1626; border: 1px solid rgba(255,255,255,.08);
  border-radius: 14px; padding: 16px; cursor: default;
  transition: transform .15s ease, border-color .15s ease;
}
/* Column flex so the pair blocks can bottom-anchor (see .pp-champs). The grid
   already stretches cards to a common height; anchoring to that shared bottom
   edge is what keeps Champions/Runners-up on one line across a row even when a
   long event name ("Riyadh Season P1 2026" at ~260px columns, i.e. on a 1920px
   screen) wraps its title to two lines.
   Scoped by :has(.pp-champs__rows) — the row-group wrapper — rather than
   applied to bare .pp-card, because .pp-card is shared well beyond the two
   pair-card templates and column flex also stops sibling margins collapsing.
   Both _premier_card.html and _serie_card.html emit the wrapper, so both opt
   in; a card without a pair block (the rail's upcoming stops, placeholders) is
   untouched. Keep the two templates in step. */
.pp-card:has(.pp-champs__rows) { display: flex; flex-direction: column; }
/* Faded tournament poster as card texture — kept subtle so the card text
   stays legible; the scrim darkens toward the bottom where dates/champions sit. */
.pp-card__bg {
  position: absolute; inset: 0; z-index: -1;
  background: var(--pp-poster) center 15% / cover no-repeat;
  opacity: .16; transition: opacity .15s ease;
}
.pp-card__bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(14,22,38,.45) 0%, rgba(14,22,38,.80) 100%);
}
.pp-card--poster { border-color: rgba(255,255,255,.12); }
.pp-card[data-pp-slug]:hover .pp-card__bg { opacity: .24; }
.pp-card[data-pp-slug] { cursor: pointer; }
.pp-card[data-pp-slug]:hover { transform: translateY(-2px); border-color: rgba(82,225,254,.4); }
/* Name is the card anchor now that the tier badge is gone: bolder, tighter,
   near-white display type. Tier still reads from the name itself ("P1"). */
.pp-card__name {
  color: #f3f6fb; font-family: var(--font-display), 'Manrope', sans-serif;
  font-size: 19px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.15;
  margin: 2px 0 6px; padding-right: 24px;
}
.pp-card__meta { display: flex; align-items: center; gap: 7px; color: #9aa6bc; font-size: 12px; margin: 0; }
.pp-card__dates { color: #9aa6bc; font-size: 12px; margin: 4px 0 0; }
.pp-card__pending { color: #5d6a82; font-size: 12px; font-style: italic; margin: 10px 0 0; }
/* /rankings/serie: the pre-draw sign-up line reuses the pending slot's position
   so cards in a row keep their baseline, but drops the italic — italic is the
   house voice for "nothing here yet", and this IS the content. The count leads
   in --text; the deadline drops to its own muted line so a long date can never
   push the number onto a second row. */
.dps-card__signups { font-style: normal; color: var(--text-muted); }
.dps-signups__n { color: var(--text); font-weight: 700; font-size: 13.5px; }
.dps-signups__close { display: block; margin-top: 2px; font-size: 11px; color: #5d6a82; }

/* Live signal = the fip-radar spinning padel ball (.rd-live, from fip-radar.css,
   already loaded on this page). With the header row gone it floats in the
   card's top-right corner. */
.pp-card--live { border-color: rgba(255,107,94,.4); }
.pp-card__live { position: absolute; top: 14px; right: 14px; z-index: 1; }

/* Postponed: the whole card fades back and a large "Udskudt" rubber-stamp is
   struck diagonally across it. Everything but the stamp is dimmed so the stamp
   is unmistakably the card's headline. */
.pp-card--postponed > :not(.pp-stamp) { opacity: .34; }
.pp-stamp {
  position: absolute; top: 50%; left: 50%; z-index: 3; pointer-events: none;
  transform: translate(-50%, -50%) rotate(-11deg);
  font-family: var(--font-mono), monospace; font-weight: 800;
  font-size: clamp(18px, 5vw, 26px); letter-spacing: .14em;
  text-transform: uppercase; white-space: nowrap; color: #ff6b5e;
  padding: 6px 18px; border-radius: 8px;
  border: 3px solid rgba(255,107,94,.6);
  box-shadow: inset 0 0 0 2px rgba(255,107,94,.18);
  background: rgba(255,107,94,.08);
  opacity: .92;
}

/* Champions — no gender labels; a quiet gold "Mestre" eyebrow lends prestige
   and the winning pairs read as the card's payoff. */
/* margin-top:auto bottom-anchors the whole champions+runners stack to the card's
   shared bottom edge, so the two eyebrows line up across a row no matter what
   happens above them (wrapped title, missing city, longer date range). */
.pp-champs { margin: 13px 0 0; display: flex; flex-direction: column; gap: 5px; }
.pp-card:has(.pp-champs__rows) > .pp-champs { margin-top: auto; padding-top: 13px; }
.pp-champs__label {
  font-family: var(--font-mono), monospace; font-size: 9px; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: #ffb547;
  margin: 0 0 1px;
}
/* Both pair blocks reserve room for TWO two-line rows whether or not the names
   wrap. The grid is fluid (auto-fill minmax(240px, 1fr)), so column width — and
   therefore which pairs wrap — changes continuously with the viewport; without a
   reservation the Runners-up eyebrow lands at a different y on almost every card
   and the band reads ragged. Measured across 320–1920px a pair never exceeds two
   lines, so two is a true ceiling, not a clamp — nothing is ever truncated.
   Reserved on the ROW GROUP, not on each row: the slack then collects in one
   pocket at the foot of the block instead of opening a gap under every single
   line, which is what keeps the rhythm tight when nothing wraps at all. */
.pp-champs__rows {
  display: flex; flex-direction: column; gap: 5px;
  min-height: calc(2 * (2 * 13.5px * 1.25) + 5px);
}
.pp-champ {
  display: flex; align-items: baseline; gap: 7px;
  color: #f3f6fb; font-size: 13.5px; font-weight: 700; letter-spacing: -.01em;
  line-height: 1.25; margin: 0;
}
.pp-champ__ico { font-size: 12px; flex-shrink: 0; }

/* Runners-up — the same two-row shape as champions, in the same M-then-W order,
   but stepped down on every axis (silver eyebrow, lighter weight, dimmer ink)
   so the winners keep the card's payoff and the finalists read as the footnote.
   Sits tight under the champions block: one group, two tiers. */
.pp-runners { margin: 9px 0 0; display: flex; flex-direction: column; gap: 4px; }
.pp-runners__label {
  font-family: var(--font-mono), monospace; font-size: 9px; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: #9fb0c6;
  margin: 0 0 1px;
}
.pp-runners__rows {   /* same two-line-per-pair reservation as .pp-champs__rows */
  display: flex; flex-direction: column; gap: 4px;
  min-height: calc(2 * (2 * 12.5px * 1.25) + 4px);
}
.pp-runner {
  display: flex; align-items: baseline; gap: 7px;
  color: #c3cee0; font-size: 12.5px; font-weight: 600; letter-spacing: -.01em;
  line-height: 1.25; margin: 0;
}
.pp-runner__ico { font-size: 11px; flex-shrink: 0; }

/* The draw option in the .lab-plm chooser is a <button> (the others are <a>);
   strip the default UA button chrome so it matches the anchor rows instead of
   rendering as a light native button. */
button.lab-plm__item {
  -webkit-appearance: none; appearance: none;
  background: none; width: 100%; margin: 0;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
/* fip-mark.svg is the FIP emblem cropped out of the wide wordmark (white ball).
   Show it contained on a dark badge so it reads like the Padelytics mark. */
.pp-badge-fip {
  background: #12233d; object-fit: contain; padding: 3.5px;
}

/* ── Draw viewer (/premier-padel/<slug>) ── faded per-event banner hero +
   the FIP bracket rendered below by fip-radar.js. Reuses .pp-hero__visual. */
.pp-draw-hero {
  position: relative; overflow: hidden; isolation: isolate;
  border-radius: 16px; border: 1px solid rgba(255,255,255,.08);
  background: #0a1020; margin: 4px 0 22px;
  padding: clamp(20px, 3.4vw, 38px);
  min-height: clamp(150px, 20vw, 220px);
  display: flex; flex-direction: column; justify-content: center;
}
/* Per-event posters are portrait-ish with players' faces in the upper band;
   the wide hero crops a horizontal slice, so bias it toward the top (like the
   cards' `center 15%`) to catch faces far more often than a centred crop.
   Scoped to the draw hero so the hub's designed landscape banner stays centred. */
.pp-draw-hero .pp-hero__visual { background-position: center 28%; }
.pp-draw-hero__body { position: relative; display: flex; flex-direction: column; align-items: flex-start; width: 100%; }
.pp-draw-hero__top {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; margin: 0 0 16px;
}
.pp-draw-back, .pp-draw-fip {
  font-family: var(--font-mono), monospace; font-size: 11px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; text-decoration: none;
  color: #9aa6bc; display: inline-flex; align-items: center; gap: 6px;
  transition: color .15s ease;
}
.pp-draw-back:hover { color: #52e1fe; }
.pp-draw-fip:hover { color: #ffb547; }
.pp-draw-tier { margin: 0 0 10px; }
.pp-draw-title {
  font-family: var(--font-display), 'Manrope', sans-serif;
  font-weight: 800; font-size: clamp(24px, 3.6vw, 40px); line-height: 1.04;
  letter-spacing: -0.03em; color: #f3f6fb; margin: 0 0 10px;
}
.pp-draw-meta {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13px; color: #9aa6bc; margin: 0;
}
.pp-draw-meta .flag { border-radius: 2px; }
.pp-draw-dot { color: #445; }
.pp-draw-champ {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 12px 0 0; font-size: 13px; color: #cdd6e6;
}
.pp-draw-champ__lbl {
  font-family: var(--font-mono), monospace; font-size: 9px; letter-spacing: .2em;
  text-transform: uppercase; color: #ffb547;
}
.pp-draw-champ__pair { color: #f3f6fb; }

/* Bracket host: let the fipdraw grid scroll horizontally inside the column
   without pushing the page. fip-radar.css styles the .fipdraw-* children. */
.pp-draw { position: relative; }
.pp-draw-body { min-width: 0; }
/* The shared renderer wraps the bracket in .fipdraw-panel{overflow:hidden}
   (needed for the overlay's rounded clip). On the full-page viewer there is no
   clip to keep, and that hidden overflow would break the sticky round-jump rail
   — lift it so the rail can pin to the viewport top as the page scrolls. */
.pp-draw-body .fipdraw-panel { overflow: visible; }
.pp-draw-body[data-state="loading"] .fipdraw-loading { display: block; }

/* Premier draw — match-details modal (result block + clickable cells).
   H2H + shell reuse the .lab-ms-* classes from tournament-cards.css. */
[data-premier-draw] .fipdraw-match.is-clickable{position:relative;cursor:pointer;}
/* Hover tints the card's --fd-edge (see .fipdraw-match in fip-radar.css) instead
   of washing the whole tile amber. The card's fill is now the only thing saying
   "nothing to report about this box" — a hover that floods it competes with the
   winner-row lift inside, and made the two rows harder to tell apart exactly when
   the pointer is on them. The edge is the container's own affordance. */
[data-premier-draw] .fipdraw-match.is-clickable:hover{--fd-edge:rgba(255,181,71,0.45);}
/* ══ .pp-mk — the rivalry marker ════════════════════════════════════════════
   ONE component, three surfaces: the draw cell, "Latest results" and "Upcoming
   matches". Markup comes from PremierH2H.markerHtml (premier-h2h.js) so a
   matchup cannot read hot in the bracket and cool on a card.

   TWO CHANNELS, NO OVERLAP:
     form   → WHICH relation. Two bars = two teams met. Four dots = four
              players crossed in some other pairing.
     colour → HOW intense. The four-step ramp below, nothing else.
   They must not trade places. The ramp already spends every hue it has on
   intensity, so if kind also took a hue a "player" marker could not read as
   hot. This is why the glyph exists at all.

   The COUNT is bare — no 'v' suffix. That suffix only distinguished the two
   markers back when they lived in opposite corners; the glyph does it now.

   Superseded (do not reintroduce): .fipdraw-h2h top-right + .fipdraw-x
   bottom-right, told apart by position and a dashed border. Two corners meant
   the carousels had nowhere to put a marker without inventing a third
   convention, and a 1px dashed ring is the first thing to disappear over an
   event poster. */
:root{
  /* Re-spaced 2026-07-30. The old ramp (#f59e0b/#fb923c/#ef4444) had two
     measured faults. Amber→orange was ΔE 18.1 at 11.7° of hue with near-equal
     lightness — below where 11px text stays separable. And #ef4444 was the
     DIMMEST colour in the ramp at 4.80:1 on --card, so the hottest tier
     receded exactly where it should advance; the old solid-red plate was
     covering for that. Re-spacing puts the steps at ΔE 91.5 / 45.0 / 28.5 and
     lifts tier 4 to 6.52:1.
     ⚠️ Shared by the draw bracket and both carousels — changing one of these
     hues repaints 32 bracket cells too. Verify a bracket, not just a card. */
  --mk-t1:#94a3b8;   /* 1 — they have a history      7.05:1 */
  --mk-t2:#fbbf24;   /* 2 — familiar opponents      10.83:1 */
  --mk-t3:#fb7a3c;   /* 3 — a real rivalry           7.31:1 */
  --mk-t4:#ff6b6b;   /* 4 — marquee                  6.52:1 */
}

.pp-mk{
  --c:var(--mk-t1);
  display:inline-flex;align-items:center;gap:6px;
  padding:3px 8px 3px 6px;border-radius:6px;min-height:20px;
  border:1px solid transparent;
  font-family:var(--font-mono),monospace;font-variant-numeric:tabular-nums;
  font-size:11px;font-weight:700;line-height:1;letter-spacing:.02em;
  color:var(--c);
  /* The card is the hit target; this must never intercept a tap meant for it. */
  pointer-events:none;
  /* Colour only fires on a live-score update — rare, and worth noticing. */
  transition:color 150ms ease,background-color 150ms ease;
  /* Near-opaque NEUTRAL scrim, not a hue wash. Roughly half the carousel cards
     carry an event poster, and .rr-sig__bg::after fades .82 → .12 toward the
     BOTTOM-RIGHT — the marker sits on the least-protected pixel of the card.
     Two reasons the scrim is neutral rather than tinted: it fixes the ground so
     the contrast figures above hold on poster cards as well as plain ones, and
     a translucent hue fill would let the poster bleed through and SHIFT the
     colour, which is fatal when the colour is the tier. Hue lives in the glyph
     and the digit, both sitting on a known ground.
     No backdrop-filter: at .86 the scrim already does the job, and a blur on
     every card in a scrolling rail is cost with no payoff. */
  background:rgba(14,22,38,.86);
}
.pp-mk[data-heat="1"]{--c:var(--mk-t1);}
.pp-mk[data-heat="2"]{--c:var(--mk-t2);}
.pp-mk[data-heat="3"]{--c:var(--mk-t3);}
.pp-mk[data-heat="4"]{--c:var(--mk-t4);}

/* The glyph. Four <i> ship for both kinds (see markerHtml) — CSS spans the
   first two into bars for a pair and hides the rest; the player marker leaves
   all four as dots. Same node count keeps the two markers the same width, and
   they share one grid cell in the card footer. */
.pp-mk__glyph{display:grid;grid-template-columns:repeat(2,1fr);gap:2px;width:11px;}
.pp-mk__glyph i{display:block;height:3px;border-radius:1px;
  background:currentColor;opacity:.85;}
.pp-mk[data-kind="pair"] .pp-mk__glyph i:nth-child(1),
.pp-mk[data-kind="pair"] .pp-mk__glyph i:nth-child(2){grid-column:1/-1;}
.pp-mk[data-kind="pair"] .pp-mk__glyph i:nth-child(3),
.pp-mk[data-kind="pair"] .pp-mk__glyph i:nth-child(4){display:none;}
.pp-mk[data-kind="cross"] .pp-mk__glyph i{
  height:3px;width:3px;border-radius:50%;justify-self:center;}

/* Pair takes a hue veil LAYERED OVER the scrim, never blended into it, so the
   tint is computed against a fixed backdrop and a pair chip reads identically
   on a poster card and a plain one. Player takes the scrim alone. That veil is
   the whole fill difference between the two kinds — the glyph is the primary
   cue and is doing the real work. */
.pp-mk[data-kind="pair"]{
  background:
    linear-gradient(color-mix(in srgb,var(--c) 15%,transparent),
                    color-mix(in srgb,var(--c) 15%,transparent)),
    rgba(14,22,38,.86);
}
/* Tier 4 is the one place the plate goes solid. No glow ring: a spread
   box-shadow at low alpha over a dark ground renders as a muted band and reads
   as a second border, which is the thing this component removed everywhere
   else. It was only ever propping up the old dim red. Dark ink, not white —
   white on the lifted red is 2.9:1, ink is 7.4:1. */
.pp-mk[data-kind="pair"][data-heat="4"]{
  background:var(--mk-t4);color:#160a0a;}

/* ── Draw cell placement ── one slot, bottom-right, both kinds. Was two
   corners; see the superseded note above. */
[data-premier-draw] .fipdraw-match .pp-mk{
  position:absolute;right:6px;bottom:6px;z-index:2;
  opacity:.9;transition:opacity .15s ease,color 150ms ease;}
[data-premier-draw] .fipdraw-match.is-clickable:hover .pp-mk{opacity:1;}

/* ══ Match-details modal (Premier draw matchup) ═════════════════════════════
   Two blocks, one vocabulary: THIS match's result up top, then the pair-vs-pair
   ledger below. Outcome is carried by a mint/rose fade off each pair's own edge
   — the same device the bracket card uses (.fipdraw-match/--fd-half, fip-radar.css
   ~line 925) and the same tokens (mint rgba(74,222,128), rose rgba(255,107,107),
   rose at a lower alpha because red reads louder at equal opacity). No coloured
   left rail anywhere: a bar beside a row says "this row is flagged", a wash
   behind it says "this pair won", and only the second one is true here. */

/* Scroll. The shared `.fipdraw-modal .lab-ms-body{overflow:hidden}` rule (fip-radar.css
   ~line 1311) exists so the DRAW overlay's bracket pane can be the one and only
   scroller — Chrome latches a diagonal wheel gesture to the innermost scroller
   that can consume any of it, so two nested scrollers made the draw modal stall.
   This sheet has no inner scroller at all: its body is a plain vertical stack, and
   inheriting that rule clipped the H2H ledger with no way to reach it. Opt back in. */
.fipdraw-modal.pp-md-modal .lab-ms-body{
  display:block;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
}
/* Fade over the last few pixels of the scroller while there is more below, so the
   sheet never ends on a crisp edge that reads as "this is all of it". Sits above
   the body, inside the modal's own overflow:hidden, and is toggled by syncFade()
   in fip-radar.js. Colour matches the sheet's bottom gradient stop. */
.pp-md-modal::after{
  content:'';position:absolute;left:1px;right:1px;bottom:0;height:56px;
  background:linear-gradient(180deg,rgba(20,24,32,0) 0%,rgba(20,24,32,0.92) 78%,rgba(20,24,32,0.98) 100%);
  opacity:0;transition:opacity 0.18s ease;pointer-events:none;z-index:3;
}
.pp-md-modal.has-more::after{opacity:1;}
@media (prefers-reduced-motion:reduce){
  .pp-md-modal::after{transition:none;}
}

/* ── Live state in the header (2026-07-30) ───────────────────────────────────
   For a match on court this line REPLACES the schedule line (fip-radar.js emits
   one or the other, never both): a start time that has already passed was the
   only thing left on the page contradicting the band's own LIVE card. Same idiom
   as that card's .pp-up__when--live — dot + uppercase label, no pill, colour
   carries the state — so the two read as one system.
   Mint is the literal #7fe3a5, not a token: the only COLOUR tokens that exist in
   this codebase are --text, --text-muted and --card-border, and any other colour
   var() silently resolves to its fallback and ships wrong. Non-colour tokens are
   a separate question — --font-mono below is real (components.css).
   The dot does NOT blink. .lab-ms-kicker-dot immediately above it already pulses
   (amber), and the card's own dot is static — a second pulse in one header reads
   as two competing alarms rather than one state. */
/* flex, NOT inline-flex: this replaces a plain block div, and .lab-ms-kicker
   above it is itself inline-flex — so an inline box here joined that eyebrow into
   one run-on line ("MATCH DETAILS ●LIVE · SET 2"). Own line, own row.

   Geometry (10px mono / 800 / 0.24em / 6px dot + 8px gap) is .lab-ms-kicker's,
   restated rather than inherited, and it is NOT the card's slightly tighter
   kicker: this line sits directly under that eyebrow, so two dot-led lines whose
   dots and text do not share a left edge read as a rendering mistake. The IDIOM
   travels from the card (dot, uppercase, mint, no pill); the metrics belong to
   the sheet. Change these two rules together with .lab-ms-kicker or the pair
   drifts out of alignment. */
.pp-md-live{
  display:flex;align-items:center;gap:8px;
  font-family:var(--font-mono),monospace;font-size:10px;font-weight:800;
  letter-spacing:0.24em;text-transform:uppercase;color:#7fe3a5;
}
.pp-md-live__dot{
  width:6px;height:6px;border-radius:50%;
  background:currentColor;flex:0 0 auto;
}

/* ── Result block: this match ───────────────────────────────────────────── */
.pp-md-result{display:flex;flex-direction:column;gap:8px;}
.pp-md-team{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:12px 14px;border-radius:12px;
  border:1px solid var(--card-border,rgba(255,255,255,0.08));
  background:rgba(255,255,255,0.02);
}
/* Wash runs off the row's leading edge and dies before the score, so the colour
   describes the pair rather than tinting a whole box the way a flag would. */
.pp-md-team--win{
  border-color:rgba(74,222,128,0.30);
  background:linear-gradient(90deg,rgba(74,222,128,0.10),rgba(74,222,128,0) 62%),
             rgba(255,255,255,0.02);
}
.pp-md-team--loss{
  border-color:rgba(255,107,107,0.20);
  background:linear-gradient(90deg,rgba(255,107,107,0.065),rgba(255,107,107,0) 62%),
             rgba(255,255,255,0.02);
}
.pp-md-name{font-weight:600;color:var(--text-muted,#b9bdc7);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pp-md-team--win .pp-md-name{color:var(--text,#f4f5f7);font-weight:700;}
.pp-md-sets{display:inline-flex;gap:8px;flex:0 0 auto;}
.pp-md-set{
  font-family:var(--font-mono,"JetBrains Mono"),monospace;
  font-weight:700;font-size:15px;font-variant-numeric:tabular-nums;
  color:var(--text-dim,#8a8f9a);min-width:16px;text-align:center;
}
.pp-md-team--win .pp-md-set{color:#7fe3a5;}
/* Live games, in the same column and the same mint the card uses down its right
   edge (.pp-up__set). Mint here says "these numbers are live", not "this side
   won" — a live match has no winner, so no row carries --win/--loss at all.
   ⚠️ Must stay AFTER .pp-md-set: both are 0-1-0, so source order is the only
   thing making this colour win over that rule's own. */
.pp-md-set--live{color:#7fe3a5;}

/* ── Rail counter — "10 meetings" trailing the section slug ─────────────── */
.ppmd-count{
  flex:0 0 auto;
  font-family:var(--font-mono),monospace;font-size:9.5px;font-weight:700;
  letter-spacing:0.16em;text-transform:uppercase;color:var(--text-dim);
}
.ppmd-count:empty{display:none;}

/* ── H2H record ─────────────────────────────────────────────────────────── */
.ppmd-rec{
  display:flex;align-items:center;gap:14px;margin:0 0 12px;
  padding:12px 14px;border-radius:var(--radius-md,12px);
  background:rgba(255,255,255,0.025);
  border:1px solid var(--card-border);
}
.ppmd-rec__t{
  flex:1;min-width:0;font-size:13px;font-weight:600;color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ppmd-rec__t--r{text-align:right;}
.ppmd-rec__t.is-lead{color:var(--text);font-weight:700;}
.ppmd-rec__s{display:inline-flex;align-items:baseline;gap:6px;flex:0 0 auto;
  font-family:var(--font-mono),monospace;font-variant-numeric:tabular-nums;}
.ppmd-rec__n{font-size:22px;font-weight:700;color:var(--text-muted);}
/* Mint/rose here too, not amber: the ledger below spends green on "won" a few
   dozen times, and a gold leader above it would be a third state that means the
   same thing. */
.ppmd-rec__n.is-lead{color:#7fe3a5;text-shadow:0 0 12px rgba(74,222,128,0.30);}
.ppmd-rec__n.is-trail{color:#e59a97;}
.ppmd-rec__dash{font-size:14px;color:var(--text-dim);}

/* ── The ledger: one card per meeting ───────────────────────────────────── */
.ppmd-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px;}
.ppmd-m{
  border:1px solid var(--card-border);border-radius:var(--radius-sm,10px);
  background:rgba(255,255,255,0.018);
  padding:7px 9px 8px;
  transition:border-color 0.16s ease;
}
.ppmd-m:hover{border-color:rgba(255,255,255,0.13);}
/* Meta strip — date, event, hairline, round. Mono for the two data atoms, sans
   for the event name, same split the bracket's schedule slot makes. */
.ppmd-m__meta{
  display:flex;align-items:center;gap:8px;
  padding:0 2px 6px;min-width:0;
}
.ppmd-m__date{
  flex:0 0 auto;
  font-family:var(--font-mono),monospace;font-size:10px;font-weight:700;
  letter-spacing:0.04em;color:var(--text-dim);font-variant-numeric:tabular-nums;
}
.ppmd-m__where{
  min-width:0;font-size:11.5px;color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ppmd-m__rule{display:block;flex:1 1 auto;min-width:8px;height:1px;
  background:var(--om-hair,rgba(255,255,255,0.06));}
.ppmd-m__round{
  flex:0 0 auto;
  font-family:var(--font-mono),monospace;font-size:9.5px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;color:var(--text-dim);
}

/* Two-row scoreboard — DELIBERATELY UNTINTED (2026-07-28).
   These rows used to carry the bracket card's mint/rose half-fade, one per pair,
   on every meeting. Correct in isolation and wrong in aggregate: a marquee rivalry
   runs 37 cards, so the ledger became ~74 tinted bands and the hue stopped meaning
   "won" and started meaning "row". Colour has to stay scarce to stay legible, so
   it is spent ONCE per sheet — on the result block up top, which is the one match
   the sheet is about. Down here the outcome is carried by type weight (winner at
   --text/700 against the beaten pair's --text-muted/500) and by the winner's mint
   set chips, which are now the only hue in the list and read all the louder for it.
   Do not reintroduce a per-row wash. */
.ppmd-m__board{
  border-radius:7px;
  display:flex;flex-direction:column;
}
.ppmd-row{
  display:grid;grid-template-columns:1fr auto;align-items:center;gap:12px;
  padding:4px 6px;
}
.ppmd-row__nm{
  min-width:0;font-size:12.5px;line-height:1.3;font-weight:500;
  color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ppmd-row--win .ppmd-row__nm{color:var(--text);font-weight:700;}
/* Section 02's partner half ("Coello / TAPIA"). Held at --text-muted in BOTH
   states — not --text-dim, which fails AA at this size (see below) — so the
   demotion is carried by the winner's weight lift not landing on it. On a
   losing row the lineup reads flat, exactly like section 01's. */
.ppmd-row__pt{color:var(--text-muted);font-weight:500;}
/* The beaten pair holds at --text-muted rather than dropping to --text-dim: the
   name is the row's primary content and --text-dim under a rose wash lands below
   AA. The winner's weight lift carries the demotion instead. Same call
   .fipdraw-side makes in the bracket. */

/* Fixed-width cells, not just tabular figures — the set columns have to line up
   between the two rows even when one side's token is a digit longer ("6" vs
   "7(4)"), or the scoreboard stops reading as columns. */
.ppmd-row__sets{display:flex;gap:4px;flex:0 0 auto;}
.ppmd-set{
  display:flex;align-items:center;justify-content:center;
  min-width:23px;height:23px;border-radius:5px;
  font-family:var(--font-mono),monospace;font-weight:700;font-size:12.5px;
  font-variant-numeric:tabular-nums;color:var(--text-dim);
}
/* A set this row actually won. Neutral by default — a set stolen by the pair that
   went on to lose the match is still a set won, and painting it rose would lie. */
.ppmd-set--won{color:var(--text);background:rgba(255,255,255,0.055);}
.ppmd-row--win .ppmd-set--won{color:#7fe3a5;background:rgba(74,222,128,0.14);}
.ppmd-set--wo{
  min-width:auto;padding:0 8px;font-size:10px;letter-spacing:0.08em;
  color:var(--text-muted);background:rgba(255,255,255,0.055);
}
.ppmd-set--none{color:var(--text-dim);opacity:0.55;}
/* .fipdraw-tb (scoreHtml's tiebreak superscript) raises itself with
   vertical-align, which is INERT on a flex item — .ppmd-set is a flex box, so the
   raise has to come from a transform sized in the sup's own em. Mirrors .sb-tb. */
.ppmd-set .fipdraw-tb{
  align-self:center;transform:translateY(-0.62em);line-height:1;
  font-size:0.6em;color:inherit;opacity:0.72;
}

/* Collapse: the tail is hidden, not unrendered — the toggle is a class flip. */
.ppmd-list--collapsed .ppmd-m:nth-child(n + 4) { display: none; }

.ppmd-more {
  display: block;
  width: 100%;
  margin: 10px 0 0;
  padding: 9px 12px;
  border: 1px solid var(--card-border);
  border-radius: 10px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}
.ppmd-more:hover {
  color: var(--text);
  border-color: rgba(255,255,255,0.16);
  background: rgba(255, 255, 255, .03);
}
.ppmd-more:focus-visible { outline: 2px solid #7fe3a5; outline-offset: 2px; }

/* Narrow: the modal goes full-screen (<700px, fip-radar.css), but a phone can't
   hold two full pair names side by side — at 390px the record row truncated BOTH
   to an ellipsis, which is the one thing a head-to-head header must never do. */
@media (max-width:520px){
  /* Record folds into the same two-row scoreboard shape as everything below it:
     both pairs stacked on the left, the record spanning them on the right. DOM
     order is name / score / name, so grid-template-areas does the reordering. */
  .ppmd-rec{
    display:grid;grid-template-columns:1fr auto;
    grid-template-areas:"a s" "b s";
    gap:3px 12px;align-items:center;padding:11px 13px;
  }
  .ppmd-rec__t{grid-area:a;font-size:12px;}
  .ppmd-rec__t--r{grid-area:b;text-align:left;}
  .ppmd-rec__s{grid-area:s;}
  .ppmd-rec__n{font-size:19px;}
  /* Event name drops to its own line rather than fighting the round label for
     the same row; the hairline has nothing left to span. */
  .ppmd-m__meta{flex-wrap:wrap;gap:6px;}
  .ppmd-m__rule{display:none;}
  .ppmd-m__where{flex:1 1 100%;order:3;}
  .ppmd-row__nm{font-size:12px;}
  .ppmd-set{min-width:21px;height:21px;font-size:12px;}
  /* The result block up top carries FULL names — wrap them rather than ellipsing
     a player out of their own match. */
  .pp-md-name{white-space:normal;line-height:1.35;}
  .pp-md-team{padding:11px 13px;}
}

/* ══ "Latest results" band (2026-07-27) ═════════════════════════════════════
   Reuses fip-radar.css's .radar-band chrome (bar / nav / arrows / rail) and its
   .rr-sig card shell — only the result-specific bits live here.

   Card sizing (widened 2026-07-29 to make room for the #rank badge) lives HERE,
   in this file, and deliberately not in fip-radar.css. premier-padel.css is
   linked by exactly two templates — premier_padel.html:31 and
   premier_padel_draw.html:22 — and by nothing else. /rankings/fip-radar renders
   through rankings_shell.html, which loads fip-radar.css but NOT this file, and
   the signups rail's partial (_fip_radar_ticker.html, reached only via
   partials/_fip-radar.html) is never included by premier_padel.html. So no rule
   in this file can reach the FIP Radar signups rail. That is why PLAIN .rr-sig
   selectors are correct and sufficient here — no .pp-res qualifier is needed to
   protect anything — and why the Premier bands can be resized without touching
   fip-radar.css's shared .rr-sig base rule, which stays at its original
   /4 + 270px and is what /rankings/fip-radar keeps getting.

   How this beats that base rule: the two selectors are IDENTICAL
   (.radar-band__rail .rr-sig, 0-2-0), so specificity ties and SOURCE ORDER
   decides — both Premier templates link fip-radar.css before premier-padel.css.
   Only flex + min-width are restated; scroll-snap-align still comes from the
   base rule. Kept in step with .radar-band__rail .pp-up below, which restates
   the same two values for the upcoming band. */
.radar-band__rail .rr-sig{flex:0 0 calc((100% - 42px)/3.7);min-width:288px;}
.pp-results{margin:0 0 26px;}
.pp-res{display:flex;flex-direction:column;gap:10px;}
/* ONE rule for BOTH bands' top-right label (merged 2026-07-29). The two bands
   stack on the same page, so their right-hand label is deliberately one voice -
   same mono size, tracking, weight and colour - and the round label now lives
   inside it (`MEN R32`). Kept as a single selector list because two identical
   copies 120 lines apart drift: change the voice here and both bands move
   together, or don't change it at all. */
.pp-res__draw,
.pp-up__draw{margin-left:auto;font-family:var(--font-mono),monospace;font-size:9.5px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);
  white-space:nowrap;font-weight:600;}
.pp-res__pairs{display:flex;flex-direction:column;gap:6px;}
/* No rail, no dot, no caret (2026-07-27 redesign) — win/loss now reads purely
   from the per-pair scoreboard: bright vs dim games alongside the existing
   bright/dim name-weight contrast. name-column + games-column, the names side
   shrinking (min-width:0, so long names keep ellipsing) and the games side
   fixed so the set columns never get squeezed. */
.pp-res__pair{display:flex;align-items:center;justify-content:space-between;gap:8px;}
.pp-res__pair.is-won .nm{color:var(--text);font-weight:600;}
.pp-res__pair:not(.is-won) .nm{color:var(--text-muted);}
.pp-res__names{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto;}
.pp-res__pl{display:flex;align-items:center;gap:5px;min-width:0;}
.pp-res__pl .nm{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* #rank badge: a SIBLING of .nm, never inside it — .nm's own ellipsis/nowrap
   would truncate the rank away together with the name if it lived inside.
   flex:0 0 auto here plus min-width:0 on .nm (below) is the whole mechanism:
   the name ellipses, the rank always survives. color is a fixed --text-muted,
   not inherit — a losing result row already dims .nm, and inherit would
   double-dim the badge (rank isn't a result signal). white-space:nowrap for
   the same reason .mu-rank (draw.css:556) carries it. */
.pp-rank{flex:0 0 auto;margin-left:4px;font-family:var(--font-mono),monospace;
  font-size:9.5px;font-weight:600;white-space:nowrap;
  font-variant-numeric:tabular-nums;color:var(--text-muted);}
.pp-res__pl .nm,.pp-up__pl .nm{min-width:0;}
/* Games column: fixed-width cells (not just tabular-nums) so the set columns
   line up between the two pair rows even when one side's digit count differs
   (e.g. a walkover's "W/O" word sharing the slot on the winning row only —
   see .pp-res__wo below). flex-shrink:0 keeps 3 columns from ever giving way
   to the names column on the narrowest card the rail ever renders — 248px at
   ≤360px (288px at desktop, 272px at ≤520px). */
.pp-res__games{display:flex;gap:4px;flex:0 0 auto;}
.pp-res__gm{display:inline-block;width:17px;flex-shrink:0;text-align:center;
  font-family:var(--font-mono),monospace;font-size:12.5px;font-weight:600;
  font-variant-numeric:tabular-nums;color:var(--text-muted);}
.pp-res__pair.is-won .pp-res__gm{color:var(--text);}
/* Walkover fallback: games is [] on both sides, so the winning row shows the
   "W/O" string (r.score) in the same slot the games would occupy instead of
   leaving the card silent on what happened. */
.pp-res__wo{font-family:var(--font-mono),monospace;font-size:11px;font-weight:700;
  letter-spacing:.03em;color:var(--text);white-space:nowrap;}
/* Tiebreak superscript (7⁵) from the raw {{ g.tb }} value — same recipe as
   fip-player.css .ff-tb / score_html, mirrored here because /premier-padel
   loads fip-radar.css, not fip-player.css. line-height:0 keeps the raise from
   nudging the row's height. */
.pp-res__gm .ff-tb{font-size:.62em;vertical-align:super;line-height:0;
  margin-left:1px;font-weight:600;}

/* ── Section 02 · Player vs player ──────────────────────────────────────────
   The four opponent-vs-opponent records, each expanding in place to its own
   meetings (rendered with the same .ppmd-m scoreboard as the pair ledger).

   DELIBERATELY UNTINTED, exactly like .ppmd-m__board above: this sheet spends
   colour ONCE, on the result block at the top. A per-row mint/rose wash was
   tried on the ledger and removed (8d9f5bfc) — across a long list hue stopped
   meaning "won" and just read as noise. Lead is carried by weight. */
.ppmd-x-list{list-style:none;margin:0;padding:0;display:grid;gap:6px;}
.ppmd-x__row{display:grid;grid-template-columns:1fr auto 1fr auto;align-items:center;
  gap:12px;width:100%;padding:11px 14px;border:1px solid var(--card-border);
  border-radius:12px;background:rgba(255,255,255,0.02);color:inherit;font:inherit;
  text-align:left;cursor:pointer;transition:border-color .16s ease,background .16s ease;}
.ppmd-x--none .ppmd-x__row{cursor:default;opacity:.5;}
.ppmd-x:not(.ppmd-x--none) .ppmd-x__row:hover{border-color:rgba(255,255,255,0.16);
  background:rgba(255,255,255,0.035);}
.ppmd-x__row:focus-visible{outline:2px solid #7fe3a5;outline-offset:2px;}
.ppmd-x__nm{font-size:.9rem;font-weight:600;color:var(--text-muted);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.ppmd-x__nm--r{text-align:right;}
.ppmd-x__rec{display:inline-flex;align-items:baseline;gap:6px;
  font-variant-numeric:tabular-nums;}
.ppmd-x__n{font-size:1.02rem;font-weight:500;color:var(--text-muted);}
.ppmd-x__n.is-lead{font-weight:700;color:var(--text);}
.ppmd-x__dash{font-style:normal;color:rgba(255,255,255,0.28);}
.ppmd-x__chev{width:7px;height:7px;border-right:1.5px solid rgba(255,255,255,0.30);
  border-bottom:1.5px solid rgba(255,255,255,0.30);
  transform:translateY(-2px) rotate(45deg);transition:transform .18s ease;}
.ppmd-x__row[aria-expanded="true"] .ppmd-x__chev{transform:translateY(1px) rotate(-135deg);}
.ppmd-x__panel{padding:8px 0 2px;}
@media (max-width:520px){
  .ppmd-x__row{grid-template-columns:1fr auto 1fr;gap:8px;padding:10px;}
  .ppmd-x__chev{display:none;}
  .ppmd-x__nm{font-size:.82rem;}
}

/* ══ "Upcoming matches" band (2026-07-29) ═══════════════════════════════════
   Same chrome as .pp-results above: fip-radar.css's .radar-band supplies the
   bar / nav / rail and .rr-sig the card shell, so only the upcoming-specific
   bits live here. The card is deliberately the RESULT card minus the score -
   the relative-age slot carries the schedule sentence instead. Only --text,
   --text-muted and --card-border exist as tokens; mint is #7fe3a5 literally. */
.pp-upcoming{margin:0 0 26px;}
/* .pp-up isn't .rr-sig (it must not inherit .rr-sig:hover's cyan lift, which
   would promise a click on a card whose H2H sheet is empty), so it restates the
   shell the rail sizes against. Every value here is what .rr-sig resolves to ON
   THIS PAGE - background, padding, radius, the scroll-snap the rail's
   `scroll-snap-type:x mandatory` needs, and the 3.7-per-view / 288px sizing -
   because the two bands stack, and a card that is 2px off in padding or a shade
   off in fill reads as a different component rather than the same card without
   a score. Note the flex basis and min-width match the
   `.radar-band__rail .rr-sig` override in THIS file (see the "Latest results"
   header above), not fip-radar.css's base rule, which is still /4 + 270px; the
   two must be changed together or the stacked bands drift apart. */
.radar-band__rail .pp-up{flex:0 0 calc((100% - 42px)/3.7);min-width:288px;
  scroll-snap-align:start;position:relative;display:flex;flex-direction:column;
  gap:10px;padding:12px 13px 11px;border:1px solid var(--card-border);
  border-radius:14px;background:var(--card);overflow:hidden;}
/* The schedule slot is a kicker matching .pp-up__draw opposite it in the row
   (uppercase, tracked-out, same weight) - JS fills it for every schedule
   shape now, chained followed_by rows included, so the two ends of .rr-top
   read as one matched pair of labels. :empty stays guarded (Global Constraint
   4): the no-anchor-no-date case still returns '' and must not pay .rr-top's
   7px gap. (.pp-up__draw itself is styled up in the "Latest results" section,
   merged with .pp-res__draw - both bands' label is one shared rule.) */
/* min-width:0 + a 2-line clamp are containment, not decoration. The longest
   kicker oopKicker can emit is "TOMORROW · NEXT AFTER 09:00 (LOCAL TIME)" -
   reachable when oop_time_utc is absent but oop_time_local is present - which at
   10px mono with .06em tracking is ~264px of text against ~148px of free row
   width on the narrowest (248px, the <=360px step) card. A flex item defaults to
   min-width:auto, so without this it refuses to shrink; and because
   .radar-band__rail stretches its cards to the tallest one, a single wrapped
   card grows EVERY card in the band. The copy is specified behaviour (the
   trailing "(local time)" included), so the fix is CSS-side only.
   :empty still collapses the element - .pp-up__when:empty is 0-2-0 against this
   rule's 0-1-0, so its display:none beats display:-webkit-box on specificity as
   well as source order. */
.pp-up__when{font-family:var(--font-mono),monospace;font-size:10px;
  text-transform:uppercase;letter-spacing:.06em;font-weight:600;
  color:var(--text-muted);min-width:0;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden;}
.pp-up__when:empty{display:none;}
/* Live state on an upcoming card. Colour carries the state — no pill, matching the
   band's de-pill convention (dot + uppercase label). Mint #7fe3a5 is the same live
   accent the radar uses; do NOT reach for a var() token here, only --text,
   --text-muted and --card-border exist and anything else resolves to its fallback.
   The dot is inline-block inside a -webkit-box, which lays its children out as
   blocks - hence the explicit vertical-align and the em-relative size, so it tracks
   the 10px kicker rather than needing its own breakpoints. */
.pp-up__when--live{color:#7fe3a5;}
/* /rankings/serie only: the slot has passed but no result is in yet ("DUE
   17:00"). It dims rather than colours BECAUSE it is not the live state above —
   RankedIn publishes no live score and no result-entered timestamp, so mint
   here would assert a match is on court that may have finished half an hour
   ago. Opacity on --text-muted rather than a new token: only --text,
   --text-muted and --card-border exist, and anything else silently resolves to
   its var() fallback. */
.pp-up__when--due{opacity:.62;}
.pp-up__dot{display:inline-block;width:.42em;height:.42em;margin-right:.45em;
  border-radius:50%;background:currentColor;vertical-align:baseline;}
/* A result card separates its two sides with a scoreboard; this one has no
   score, so the only thing telling four names apart into two pairs is this gap.
   It has to stay clearly wider than .pp-up__pair's internal 2px - at 12.5px
   names the old 6px read as one flat list of four. */
.pp-up__pairs{display:flex;flex-direction:column;gap:10px;}
/* .pp-up__pair is the ROW (names left, live games right) and .pp-up__pls is the
   name COLUMN that used to be .pp-up__pair itself - the 2px stack is unchanged, it
   just moved one level in. min-width:0 has to travel with it: without it the name
   column refuses to shrink and a long pair pushes the games column off the card
   (and, because the rail stretches every card to the tallest, would grow the whole
   band). The games column is a fixed-content sibling, so it never needs to shrink. */
.pp-up__pair{display:flex;flex-direction:row;align-items:center;gap:8px;}
.pp-up__pls{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto;}
/* Right edge of the card, one tabular digit per started set so the two sides' sets
   line up vertically as a scoreboard rather than two ragged lists. */
.pp-up__sets{display:inline-flex;gap:.5em;margin-left:auto;flex:0 0 auto;
  font-variant-numeric:tabular-nums;font-family:var(--font-mono),monospace;
  font-size:12.5px;}
.pp-up__set{color:#7fe3a5;font-weight:600;}
/* Tiebreak points raised to a superscript (6²) on the side that LOST that set, exactly as
   FIP's own widget prints them. Same recipe as .pp-res__gm .ff-tb / fip-player.css .ff-tb;
   vertical-align works here (unlike .sb-tb, whose parent is a flex container — .pp-up__set
   is itself a flex ITEM, so inline layout inside it is normal).
   🚨 It must not change the cell's measured width. .pp-up__sets is right-aligned
   (margin-left:auto), so a superscript on the loser's row ALONE widens that row and drags
   both rows' set columns out of vertical line — the same failure mode .pp-up__ptv's fixed
   width exists to prevent, and the reason the JS renders an invisible GHOST sup on the
   opposite side carrying the same digits (measured in Chromium: 4.88px drift without it,
   0.00 with; tabular-nums does not reach the sup, so a same-LENGTH filler still left 0.42px).
   Dimmer than the mint digits for the same reason the points cell is: it is detail ABOUT a
   set score, not a set score. */
.pp-up__tb{font-size:.62em;vertical-align:super;line-height:0;margin-left:1px;
  font-weight:600;color:var(--text-dim);}
.pp-up__tb--ghost{visibility:hidden;}
/* Current GAME points ('0'/'15'/'40'/'Ad'), client-only and ~30s fresh — deliberately
   NOT mint like the set digits beside it. The two are different kinds of number and a
   point is the softer claim of the two: it is the one value that can be a few seconds
   stale, so it must not out-shout a set score. The leading separator is a pseudo-element
   so the digits stay copy-pasteable as a score.
   ⚠️ Set cells are mint (#7fe3a5 — see dpf-css-undefined-tokens: the token does not
   exist, the literal is the value); --text-dim IS defined and is used here.

   🚨 FIXED WIDTH, and that is why the VALUE gets its own box (.pp-up__ptv) rather than
   this element carrying the min-width. The row is right-aligned (margin-left:auto on
   .pp-up__sets), so a variable-width trailing cell DRAGS THE SET DIGITS SIDEWAYS: '0'
   against '15' knocked the two rows' set columns out of line mid-game and the score
   stopped reading as a scoreboard every time a point was won.
   ⚠️ Putting min-width HERE does not work and looks like it should: the '·' is a ::before
   INSIDE this box, so measured width is separator + margin + text — 21.3px for '0' vs
   28.8px for '15', both already past a 2ch (15px) floor, which therefore never binds.
   Measured in the browser; the symmetric '30'/'40' case passes either way, which is how
   the first attempt at this looked correct while being wrong. The inner box is the fix:
   2ch holds every value the widget emits ('0', '15', '30', '40', 'Ad', '') because the row
   is monospaced. */
.pp-up__pt{color:var(--text-dim);font-weight:600;}
.pp-up__pt::before{content:"·";margin-right:.5em;opacity:.55;}
.pp-up__ptv{display:inline-block;min-width:2ch;text-align:right;}
/* Name size, flag gap and weight are .pp-res__pl's exactly (12.5px / 5px / the
   is-won treatment): the two bands sit one above the other, so a name has to
   look the same in both or the page reads as two unrelated widgets. */
.pp-up__pl{display:flex;align-items:center;gap:5px;min-width:0;
  font-weight:600;color:var(--text);}
/* No score means no side leads, so nothing here spends weight or colour on a
   winner - unlike .pp-res__pair.is-won. */
.pp-up__pl .nm{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;}

/* The click affordance, bottom-right in .rr-foot opposite the tournament name.
   It replaced the draw's numeric H2H marker on this card (2026-07-29): a bare
   "10v" carried two different facts - pairs who have met vs players who have
   only met across other partnerships - named neither, and said nothing about
   being clickable. So this names the ACTION; the counts and the pair/cross
   distinction are the modal's job (the count survives as the title tooltip).

   NOT a pill. This surface de-pilled its cards deliberately (see the FIP Radar
   kickers): the vocabulary here is a dim uppercase mono kicker whose COLOUR
   carries state, and a bordered chip in the footer read as a stray badge on an
   editorial card. Same voice as .rr-tc opposite it, so the footer is one row of
   two kickers: where it is on the left, what it does on the right. */
/* ── The action column ── shared by BOTH carousels since 2026-07-30, when
   "Latest results" became clickable too. The footer is a 2×2: the event holds
   the left column, and the right column stacks the rivalry marker over the
   click cue.

     "name  mark"
     "city  cue "

   The marker is anchored to the NAME row, not to the footer's bottom edge. That
   is load-bearing, not cosmetic: applyMarker fails OPEN, so a card whose H2H
   probe never answered (429, offline, 5xx) stays clickable with NO marker. On
   its own row that absence leaves an empty cell and the cue does not move;
   bottom-anchored, the cue would jump a line every time a probe timed out.

   Classes are band-neutral (.pp-foot / .pp-cta, not .pp-up__*) because the two
   bands render the identical contract — a pp-up-prefixed class on a results
   card would be a lie about what it is. */
/* Two-class selectors on purpose: .rr-foot in fip-radar.css sets display:flex at
   the same specificity, and which sheet loads last must not decide the layout. */
.pp-up .pp-foot,
.pp-res .pp-foot{display:grid;grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"name mark" "city cue";column-gap:10px;row-gap:3px;
  align-items:end;}
.pp-up .pp-foot .rr-tn,
.pp-res .pp-foot .rr-tn{grid-area:name;}
.pp-up .pp-foot .rr-tc,
.pp-res .pp-foot .rr-tc{grid-area:city;min-width:0;}
.pp-up .pp-foot .pp-mk,
.pp-res .pp-foot .pp-mk{grid-area:mark;justify-self:end;align-self:center;}
.pp-up .pp-cta,
.pp-res .pp-cta{grid-area:cue;justify-self:end;}

.pp-cta{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;
  font-family:var(--font-mono),monospace;font-size:9.5px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--text-muted);
  line-height:1;
  /* Hidden but SPACE-RESERVING until the H2H fetch answers (~a beat after
     paint): display:none would reflow the footer when it lands, and an element
     that pops from nothing reads as a glitch. It fades up instead.
     visibility (not opacity alone) keeps it off the a11y tree meanwhile. */
  visibility:hidden;opacity:0;transform:translateY(2px);
  transition:opacity 180ms cubic-bezier(0.23,1,0.32,1),
    transform 180ms cubic-bezier(0.23,1,0.32,1),color 140ms ease;}
.is-clickable .pp-cta{visibility:visible;opacity:1;transform:none;}
/* The marker rides the SAME transition as the cue and carries no delay, so the
   two land as one unit. Staggering them would advertise that a fetch happened;
   landing together reads as one thing appearing. It is injected by JS, so
   unlike the cue it starts from nothing rather than being pre-rendered. */
.pp-foot .pp-mk{opacity:0;transform:translateY(2px);
  transition:opacity 180ms cubic-bezier(0.23,1,0.32,1),
    transform 180ms cubic-bezier(0.23,1,0.32,1),color 150ms ease;}
.is-clickable .pp-foot .pp-mk{opacity:1;transform:none;}
/* Touch wording is the default and the fine-pointer query overrides it: a
   device that answers neither query (or a browser without them) then says
   "Tap", which is the safer miss on a padel site read mostly on phones. */
.pp-cta__lbl--fine{display:none;}
@media (hover:hover) and (pointer:fine){
  .pp-cta__lbl--coarse{display:none;}
  .pp-cta__lbl--fine{display:inline;}
  /* Hover gated on a real pointer: on touch it fires on tap and leaves the
     card stuck lit after the modal closes. */
  .pp-up.is-clickable:hover .pp-cta,
  .pp-res.is-clickable:hover .pp-cta{color:#7fe3a5;}
  .pp-up.is-clickable:hover .pp-cta__chev,
  .pp-res.is-clickable:hover .pp-cta__chev{transform:translateX(3px);}
}
.pp-up.is-clickable:focus-visible .pp-cta,
.pp-res.is-clickable:focus-visible .pp-cta{color:#7fe3a5;}
/* The chevron is the door. It leans toward it on hover - the one bit of motion
   on the card, and the only property animated is a transform. */
.pp-cta__chev{font-size:11px;line-height:1;letter-spacing:0;
  transition:transform 180ms cubic-bezier(0.23,1,0.32,1);}
/* Results cards become real controls, so they need the pointer and the ring the
   upcoming band already had. */
.pp-res.is-clickable{cursor:pointer;}

/* Clickable only once the H2H fetch resolved to something worth opening - the
   class is the render's verdict, the click handler reads it back and the
   kicker's own visibility hangs off it, so all three can't drift (same rule as
   the bracket cell). */
.pp-up.is-clickable,
.pp-res.is-clickable{cursor:pointer;transition:border-color .16s ease,
  background .16s ease,transform 130ms cubic-bezier(0.23,1,0.32,1);}
.pp-up.is-clickable:hover{border-color:rgba(255,255,255,0.16);
  background:rgba(255,255,255,0.035);}
/* .pp-res is .rr-sig, which already owns a cyan hover lift of its own — it does
   NOT take the white wash above or it would fight that rule. Only the press and
   the focus ring are added. */
/* Press feedback. The whole card is the button, so the whole card answers -
   subtle, because a 288px card scaling hard in a rail reads as a wobble. */
.pp-up.is-clickable:active,
.pp-res.is-clickable:active{transform:scale(.985);}
.pp-up.is-clickable:focus-visible,
.pp-res.is-clickable:focus-visible{outline:2px solid #7fe3a5;outline-offset:2px;}

/* Reduced motion keeps every state change that CARRIES meaning (the kicker
   still appears, still turns mint) and drops only the movement. */
@media (prefers-reduced-motion:reduce){
  .pp-cta,.pp-foot .pp-mk{transform:none;
    transition:opacity 180ms ease,color 140ms ease;}
  .is-clickable .pp-foot .pp-mk{transform:none;}
  .pp-cta__chev{transition:none;}
  .pp-up.is-clickable,
  .pp-res.is-clickable{transition:border-color .16s ease,background .16s ease;}
  .pp-up.is-clickable:active,
  .pp-res.is-clickable:active{transform:none;}
  .pp-up.is-clickable:hover .pp-cta__chev,
  .pp-res.is-clickable:hover .pp-cta__chev{transform:none;}
}

/* Both bands step down together (2026-07-29, #rank badge widened them to 288px)
   so neither ever reads as a different-sized sibling of the other.

   PLAIN .rr-sig on purpose — do not "harden" this to .rr-sig.pp-res. This file
   only ships on /premier-padel and /premier-padel/<slug> (see the "Latest
   results" header comment for the load paths), so these rules cannot reach the
   FIP Radar signups rail and need no qualifier to stay off it. A .pp-res
   qualifier would be inert extra specificity AND a trap: drop .pp-res from the
   results <article> and the band would silently lose its mobile step-downs
   while .pp-up kept working. Both bands' desktop rules live in this file too,
   so plain selectors here simply override them by source order. */
@media (max-width:520px){
  .radar-band__rail .rr-sig,
  .radar-band__rail .pp-up{min-width:272px;}
}
/* Small phones: 272px would nearly fill a 360px rail and hide the "more than
   one card" peek that is the whole requirement, so it steps down further. */
@media (max-width:360px){
  .radar-band__rail .rr-sig,
  .radar-band__rail .pp-up{min-width:248px;}
}

/* ── Results sheet (per-event pair runs) ────────────────────────────────────
   The sheet is built from OTHER files' components on purpose, so it cannot drift
   away from them: the shell and gender rail are fip-radar's (.lab-ms-*,
   .fipreg-toggle), the pair row is fip-radar's .fipreg-row anatomy — flags, full
   names, seed cell, mono right cell — and the expanded run panel is
   tournament-cards' .lab-run__* from /rankings/serie. The Results and Players
   sheets open from adjacent rows of ONE chooser, so nothing below re-declares a
   font size that either of those already sets; these rules only cover what this
   sheet does differently.

   ⚠️ The earlier version of this block widened .lab-result__head/.lab-result__row
   to fit "Q R32" in a 34px column. Both are gone: the pair row is a .fipreg-row
   now, whose third cell is `auto`-width, so the exit round sizes itself and there
   is no head grid left to keep in step. That whole class of ~30px label drift is
   designed out rather than guarded. */

/* The run panel's rail, its padding and its whole type scale are tournament-cards'
   `.lab-run`, shared with /rankings/serie's Results sheet. The ONE thing this sheet
   supplies is where the panel's first column sits, and it is not a taste value: it
   is this row grid's own name-column offset, so the run's W mark lands exactly under
   the pair name above it.

   `.fipreg-row` is `padding:9px 10px` + `grid-template-columns:30px 1fr auto 16px`
   with `gap:12px` → 10 + 30 + 12 = 52px. Measured, not guessed; an earlier
   `margin-left:42px` + 13px padding + 2px border put the run 5px PAST the name,
   which read as an accident. If that grid changes, this number moves with it. */
[data-ppr-panel] .lab-run{ --run-indent:52px; }

/* The exit round sits where the Players sheet puts entry points, so it inherits
   that cell's mono treatment; only the stage-label casing is ours. */
[data-ppr-panel] .ppr-row .fipreg-pts{
    text-transform:uppercase;letter-spacing:0.04em;
}
/* ⚠️ No chevron rule here on purpose. .fipreg-chev is NOT an empty cell — fip-radar.css
   draws the whole glyph out of a rotated border corner and already rotates it to point up
   on .fipreg-row.is-open, which is exactly the open-state affordance this sheet needs. An
   earlier version added a "\203A" ::before on top of that, which rendered TWO arrows in
   one cell. The open state comes free with the shared class; don't re-add a glyph. */
