/* ══════════════════════════════════════════════════════════════════════════
   fpl — SITE EXTENSION TO CLASSICAL.  Not part of the design system.

   styles.css is Classical, verbatim and untouched, and remains the source of
   truth. This file adds ONLY what Classical has no answer for, and is built
   entirely from Classical's own var(--*) tokens: no new hex, no new font, no
   px value the --space-* / --radius-* scales already carry. Nothing Classical
   already defines is restyled here. Deleting this file degrades the pages to
   plain Classical; it never fights it.

   Four additions, each justified where it appears:
     1. page shell + justified prose column  (Classical ships no layout classes)
     2. tabular figures                      (a stated rule with no class to apply it)
     3. horizontally scrolling table with a pinned identity column
                                             (.table has neither behaviour)
     4. the component ramp                   (nine signed additive parts, no fills)
   ══════════════════════════════════════════════════════════════════════ */


/* ── 1. Page shell ────────────────────────────────────────────────────────
   Classical defines type, colour and components but no layout primitives.
   The measure below is a text measure, not an arbitrary width: it is set in
   `ch` so it tracks Lora at the body size the system already fixed.
   The explicit background on the shell is deliberate — see .ground below. */

.ground {
  /* Classical's own `body` rule already paints var(--color-bg). This repeats it
     on the shell so that if these pages are ever embedded, or opened inside a
     host that paints its own dark ground behind the document, the page still
     stands on the light ground the system's theme.json declares ("band":
     "light"). Classical defines no dark treatment and none is invented here. */
  background: var(--color-bg);
  color: var(--color-text);
  min-height: 100vh;
  /* The ramp's stroke width, hoisted so the legend swatches can share it.
     The one literal in this file. Classical has no stroke-width token; 6px is
     the weight it gives .plate's border, which is the heaviest stroke the
     system states, and at the ramp's height it still reads 1:9 or narrower —
     a rule, not a filled block. */
  --ramp-stroke: 6px;
}

/* Classical's .nav declares no wrapping behaviour, and four links plus the brand
   overflow a 320px viewport in one line — which would break the page sideways.
   This adds the property the system leaves unset; it changes nothing Classical
   declares, and nothing at all above 22em. */
.nav { flex-wrap: wrap; row-gap: var(--space-2); }

.wrap {
  max-width: 74ch;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-8);
}
.wrap-wide { max-width: 108ch; }

.masthead { padding-top: var(--space-4); }
.masthead h1 { font-weight: 400; }          /* "the bigger the text, the lighter
                                                it sets" — display drops to the
                                                normal cut, per the type rules */
.kicker {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-accent-700);
  font-feature-settings: "tnum";            /* kickers carry figures */
  margin: 0 0 var(--space-2);
}
.section { margin-top: var(--space-8); }
.section > h2, .section > h3 { margin-bottom: var(--space-3); }

/* Justified body copy in a column, per the system's direction. Applied only to
   prose — never to tables (see .table-note in §2). Hyphenation is what keeps
   justified Lora from opening rivers at this measure. */
.prose p, .prose li {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}
.prose { max-width: 66ch; }
.prose ul, .prose ol { padding-left: var(--space-4); margin: 0 0 var(--space-3); }
.prose li { margin-bottom: var(--space-2); }

.lede { font-size: 17px; }

/* Two-column reading measure for the front page, collapsing on a phone. */
.cols { display: grid; gap: var(--space-6); }
@media (min-width: 46em) { .cols-2 { grid-template-columns: 1fr 1fr; } }

.stack { display: flex; flex-direction: column; gap: var(--space-3); }
.row-wrap { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: baseline; }


/* ── 2. Tabular figures ───────────────────────────────────────────────────
   Classical's type rules require figures to set tabular wherever they stand as
   figures or columns, and require running prose to keep Lora's text figures
   (its tnum also widens word-spaces, which would loosen justified copy). The
   system states the rule but ships no class for it. This is that class. */

.num { font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }
.table td.num, .table th.num { text-align: right; }
.table th.num { text-align: right; }

/* Numeric tables are NOT justified: justification is for prose. Left-align the
   label columns, right-align the figures, and let the hairline row rules that
   Classical already draws carry the horizontal structure. */
/* A table caption, placed BEFORE .scroller rather than inside the table. A real
   <caption> takes the table's width, so inside a horizontally scrolling table it
   renders one long clipped line on a phone; out here it wraps to the readable
   width and stays put while the table scrolls under it. The table keeps the
   association through aria-describedby. */
.t-caption {
  margin: 0 0 var(--space-2); font-size: 12px;
}
/* A caption that follows another table needs air above it. */
.scroller + .t-caption, .scroll-note + .t-caption { margin-top: var(--space-6); }
.t-caption {
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
}
.table td.pos, .table th.pos { white-space: nowrap; }

/* A figure standing alone — the headline objective numbers. */
.figure-lg {
  font-family: var(--font-heading); font-weight: 400;
  font-size: 42px; line-height: 1.05;
  font-feature-settings: "tnum"; font-variant-numeric: tabular-nums;
}
.figure-md {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 25px; line-height: 1.1;
  font-feature-settings: "tnum"; font-variant-numeric: tabular-nums;
}
.figure-label {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
}

/* Provenance / definition lists — hairline-separated, per the system's
   "hairline dividers carry structure". */
.dl { margin: 0; }
.dl > div {
  display: grid; grid-template-columns: 1fr;
  gap: 0 var(--space-4);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-divider);
}
@media (min-width: 34em) { .dl > div { grid-template-columns: 20ch 1fr; } }
.dl dt {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
}
.dl dd { margin: 0; font-size: 14px; }
.dl dd.mono { word-break: break-all; font-feature-settings: "tnum"; }


/* ── 3. Scrolling table with a pinned identity column ─────────────────────
   Classical's .table has no internal scroll and no sticky column, and the
   weekly table is wider than a phone. Rather than let the page break, the
   table scrolls inside its own box. The identity cell pins so the row never
   loses its name. The pinned cell needs an opaque ground to cover the cells
   sliding under it — that ground is var(--color-bg), a surface, not colour
   used as fill; the seam is marked with the system's hairline. */

.scroller {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border-bottom: 1px solid var(--color-divider);
}
.scroller > .table { min-width: 100%; }
.scroller .table th.stick, .scroller .table td.stick {
  position: sticky; left: 0; z-index: 1;
  background: var(--color-bg);
  border-right: 1px solid var(--color-divider);
}
.scroll-note {
  font-size: 11px; margin-top: var(--space-1);
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
@media (min-width: 60em) { .scroll-note { display: none; } }

.who { display: block; }
.who b { font-weight: var(--font-heading-weight); font-family: var(--font-heading); font-size: 16px; }
.who small {
  display: block; font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}


/* ── 4. The component ramp ────────────────────────────────────────────────
   Nine additive, signed components per player. Classical forbids colour as
   fill, which rules out the usual stacked bar. Instead:

     · IDENTITY is carried by POSITION. Nine fixed slots, always drawn, always
       in the same order, so slot 2 is always "goals" whether or not it has a
       value. A monochrome value ramp cannot separate nine categories; position
       can, and it costs no colour at all.
     · MAGNITUDE is carried by the LENGTH of a narrow rule — a stroke, the
       system's sanctioned use of colour, not a filled block. One shared scale
       across every player on the page, so rows are directly comparable, and
       hairline gridlines every 1.00 xP so a length can be READ and not merely
       compared.
     · SIGN is carried twice over: positives rise above a zero baseline,
       negatives descend below it, AND negatives are drawn dotted rather than
       solid. The redundancy means sign survives greyscale printing and
       colour-vision deficiency without spending a second hue — which the mono
       scheme does not have to spend.

   The negative half is shorter than the positive half only because the data's
   negative extent is smaller; both halves are drawn at the SAME points-per-
   pixel rate, so a rule of a given length means the same magnitude either way.

   Three weights of furniture, so the eye can rank them: the zero baseline is
   the strongest line, the 1.00-xP gridlines are hairlines, and the per-slot
   vertical guides are fainter still — present only so an empty slot can still
   be counted. */

.ramp {
  --ramp-max: 3.2048;                            /* largest |component| on the page */
  --ramp-pos: calc(var(--space-8) * 1.5);        /* room for the full --ramp-max */
  --ramp-neg: calc(var(--ramp-pos) * 0.375);     /* same rate, 1.20 xP of room */
  --ramp-unit: calc(var(--ramp-pos) / var(--ramp-max));   /* one xP, in px */
  --ramp-slot: var(--space-8);                   /* slot pitch */
  position: relative;
  display: grid;
  grid-template-columns: repeat(9, var(--ramp-slot));
  grid-template-rows: var(--ramp-pos) var(--ramp-neg);
  width: max-content;
}

/* Gridlines and slot guides, painted first so the strokes sit over them.
   Weighted so the eye ranks them: the 1.00-xP gridlines are hairlines in the
   system's divider colour and stop at the baseline; the per-slot guides are a
   step fainter again (neutral-200) and live only in the positive half, present
   just so an empty slot can still be counted without hunting for the header. */
.ramp::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(to right,
      transparent 0 calc(var(--ramp-slot) / 2 - 0.5px),
      var(--color-neutral-200) calc(var(--ramp-slot) / 2 - 0.5px) calc(var(--ramp-slot) / 2 + 0.5px),
      transparent calc(var(--ramp-slot) / 2 + 0.5px) var(--ramp-slot)),
    repeating-linear-gradient(to top,
      var(--color-divider) 0 1px, transparent 1px var(--ramp-unit));
  background-size: 100% var(--ramp-pos), 100% var(--ramp-pos);
  background-position: top left, top left;
  background-repeat: no-repeat;
}

/* The zero baseline — the strongest line in the figure. */
.ramp::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: var(--ramp-pos); height: 1px;
  background: var(--color-neutral-500);
}

.ramp i {
  position: relative;                            /* paints above the gridlines */
  display: block; width: var(--ramp-stroke); margin-inline: auto;
  height: calc(var(--m) * var(--ramp-pos));      /* --m is the datum, 0..1 */
}
.ramp i.up   { grid-row: 1; align-self: end;   background: var(--color-accent-700); }
.ramp i.down { grid-row: 2; align-self: start;
  background: repeating-linear-gradient(var(--color-accent-700) 0 var(--ramp-stroke),
              transparent var(--ramp-stroke) calc(var(--ramp-stroke) * 2));
}

.ramp-head {
  display: grid;
  grid-template-columns: repeat(9, var(--space-8));
  width: max-content;
  font-size: 10px; text-align: center;           /* the .card-kicker step */
  font-feature-settings: "tnum";
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.ramp-key { font-size: 12px; }
.ramp-key ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3);
}
.ramp-key li { white-space: nowrap; }
.ramp-key b {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-feature-settings: "tnum";
  color: var(--color-accent-700);
}

/* The same two marks at legend size, so the key shows the actual strokes. */
.swatch {
  display: inline-block; width: var(--ramp-stroke); height: var(--space-3);
  vertical-align: -2px;
}
.swatch-up { background: var(--color-accent-700); }
.swatch-down { background: repeating-linear-gradient(
  var(--color-accent-700) 0 var(--ramp-stroke),
  transparent var(--ramp-stroke) calc(var(--ramp-stroke) * 2)); }


/* ── 5. Column widths for the squad tables ────────────────────────────────
   Classical has no opinion on table layout, and the browser's default
   algorithm inverts the hierarchy here: it hands ~540px to five columns whose
   longest string is "£12.0m" and leaves 69px for the nine-slot ramp, which is
   the table's primary display. Fixed layout with an explicit colgroup puts the
   space where the information is. The ramp column takes no width of its own —
   it absorbs whatever the others do not need. */

/* Specificity note: .scroller > .table above also sets min-width, so this
   selector has to out-rank it or the ramp column collapses to zero. */
.scroller > .table.t-squad { table-layout: fixed; width: 100%; min-width: 968px; }
.t-squad .c-who  { width: 260px; }
.t-squad .c-nar  { width: 62px; }   /* £12.0m, 51.1% */
.t-squad .c-mid  { width: 72px; }   /* GW1 XP header */
.t-squad .c-wide { width: 82px; }   /* 5-GW XP, P(START) headers */
/* .c-ramp is deliberately unsized. */

@media (max-width: 46em) {
  /* On a phone the table scrolls anyway, so trade some ramp pitch and some
     identity width for a shorter scroll. The scale is unchanged — only the
     slot pitch narrows, so lengths still mean what they mean. */
  .ramp { --ramp-slot: var(--space-6); }
  .ramp-head { grid-template-columns: repeat(9, var(--space-6)); }
  .scroller > .table.t-squad { min-width: 800px; }
  .t-squad .c-who { width: 190px; }
}


/* ── disclosure ───────────────────────────────────────────────────────────
   One disclosure for the full-precision figures, not one per player. <details>
   needs no JavaScript and, in a browser that does not support it, simply shows
   its contents — which is the safe failure for a page about not hiding things. */

.reveal { border-top: 1px solid var(--color-divider); padding-top: var(--space-3); }
.reveal > summary {
  cursor: pointer; list-style: none;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; color: var(--color-accent-700);
  padding: var(--space-1) 0;
}
.reveal > summary::-webkit-details-marker { display: none; }
.reveal > summary::before { content: "+ "; }
.reveal[open] > summary::before { content: "– "; }
.reveal > summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.reveal > div { padding-top: var(--space-3); }

/* An empty state that shows the shape of what will fill it. */
.awaiting td {
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
  font-style: italic;
}
.footer {
  margin-top: var(--space-8); padding-top: var(--space-4);
  border-top: 1px solid var(--color-divider);
  font-size: 12px;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}


/* ── 6. The per-position disagreement report ──────────────────────────────
   The only place on the page where the model is measured against something
   outside itself, so the two delta columns are the point of the section and
   get the room. Deliberately NO component ramp here: the ramp answers "where
   do this player's points come from", which is not this section's question,
   and thirty-two more of them would both dwarf the fifteen and make the page's
   one picture ordinary. The components for these rows are in the full-precision
   disclosure at the foot of the page instead.

   The delta gauge reuses the ramp's grammar rather than inventing a second
   one: a zero line, side-of-the-line for sign, length for magnitude, drawn as
   a stroke in the same accent-700. Left of the tick means a negative delta —
   we rank the player above where the field does. One shared scale across both
   columns and all four positions, so every stroke on the page is comparable.
   The ramp's dotted texture is not repeated here: in the ramp it is redundant
   reinforcement of a sign the position already gives, and at this size a
   dotted 2px rule would only read as a lighter one. */

.t-disagree { }
.scroller > .table.t-disagree {
  table-layout: fixed; width: 100%; min-width: 964px;
}
.t-disagree .c-who   { width: 230px; }
.t-disagree .c-money { width: 58px; }
.t-disagree .c-xp    { width: 66px; }
.t-disagree .c-xph   { width: 74px; }
.t-disagree .c-ep    { width: 74px; }
.t-disagree .c-own   { width: 62px; }
.t-disagree .c-delta { width: 200px; }

.dcell { display: block; }
.dnum {
  display: block; text-align: right;
  font-feature-settings: "tnum"; font-variant-numeric: tabular-nums;
}
.dnum .drank { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.dnum .ddelta { display: inline-block; min-width: 6ch; }

/* The gauge itself. A horizontal rule wants about a third of the vertical
   stroke's weight to read as a rule rather than a bar, so it is derived from
   --ramp-stroke rather than being a new number. */
.gauge {
  display: block;                      /* an inline span cannot carry a height */
  position: relative; height: calc(var(--ramp-stroke) / 2);
  margin-top: var(--space-1);
}
.gauge::before {
  /* The zero tick. Same colour as the ramp's zero baseline, because it is the
     same thing: the line a stroke's side is measured against. Down a column
     the ticks line up, so the eye reads one axis rather than eight. */
  content: ""; position: absolute; left: calc(50% - 0.5px);
  top: -4px; height: calc(100% + 8px); width: 1px;
  background: var(--color-neutral-500);
}
.gauge i { position: absolute; top: 0; bottom: 0; background: var(--color-accent-700); }
.gauge i.neg { right: 50%; width: calc(var(--g) * 50%); }
.gauge i.pos { left: 50%;  width: calc(var(--g) * 50%); }

/* Air between one position block and the next. */
.scroll-note + h3, .scroller + h3, .card + h3 { margin-top: var(--space-8); }

/* A note card inside a wide section still wants the reading measure. */
.card-note { max-width: 66ch; }

.d-rankcol {
  font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.who .ord {
  font-family: var(--font-heading); font-feature-settings: "tnum";
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
  margin-right: var(--space-1);
}

@media (max-width: 46em) {
  .scroller > .table.t-disagree { min-width: 880px; }
  .t-disagree .c-who { width: 175px; }
  .t-disagree .c-delta { width: 165px; }
}
