/* ============================================================================
   pitch_component.css — styles for pitch_component.js's `.gaf-pitch*` markup.

   Load in the same document as any page using pitch_component.js (order
   relative to season_record.css / style.css does not matter — every
   selector here is scoped to `.gaf-pitch`-prefixed classes or to the
   unprefixed `.jersey*` vocabulary, and no rule outside this file touches
   `.gaf-pitch*`, so there is no load-order dependency).

   ORIGIN. Ported from season_record.css's `.sr-pitch*` rules (themselves
   visually patterned on style.css's `.pitch-formation` / `.formation-row` /
   `.player-card` / `.bench-card`, and on `#tab-draftbuilder`'s `.db-pitch*`
   grass technique) — under an entirely new `.gaf-pitch*` prefix so this
   cannot collide with `.sr-*`, `.db-*`, `.pitch`/`.player-card`, or `.fx-*`
   while all four legacy pitch surfaces still exist and are migrated onto
   this component one at a time.

   FIDELITY. Every rule below reproduces season_record.css's `.sr-pitch*`
   layout, turf, name/stat/fixture-chip type and bench sizing byte-for-byte
   (same pixel values, same colors, same breakpoints), with two additions:
     1. The six original custom properties (cell size, gap, jersey size,
        turf top/bottom color, pitch-line color) are joined by THREE MORE —
        name/stat/fixture-chip font sizes — turning what used to be
        hardcoded per-breakpoint rem values into override-able parameters,
        per this migration's own requirement.
     2. The jersey badge carries the "edge stroke / dark-ink halo /
        unknown-club ring" treatment ported from `#tab-draftbuilder
        .jersey*` (style.css ~7900-7990), scoped under a
        `.gaf-pitch--jersey-edge` modifier class — DEFAULT ON. Ruling from
        Harold, superseding an earlier one: Draft Builder is now the design
        of record for the whole app's pitch look, not Season Opener, so its
        jersey treatment is what every migrated surface gets unless a
        caller explicitly opts OUT (`jerseyEdge: false` — see
        pitch_component.js's CONTRACT for the exact default-true check).
        Draft Builder itself stays on its own original `.db-*` markup and
        never calls this component.
     3. An optional ★ template-star before the name (`.gaf-pitch-star`),
        gated behind `opts.templateStar` + per-player `p.is_template` — see
        pitch_component.js's CONTRACT. Ported from My Team's legacy
        `.template-star` (style.css) as a first-class shared capability
        rather than a one-off: real ownership/differential signal, not
        cosmetic, per Harold's ruling — ON for My Team, OFF by default
        everywhere else.
     4. An optional 5-fixture-ahead "run" strip (`.gaf-pitch-fixstrip*`),
        gated behind `opts.fixtureRun` — see pitch_component.js's CONTRACT
        and the dedicated block near the end of this file. For the Template
        & Differentials Research Page; opt-in, additive, no existing caller
        passes the flag.

   NARROW-VIEWPORT STRATEGY — unchanged from `.sr-pitch*`: each row is a
   single nowrap flex line, and every XI cell is `flex: 1 1 0` with
   `min-width: 0` and a `max-width` cap, so a 5-wide DEF/MID row divides the
   row's actual width instead of ever overflowing it, regardless of
   viewport. The custom properties below are only ENLARGED at wider
   breakpoints, never relied on to prevent overflow — that guarantee is
   structural, not a media query.

   ---------------------------------------------------------------------------
   UNIFICATION + REDESIGN PASS (2026-09-22) — read before touching sizing
   ---------------------------------------------------------------------------
   Prompted by: "the pitch on my team page looks nicer" + a follow-up
   "full app sweep... pitches, same size... all readable" complaint.

   1. SINGLE SOURCE OF TRUTH FOR CELL/JERSEY SIZE.
      Before: this file's own default breakpoints were never actually used
      by 6 of the 8 adopters, because 6 separate per-surface CSS files each
      hardcoded a FLAT `--gaf-pitch-cell: 80px; --gaf-pitch-jersey: 45px;`
      with no media query — freezing size at every viewport instead of
      letting this file's own responsive cascade run. That's how a 7th
      surface (template_diff.css) drifted to 86px unnoticed, and why any
      future change to "the" pitch size would have required editing 6
      files in lockstep. Now: the responsive cascade below IS the single
      source of truth. Five of the six duplicate freezes have been deleted
      outright (My Team, Season Opener, Projections, Free Hit/Wildcard,
      Form XI) and simply inherit the cascade. Template & Differentials'
      >=768px freeze is KEPT deliberately — it's a real constraint (two
      pitches side-by-side; letting cells grow to this cascade's 100px+
      tier at wide viewports would compound both pitches' height on a page
      that's already vertically tight), not drift.
   2. THE COMPONENT NOW SELF-CAPS AND SELF-CENTERS.
      Before: `.gaf-pitch` was a plain block, stretching to 100% of
      whatever container it's in. Five of the six per-surface CSS files
      above existed ONLY to undo that (`max-width: 440px; margin-left/
      right: auto;`) with the same hand-calculated value, repeated five
      times. Year in Review's Best XV pitch never got this treatment at
      all — the one surface with NO per-surface override — so it rendered
      full-bleed-width with no cap the whole time. Now: `.gaf-pitch` itself
      defaults to `width: fit-content; max-width: 100%; margin-inline:
      auto;`. It sizes to its own content and centers itself everywhere,
      automatically — Year in Review included, no override needed anywhere.
   3. READABILITY BUMP (owner's explicit "all readable" complaint).
      Old base tier (<640px, i.e. real phones): 0.64rem name / 0.6rem stat
      / 0.56rem fix — genuinely cramped, and it's what every non-forced
      surface actually used on a real phone (the six 80px-cell overrides
      never touched font size, so a forced-bigger cell at that width still
      carried the tiniest font tier). Bumped one notch at every tier — see
      the tier table below.
   4. VISUAL POLISH ("make it nicer / more tasteful").
      Turf: slightly stronger mow-stripe contrast, a soft outer card shadow
      so the pitch reads as an elevated panel instead of a flat green
      rectangle on the page background, a subtle inner vignette for depth.
      Markings: center circle/halfway line contrast nudged up slightly, no
      new elements. Breathing room: outer padding and inter-row gap opened
      up a touch at the base tier. Every data element (armband, star, stat
      line, fixture chip + FDR color, bench-order number, jersey-edge
      treatment) is unchanged in substance — only sized/spaced differently.
   5. NEW: OPT-IN HORIZONTAL ("broadcast graphic") LAYOUT.
      `opts.layout: "horizontal"` (pitch_component.js) adds a
      `.gaf-pitch--horizontal` modifier class to the root. GK renders far
      left, DEF/MID/FWD as columns marching right — same DOM order the
      vertical layout already produces (GK row first, FWD row last), so no
      JS restructuring was needed beyond adding the opt — only CSS repoints
      the flex axis. Existing callers that don't pass `layout` get
      pixel-for-pixel the old vertical behavior (still the default) — fully
      backward compatible. Wired for My Team only in this pass (see
      style.css's `.myteam-layout` for the wrapper half of that change);
      every other surface stays vertical.
   ========================================================================= */

.gaf-pitch {
  /* ---- the six original override points ---- */
  /* ---- tier table, single source of truth ----
     viewport      cell   jersey   name      stat      fix
     <640px (base)  66px   38px    0.68rem   0.64rem   0.58rem
     >=640px        82px   46px    0.76rem   0.70rem   0.64rem
     >=960px        92px   50px    (unchanged from 640 tier)
     >=1280px      100px   54px    0.84rem   0.76rem   0.68rem
     Numbers chosen to land close to the old "everyone agreed on 80/45"
     value right at the 640px tier (where most real usage sits), while
     actually reaching the wider tiers instead of being frozen out of
     them, and to no longer collapse to the tiniest font tier on a real
     phone just because some surface's cell was force-set larger. */
  --gaf-pitch-cell: 66px;
  --gaf-pitch-gap: 4px;
  --gaf-pitch-jersey: 38px;
  --gaf-pitch-turf-top: #1e8c42;
  --gaf-pitch-turf-bot: #1a7a3a;
  --gaf-pitch-line: rgba(255, 255, 255, 0.28);
  /* Internal to the turf gradient, not one of the six named override
     points, but still a token rather than a literal, matching the original
     `--sr-turf-stripe`. Nudged up slightly (0.045 -> 0.055) for a bit more
     visible mow-stripe texture — part of the "tasteful" polish pass. */
  --gaf-pitch-turf-stripe: rgba(255, 255, 255, 0.055);

  /* ---- the three added font-size override points ---- */
  --gaf-pitch-name-size: 0.68rem;
  --gaf-pitch-stat-size: 0.64rem;
  --gaf-pitch-fix-size: 0.58rem;

  /* Jersey edge-stroke color — part of the ported Draft Builder treatment,
     not one of Season Opener's original six, but exposed the same way. */
  --gaf-pitch-jersey-edge: rgba(0, 0, 0, 0.32);

  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--radius-lg, 10px);
  /* A touch more horizontal breathing room at the base tier (space-2 ->
     space-3) — the old value was tightest exactly where cells are
     smallest. */
  padding: var(--space-4, 16px) var(--space-3, 12px) var(--space-3, 12px);
  margin-bottom: var(--space-3, 12px);
  background: var(--gaf-pitch-turf-bot); /* fallback ground if .gaf-pitch-turf is ever absent */
  /* The component sizes itself to its own content and centers in
     whatever container it's given, instead of stretching full-width like
     a plain block. This is what let five separate per-surface "cap +
     center" CSS blocks (season_record.css, style.css x4) be deleted
     outright. Year in Review's pitch (the one surface that never had a
     per-surface override) picks this up for free too.

     BUG FIX (2026-09-22, found via a wide-monitor width audit on
     Projections): this was `width: fit-content` originally, which reads
     as "size to whatever the content needs" but does NOT correctly
     compute through nested flexbox here -- `.gaf-pitch-row`'s children
     (`.gaf-pitch-player`) use `flex: 1 1 0; max-width: var(--gaf-pitch-
     cell)`, and a `flex-basis: 0` item's contribution to an ANCESTOR's
     fit-content/max-content intrinsic size is its own CONTENT's natural
     width (crest + name text), not its `max-width` cap -- flex-grow only
     resolves once the flex container itself has a definite size to
     distribute, which is exactly the circular problem intrinsic sizing
     hits here. Measured live: with `--gaf-pitch-cell: 100px`, actual
     rendered player width was 64px -- the pitch had been rendering
     visibly smaller than its own size variables implied, on every
     surface using this (vertical, non-horizontal) layout, since this
     component's fit-content redesign shipped. The pre-redesign code
     never hit this, because every surface hardcoded an explicit
     max-width (e.g. "5*80px cells + 4*4px gaps + 16px side padding =
     432px" -- see the deleted per-surface comments in style.css's git
     history) instead of trusting the browser to infer it. Replaced with
     the same explicit calc that comment was already doing by hand, in
     ONE place instead of six: 5 columns is FPL's real ceiling (DEF and
     MID can each run 5-wide; GK is always 1, FWD caps at 3), so this is
     correctly sized for the widest row any formation can produce, with
     narrower rows just centering within it via `.gaf-pitch-row`'s own
     `justify-content: center`. `.gaf-pitch--horizontal` below opts OUT
     of this entirely (it sets its own `width: 100%`, unaffected either
     way -- a broadcast-strip layout was never fit-content in the first
     place). */
  width: calc(5 * var(--gaf-pitch-cell) + 4 * var(--gaf-pitch-gap) + 2 * var(--space-3, 12px));
  max-width: 100%;
  margin-inline: auto;
  /* Soft elevation so the pitch reads as a panel/card, not a flat green
     rectangle glued to the page background. Same in both themes; night's
     darker turf already provides enough contrast against it that no
     separate night value is needed. */
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
}
.gaf-pitch-turf {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(to bottom,
      var(--gaf-pitch-turf-stripe) 0 calc(100% / 12),
      transparent calc(100% / 12) calc(100% / 6)),
    /* Subtle radial vignette layered under the stripes, on top of the
       top/bottom gradient — cheap depth cue, no new markup. */
    radial-gradient(ellipse at 50% 15%, rgba(255, 255, 255, 0.05), transparent 60%),
    linear-gradient(to bottom, var(--gaf-pitch-turf-top), var(--gaf-pitch-turf-bot));
}
.gaf-pitch-markings {
  position: absolute;
  inset: var(--space-2, 8px);
  z-index: 1;
  pointer-events: none;
  border: 2px solid var(--gaf-pitch-line);
  border-radius: 3px;
  background: linear-gradient(to bottom,
    transparent calc(50% - 1px),
    var(--gaf-pitch-line) calc(50% - 1px),
    var(--gaf-pitch-line) calc(50% + 1px),
    transparent calc(50% + 1px));
}
.gaf-pitch-markings::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 2px solid var(--gaf-pitch-line);
  border-radius: 50%;
}
.gaf-pitch-markings::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -2px;
  width: 42%;
  height: 40px;
  transform: translateX(-50%);
  border: 2px solid var(--gaf-pitch-line);
  border-top: 0;
  border-radius: 0 0 3px 3px;
}
.gaf-pitch-field { position: relative; z-index: 2; display: flex; flex-direction: column; gap: var(--space-2, 8px); }
.gaf-pitch-row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: flex-start;
  gap: var(--gaf-pitch-gap);
}
.gaf-pitch-player {
  flex: 1 1 0;
  min-width: 0;
  max-width: var(--gaf-pitch-cell);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 2px;
  position: relative;
}
.gaf-pitch-player--bench { opacity: 0.85; }
.gaf-pitch-bench-order {
  position: absolute;
  top: -3px;
  left: 2px;
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--color-text-muted, #666);
  font-variant-numeric: tabular-nums;
}
/* `.gaf-pitch-kit` sits in a column flexbox with align-items:center
   (`.gaf-pitch-player`), a shrink-to-fit context — it has no definite width
   of its own. An `<img>` child got away with `width:100%` there anyway
   (replaced elements fall back to their own intrinsic size in an
   indeterminate-width context); a plain `<span>` (the crest) cannot, and
   silently renders at 0x0. Giving the wrapper itself an explicit width
   fixes both. */
.gaf-pitch-kit { line-height: 0; width: var(--gaf-pitch-jersey); margin: 0 auto; }
.gaf-pitch-kit .jersey { display: block; width: 100%; max-width: var(--gaf-pitch-jersey); height: auto; margin: 0 auto; }
/* .gaf-crest is a <span> with only a background-image — unlike an <img>, it
   has no intrinsic size to drive `height: auto` from (that collapses to 0).
   aspect-ratio gives it one; crests render in a square slot, same footprint
   the jersey's roughly-square 64x64 viewBox always occupied. */
.gaf-pitch-kit .gaf-crest { display: block; width: 100%; max-width: var(--gaf-pitch-jersey); aspect-ratio: 1 / 1; margin: 0 auto; }

/* ---- ported from #tab-draftbuilder .jersey* (style.css ~7900-7990) ----
   DEFAULT ON (`.gaf-pitch--jersey-edge`, added by gafRenderPitch unless a
   caller passes `jerseyEdge: false` — see pitch_component.js CONTRACT). */
.gaf-pitch--jersey-edge .jersey,
.gaf-pitch--jersey-edge .gaf-crest {
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.38));
}
.gaf-pitch--jersey-edge .jersey-body,
.gaf-pitch--jersey-edge .jersey-sleeve,
.gaf-pitch--jersey-edge .jersey-collar {
  /* NO `fill` HERE — EVER. The per-club colors arrive as SVG presentation
     attributes from GAF_KIT via gafJerseyHtml; any CSS fill here would
     outrank all twenty at once. stroke is paint, not fill, so no club
     color moves — it only gives a pale shirt (FUL/LEE/TOT bodies,
     IPS/LIV/BRE/CHE sleeves) a silhouette edge against the turf. */
  shape-rendering: geometricPrecision;
  stroke: var(--gaf-pitch-jersey-edge);
  stroke-width: 0.8;
  stroke-linejoin: round;
}
.gaf-pitch--jersey-edge .jersey-code--dark {
  /* Dark ink on a mid-tone body (MCI, COV, HUL) needs separation; a
     hairline light halo gives it without repainting the shirt. Invisible on
     white bodies. `.jersey-code--light` needs no rule — white ink on a dark
     body needs nothing added. */
  paint-order: stroke fill;
  stroke: #ffffff;
  stroke-width: 0.055em;
  stroke-linejoin: round;
}
/* A club with no entry in GAF_KIT — surfaced visibly, never a silent
   guess, via an amber dashed ring on top of the neutral grey shirt
   gafKitFor already returns. */
.gaf-pitch--jersey-edge .jersey--unknown {
  outline: 2px dashed var(--color-warning, #e0a030);
  outline-offset: 2px;
  border-radius: var(--radius-sm, 4px);
}
.gaf-pitch--jersey-edge .jersey--unknown .jersey-body {
  stroke: var(--color-warning, #e0a030);
  stroke-width: 1.4;
  stroke-dasharray: 3 2;
}

.gaf-pitch-name-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  max-width: 100%;
  min-width: 0;
}
/* Template-star — own class, NOT a dependency on style.css's `.template-star`
   (same "own name, not borrowed" rule as the armband above; that class stays
   live elsewhere in app.js and keeps its own rule). Opt-in via
   opts.templateStar + p.is_template, see pitch_component.js's CONTRACT. */
.gaf-pitch-star {
  color: var(--color-primary, #00ff87);
  font-weight: 700;
  font-size: var(--gaf-pitch-name-size);
  flex: 0 0 auto;
  line-height: 1;
}
.gaf-pitch-name {
  font-weight: 700;
  font-size: var(--gaf-pitch-name-size);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Fixed white ink, not var(--color-text) — the turf is dark green in BOTH
     themes, so a themed token (#111 in day mode) would vanish into the
     grass. Same fix `.sr-pitch-name` / Draft Builder's `.db-cell-name`
     apply, same reason. */
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
/* This component's own armband, NOT a dependency on `.sr-armband` (that
   class belongs to season_record.css and this file must not reach for it —
   rule 5 of this migration). Colors match `.sr-armband`'s so the badge
   still reads identically; the class name and definition are wholly
   independent. */
.gaf-pitch-armband {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  font-size: 0.54rem;
  font-weight: 800;
  line-height: 1;
  flex: 0 0 auto;
}
.gaf-pitch-armband--c { background: var(--color-primary, #00ff87); color: #05140d; }
.gaf-pitch-armband--v { background: var(--color-border); color: var(--color-text, #111); }
body.night .gaf-pitch-armband--v { color: var(--color-text, #e8ecf1); }

.gaf-pitch-stat {
  font-size: var(--gaf-pitch-stat-size);
  /* Fixed white-dim ink, same reasoning as `.gaf-pitch-name` — the muted-gray
     theme token fails contrast against the green turf in both themes. */
  color: rgba(255, 255, 255, 0.82);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  font-variant-numeric: tabular-nums;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gaf-pitch-fix {
  display: inline-block;
  max-width: 100%;
  padding: 1px 4px;
  border-radius: 3px;
  font-size: var(--gaf-pitch-fix-size);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Graceful "no fdr class at all" fallback — same idea as Draft Builder's
   `.db-cell-fix:not([class*="fdr-bg-"])`, implemented under this
   component's own class names. */
.gaf-pitch-fix:not([class*="fdr-bg-"]) {
  background: rgba(0, 0, 0, 0.34);
  color: rgba(255, 255, 255, 0.74);
}
/* The pre-rollover / no-fixture-data dash — same dashed-absence convention
   Draft Builder uses for `.db-cell-fix--none`, paired with the global
   `fdr-bg-0` neutral tint rather than inventing a second neutral color. */
.gaf-pitch-fix--none {
  background: none;
  border: 1px dashed rgba(255, 255, 255, 0.4);
  color: rgba(255, 255, 255, 0.82);
}
.gaf-pitch-bench-wrap { margin-top: var(--space-2, 8px); }
/* BUG FIX (2026-09-22): `gafRenderPitch` emits the bench wrap as a SIBLING
   of `.gaf-pitch`, not a descendant -- `return `<div class="gaf-pitch">
   ...</div>${benchHtml}`` -- so it never inherited `--gaf-pitch-cell`/
   `--gaf-pitch-jersey` from `.gaf-pitch` (CSS custom properties only
   cascade down the DOM tree, never sideways to siblings). `.gaf-pitch-kit`
   resolves `width: var(--gaf-pitch-jersey)` against nothing there, which
   is invalid-at-computed-value -- the whole declaration is dropped, the
   kit collapses to its default auto/content width (zero, since its own
   child `.gaf-crest` is a background-image-only span with no intrinsic
   size), and the bench player's crest silently renders as a 0x0 box.
   Confirmed live on Projections (My Team panel's bench row): the crest
   `<span>` was present in the DOM with the correct background-image URL,
   computed width/height both 0px. This affects every surface using the
   plain shared bench markup -- Season Opener, Free Hit, Wildcard, Form
   XI, Year in Review, Template & Differentials, Projections. My Team is
   the ONE surface that already dodges this, because its own CSS
   (style.css's `.myteam-bench-block .myteam-bench-wrap`) restates these
   same custom properties locally -- a per-surface patch for a bug that
   is actually generic to the shared component, so every OTHER adopter
   stayed silently broken. Fixed once, here, for all of them: the adjacent-
   sibling combinator targets exactly the bench wrap `gafRenderPitch`
   itself emits right after `.gaf-pitch`, so no caller-side change is
   needed anywhere. Values mirror `.gaf-pitch`'s own base-tier + responsive
   cascade above exactly (same tier table) -- kept as a literal restatement
   rather than some cleverer inheritance trick, matching how My Team's own
   (now-redundant-but-harmless) copy already did it. */
.gaf-pitch + .gaf-pitch-bench-wrap {
  --gaf-pitch-cell: 66px;
  --gaf-pitch-jersey: 38px;
}
@media (min-width: 640px) {
  .gaf-pitch + .gaf-pitch-bench-wrap { --gaf-pitch-cell: 82px; --gaf-pitch-jersey: 46px; }
}
@media (min-width: 960px) {
  .gaf-pitch + .gaf-pitch-bench-wrap { --gaf-pitch-cell: 92px; --gaf-pitch-jersey: 50px; }
}
@media (min-width: 1280px) {
  .gaf-pitch + .gaf-pitch-bench-wrap { --gaf-pitch-cell: 100px; --gaf-pitch-jersey: 54px; }
}
/* Ported from `.sr-week-label` (season_record.css) so this file has no
   dependency on it — same type treatment, own class name. */
.gaf-pitch-bench-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted, #666);
  margin: 10px 0 6px;
}
.gaf-pitch-bench {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 2px;
}
/* Overrides the base `.gaf-pitch-player`'s flex:1 1 0 (which grows to fill
   the whole row, making `justify-content: center` above a no-op) — bench
   tokens size to their own cell width and no more, so the group sits as a
   tight, genuinely centered cluster instead of spread across the full row. */
.gaf-pitch-bench .gaf-pitch-player {
  flex: 0 0 auto;
  width: var(--gaf-pitch-cell);
  padding-top: 9px;
}

/* ---- night theme --------------------------------------------------------
   `.gaf-pitch-name` / `.gaf-pitch-stat` / `.gaf-pitch-fix--none` are
   DELIBERATELY excluded from theme-color overrides — they use fixed
   white-based ink in BOTH themes because the turf is dark green in both,
   same reasoning as `.sr-pitch-name` / Draft Builder's own pitch ink. Only
   the turf's own luminance darkens for night, plus the jersey edge and its
   added halo. */
body.night .gaf-pitch {
  --gaf-pitch-turf-top: #17703a;
  --gaf-pitch-turf-bot: #135e30;
  --gaf-pitch-turf-stripe: rgba(255, 255, 255, 0.045);
  --gaf-pitch-line: rgba(255, 255, 255, 0.22);
  --gaf-pitch-jersey-edge: rgba(0, 0, 0, 0.40);
  /* The card shadow needs to be a touch stronger at night to read against
     a dark app background (the day value all but disappears on a light
     page, but on a near-black night page a light-tuned shadow
     under-separates the panel from its surroundings). */
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}
body.night .gaf-pitch--jersey-edge .jersey {
  /* A dark edge alone cannot separate a near-black shirt (NEW, AVL) from a
     darker night turf — one stroke color cannot serve both a white shirt
     and a black one. Night adds a faint outward halo on top of the shadow;
     day does not need it because the turf is bright enough for the shadow
     alone to read. Ported from `#tab-draftbuilder`'s identical night rule. */
  filter:
    drop-shadow(0 2px 3px rgba(0, 0, 0, 0.50))
    drop-shadow(0 0 1px rgba(255, 255, 255, 0.30));
}

/* ---- wider viewports: pitch cells enlarge, type scales up ----------------
   See the tier table in the base ruleset comment above. This is now the
   ONLY place pitch size is declared — every per-surface "80/45px" freeze
   that used to compete with (and mostly just coincidentally restate) this
   cascade has been deleted (see style.css / season_record.css). */
@media (min-width: 640px) {
  .gaf-pitch {
    --gaf-pitch-cell: 82px;
    --gaf-pitch-gap: var(--space-2, 8px);
    --gaf-pitch-jersey: 46px;
    --gaf-pitch-name-size: 0.76rem;
    --gaf-pitch-stat-size: 0.70rem;
    --gaf-pitch-fix-size: 0.64rem;
  }
  .gaf-pitch-fix { padding: 2px 6px; }
  .gaf-pitch-armband { width: 16px; height: 16px; font-size: 0.62rem; }
}
@media (min-width: 960px) {
  .gaf-pitch { --gaf-pitch-cell: 92px; --gaf-pitch-jersey: 50px; }
}
@media (min-width: 1280px) {
  .gaf-pitch {
    --gaf-pitch-cell: 100px;
    --gaf-pitch-gap: var(--space-3, 12px);
    --gaf-pitch-jersey: 54px;
    --gaf-pitch-name-size: 0.84rem;
    --gaf-pitch-stat-size: 0.76rem;
    --gaf-pitch-fix-size: 0.68rem;
  }
}

/* ============================================================================
   HORIZONTAL LAYOUT — opt-in via `opts.layout: "horizontal"`
   (pitch_component.js). Root gets `.gaf-pitch--horizontal` in addition to
   `.gaf-pitch`; markup is otherwise byte-identical (same `.gaf-pitch-row`
   per position, same `.gaf-pitch-player` tokens, same bench-wrap below) —
   only the flex axis of the field and its rows flips, plus the turf/
   markings decoration is redrawn along the other axis. No new DOM.

   GK renders far LEFT, FWD far RIGHT — this falls out of the existing DOM
   order for free: gafRenderPitch already emits `.gaf-pitch-row--gk` first
   and `.gaf-pitch-row--fwd` last (posOrder default ["GK","DEF","MID","FWD"]);
   turning the field from a column of rows into a ROW of columns just makes
   that same source order read left-to-right instead of top-to-bottom.

   WHY THIS OPTS BACK OUT OF THE fit-content DEFAULT: a broadcast-style
   formation strip is supposed to use the width its container actually
   gives it (that's the entire point of choosing it for a page with dead
   space to fill) — each position "row" (now a column) uses `flex: 1 1 0`
   with NO max-width, so on a wide container the columns spread out and use
   the room, while individual player tokens inside stay pinned to
   `--gaf-pitch-cell` width and simply center within the wider column
   instead of stretching (a stretched jersey would look broken). A
   caller-side max-width (style.css's `.myteam-layout`) still bounds how
   wide this is allowed to get on an ultra-wide monitor. */
.gaf-pitch--horizontal {
  width: 100%;
  max-width: 900px;
}
.gaf-pitch--horizontal .gaf-pitch-field {
  flex-direction: row;
  align-items: stretch;
  justify-content: center;
  gap: var(--space-4, 16px);
}
.gaf-pitch--horizontal .gaf-pitch-row {
  flex: 1 1 0;
  min-width: 0;
  flex-direction: column;
  align-items: center;
  /* Distributes however many players are in this column across the FULL
     column height (which stretches to match the tallest column via the
     field's `align-items: stretch` above) — a lone GK centers in the
     middle of the goal line instead of pinning to one edge, and a 5-man
     back line spreads evenly top-to-bottom, the way a real broadcast
     lineup graphic reads. */
  justify-content: space-evenly;
  gap: var(--gaf-pitch-gap);
}
.gaf-pitch--horizontal .gaf-pitch-player {
  /* Row-layout's flex:1 1 0 (fill the row width) and its max-width cap
     (stop it filling TOO much) both existed to solve a row-of-players
     problem; in a column, the player token should just be its natural
     cell size, not stretched or capped against the column's own width. */
  flex: 0 0 auto;
  max-width: none;
  width: var(--gaf-pitch-cell);
}
/* Bench stays a plain horizontal strip below the field regardless of
   layout — a bench has no "position column" concept, and a landscape strip
   sitting under a landscape pitch already reads correctly with zero
   changes. */

/* Turf/markings redrawn along the other axis — same visual vocabulary
   (mow stripes, halfway line, center circle, one goal box) as the
   vertical pitch, just rotated 90°: the halfway line runs vertically
   through the pitch's actual midpoint, the goal box sits flush against
   the LEFT edge (behind the GK column, matching where the vertical
   layout's box sits behind the GK row), and the center circle is sized
   off the pitch's HEIGHT instead of its width since a landscape pitch is
   wider than it is tall. */
.gaf-pitch--horizontal .gaf-pitch-turf {
  background:
    repeating-linear-gradient(to right,
      var(--gaf-pitch-turf-stripe) 0 calc(100% / 12),
      transparent calc(100% / 12) calc(100% / 6)),
    radial-gradient(ellipse at 12% 50%, rgba(255, 255, 255, 0.05), transparent 60%),
    linear-gradient(to right, var(--gaf-pitch-turf-top), var(--gaf-pitch-turf-bot));
}
.gaf-pitch--horizontal .gaf-pitch-markings {
  background: linear-gradient(to right,
    transparent calc(50% - 1px),
    var(--gaf-pitch-line) calc(50% - 1px),
    var(--gaf-pitch-line) calc(50% + 1px),
    transparent calc(50% + 1px));
}
.gaf-pitch--horizontal .gaf-pitch-markings::before {
  /* Same center circle, sized off height (the constrained dimension in a
     landscape box) instead of width. */
  width: auto;
  height: 46%;
  aspect-ratio: 1;
}
.gaf-pitch--horizontal .gaf-pitch-markings::after {
  /* The vertical layout's box is a 42%-wide, 40px-tall bar flush against
     the top edge, rounded on its far corners. Rotated: a 42%-tall, 40px-
     wide bar flush against the LEFT edge, rounded on ITS far corners. */
  left: -2px;
  top: 50%;
  width: 40px;
  height: 42%;
  transform: translateY(-50%);
  border-top: 2px solid var(--gaf-pitch-line);
  border-left: 0;
  border-radius: 0 3px 3px 0;
}

/* ============================================================================
   FIXTURE-RUN STRIP — ADDITIVE, opt-in via `opts.fixtureRun` (see
   pitch_component.js's CONTRACT). No caller of fixtureRun also uses
   layout:"horizontal" — if one is added later, the labels row sits BELOW
   the stat line inside `.gaf-pitch-player` regardless of column vs. row
   orientation, so it likely needs no extra work, but that combination has
   not been exercised.
   ========================================================================= */
.gaf-pitch-fixstrip-labels {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  align-self: stretch;
  width: 100%;
  gap: 2px;
  margin-top: 1px;
  max-width: 100%;
}
.gaf-pitch-fixstrip-label {
  display: inline-block;
  flex: 1 1 0;
  min-width: 0;
  padding: 0;
  border-radius: 2px;
  font-size: 7px;
  font-weight: 700;
  letter-spacing: -0.15px;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}
.gaf-pitch-fixstrip-side { display: none; }

@media (max-width: 480px) {
  .gaf-pitch-fixstrip-label {
    font-size: 5.5px;
    letter-spacing: -0.3px;
    padding: 0 1px;
  }
  .gaf-pitch-fixstrip-labels {
    gap: 2px;
  }
}
