/* The club: the home and player pages.
 *
 * Every colour, size and face is a token from design/tokens.css; every object
 * (eyebrow, mono label, signage, sticker, flyer tape and title, marquee title)
 * is design/components.css's; the strip over the door and the column under it
 * are shell.css's, now that the race pages wear them too. This file places
 * what is left, which is the front and a player's page. */

/* Grain and scanlines cover the WHOLE window, as the charter asks — hung on
   the content column they stopped at 1120px and left the cover and the band
   bare. Fixed and pointer-events-none, above the page so the texture reads on
   the deep-ink cover as well as on paper; z-index beats the cards, which are
   `position: relative` and would otherwise paint over it. */
.page-fx {
  position: fixed;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  --fx-grain-opacity: var(--grain-opacity-min);
}

/* The rise of a 1° line across the window — tan(1°) = 0.01746 — which is what
   the chequer leans by, and so what everything meeting it is measured against.
   On the column, because the band above the chequer and the chapter below it
   both have to read the same figure: a horizontal edge against a leaning band
   shows bare paper beneath whichever end lifts, and no amount of moving either
   one fixes that — the edges have to be parallel, or reach past each other. */
.hall {
  --slant: calc(100vw * 0.01746);
  /* The dot field, read off pollen-robotics.com/microduck rather than
     invented: orange at a third, 3.2px dots on a 16px grid, under a wash that
     clears at one edge and is solid by 40% in. Declared here because two
     surfaces wear it — the band on the front page and the foot of a player's
     — and a recipe copied twice is a recipe that drifts. */
  --dots: radial-gradient(
    color-mix(in srgb, var(--orange) 34%, transparent) 3.2px,
    transparent 3.9px
  );
  --dots-grid: 16px 16px;
  --dots-wash-up: linear-gradient(to top, transparent 0%, var(--warm-paper) 40%);
}

.ladder-row:focus-visible,
.profile-results a:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; }

/* ── Masthead: the charter's cover, whole ──────────────────────────────────── */

/* Deep ink, because that is the only ground the charter puts a yellow word on:
   #FFD23F on #FAF8F2 cannot be read at any size. The fan is this page's one —
   tilted by its own --ray-fan-from and never by a rotate(). Bottom padding is
   the gutter, not the chapter gap: the charter's cover lockup is the deep-ink
   block with the yellow band immediately beneath it, not 104px of empty ink
   before that band. */
.masthead {
  position: relative;
  background-color: var(--deep-ink);
  padding: var(--space-gutter-max) var(--space-gutter-min);
}
/* The mark and the locker on ONE line, not stacked: the locker was sitting
   above the mark like a second header. */
.masthead-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-gutter-max);
  max-width: var(--space-content-column);
  margin-inline: auto;
}
.masthead-lockup {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 4vw, 3rem);
  min-width: 0;
}
/* Pushed out past the content column toward the window's edge, but never past
   the gutter and never on a narrow screen, where the max() falls to zero and
   the plate stays inside the column with everything else. */
.masthead-player {
  /* The plate and the way out, side by side at the cover's top right. Aligned
     to the top, so the small button sits against the plate's upper edge
     rather than floating at its middle. */
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex: none;
  margin-right: calc(-1 * min(
    96px,
    max(0px, (100vw - var(--space-content-column)) / 2 - var(--space-gutter-min))
  ));
}

/* The stand-in, while /api/me is in flight: the same square, a duck on beige,
   at the size the plate will be so nothing moves when it lands. */
.player-card--waiting {
  display: grid;
  place-items: center;
  width: 10.5rem;
  aspect-ratio: 1;
  border: 3px solid var(--ink);
  background: var(--warm-paper);
}
.player-card--waiting .duckhead { width: 56px; }

/* A square card, paper on the cover's ink: your picture, your handle, and
   where you stand. Square because it reads as a locker plate rather than as
   another line of the header. */
.player-card-you, .player-card-in {
  /* Orange, which is the page's carrying accent. The charter names the
     magenta/cyan pair for an offset shadow; this is the one place it is
     spent on the accent instead, on the page's own locker plate. */
  --fx-shadow-color: var(--orange);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 10.5rem;
  aspect-ratio: 1;
  padding: var(--space-card-padding-min);
  border: 3px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  text-align: center;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.player-card-name { overflow-wrap: anywhere; }
.player-card-you:hover, .player-card-in:hover { background: var(--cream); }
.player-card-you:focus-visible, .player-card-in:focus-visible {
  outline: 3px solid var(--cyan);
  outline-offset: 3px;
}
/* Beige, like the signed-in plate beside it and like the picture's own ground:
   the orange made the one square on the cover that is only an invitation shout
   louder than the mark it sits next to. */
.player-card-in { background: var(--warm-paper); }
/* Smaller than the signed-in plate's picture: that one IS the person, this
   one only says whose door this is. */
.player-card-in .duckhead { width: 48px; height: auto; }
/* A square of its own inside the square plate, rather than filling it: at full
   width the picture was the card and the handle read as a caption under it. */
.player-card-you .avatar {
  flex: none;
  width: 62px;
  height: 62px;
  font-size: 36px;
}
.player-card-name {
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: var(--type-lede-size);
  line-height: var(--type-card-title-line);
  letter-spacing: var(--type-card-title-tracking);
}
.player-card-score {
  /* The place and the score are one line. Wrapped, "#2" and "360 PTS" read as
     two unrelated figures. */
  white-space: nowrap;
  font-family: var(--type-mono-label-family);
  font-weight: var(--type-mono-label-weight);
  font-size: var(--type-mono-label-size);
  letter-spacing: var(--type-mono-label-tracking);
  text-transform: uppercase;
}
/* The LOCKUP's duck, not every duck in the masthead: the sign-in plate holds
   one too, and while both rules said `.masthead .duckhead` they had the same
   specificity, so this one won on source order alone and the plate's duck
   stayed the size of the mark's. */
.masthead-lockup .duckhead { width: clamp(72px, 14vw, 148px); }

.wordmark--cover {
  font-size: var(--type-hero-title-size);
  line-height: var(--type-hero-title-line);
  letter-spacing: var(--type-hero-title-tracking);
}

.duck { position: relative; display: inline-flex; flex: none; }
/* Over the closed head, anchored at the top and matched in width: the open
   sprite is taller and the extra height is all beak, so any other anchor
   makes the skull jump when the mouth opens. */
.duckhead--open {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  opacity: 0;
}
.duck-quack {
  position: absolute;
  top: -10px;
  left: 100%;
  margin-left: -8px;
  opacity: 0;
  rotate: var(--rotate-3-0);
  pointer-events: none;
}

/* The beak and the shout are not motion, and reduced motion asks for less
   movement rather than for less to look at — so they happen either way. */
.duck:hover > .duckhead:not(.duckhead--open) { opacity: 0; }
.duck:hover .duckhead--open,
.duck:hover .duck-quack { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .duck .duckhead { transition: rotate 140ms ease, translate 140ms ease, opacity 90ms ease; }
  .duck-quack { transition: opacity 120ms ease; }
  .duck:hover .duckhead { rotate: calc(-1 * var(--rotate-4-0)); translate: 0 -4px; }
}

/* The locker RISES rather than turning. A card on the wall tilts toward the
   hand reaching for it; a plate is screwed flat and the only honest thing it
   can do is come off the surface. The shadow steps up a token with it, which is what
   makes the rise read as height rather than as a slide. */
.player-card-you, .player-card-in { --fx-shadow-size: var(--shadow-card-min); }

@media (prefers-reduced-motion: no-preference) {
  .player-card-you, .player-card-in {
    transition: translate 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .player-card-you:hover, .player-card-in:hover {
    --fx-shadow-size: var(--shadow-card-max);
    translate: 0 -6px;
  }
}

/* ── The flock band: the charter's yellow strip, standing still ────────────── */

.flock-band {
  background: var(--yellow);
  color: var(--ink);
  border-block: 3px solid var(--ink);
  padding: 10px var(--space-gutter-min);
}
.flock-band-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px 14px;
  max-width: var(--space-content-column);
  margin-inline: auto;
  font-family: var(--type-sticker-family);
  font-size: var(--type-sticker-size);
  letter-spacing: var(--type-sticker-tracking);
  text-transform: uppercase;
}
.flock-band-row a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: inherit;
  text-decoration: none;
}
/* Ink, like the words beside it — the strip is one voice, not a brand badge. */
.flock-mark { width: 15px; height: 15px; flex: none; }
.flock-band-row a:hover { text-decoration: underline; }
.flock-band-row a:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.flock-star { color: var(--orange-inked); }

/* ── Door: the hero over the hall ──────────────────────────────────────────── */

/* What this is, on the left; who is winning it, on the right. Wide enough for
   both, they sit side by side; below that they stack, pitch first.
   A band between two rules, running the window's full width: warm paper with
   the charter's grain over it at the heavier of the two opacities, so the
   chapter reads as its own sheet of paper without a box around anything
   inside it. The horizontal padding centres the content back into the column
   the rest of the page keeps. */
.door {
  /* Back to the charter's own ceiling. The visible texture is the dot field
     below, not the grain — the grain is the print artifact underneath it. */
  --fx-grain-opacity: var(--grain-opacity-max);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  /* The pitch holds the left edge, the scoreboard the right: both carry a
     max-width, so the slack between them is real and this pushes all of it
     into the middle instead of trailing after the board. */
  justify-content: space-between;
  gap: var(--space-title-to-grid-max);
  width: 100vw;
  margin-left: calc(50% - 50vw);
  /* Flush under the yellow strip — cancelling the hall's own top padding —
     so no stripe of bare paper sits between the two. Half the rise at the
     bottom, so the cut edge meets the chequer rather than leaving a wedge. */
  margin-top: calc(-1 * var(--space-gutter-max));
  margin-bottom: calc(-0.5 * var(--slant));
  padding:
    var(--space-title-to-grid-max)
    max(var(--space-gutter-min), calc(50vw - var(--space-content-column) / 2))
    calc(var(--space-title-to-grid-max) + var(--slant));
  background-color: var(--warm-paper);
  /* Square on top, where it meets the yellow strip's own straight border; the
     bottom edge RISES to the right, parallel to the chequer beneath it. It
     also trims the dot layer, which is inset -6% and turned 2.5° and would
     otherwise show its corners past the band. */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--slant)), 0 100%);
  /* The stacking context the z-index: -1 layers below depend on. Declared
     rather than inherited from a property that happens to create one, so
     removing that property cannot break the layering in silence. */
  isolation: isolate;
}

/* The dot field, read off pollen-robotics.com/microduck rather than invented:
   orange at a fifth, 3.2px dots on a 16px grid, the whole layer tilted -2.5°
   and masked away by 7% down the band. Three things follow from that recipe
   and each one was asked for by name — the dots are ORANGE and not ink, they
   are only at the TOP, and they lean with the rest of the page instead of
   sitting square. `inset: -6%` is the site's too: it gives the rotation room
   so the turn never drags a bare corner into view.
   The alpha is mixed from the token, so this adds no hex the charter has not
   already named. The band's overflow trims this layer to its own shape. */
.door::before { z-index: -1; }

.door::after {
  content: "";
  position: absolute;
  inset: -6%;
  pointer-events: none;
  /* BEHIND the text. An absolutely positioned pseudo-element paints above the
     in-flow content, so the wash that fades the dots out was also fading out
     the title and swallowing every ladder row after the first. `.door`'s own
     `isolation: isolate` makes it a stacking context, so -1 lands between its
     background and its children rather than behind the page. */
  z-index: -1;
  transform: rotate(calc(-1 * var(--rotate-2-5)));
  /* Stronger than the site's 20% / 7.1%: its dot layer sits on a short hero,
     where 7% of the height is a wide stripe. This band is the page's tallest
     block, so the same 7% was a hairline nobody could see. The dot size and
     the 16px grid are the site's, untouched — it is the reach and the weight
     that changed, not the rhythm. */
  /* The fade is a background LAYER, not a mask. `mask-image` resolves through
     `mask-mode: match-source`, which browsers have differed on. A
     paper-coloured wash painted OVER the dots cannot be read two ways: what it
     covers is gone, whatever the engine thinks a mask is. The first layer
     listed paints on top.
     `to top` starts the gradient at the BOTTOM edge: clear there, solid by 40%
     of the way up — so the dots gather along the band's lower edge, against
     the tilted rule, rather than under the yellow strip. */
  background-image: var(--dots-wash-up), var(--dots);
  background-size: 100% 100%, var(--dots-grid);
  background-repeat: no-repeat, repeat;
}
/* The pitch keeps the charter's measure, alone on the band's first line. */
.door-pitch { flex: 1 1 26rem; max-width: 38rem; min-width: 0; }

/* The channel and the standings side by side under it, their headings level;
   below 820px they stack, the channel first. The board is a column so the
   ladder can stretch to the television's height. */
.door-row {
  flex: 1 1 100%;
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3rem);
  align-items: stretch;
}
.door-meet { min-width: 0; }
.door-board { display: flex; flex-direction: column; min-width: 0; }
.door-row .eyebrow {
  display: block;
  margin-bottom: var(--space-gutter-min);
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: min(var(--type-card-title-size), 1.7rem);
  line-height: var(--type-card-title-line);
  letter-spacing: var(--type-card-title-tracking);
  color: var(--ink);
}
@media (max-width: 820px) {
  .door-row { grid-template-columns: 1fr; }
}

.hero {
  margin: 4px 0 0;
  text-wrap: balance;
  font-family: var(--type-hero-title-family);
  font-weight: var(--type-hero-title-weight);
  /* A step down from section-title: beside a scoreboard it was shouting over
     the thing it introduces. Capped rather than re-scaled — the token still
     sets the size, this only stops its vw term running away on a wide screen
     where the band gives it a narrower column than a full-width hero. */
  font-size: min(var(--type-display-subtitle-size), 3rem);
  line-height: var(--type-hero-title-line);
  letter-spacing: var(--type-hero-title-tracking);
  text-transform: uppercase;
}

.lede {
  max-width: 62ch;
  margin: var(--space-gutter-min) 0 0;
  font-family: var(--type-lede-family);
  font-size: var(--type-lede-size);
  line-height: var(--type-lede-line);
}
.lede b { font-weight: 700; }

/* The charter's own rhythm. These tokens were defined and referenced nowhere:
   a chapter opened on a 28 px grid gutter, which is why the hero, the ladder
   and the wall read as one column. */
.chapter:last-child { margin-bottom: var(--space-chapter-bottom-min); }
/* A chapter's head is a SECTION title, not a caption: at the eyebrow's 0.8rem
   it read as a label on the thing below rather than as the thing's name. The
   card-title step is the charter's next one up in the display face. */
.chapter > .eyebrow {
  margin-bottom: var(--space-title-to-grid-min);
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: var(--type-card-title-size);
  line-height: var(--type-card-title-line);
  letter-spacing: var(--type-card-title-tracking);
  color: var(--ink);
}

/* The chequer between chapters: orange on beige, the finish line — the same
   device the race page lays over its strip of other rounds, so the two read
   as one site. It breaks out of the content column and runs past the window's
   full width; a band that stopped at 1120px would read as an underline on the
   text rather than as a division of the page. Two offset rows of a 45° gradient,
   the way components.css draws the gym floor: gradients as a PATTERN, never
   the colour ramp the charter forbids. */
.chapter-checker {
  height: 16px;
  /* Leaning -1°, as the rule it replaced did. Wider than the window and
     pulled back by half the excess: a 1° turn lifts one end and drops the
     other, and at exactly 100vw that opens a triangle of bare paper at each
     edge of the screen. The overhang carries those triangles off-screen. */
  width: 108vw;
  margin-left: calc(50% - 54vw);
  rotate: calc(-1 * var(--rotate-1-0));
  margin-top: var(--space-chapter-top-min);
  background-color: var(--warm-paper);
  background-image:
    linear-gradient(45deg, var(--orange) 25%, transparent 25% 75%, var(--orange) 75%),
    linear-gradient(45deg, var(--orange) 25%, transparent 25% 75%, var(--orange) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}

/* A chapter opens on the charter's chapter-top space, unless the chequer sits
   above it — the chequer carries that space itself, and (0,2,0) beats
   (0,1,0). */
.chapter { margin-top: var(--space-chapter-top-min); }
.chapter-checker + .chapter { margin-top: var(--space-title-to-grid-max); }
/* The band already carries the space below it in its own padding. */
.door + .chapter-checker { margin-top: 0; }

/* ── Ladder: one rung per player ───────────────────────────────────────────── */

/* The frame holds still; the rungs scroll inside it. Ten rungs is what the
   frame shows — the eleventh is the invitation to scroll, so the frame must
   cut a row rather than land between two. */
.ladders {
  --ladder-rung: 44px;
  --fx-shadow-color: var(--ink);
  /* The charter's whitest ground, against the band's warm one. */
  background: var(--paper);
  border: 3px solid var(--ink);
  color: var(--ink);
}
.ladder-scroll {
  max-height: calc(var(--ladder-rung) * 10.5);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* Beside the television, the frame stretches to its height and the rungs
   scroll inside it. The 26px under it is the television's feet, so the
   ladder's foot lines up with the cabinet's. */
.door-board .ladders { flex: 1; position: relative; min-height: 22rem; margin-bottom: 26px; }
.door-board .ladder-scroll { position: absolute; inset: 0; max-height: none; }


.ladder-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-gutter-min);
  padding: 8px var(--space-card-padding-min);
  border-top: 3px solid var(--ink);
  color: inherit;
  text-decoration: none;
}
.ladder > .ladder-row:first-child { border-top: 0; }
.ladder-row:hover { background: var(--cream); }

/* The display face at the lede step rather than the card-title one: the
   charter reserves card-title for a key figure, and a rung in a sidebar
   scoreboard is a line in a list. */
.ladder-rank, .ladder-points {
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: var(--type-lede-size);
  line-height: var(--type-card-title-line);
  letter-spacing: var(--type-card-title-tracking);
}
.ladder-rank { min-width: 2ch; }


.ladder-name { font-weight: 600; overflow-wrap: anywhere; }
.ladder-empty { margin: 0; padding: 12px var(--space-card-padding-min); }

/* ── The channel: a vintage television beside the standings ────────────────── */

/* A set from the den rather than a screen from the charter: wood, a cream
   bezel, curved glass, gold knobs. Its radii and its browns and golds are the
   exception the redesign asked for — the television is an object, and an
   object keeps its own shape. */
.tv {
  --wood: #5a3a22;
  --wood-hi: #7a5030;
  --wood-lo: #4a2e1a;
  --cream-shell: linear-gradient(180deg, #e9dcc0, #cdbb94);
  --gold: #d9b25a;
  --gold-hi: #f6e3a1;
  --gold-lo: #8a6a24;
  position: relative;
}
.tv-case {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 82px;
  gap: 18px;
  padding: 22px;
  border: 4px solid var(--ink);
  border-radius: 34px;
  background:
    repeating-linear-gradient(92deg, rgba(0, 0, 0, 0.07) 0 2px, transparent 2px 9px),
    linear-gradient(180deg, var(--wood-hi), var(--wood) 60%, var(--wood-lo));
  /* Orange, the page's carrying accent: the channel is the one thing on the
     front that moves. */
  box-shadow:
    10px 10px 0 var(--orange),
    inset 0 3px 0 rgba(255, 255, 255, 0.18),
    inset 0 -4px 0 rgba(0, 0, 0, 0.25);
}
/* The gold hairline inlaid in the wood. */
.tv-case::before {
  content: "";
  position: absolute;
  inset: 8px;
  border: 2px solid color-mix(in srgb, var(--gold-hi) 35%, transparent);
  border-radius: 27px;
  pointer-events: none;
}
.tv-bezel {
  padding: 16px;
  background: var(--cream-shell);
  border: 3px solid var(--ink);
  border-radius: 26px;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.6), inset 0 -3px 6px rgba(0, 0, 0, 0.25);
}

/* The tube. The channel draws 16:9 and the glass is 4:3, so the picture sits
   letterboxed in it, as a broadcast did on a set like this one: cropped to
   fill, it lost the LIVE bug and both ends of the commentators' caption. */
.tv-glass {
  position: relative;
  display: grid;
  align-content: center;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--deep-ink);
  border: 3px solid var(--ink);
  border-radius: 18% / 22%;
  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.8);
}
.tv-stage { filter: saturate(1.05) contrast(1.05) sepia(0.18); }
/* The set's own display says the channel is on, so the channel's LIVE bug
   stands down rather than printing over it; off air, both are dark and the
   card on the glass says so. */
.tv-stage .live-bug { display: none; }
.tv-glass:has(.live[data-live-state="off-air"]) .tv-osd { display: none; }

/* What the glass lays over the picture, in order: the phosphor mask, the
   scanlines, a slow rolling band, the vignette and the glare. None of them
   takes a click — the sound button underneath must still be pressable. */
.tv-fx { position: absolute; inset: 0; pointer-events: none; }
.tv-mask {
  background: repeating-linear-gradient(90deg,
    color-mix(in srgb, var(--magenta) 7%, transparent) 0 1px,
    color-mix(in srgb, var(--cyan) 7%, transparent) 1px 2px,
    color-mix(in srgb, var(--yellow) 5%, transparent) 2px 3px);
  mix-blend-mode: screen;
}
/* Taller than the glass by one period, so moving it never shows its edge. */
.tv-scan { top: -3px; background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.32) 0 1px, transparent 1px 3px); }
.tv-roll {
  background: linear-gradient(to bottom, transparent 40%, rgba(255, 255, 255, 0.06) 48%,
    rgba(255, 255, 255, 0.11) 50%, transparent 56%);
  translate: 0 -100%;
}
.tv-vignette {
  background: radial-gradient(110% 110% at 50% 50%, transparent 50%, rgba(0, 0, 0, 0.55) 80%, rgba(0, 0, 0, 0.92) 100%);
}
.tv-glare {
  background:
    radial-gradient(60% 40% at 30% 14%, rgba(255, 255, 255, 0.24), transparent 62%),
    radial-gradient(30% 20% at 80% 88%, rgba(255, 255, 255, 0.07), transparent 70%);
}

/* The set's own on-screen display, in the VCR's mono with a colour fringe.
   Centred in the black bar above the picture — 12.5% of the glass, the gap
   between 4:3 and 16:9 — because over the picture it printed on the clips'
   own top band. */
.tv-osd {
  position: absolute;
  z-index: 2;
  top: 6.25%;
  translate: 0 -50%;
  white-space: nowrap;
  font: 400 clamp(0.6rem, 1.1vw, 0.85rem)/1 var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper);
  text-shadow:
    2px 0 0 color-mix(in srgb, var(--magenta) 70%, transparent),
    -2px 0 0 color-mix(in srgb, var(--cyan) 70%, transparent);
  pointer-events: none;
}
.tv-osd--play { left: 11%; }
.tv-osd--channel { right: 11%; display: flex; align-items: center; gap: 0.5em; }
.tv-rec { width: 0.7em; height: 0.7em; border-radius: 50%; background: #ff3b30; }

/* The way in, as a button that appears when the pointer is on the picture.
   It used to be a link across the whole box: clicking the channel anywhere
   left the front page with no warning, and the page it arrived at had no way
   back. Now nothing happens unless you press the thing that says what it
   does. */
.tv-full {
  position: absolute;
  right: 11%;
  bottom: 10%;
  z-index: 3;
  padding: 7px 14px;
  border: 2px solid var(--paper);
  border-radius: 6px;
  background: rgba(8, 8, 12, 0.72);
  color: var(--paper);
  font: 400 0.8rem/1 var(--display);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  opacity: 0;
}
.tv-glass:hover .tv-full,
.tv-full:focus-visible { opacity: 1; }
.tv-full:hover { background: var(--orange); border-color: var(--orange); color: var(--ink); }
.tv-full:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; }

/* The control panel: a gold plate, two knobs, the speaker and the power
   light. Decoration — nothing on it does anything. */
.tv-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  background: var(--cream-shell);
  border: 3px solid var(--ink);
  border-radius: 20px;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.6);
}
.tv-label {
  font: 700 0.62rem/1 var(--mono);
  letter-spacing: 0.16em;
  color: var(--wood);
  text-transform: uppercase;
}
.tv-brand {
  padding: 2px 8px;
  border: 2px solid var(--ink);
  border-radius: 5px;
  font: 400 0.95rem/1.2 var(--display);
  letter-spacing: 0.06em;
  background: linear-gradient(180deg, var(--gold-hi), var(--gold) 50%, var(--gold-lo));
}
.tv-knob {
  position: relative;
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background:
    repeating-conic-gradient(from 0deg, rgba(0, 0, 0, 0.12) 0 6deg, transparent 6deg 12deg),
    radial-gradient(circle at 35% 28%, var(--gold-hi), var(--gold) 45%, var(--gold-lo) 90%);
  border: 3px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink), inset 0 0 0 5px rgba(0, 0, 0, 0.08);
}
.tv-knob::before {
  content: "";
  position: absolute;
  inset: 11px;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 30%, var(--gold-hi), var(--gold) 60%);
  border: 2px solid rgba(0, 0, 0, 0.35);
}
.tv-knob::after {
  content: "";
  position: absolute;
  left: calc(50% - 1.5px);
  top: 3px;
  width: 3px;
  height: 10px;
  background: var(--ink);
  border-radius: 2px;
}
.tv-grille {
  width: 70%;
  flex: 1;
  min-height: 60px;
  border: 2px solid var(--ink);
  border-radius: 8px;
  background: radial-gradient(circle, #3a2616 1.4px, transparent 1.8px) 0 0 / 6px 6px, #cdbb94;
}
.tv-led-row { display: flex; align-items: center; gap: 6px; }
.tv-led {
  width: 9px;
  height: 9px;
  border: 1.5px solid var(--ink);
  border-radius: 50%;
  background: var(--orange);
  box-shadow: 0 0 8px var(--orange);
}

/* Two skewed gold legs, standing over the cabinet's orange shadow. */
.tv-feet {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  padding: 0 22% 0 10%;
}
.tv-feet i {
  width: 16px;
  height: 26px;
  background: linear-gradient(180deg, var(--gold), var(--gold-lo));
  border: 3px solid var(--ink);
  border-top: 0;
  border-radius: 0 0 6px 6px;
  transform: skewX(-14deg);
}
.tv-feet i:last-child { transform: skewX(14deg); }

/* On a phone the panel lies under the screen instead of beside it. */
@media (max-width: 560px) {
  .tv-case { grid-template-columns: 1fr; padding: 12px; }
  .tv-side { flex-direction: row; justify-content: space-between; gap: 8px; padding: 8px 12px; }
  /* Smaller knobs, or the speaker between them shrinks to a sliver. */
  .tv-knob { width: 38px; height: 38px; }
  .tv-knob::before { inset: 8px; }
  .tv-grille { flex: 1; min-height: 36px; height: 36px; }
}

/* Every movement on the set: transforms and opacity only, so the glass costs
   the compositor rather than a repaint per frame. */
@media (prefers-reduced-motion: no-preference) {
  .tv-stage { animation: tv-jitter 6s steps(1) infinite; }
  .tv-scan { animation: tv-scan 0.9s steps(4) infinite; }
  .tv-roll { animation: tv-roll 7s linear infinite; }
  .tv-rec { animation: tv-blink 1.2s steps(1) infinite; }
  .tv-led { animation: tv-led 2.4s ease-in-out infinite; }
  .tv-knob { transition: rotate 200ms cubic-bezier(0.4, 0, 0.2, 1); }
  .tv-knob:hover { rotate: 35deg; }
  .tv-full { transition: opacity 140ms ease; }
}
@keyframes tv-jitter {
  0%, 100% { translate: 0 0; }
  91% { translate: 1px 0; }
  92% { translate: -2px 1px; }
  93% { translate: 0 0; }
}
@keyframes tv-scan { to { translate: 0 3px; } }
@keyframes tv-roll { from { translate: 0 -100%; } to { translate: 0 200%; } }
@keyframes tv-blink { 50% { opacity: 0; } }
@keyframes tv-led { 50% { opacity: 0.55; } }

/* ── The wall: the rounds, dealt as cards (cards.css) ──────────────────────── */

/* Scanlines on the wall alone, and BEHIND the sheets rather than over them:
   they show in the gutters between cards and stop at the paper. `fx-scanlines`
   is an ::after, which paints above in-flow content, so it drops to -1 and the
   section isolates itself rather than leaning on some ancestor's stacking
   context. Full width like everything else on this page; the chapter's own box
   is what scopes it vertically. */
/* Wider than the column, as wide as four cards need, and never past the
   gutter: the cards are the page's widest row. */
.chapter--wall {
  isolation: isolate;
  margin-inline: calc(50% - min(620px, 50vw - var(--space-gutter-min)));
}
.chapter--wall::after {
  z-index: -1;
  left: calc(50% - 50vw);
  right: auto;
  width: 100vw;
  /* Out past the section's own box at both ends, or the stripes start 48px
     below the rule and stop at the last sheet. Up by the space the rule
     leaves above the chapter; down by the chapter's closing space plus the
     hall's own bottom padding, which together are everything between the last
     sheet and the foot of the page. */
  top: calc(-1 * (var(--space-title-to-grid-max) + 0.5 * var(--slant)));
  bottom: calc(-1 * (var(--space-chapter-bottom-min) + var(--space-gutter-max)));
  /* And the top edge LEANS, parallel to the chequer above it — the same fix
     the band's bottom needed, for the same reason and in the other direction.
     Raising a horizontal edge to meet the end that lifts only pushed it over
     the end that dips, and the stripes spilled onto the warm paper on the
     left. The box is already half a slant high; the polygon takes a full
     slant back at its left corner, so the cut runs from half a slant below
     the box on the left to the box itself on the right — which is exactly
     where the chequer's lower edge is at each end. */
  clip-path: polygon(0 var(--slant), 100% 0, 100% 100%, 0 100%);
}

/* ── Profile: a paper scrapbook ────────────────────────────────────────────── */

/* The chequer under the strip, where a player's page begins. Straight, unlike
   the front page's: there it parts a leaning band from a lined ground and has
   to be parallel to both; here it sits under a strip with a straight edge,
   and a lean would only tilt away from it. Straight means it needs no
   overhang either — the turn was the only reason for one. */
.page-checker {
  position: relative;
  z-index: 1;
  height: 16px;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background-color: var(--warm-paper);
  background-image:
    linear-gradient(45deg, var(--orange) 25%, transparent 25% 75%, var(--orange) 75%),
    linear-gradient(45deg, var(--orange) 25%, transparent 25% 75%, var(--orange) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}

/* The dot field at the foot of the page, the same one the front page's band
   wears, gathered along the bottom edge and running the window's full width.
   Not turned, unlike the band's. A 2.5° turn over 112vw swings the layer some
   47px at each end, so one of them dipped past the foot of the page and the
   dots showed BELOW the footer. On a strip this wide the turn buys nothing
   and costs that, so it stops here and the layer sits square on the page's
   own bottom edge. Behind the results. */
.hall--warm::after {
  content: "";
  position: absolute;
  top: 40%;
  bottom: 0;
  left: calc(50% - 50vw);
  right: auto;
  width: 100vw;
  z-index: -1;
  pointer-events: none;
  background-image: var(--dots-wash-up), var(--dots);
  background-size: 100% 100%, var(--dots-grid);
  background-repeat: no-repeat, repeat;
}

/* The player's page stands on warm paper, not the hall's lighter ground: it
   is one long sheet of results and the beige is what the rest of the site
   uses for a surface things are laid ON. Fixed and behind, so it covers the
   window however far the page scrolls. */
.hall--warm::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--warm-paper);
}

.profile { max-width: 1100px; margin-inline: auto; }
.profile-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1rem, 4vw, 3rem);
}
.profile-header > div { min-width: 0; flex: 1; }
.profile-header .marquee-title { margin: 0; overflow-wrap: anywhere; }

/* The section head under the header needs its own room. The picture beside
   the name carries a 7px offset shadow below it, and an eyebrow with no
   margin of its own landed in it. */
.profile-header + .eyebrow {
  display: block;
  margin: var(--space-title-to-grid-max) 0 var(--space-gutter-min);
}
.profile-duck {
  position: relative;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: clamp(4rem, 12vw, 8rem);
  aspect-ratio: 1;
  font-size: clamp(2.5rem, 8vw, 5rem);
  /* Beige, for the same reason as the small one in the strip: a transparent
     picture should show paper through it. */
  background: var(--warm-paper);
  border: 3px solid var(--ink);
  box-shadow: var(--shadow-card-min) var(--shadow-card-min) 0 var(--ink);
}
.profile-duck .avatar { width: 100%; height: 100%; border: 0; font-size: inherit; }

/* The way out, beside the two figures on your own page. Ink on paper, unlike
   the stickers next to it: it undoes something, and a control that undoes
   should not look like the ones that boast. */
.sign-out {
  padding: 8px 16px;
  border: 2px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--type-sticker-family);
  font-size: var(--type-sticker-size);
  letter-spacing: var(--type-sticker-tracking);
  text-transform: uppercase;
  cursor: pointer;
}
.sign-out:hover { background: var(--magenta); color: var(--paper); }
.sign-out:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; }
.sign-out[disabled] { cursor: progress; opacity: 0.6; }

/* Pushed to the far end of the header's row, and wrapping under the name on a
   narrow window rather than squeezing it. */
.profile-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  margin-left: auto;
}
.profile-summary .sticker { font-size: clamp(1.1rem, 3vw, 1.8rem); cursor: default; }
.profile-summary .sticker:active {
  translate: none;
  box-shadow: var(--shadow-sticker-min) var(--shadow-sticker-min) var(--shadow-blur) var(--ink);
}

/* One fold per event: its name and where this player stands on it outside,
   the policies within. A sheet of paper with a tab, so a shut fold still reads
   as something to open. */
.profile-rounds { display: grid; gap: var(--space-gutter-min); }

.profile-round {
  --fx-shadow-color: var(--ink);
  --fx-shadow-size: var(--shadow-card-min);
  border: 3px solid var(--ink);
  /* The lighter paper, against the beige the page now stands on: a sheet laid
     on a surface, which is the whole figure this page is built from. */
  background: var(--paper);
  box-shadow: var(--shadow-card-min) var(--shadow-card-min) var(--shadow-blur) var(--ink);
}
.profile-round > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-gutter-min);
  padding: var(--space-card-padding-min);
  cursor: pointer;
  list-style: none; /* the default marker; the arrow below replaces it */
}
.profile-round > summary::-webkit-details-marker { display: none; }
/* The arrow keeps its own distance, which is what sets the button off the
   right edge rather than leaving it pressed against the fold's marker. */
.profile-round > summary::after {
  content: "\25BE";
  margin-left: var(--space-gutter-max);
  color: var(--ink-70);
}
.profile-round[open] > summary::after { content: "\25B4"; }
/* The row lights up under the pointer — but not while the pointer is on the
   button inside it. Two things cannot both be the thing you are about to
   press, and the button is the one that will actually happen. */
.profile-round > summary:hover:not(:has(.profile-round__go:hover)) {
  background: var(--cream);
}
.profile-round > summary:focus-visible { outline: 3px solid var(--cyan); outline-offset: -3px; }

.profile-round__name {
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: min(var(--type-card-title-size), 1.5rem);
  line-height: var(--type-card-title-line);
  letter-spacing: var(--type-card-title-tracking);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
/* The standing, in the action's own colour — it is the one figure on this
   page a reader is looking for. */
.profile-round__place {
  font-family: var(--type-card-title-family);
  font-weight: var(--type-card-title-weight);
  font-size: min(var(--type-card-title-size), 1.5rem);
  color: var(--orange-inked);
}
/* The way to the round itself. In the summary, where the name is, because a
   reader looking at "2 m Sprint #1" is one click from wanting to go and
   improve on it — and the click must open the round, never the fold. */
.profile-round__go {
  /* Through the charter's primitive, at its smaller step so there is a larger
     one to grow into when the pointer arrives. */
  --fx-shadow-color: var(--ink);
  --fx-shadow-size: var(--shadow-sticker-min);
  padding: 9px 20px;
  border: 2px solid var(--ink);
  background: var(--orange);
  color: var(--ink);
  font: 400 0.95rem/1 var(--display);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
/* Lifting off its own shadow, the way every pressable thing on this site
   does: the shadow steps up and the button steps back over it. */
.profile-round__go:hover {
  --fx-shadow-size: var(--shadow-sticker-max);
  background: var(--yellow);
  translate: -2px -2px;
}
.profile-round__go:active {
  --fx-shadow-size: var(--shadow-sticker-min);
  translate: 0 0;
}
.profile-round__go:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; }

@media (prefers-reduced-motion: no-preference) {
  .profile-round__go { transition: translate 120ms ease, background 120ms ease; }
}

.profile-round__count {
  margin-right: auto;
  font-family: var(--type-mono-label-family);
  font-weight: var(--type-mono-label-weight);
  font-size: var(--type-mono-label-size);
  letter-spacing: var(--type-mono-label-tracking);
  text-transform: uppercase;
  color: var(--ink-70);
}

.profile-round > .profile-results {
  padding: 0 var(--space-card-padding-min) var(--space-card-padding-min);
  border-top: 3px solid var(--ink);
  padding-top: var(--space-card-padding-min);
}

.profile-results {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 1rem;
}
.profile-results li { min-width: 0; }
.profile-results a {
  display: grid;
  gap: 0.75rem;
  height: 100%;
  padding: 1.25rem;
  background: var(--paper);
  border: 3px solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
  color: var(--ink);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.profile-results strong { font-size: 1.25rem; }
.profile-results a:hover { background: var(--warm-paper); }
.profile-empty a { color: var(--orange-inked); }

.profile-results a.withdrawn { color: var(--ink-70); background: var(--warm-paper); }
.profile-date { color: var(--ink-70); font-size: 0.85rem; }

