/* PUBLIC DESIGN SYSTEM (P4z) -- the platform's and every league's PUBLIC
   pages, and nothing else.
   ==========================================================================

   THREE THEME SCOPES EXIST, and this file owns two of them: .pub-platform
   (the platform's own front door and the /l directory) and .pub-league (a
   league's site, on /l/<slug> or on its own domain). THE AUTHENTICATED APP
   IS UNTOUCHED BY CONSTRUCTION: every rule below is nested under one of the
   two scope classes, which only publicLayout ever puts on <html>.

   TOKEN NAMES ARE TEMPLUI'S CANONICAL SET (--background, --card,
   --foreground, --primary, --muted-foreground, --border, ...). That is not
   cosmetic: the public league tree still renders templUI components
   (standings tables, cards, buttons), whose compiled Tailwind utilities read
   exactly these variables. Redefining the canonical names under the scope
   class repaints those components in the league's colors for free -- an
   ad-hoc vocabulary would have left them in the app's palette.

   ONE TOKEN IS OURS AND SAYS SO: --pub-accent. templUI's --accent is a
   SUBTLE HOVER SURFACE, not a brand hue, and putting the league's gold in it
   would turn every component hover state gold. The public layer's second
   brand color therefore gets its own name.

   TWO SCOPES, TWO MODE RULES (P4z amendment 2026-08-26):

   BOTH SCOPES ARE THREE-STATE (P4z amendment 2026-08-26 + addendum two):
   the visitor's SYSTEM PREFERENCE by default, overridable in both directions
   from the toggle in the header. The scopes differ only in which mode is the
   UNGUARDED BASE, and that choice is load-bearing rather than cosmetic --
   IT IS WHAT A VISITOR GETS WHEN NO PREFERENCE IS DETERMINABLE (an old
   browser, no media-query support, no signal at all):

     - LEAGUE IS LIGHT-BASE. Undeterminable means LIGHT, the legible-first
       ruling for this audience. Never restructure this scope dark-base.
     - PLATFORM IS DARK-BASE. Undeterminable means DARK, which is the brand.

   The attribute comes from the ONE external script these pages load
   (assets/js/pubmode.js), the single deliberate exception to script-free
   public pages. With scripting off no attribute is ever set, which lands on
   the system preference with an inert toggle -- a supported state.

   NO "no-preference" MEDIA QUERY ANYWHERE. Dark applies to a league only
   behind an affirmative dark signal, light applies to the platform only
   behind an affirmative light one; absence of a signal is the base block.

   Both stay CDN-cacheable: the mode lives in the visitor's own localStorage,
   never in the response, so one cached document serves every visitor.

   SELECTORS ARE html.pub-*, NOT .pub-*. Specificity (0,1,1) beats both
   `:root` in the design-system stylesheet and a theme's `.dark` block, so the
   scope wins regardless of the order the two files happen to load in. THE
   PER-LEAGUE PALETTE <style> publicLayout emits uses the same selector and
   comes later in the document, which is what lets a catalog choice override
   the default set below.

   TYPE IS SYSTEM STACKS ONLY (CSP: no external font hosts).
   WCAG AA IS A GATE: body >= 4.5:1, large text >= 3:1, in all four
   scope x mode combinations. The ratios are recorded in the P4z report.
   NO EMOJI, NO ICON FONTS, NO EM-DASHES IN PUBLIC COPY. */

/* == LEAGUE SCOPE -- "Club Navy", the default curated set ================== */

html.pub-league {
  color-scheme: light;

  --background: #f9f9f6;
  --foreground: #1a2233;
  --card: #ffffff;
  --card-foreground: #1a2233;
  --popover: #ffffff;
  --popover-foreground: #1a2233;
  --primary: #1e3a6e;
  --primary-foreground: #ffffff;
  --secondary: #f1f1ea;
  --secondary-foreground: #1a2233;
  --muted: #f1f1ea;
  --muted-foreground: #57627a;
  /* --accent stays a SURFACE (templUI semantics); the brand's second hue is
     --pub-accent below. */
  --accent: #f1f1ea;
  --accent-foreground: #1a2233;
  --destructive: #a32219;
  --destructive-foreground: #ffffff;
  --success: #1a7a3f;
  --success-foreground: #ffffff;
  --border: #e4e4dd;
  --input: #e4e4dd;
  --ring: #1e3a6e;
  --radius: 0.625rem;
  --shadow-sm: 0 1px 2px rgba(26, 34, 51, 0.06);
  --shadow: 0 1px 2px rgba(26, 34, 51, 0.06);

  /* Club Navy's gold. Deep enough to read as body-adjacent text on both the
     page and card backgrounds -- the mockup's decorative gold was lightened
     until it passed AA rather than kept and excused. */
  --pub-accent: #7a5a10;
  --pub-accent-foreground: #ffffff;

  /* THE HERO IS MODE-INDEPENDENT. A photo behind a navy wash reads the same
     in both modes, and white-on-navy is the one pair that must not move when
     a visitor's OS does -- so the hero carries its own two tokens rather than
     inheriting --background/--foreground. A league with no hero photo gets
     exactly this color and no photo layers. */
  --pub-hero-bg: #16233d;
  --pub-hero-fg: #ffffff;
  --pub-hero-muted: rgba(255, 255, 255, 0.88);
}

/* LEAGUE DARK IS THREE-STATE (P4z amendment, re-ruled 2026-08-26): the
   VISITOR'S SYSTEM PREFERENCE by default, and an EXPLICIT OVERRIDE that beats
   it in BOTH directions.

   THE SAME DECLARATIONS ARE WRITTEN TWICE, and the duplication is the design:

     1. @media (prefers-color-scheme: dark), guarded
        html.pub-league:not([data-pubmode="light"]) -- the system default, which
        an explicit LIGHT choice switches off.
     2. html.pub-league[data-pubmode="dark"] -- the explicit dark choice, which
        applies with no media query at all, so it wins on a light system.

   NEITHER RULE IS ENOUGH ALONE. The media query alone cannot be overridden to
   light; the attribute alone cannot follow the OS. Both together are what makes
   the override beat the OS in both directions.

   NO STORED CHOICE MEANS NO ATTRIBUTE. assets/js/pubmode.js sets data-pubmode
   only when the visitor has actually chosen, so the ordinary page is pure
   system preference -- and WITH SCRIPTING OFF the attribute is never set at
   all, which lands on exactly that same state with an inert toggle.

   SPECIFICITY: both dark blocks are (0,2,1) and beat the light block's (0,1,1)
   whatever the source order. The per-league palette <style> emits the SAME
   three selectors and comes later in the document, so a catalog choice still
   wins in every state. */
@media (prefers-color-scheme: dark) {
  html.pub-league:not([data-pubmode="light"]) {
    color-scheme: dark;

    --background: #10141c;
    --foreground: #e9ecf1;
    --card: #171d28;
    --card-foreground: #e9ecf1;
    --popover: #171d28;
    --popover-foreground: #e9ecf1;
    --primary: #8fb0e8;
    --primary-foreground: #0d1420;
    --secondary: #1c2431;
    --secondary-foreground: #e9ecf1;
    --muted: #1c2431;
    --muted-foreground: #9aa5ba;
    --accent: #1c2431;
    --accent-foreground: #e9ecf1;
    --destructive: #f0928a;
    --destructive-foreground: #0d1420;
    --success: #7bd39a;
    --success-foreground: #0d1420;
    --border: #252d3b;
    --input: #252d3b;
    --ring: #8fb0e8;
    --shadow-sm: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
    --shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;

    --pub-accent: #d9b45c;
    --pub-accent-foreground: #0d1420;

    --pub-hero-bg: #0d1420;
  }
}

html.pub-league[data-pubmode="dark"] {
  color-scheme: dark;

  --background: #10141c;
  --foreground: #e9ecf1;
  --card: #171d28;
  --card-foreground: #e9ecf1;
  --popover: #171d28;
  --popover-foreground: #e9ecf1;
  --primary: #8fb0e8;
  --primary-foreground: #0d1420;
  --secondary: #1c2431;
  --secondary-foreground: #e9ecf1;
  --muted: #1c2431;
  --muted-foreground: #9aa5ba;
  --accent: #1c2431;
  --accent-foreground: #e9ecf1;
  --destructive: #f0928a;
  --destructive-foreground: #0d1420;
  --success: #7bd39a;
  --success-foreground: #0d1420;
  --border: #252d3b;
  --input: #252d3b;
  --ring: #8fb0e8;
  --shadow-sm: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;

  --pub-accent: #d9b45c;
  --pub-accent-foreground: #0d1420;

  --pub-hero-bg: #0d1420;
}

/* == PLATFORM SCOPE -- "floodlight" ======================================= */

html.pub-platform {
  color-scheme: dark;

  --background: #101613;
  --foreground: #e8ede9;
  --card: #161d19;
  --card-foreground: #e8ede9;
  --popover: #161d19;
  --popover-foreground: #e8ede9;
  --primary: #46c46e;
  --primary-foreground: #0b120d;
  --secondary: #1b2420;
  --secondary-foreground: #e8ede9;
  --muted: #1b2420;
  --muted-foreground: #96a29b;
  --accent: #1b2420;
  --accent-foreground: #e8ede9;
  --destructive: #f0928a;
  --destructive-foreground: #0b120d;
  --success: #46c46e;
  --success-foreground: #0b120d;
  --border: #242d27;
  --input: #242d27;
  --ring: #46c46e;
  --radius: 0.625rem;
  --shadow-sm: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;

  --pub-accent: #46c46e;
  --pub-accent-foreground: #0b120d;

  --pub-hero-bg: #0b120d;
  --pub-hero-fg: #ffffff;
  --pub-hero-muted: rgba(255, 255, 255, 0.88);
}

/* THE PLATFORM'S LIGHT HALF, three-state exactly as the league's dark half is
   and for the same reasons -- only the base mode differs. The media-query copy
   is guarded so an explicit DARK choice switches it off; the attribute copy
   carries no media query, so an explicit LIGHT choice wins on a dark system.
   THE PLATFORM'S BASE STAYS DARK: no system signal means the brand's dark. */
@media (prefers-color-scheme: light) {
  html.pub-platform:not([data-pubmode="dark"]) {
    color-scheme: light;

    --background: #fafaf7;
    --foreground: #1b221e;
    --card: #ffffff;
    --card-foreground: #1b221e;
    --popover: #ffffff;
    --popover-foreground: #1b221e;
    --primary: #146334;
    --primary-foreground: #ffffff;
    --secondary: #f1f3ef;
    --secondary-foreground: #1b221e;
    --muted: #f1f3ef;
    --muted-foreground: #4d574f;
    --accent: #f1f3ef;
    --accent-foreground: #1b221e;
    --destructive: #a32219;
    --destructive-foreground: #ffffff;
    --success: #146334;
    --success-foreground: #ffffff;
    --border: #e2e5e1;
    --input: #e2e5e1;
    --ring: #146334;
    --shadow-sm: 0 1px 2px rgba(27, 34, 30, 0.06);
    --shadow: 0 1px 2px rgba(27, 34, 30, 0.06);

    --pub-accent: #146334;
    --pub-accent-foreground: #ffffff;
  }
}

html.pub-platform[data-pubmode="light"] {
  color-scheme: light;

  --background: #fafaf7;
  --foreground: #1b221e;
  --card: #ffffff;
  --card-foreground: #1b221e;
  --popover: #ffffff;
  --popover-foreground: #1b221e;
  --primary: #146334;
  --primary-foreground: #ffffff;
  --secondary: #f1f3ef;
  --secondary-foreground: #1b221e;
  --muted: #f1f3ef;
  --muted-foreground: #4d574f;
  --accent: #f1f3ef;
  --accent-foreground: #1b221e;
  --destructive: #a32219;
  --destructive-foreground: #ffffff;
  --success: #146334;
  --success-foreground: #ffffff;
  --border: #e2e5e1;
  --input: #e2e5e1;
  --ring: #146334;
  --shadow-sm: 0 1px 2px rgba(27, 34, 30, 0.06);
  --shadow: 0 1px 2px rgba(27, 34, 30, 0.06);

  --pub-accent: #146334;
  --pub-accent-foreground: #ffffff;
}

/* == THE DOCUMENT ========================================================= */

html.pub-league,
html.pub-platform {
  --pub-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
}

html.pub-league .pub-body,
html.pub-platform .pub-body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--pub-font);
  -webkit-font-smoothing: antialiased;
}

/* 17.5px/1.65 for a league site, 16px/1.6 for the platform: the legibility
   ruling is about the audience of the league pages, not about the product
   page a site manager reads once. */
html.pub-league .pub-body {
  font-size: 17.5px;
  line-height: 1.65;
}

html.pub-platform .pub-body {
  font-size: 16px;
  line-height: 1.6;
}

html.pub-league .pub-main,
html.pub-platform .pub-main {
  flex: 1 0 auto;
}

html.pub-league a,
html.pub-platform a {
  color: inherit;
}

html.pub-league .pub-wrap,
html.pub-platform .pub-wrap {
  width: 100%;
  margin: 0 auto;
  padding: 0 20px;
}

html.pub-league .pub-wrap {
  max-width: 980px;
}

html.pub-platform .pub-wrap {
  max-width: 1160px;
}

/* == HEADER / FOOTER ======================================================
   The league header carries the site nav WP-C shipped: Schedule / Standings /
   Teams / Rules / About, a Register CTA, the light-dark changer, and a
   CSS-ONLY mobile disclosure. There is no script behind any of it except the
   changer, whose whole job is one attribute on <html>. */

html.pub-league .pub-header {
  border-bottom: 3px solid var(--primary);
  background: var(--card);
}

html.pub-platform .pub-header {
  border-bottom: 1px solid var(--border);
}

html.pub-league .pub-header .pub-wrap,
html.pub-platform .pub-header .pub-wrap {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 64px;
  flex-wrap: wrap;
}

html.pub-league .pub-brand,
html.pub-platform .pub-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  margin-right: auto;
  padding: 10px 0;
}

/* The crest chip is the league's INITIALS, derived server-side. It is not a
   logo slot: an uploaded crest arrives with the media WP. */
html.pub-league .pub-crest {
  width: 40px;
  height: 40px;
  border-radius: 9px;
  background: var(--primary);
  color: var(--primary-foreground);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 0.02em;
  flex: none;
}

html.pub-league .pub-brand-name,
html.pub-platform .pub-brand-name {
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.015em;
  line-height: 1.2;
}

html.pub-platform .pub-brand-name {
  font-size: 17px;
  letter-spacing: -0.02em;
}

html.pub-platform .pub-brand-name .pub-dot {
  color: var(--primary);
}

html.pub-league .pub-brand-sub,
html.pub-platform .pub-brand-sub {
  color: var(--muted-foreground);
  font-size: 12.5px;
}

html.pub-league .pub-footer,
html.pub-platform .pub-footer {
  border-top: 1px solid var(--border);
  padding: 26px 0 40px;
  margin-top: 8px;
}

html.pub-league .pub-footer .pub-wrap,
html.pub-platform .pub-footer .pub-wrap {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  color: var(--muted-foreground);
  font-size: 13.5px;
}

/* == THE LEAGUE SITE NAV (P4z WP-C) =======================================
   Five destinations and a CTA, translated from mock-league.html. TWO COPIES
   OF THE SAME LINKS ship in the markup -- the row and the disclosure panel --
   because the alternative is a script that moves nodes around, and this nav
   has to work with scripting off. Only one copy is ever displayed.

   THE ACTIVE PAGE IS MARKED WITH aria-current="page" and drawn with the
   brand's second hue under it. The attribute is the fact; the underline is
   the rendering of it, so a screen reader and an eye are told the same thing
   from one source. */

html.pub-league .pub-nav {
  display: flex;
  align-items: center;
  gap: 22px;
  font-size: 15px;
}

html.pub-league .pub-nav a {
  color: var(--muted-foreground);
  text-decoration: none;
  font-weight: 550;
}

html.pub-league .pub-nav a:hover,
html.pub-league .pub-nav a[aria-current] {
  color: var(--foreground);
}

html.pub-league .pub-nav a[aria-current] {
  border-bottom: 2px solid var(--pub-accent);
  padding-bottom: 2px;
}

html.pub-league .pub-nav a.pub-nav-cta,
html.pub-league .pub-mobilenav-panel a.pub-nav-cta {
  background: var(--primary);
  /* NOT var(--foreground): the CTA is a filled chip and its text has to pair
     with the FILL, which is the brand color in both modes. */
  color: var(--primary-foreground);
  font-weight: 650;
  padding: 8px 18px;
  border-radius: 8px;
  border-bottom: 0;
}

html.pub-league .pub-nav a.pub-nav-cta:hover {
  color: var(--primary-foreground);
  opacity: 0.92;
}

html.pub-league .pub-footer a.pub-footer-btn,
html.pub-platform .pub-footer a.pub-footer-btn {
  display: inline-block;
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 4px 14px;
  font-weight: 600;
  color: var(--foreground);
}

html.pub-league .pub-footer a.pub-footer-btn:hover,
html.pub-platform .pub-footer a.pub-footer-btn:hover {
  border-color: var(--primary);
  color: var(--primary);
}

/* -- the team season TABLE (Bill, 2026-08-26): a real table, header row,
   copy-paste-able; wide content scrolls in its own container. */
html.pub-league .pub-team-scroll {
  overflow-x: auto;
}

html.pub-league .pub-team-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}

html.pub-league .pub-team-table th {
  text-align: left;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

html.pub-league .pub-team-table td {
  padding: 10px 12px 10px 0;
  border-bottom: 1px solid var(--border);
  vertical-align: baseline;
}

html.pub-league .pub-team-table tr:last-child td {
  border-bottom: 0;
}

html.pub-league .pub-team-date,
html.pub-league .pub-team-clock {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--foreground);
}

html.pub-league .pub-team-opp {
  font-weight: 650;
  white-space: nowrap;
}

html.pub-league .pub-team-ha {
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

html.pub-league .pub-team-result {
  white-space: nowrap;
}

html.pub-league .pub-team-wdl {
  font-weight: 750;
  margin-right: 6px;
}

html.pub-league .pub-team-score {
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-team-field a {
  color: var(--primary);
  font-weight: 550;
  text-decoration: none;
}

html.pub-league .pub-team-row--cancelled td {
  color: var(--muted-foreground);
}

/* -- the light/dark changer (BOTH SCOPES) ----------------------------------
   A REAL <button> with aria-pressed, not a checkbox and not a link: it changes
   the state of this page and nothing else, which is what a toggle button is.
   THE ACCESSIBLE NAME NEVER CHANGES ("Dark mode") -- the PRESSED STATE is the
   state, and a control whose name flips between "Dark" and "Light" is one
   nobody can describe over the phone.

   IT IS THE ONE ICON IN THIS STYLESHEET. The no-emoji, no-icon-font rule
   stands everywhere else on these pages; a mode changer is the single control
   whose meaning is carried better by a sun and a moon than by a word, and both
   are hand-drawn inline SVG (no icon font, no external request). Which one is
   shown follows aria-pressed, so the picture and the announced state come from
   ONE attribute and cannot disagree.

   WITH SCRIPTING OFF it renders unpressed and does nothing. That is the
   documented degraded state, not a bug. */

html.pub-league .pub-mode,
html.pub-platform .pub-mode {
  appearance: none;
  cursor: pointer;
  font: inherit;
  line-height: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted-foreground);
}

html.pub-league .pub-mode:hover,
html.pub-platform .pub-mode:hover {
  color: var(--foreground);
  border-color: var(--muted-foreground);
}

html.pub-league .pub-mode[aria-pressed="true"],
html.pub-platform .pub-mode[aria-pressed="true"] {
  color: var(--foreground);
  border-color: var(--pub-accent);
}

html.pub-league .pub-mode:focus-visible,
html.pub-platform .pub-mode:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

html.pub-league .pub-mode svg,
html.pub-platform .pub-mode svg {
  width: 17px;
  height: 17px;
  display: block;
}

/* ONE ATTRIBUTE DECIDES WHICH GLYPH IS DRAWN. Both ship in the markup; the
   hidden one is display:none rather than opacity, so it is out of the
   accessibility tree as well as out of sight. */
html.pub-league .pub-mode[aria-pressed="false"] .pub-mode-moon,
html.pub-platform .pub-mode[aria-pressed="false"] .pub-mode-moon,
html.pub-league .pub-mode[aria-pressed="true"] .pub-mode-sun,
html.pub-platform .pub-mode[aria-pressed="true"] .pub-mode-sun {
  display: none;
}

/* -- the mobile disclosure -------------------------------------------------
   A <details> panel. No script, no ARIA of our own: a summary is already a
   button that owns its panel, and every browser announces it. */

html.pub-league .pub-mobilenav {
  display: none;
}

html.pub-league .pub-mobilenav summary {
  list-style: none;
  cursor: pointer;
  font-weight: 650;
  font-size: 15px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
}

html.pub-league .pub-mobilenav summary::-webkit-details-marker {
  display: none;
}

html.pub-league .pub-mobilenav[open] summary {
  border-color: var(--primary);
}

html.pub-league .pub-mobilenav-panel {
  position: absolute;
  right: 20px;
  margin-top: 8px;
  z-index: 10;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
  padding: 8px 0;
  min-width: 12rem;
}

html.pub-league .pub-mobilenav-panel a {
  display: block;
  padding: 10px 18px;
  text-decoration: none;
  color: var(--foreground);
  font-size: 16px;
}

html.pub-league .pub-mobilenav-panel a:hover {
  background: var(--secondary);
}

html.pub-league .pub-mobilenav-panel a.pub-nav-cta {
  margin: 8px 14px 4px;
  text-align: center;
}

@media (max-width: 860px) {
  html.pub-league .pub-nav {
    display: none;
  }

  html.pub-league .pub-mobilenav {
    display: block;
  }
}

/* == THE PHOTO CREDIT (P4z amendment) =====================================
   CREDITS RENDER IN THE IMAGE, never in a footer roster: the credit belongs
   to the photograph, so it rides the photograph -- static on the league's
   single-photo hero, and inside each frame of the platform carousel, where
   it fades with the frame it names.
   IT IS A LOWER-THIRD CHIP (Bill, 2026-08-26): white-at-half-opacity with a
   drop shadow was legible over a dark wash and gone over a bright patch of
   sky, because a text-shadow only helps where the pixels behind it are dark.
   The chip brings its OWN background, so the credit's contrast no longer
   depends on which photograph is underneath it or which frame of the carousel
   is showing -- the one treatment that is invariant across six images. The
   shadow is dropped: the chip does that job now, and a shadow under a filled
   chip only smears its edge.
   Position, size and letter-spacing are the amendment's, verbatim. It stays
   deliberately QUIET -- an attribution, not a caption. */

html.pub-league .pub-photo-credit,
html.pub-platform .pub-photo-credit {
  position: absolute;
  right: 12px;
  bottom: 8px;
  z-index: 2;
  font-size: 10.5px;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.85);
  background: rgba(8, 12, 20, 0.7);
  border-radius: 5px;
  padding: 2px 8px;
  pointer-events: none;
}

/* == HERO =================================================================
   FULL BLEED, and the photo is OPTIONAL. A league with no hero image renders
   the same box painted from --pub-hero-bg with no photo layers at all -- not
   a grey placeholder, not a broken <img>. The photo, when there is one,
   arrives as --pub-hero-photo from publicLayout's own <style> block (a
   per-league operator-deployed asset; the upload UI comes with the media WP,
   and a self-hosted <video muted loop> can take the same slot later). */

html.pub-league .pub-hero,
html.pub-platform .pub-hero {
  position: relative;
  overflow: hidden;
  background: var(--pub-hero-bg);
}

html.pub-league .pub-hero-media {
  position: absolute;
  inset: 0;
  /* The club-navy wash rides translucently over the photo, pulling the pitch
     green toward the site palette. Action sits top-right in the shipped
     image, so it anchors right and the scrim carries the left half where the
     headline lives. */
  background-image:
    radial-gradient(90% 70% at 75% 30%, rgba(143, 176, 232, 0.14), transparent 60%),
    linear-gradient(160deg, rgba(27, 42, 74, 0.52) 0%, rgba(18, 32, 60, 0.44) 45%, rgba(13, 24, 48, 0.58) 100%),
    var(--pub-hero-photo, none);
  background-size: cover, cover, cover;
  background-position: center, center, 75% 30%;
  background-repeat: no-repeat;
}

/* THE SCRIM AT HALF STRENGTH (P4z amendment: ~.44 at the left edge, ~.21 at
   the right). The photograph a league deployed is the point of the band, and
   the round-one values were drowning it; the hero's type carries a
   compensating shadow instead, which is the other half of the same ruling. */
html.pub-league .pub-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(13, 20, 32, 0.44) 0%, rgba(13, 20, 32, 0.3) 55%, rgba(13, 20, 32, 0.21) 100%);
}

html.pub-league .pub-hero .pub-wrap,
html.pub-platform .pub-hero .pub-wrap {
  position: relative;
  padding-top: 72px;
  padding-bottom: 72px;
}

html.pub-league .pub-hero h1 {
  font-size: clamp(1.8rem, 4.5vw, 2.7rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-weight: 750;
  margin: 0 0 14px;
  max-width: 21ch;
  text-wrap: balance;
  color: var(--pub-hero-fg);
  /* PAYS FOR THE HALVED WASH (P4z amendment). The hero's tokens are already
     mode-invariant; the shadow is what makes white type hold over whatever
     pixel of a league's own photograph happens to sit behind a letter. */
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.45);
}

/* THE HERO BODY IS THE FROZEN body_html leagueadmin rendered at save time.
   It is styled, never rewritten. */
html.pub-league .pub-hero-body {
  max-width: 54ch;
  color: var(--pub-hero-muted);
  font-size: 1.06rem;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.55), 0 1px 2px rgba(0, 0, 0, 0.4);
}

html.pub-league .pub-hero-body p {
  margin: 0 0 0.6rem;
}

html.pub-league .pub-hero-body p:last-child {
  margin-bottom: 0;
}

html.pub-league .pub-hero-body a {
  color: var(--pub-hero-fg);
  text-decoration: underline;
}

/* THE HERO'S ACTION ROW (Bill, 2026-08-26): Register + Sign in live in the
   welcome moment. Explicit colors only -- these sit on a photograph. */
html.pub-league .pub-hero-ctas {
  display: flex;
  gap: 14px;
  margin-top: 24px;
  flex-wrap: wrap;
}

html.pub-league .pub-hero-btn {
  display: inline-block;
  text-decoration: none;
  font-weight: 650;
  font-size: 16px;
  padding: 11px 24px;
  border-radius: 9px;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

html.pub-league .pub-hero-btn:active {
  transform: translateY(1px);
}

html.pub-league .pub-hero-btn-primary {
  background: #1e3a6e;
  color: #ffffff;
  /* THE DARK SEPARATION RING (Bill): a navy fill can melt into a dark patch
     of photograph; the ring plus a soft drop shadow cuts the button free of
     whatever pixels sit behind it. Same job the platform hero's primary does
     with its bright fill against the dark wash. */
  box-shadow: 0 0 0 1px rgba(8, 12, 20, 0.45), 0 3px 10px rgba(0, 0, 0, 0.3);
}

html.pub-league .pub-hero-btn-primary:hover {
  opacity: 0.92;
}

html.pub-league .pub-hero-btn-ghost {
  color: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(13, 20, 32, 0.38);
}

html.pub-league .pub-hero-btn-ghost:hover {
  border-color: #ffffff;
  background: rgba(13, 20, 32, 0.55);
}

@media (prefers-reduced-motion: reduce) {
  html.pub-league .pub-hero-btn {
    transition: none;
  }
}

/* == BANDS ================================================================ */

html.pub-league .pub-band,
html.pub-platform .pub-band {
  padding: 46px 0;
  border-top: 1px solid var(--border);
}

html.pub-league .pub-band:first-of-type,
html.pub-platform .pub-band:first-of-type {
  border-top: 0;
}

html.pub-league .pub-band h2,
html.pub-platform .pub-band h2 {
  font-size: 1.35rem;
  letter-spacing: -0.015em;
  margin: 0 0 6px;
  color: var(--foreground);
}

html.pub-league .pub-band-lead,
html.pub-platform .pub-band-lead {
  color: var(--muted-foreground);
  margin: 0 0 26px;
  max-width: 60ch;
  font-size: 0.98rem;
}

/* == PROSE ================================================================
   The list and heading semantics the design system's preflight reset strips,
   restored for the ONE place a public page emits stored markup: the frozen
   body_html of an authored section. Scoped to .section-body, the class that
   block has carried since P4x. */

html.pub-league .section-body,
html.pub-platform .section-body {
  color: var(--foreground);
  max-width: 68ch;
}

/* THE HERO WINS OVER .section-body. Same cascade trap that bit the Register
   CTA: equal specificity, later rule -- so the hero's mode-invariant text
   colors are restated AFTER the generic block. Page tokens must never paint
   text that sits on a photograph (the photo-hero law). */
html.pub-league .pub-hero .section-body {
  color: var(--pub-hero-muted);
}

html.pub-league .pub-hero .section-body a {
  color: var(--pub-hero-fg);
}

html.pub-league .section-body ol,
html.pub-platform .section-body ol {
  list-style: decimal;
  padding-left: 1.5rem;
  margin: 0.5rem 0;
}

html.pub-league .section-body ul,
html.pub-platform .section-body ul {
  list-style: disc;
  padding-left: 1.5rem;
  margin: 0.5rem 0;
}

html.pub-league .section-body li,
html.pub-platform .section-body li {
  margin: 0.25rem 0;
}

/* Real paragraph separation (Bill, 2026-08-26): 0.5rem read as line wrap,
   not a new thought -- the About prose especially. */
html.pub-league .section-body p,
html.pub-platform .section-body p {
  margin: 1rem 0;
}

html.pub-league .section-body h1,
html.pub-league .section-body h2,
html.pub-league .section-body h3,
html.pub-platform .section-body h1,
html.pub-platform .section-body h2,
html.pub-platform .section-body h3 {
  font-weight: 600;
  margin: 0.75rem 0 0.25rem;
}

html.pub-league .section-body h1,
html.pub-platform .section-body h1 {
  font-size: 1.15rem;
}

html.pub-league .section-body h2,
html.pub-platform .section-body h2 {
  font-size: 1.05rem;
}

/* SUBSECTIONS BREATHE (Bill, 2026-08-26): an authored body's h3 opens a new
   subsection, and 0.75rem of margin read as one more paragraph. The hairline
   is Bill's "---" idea drawn BY THE STYLESHEET, so every league gets the
   separation without typing rules into its markdown. Later rule wins the
   margin-top over the grouped h1-h3 block above -- deliberate this time. */
html.pub-league .section-body h3,
html.pub-platform .section-body h3 {
  margin-top: 2.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

html.pub-league .section-body h3:first-child,
html.pub-platform .section-body h3:first-child {
  margin-top: 0.75rem;
  padding-top: 0;
  border-top: 0;
}

/* And a hand-typed --- still means something: the reset strips hr entirely,
   which is why pasted separators rendered as nothing. */
html.pub-league .section-body hr,
html.pub-platform .section-body hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 1.5rem 0;
}

html.pub-league .section-body a,
html.pub-platform .section-body a {
  color: var(--primary);
  text-decoration: underline;
}

/* == DIVISIONS: ONE TABLE, FULL-ROW LINKS =================================
   The whole row is the tap target (48px minimum), because a phone user aiming
   at a 14px "Standings" link is a phone user missing. */

html.pub-league .pub-div-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

@media (max-width: 560px) {
  html.pub-league .pub-div-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

html.pub-league .pub-div-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 16px 18px;
  min-height: 48px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: var(--card-foreground);
  transition: border-color 0.15s ease, background 0.15s ease;
}

html.pub-league a.pub-div-card:hover {
  border-color: var(--primary);
  background: var(--muted);
}

html.pub-league .pub-div-name {
  font-weight: 700;
  font-size: 1.05rem;
}

html.pub-league .pub-div-teams {
  color: var(--muted-foreground);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-div-go {
  color: var(--primary);
  font-weight: 650;
  font-size: 14.5px;
  white-space: nowrap;
  margin-top: 6px;
}

html.pub-league .pub-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--pub-accent);
  border: 1px solid var(--pub-accent);
  border-radius: 4px;
  padding: 1px 7px;
  margin-left: 10px;
  vertical-align: 2px;
}

html.pub-league .pub-empty,
html.pub-platform .pub-empty {
  color: var(--muted-foreground);
  margin: 0;
}

/* == THE DIVISION PAGE'S TWO SWITCHERS (P4z WP-F) =========================
   CHOICES ARE LINKS, NOT FORMS. Both strips are plain <a> elements: they
   navigate on tap with no script, no round trip through a GET form and no
   second control to press, and every state they can be in is a shareable,
   CDN-cacheable URL. That is the whole ruling, and the CSS only has to make
   the current one obvious.

   THE CURRENT ITEM IS aria-current="page" IN BOTH, drawn from that attribute
   exactly as the site nav's underline is -- one fact, one rendering. */

html.pub-league .pub-div-tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: 22px;
  align-items: flex-end;
  border-bottom: 1px solid var(--border);
  font-size: 15px;
  /* THE STRIP SCROLLS SIDEWAYS RATHER THAN WRAPPING. A league with six
     divisions must not turn into two lines of navigation on a phone -- the
     page's own vertical rhythm is the thing being protected, and a tab strip
     that reflows moves the content under it every time the set changes. The
     overflow is THIS container's, so the page body never scrolls sideways. */
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

html.pub-league .pub-div-tabs::-webkit-scrollbar {
  display: none;
}

html.pub-league .pub-div-tabs a {
  flex: none;
  white-space: nowrap;
  color: var(--muted-foreground);
  text-decoration: none;
  font-weight: 550;
  padding-bottom: 8px;
  /* A TRANSPARENT BORDER ON EVERY TAB, not just the current one: the gold rule
     below then swaps a color rather than adding a box, so nothing shifts by two
     pixels when the selection moves. */
  border-bottom: 2px solid transparent;
}

html.pub-league .pub-div-tabs a:hover {
  color: var(--foreground);
}

html.pub-league .pub-div-tabs a[aria-current] {
  color: var(--foreground);
  border-bottom-color: var(--pub-accent);
}

/* -- the season select ----------------------------------------------------
   THE PILL ROW THIS REPLACED (Bill, 2026-08-30) was right for four seasons
   and wrong for twenty-odd: a badge apiece wrapped into six or seven lines
   above the standings table, which on a phone is most of the first screen,
   and the one thing the row existed to answer -- which season am I on -- got
   harder to read the longer the league's history got. A closed select states
   it in one line.

   THE SELECT IS THE FILLED THING now. It carries the brand border and the
   card ground, and the Switch button beside it is the primary fill: the pair
   reads as one control, and the current season is the text inside it rather
   than a pill somewhere in a wall.

   NO SCRIPT: this is a GET form, so the Switch button is the whole mechanism. */

html.pub-league .pub-season-select {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* When pubmode.js runs it stamps pub-js on <html>; selection then submits
   itself and the Switch button is no-JS fallback only. */
html.pub-js .pub-season-select button {
  display: none;
}

html.pub-league .pub-season-select label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted-foreground);
}

html.pub-league .pub-season-select select {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--foreground);
  background: var(--card);
  max-width: 100%;
}

html.pub-league .pub-season-select select:focus-visible,
html.pub-league .pub-season-select button:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 1px;
}

html.pub-league .pub-season-select button {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 6px 15px;
  border: 1px solid var(--primary);
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-foreground);
  cursor: pointer;
}

html.pub-league .pub-season-select button:hover {
  opacity: 0.92;
}

/* == FEES: CARDS, NOT A HAIRLINE PER ROW ================================== */

html.pub-league .pub-fee-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

html.pub-league .pub-fee-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px 20px;
  color: var(--card-foreground);
}

html.pub-league .pub-fee-who {
  font-weight: 650;
  margin-bottom: 2px;
}

html.pub-league .pub-fee-amt {
  font-size: 1.7rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-fee-per {
  color: var(--muted-foreground);
  font-size: 13.5px;
}

/* THE SECOND PRICE IS NOT A SECOND CARD (D8 as amended): a division may be
   billed flat or per player, and printing one would misquote half the league.
   The per-player line sits under the headline amount, in the same card. */
html.pub-league .pub-fee-alt {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  color: var(--muted-foreground);
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
}

/* == CONTACTS ============================================================= */

html.pub-league .pub-contact-row {
  display: flex;
  gap: 14px;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 8px 0;
  font-size: 16px;
}

html.pub-league .pub-contact-role {
  color: var(--muted-foreground);
  width: 12.5rem;
  font-size: 14px;
}

html.pub-league .pub-link-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

html.pub-league .pub-link-list li {
  padding: 6px 0;
}

html.pub-league .pub-link-list a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
}

html.pub-league .pub-link-list a:hover {
  text-decoration: underline;
}

/* == SECTION PAGES: /rules AND /fields (P4z WP-C) =========================
   Two pages of the league tree that are not the landing and not a fixture
   list. THEY REUSE THE BAND, so a page reads like the landing's sections
   lifted out of it -- which is exactly what /rules is. The page title is an
   h1 in the first band; nothing here invents a second page shell. */

html.pub-league .pub-page-title {
  font-size: clamp(1.6rem, 3.2vw, 2.1rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 700;
  margin: 0 0 6px;
}

/* One venue: the name (linking to its record page), the town-and-street line,
   and the league's map pin. A ROW, NOT A CARD -- a league with twenty fields
   is a list to scan, and the card grid the fees section uses would be twenty
   boxes of two lines each. */
html.pub-league .pub-field-row {
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}

html.pub-league .pub-field-list > .pub-field-row:last-child {
  border-bottom: 0;
}

html.pub-league .pub-field-name {
  font-size: 17px;
  font-weight: 650;
}

html.pub-league .pub-field-name a {
  color: var(--foreground);
  text-decoration: none;
}

html.pub-league .pub-field-name a:hover {
  text-decoration: underline;
}

html.pub-league .pub-field-site {
  color: var(--muted-foreground);
  font-size: 14px;
  margin-left: 8px;
  font-weight: 400;
}

html.pub-league .pub-field-addr {
  color: var(--muted-foreground);
  font-size: 15px;
  margin: 3px 0 0;
}

html.pub-league .pub-field-links {
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-size: 14.5px;
}

html.pub-league .pub-field-links a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
}

html.pub-league .pub-field-links a:hover {
  text-decoration: underline;
}

/* == FIXTURE-ROW GRAMMAR ==================================================
   ONE ROW SHAPE, SITE-WIDE: time | teams | field | division-tag, in that
   order, wherever a fixture renders (this week's list, a division schedule, a
   team page, and the platform's preview card). WP-B reuses these exact class
   names for the derived "thisweek" section.

   THE FIRST COLUMN IS FIXED-WIDTH. Content never sizes it -- that is the
   alignment-drift bug found in the mockup review: one long field name in row
   three and every team name in the list steps sideways. Times are tabular for
   the same reason.

   TEAM NAMES ARE ATOMIC. A name never wraps inside itself; the row wraps
   between names or it scrolls, and "Real Fair-\nfax" is not a thing this site
   prints.

   MOBILE REFLOWS TO TWO LINES: time + teams (teams right-justified, forming a
   right rail), then field (left) + division tag (right) underneath. The
   named grid areas below are what makes that a reflow rather than a second
   markup. */

html.pub-league .pub-fix-list,
html.pub-platform .pub-fix-list {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* The day header inside a fixture list ("Monday, Aug 24"). */
html.pub-league .pub-fix-day,
html.pub-platform .pub-fix-day {
  padding: 11px 18px 7px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  background: var(--muted);
  border-bottom: 1px solid var(--border);
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
}

html.pub-league .pub-fix,
html.pub-platform .pub-fix {
  display: grid;
  /* minmax(0, ...) on the flexible tracks: without it a long venue name in a
     nowrap cell pushes the row wider than the list and gets clipped by the
     list's own overflow. The FIRST track stays fixed -- that is the rule. */
  grid-template-columns: 3.4rem minmax(0, 1fr) minmax(0, auto) auto;
  gap: 12px;
  align-items: baseline;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border);
  font-size: 16px;
  color: var(--card-foreground);
}

/* A DATED variant for the lists that are not grouped by day: a division's
   season schedule and a team's, where every row must name its own date. The
   column is WIDER, and still FIXED. */
html.pub-league .pub-fix--dated,
html.pub-platform .pub-fix--dated {
  grid-template-columns: 6.5rem minmax(0, 1fr) minmax(0, auto) auto;
}

/* A WEEK variant (WP-B) for the thisweek block, where the day header carries
   the date and the cell is the clock alone. It is wider than the base track
   because the clock is printed with its meridiem ("12:30 PM") rather than as
   the mockup's bare "8:30" -- a league that ever kicks off in the morning must
   not be misread -- and the column stays FIXED, which is the rule. */
html.pub-league .pub-fix--week,
html.pub-platform .pub-fix--week {
  grid-template-columns: 4.9rem minmax(0, 1fr) minmax(0, auto) auto;
}

/* THE SCHEDULE variant (Bill, 2026-08-27), for the full-season page: day and
   date lead as their own FIXED cells (the band above names the week, so every
   row names its day), then the week row's clock-teams-field-division. Three
   fixed leading tracks, because content never sizes a leading column. */
html.pub-league .pub-fix--sched,
html.pub-platform .pub-fix--sched {
  grid-template-columns: 2.9rem 3.9rem 4.9rem minmax(0, 1fr) minmax(0, auto) auto;
}

html.pub-league .pub-fix-dow,
html.pub-platform .pub-fix-dow {
  color: var(--muted-foreground);
  font-size: 15px;
}

html.pub-league .pub-fix-date,
html.pub-platform .pub-fix-date {
  color: var(--foreground);
  font-weight: 650;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

html.pub-league .pub-fix-list > .pub-fix:last-child,
html.pub-platform .pub-fix-list > .pub-fix:last-child {
  border-bottom: 0;
}

html.pub-league .pub-fix-time,
html.pub-platform .pub-fix-time {
  color: var(--foreground);
  font-weight: 650;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

html.pub-league .pub-fix-date,
html.pub-platform .pub-fix-date {
  font-weight: 650;
}

html.pub-league .pub-fix-clock,
html.pub-platform .pub-fix-clock {
  color: var(--muted-foreground);
  font-weight: 500;
  font-size: 13.5px;
}

html.pub-league .pub-fix-teams,
html.pub-platform .pub-fix-teams {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.45rem;
  min-width: 0;
}

html.pub-league .pub-fix-team,
html.pub-platform .pub-fix-team {
  font-weight: 650;
  white-space: nowrap;
}

html.pub-league .pub-fix-team a,
html.pub-platform .pub-fix-team a {
  text-decoration: none;
}

html.pub-league .pub-fix-team a:hover,
html.pub-platform .pub-fix-team a:hover {
  text-decoration: underline;
}

html.pub-league .pub-fix-v,
html.pub-platform .pub-fix-v {
  color: var(--muted-foreground);
  white-space: nowrap;
}

html.pub-league .pub-fix-score,
html.pub-platform .pub-fix-score {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

html.pub-league .pub-fix-field,
html.pub-platform .pub-fix-field {
  color: var(--primary);
  font-weight: 550;
  font-size: 14.5px;
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The field name is a LINK to the venue's public record page (WP-B), which is
   where the address, the directions and the map pin live. It takes the cell's
   own color rather than a link color: the cell is already the accent, and a
   second color on top of it would make one word of a fixture row shout. */
html.pub-league .pub-fix-field a,
html.pub-platform .pub-fix-field a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

html.pub-league .pub-fix-field a:hover,
html.pub-platform .pub-fix-field a:hover {
  color: var(--foreground);
}

/* The field's AREA NOTE (Bill, 2026-08-27, option A -- supersedes the
   full-width second line): inline beside the venue link, muted -- and HARD
   CAPPED, which is Bill's condition. The field track is auto-sized, so an
   uncapped note would widen the cell and squeeze the team names; the cap
   ellipsizes the note instead, and the title attribute carries the whole
   text. "Field 3A" fits with room to spare. */
html.pub-league .pub-fix-fieldnote,
html.pub-platform .pub-fix-fieldnote {
  display: inline-block;
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
  color: var(--muted-foreground);
  font-weight: 400;
  font-size: 13px;
}

/* Column four: the DIVISION TAG where several divisions share a list, and the
   row's STATUS (Final, Cancelled) where they do not. Both sit in the same
   slot, so the grid has four tracks either way. */
html.pub-league .pub-fix-div,
html.pub-platform .pub-fix-div {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--pub-accent);
  white-space: nowrap;
}

html.pub-league .pub-fix-status,
html.pub-platform .pub-fix-status {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  white-space: nowrap;
}

html.pub-league .pub-fix-status--cancelled,
html.pub-platform .pub-fix-status--cancelled {
  color: var(--destructive);
}

/* THE MATCH RECORDING'S LINK (P4z WP-D). It rides INSIDE the fourth slot --
   beside "Final" on a division page, beside the division tag in this week's
   list -- so a row that has a video is the same four-track shape as a row that
   does not.

   IT CANCELS THE SLOT'S OWN TYPE. The tag and the status are 11.5px uppercase
   metadata; a link a person is meant to click is neither, so this resets the
   case and the tracking and takes the link color the rest of the page uses. */
html.pub-league .pub-fix-video,
html.pub-platform .pub-fix-video {
  margin-left: 8px;
  color: var(--primary);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

html.pub-league .pub-fix-video:hover,
html.pub-platform .pub-fix-video:hover {
  color: var(--foreground);
}

html.pub-league .pub-fix--cancelled .pub-fix-team,
html.pub-platform .pub-fix--cancelled .pub-fix-team {
  color: var(--muted-foreground);
}

/* == THE MATCHDAY CARD GRID (P4z WP-G) ====================================
   The DIVISION PAGE's fixtures and results, and nowhere else. The one-line
   grammar above is still the idiom for this week's list and for a team's own
   schedule -- two different jobs, two layouts, deliberately (the spec).

   A CARD IS A GAME: both sides stacked one per line with the crest at the left
   and the score at the right, a rail on the right of the card for the date and
   the state, and the place along the bottom. Two columns of cards on a desktop,
   ONE under 720px.

   IT REUSES THE CARD VOCABULARY -- var(--card) on var(--border) with
   var(--shadow-sm) -- so a match card, a fee card and a fixture list are the
   same object in three shapes rather than three inventions. */

html.pub-league .pub-matchday-head {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
  /* THE GROUPING BAND (Bill, from the Google-EPL reference): the matchweek
     header is a filled bar, not a bordered line -- the fill is what makes a
     week read as one group when the rows below are hairline-flat. */
  background: var(--muted);
  border-radius: 8px;
  padding: 8px 14px;
  margin-bottom: 10px;
}

html.pub-league .pub-matchday-head h3 {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--foreground);
}

/* The Mon-Sun window the matchday covers. Secondary to the number: the number
   is what a player says out loud, the dates are what they check against. */
html.pub-league .pub-matchday-head span {
  font-size: 12.5px;
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-match-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 0;
  row-gap: 0;
}

/* THE COLUMN DIVIDER (Bill): a vertical line between the two match columns,
   drawn by the GRID down its own center -- a child's border disappears with
   the child (a one-match week lost its line), the grid's line never does.
   Single-column drops it. */
@media (min-width: 721px) {
  html.pub-league .pub-match-grid {
    position: relative;
  }
  html.pub-league .pub-match-grid::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 1px;
    background: var(--border);
  }
  html.pub-league .pub-match-grid > .pub-match:nth-child(odd) {
    padding-right: 18px;
  }
  html.pub-league .pub-match-grid > .pub-match:nth-child(even) {
    padding-left: 18px;
  }
}

html.pub-league .pub-match {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 14px;
  align-items: start;
  /* FLAT ROWS, NOT CARD-ON-CARD (Bill): the grid already sits inside the
     page card, so a second box per match doubles borders and whitespace.
     A hairline under each match does the dividing, the results-table way. */
  padding: 12px 4px;
  border-bottom: 1px solid var(--border);
  color: var(--card-foreground);
}

/* the last match in each COLUMN keeps its hairline on desktop (columns end at
   different heights); at single-column the final row drops it */
@media (max-width: 720px) {
  html.pub-league .pub-match-grid > .pub-match:last-child {
    border-bottom: 0;
  }
}

html.pub-league .pub-match-sides {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

/* THE SIDE'S THREE TRACKS ARE FIXED, FLEXIBLE, FIXED: the crest box never
   changes size, the score column is always there (empty before kickoff), and
   only the NAME takes the slack. That is what keeps the home and away names of
   every card in the grid starting and ending on the same two lines -- the
   alignment rule the fixture grammar was corrected for in review. */
html.pub-league .pub-match-side {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) 1.6rem;
  align-items: center;
  column-gap: 9px;
}

html.pub-league .pub-match-crest {
  width: 22px;
  height: 22px;
  border-radius: 5px;
  object-fit: contain;
  flex: none;
}

/* The chip a team with no crest gets. Same box, so a badged card and an
   unbadged one are the same shape. */
html.pub-league .pub-match-crest--chip {
  display: grid;
  place-items: center;
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* TEAM NAMES ARE ATOMIC. A name never wraps inside itself; a name too long for
   the card is clipped with an ellipsis rather than broken across two lines,
   which is the rule the grammar above carries too. */
html.pub-league .pub-match-team {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 650;
  font-size: 15.5px;
}

html.pub-league .pub-match-team a {
  text-decoration: none;
}

html.pub-league .pub-match-team a:hover {
  text-decoration: underline;
}

html.pub-league .pub-match-score {
  justify-self: end;
  font-weight: 700;
  font-size: 15.5px;
  font-variant-numeric: tabular-nums;
  color: var(--foreground);
}

html.pub-league .pub-match-rail {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  text-align: right;
  white-space: nowrap;
}

html.pub-league .pub-match-when {
  font-size: 12.5px;
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-match-kick {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
}

html.pub-league .pub-match-status {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

html.pub-league .pub-match-status--cancelled {
  color: var(--destructive);
}

/* THE FRIENDLY TAG (delta 0022): an interdivisional friendly is shown on both
   participants' division pages and counts in neither table, so the card says so
   in a word. Quieter than the status above it -- it qualifies the fixture, it is
   not its state. */
html.pub-league .pub-match-friendly {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 6px;
  line-height: 16px;
}

/* THE VIDEO CHIP: a play triangle and the word, under the state. The glyph is
   an inline SVG filled from currentColor -- it is a piece of the design, not a
   character, and the no-emoji rule stands everywhere else. */
html.pub-league .pub-match-video {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  color: var(--primary);
  font-size: 13px;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

html.pub-league .pub-match-video:hover {
  color: var(--foreground);
}

html.pub-league .pub-match-play {
  width: 9px;
  height: 11px;
  flex: none;
}

/* THE CARD'S FOOTER: the field, and the fixture's own public line under it. It
   spans the whole card rather than riding in the rail -- a field name is as
   long as the club that named it, and a place squeezed into a right-hand
   column would wrap into three lines or be clipped to nothing. */
html.pub-league .pub-match-meta {
  grid-column: 1 / -1;
  margin-top: 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 12px;
}

html.pub-league .pub-match-field {
  color: var(--primary);
  font-weight: 550;
  font-size: 13.5px;
  min-width: 0;
}

html.pub-league .pub-match-field a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

html.pub-league .pub-match-field a:hover {
  color: var(--foreground);
}

/* The match card's note rides the field line since Bill's 2026-08-27 option-A
   ruling -- it wears .pub-fix-fieldnote (inline, muted, width-capped) instead
   of the full-width second line this rule used to draw. */

/* A CALLED-OFF GAME keeps its card -- "there was supposed to be a game here" is
   information. The names and the scores are struck through with the design
   system's own utility (that is what struck through has meant on these pages
   since P4); the muting is the card's job. */
html.pub-league .pub-match--cancelled .pub-match-team,
html.pub-league .pub-match--cancelled .pub-match-score {
  color: var(--muted-foreground);
}

/* == THE TEAMS PAGE'S DIRECTORY (Bill, 2026-08-27; supersedes WP-H's cards)
   One league-wide page: a header row (title + season pills), then each
   division as a GROUP of compact rows in a two-column GRID. A grid and not
   CSS columns, deliberately: columns flow groups downward so o60 butts
   against whatever is above it -- the grid top-aligns each row of groups
   (Bill's o55/o60 alignment call). One column on a phone. */

html.pub-league .pub-headrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

html.pub-league .pub-teams-dir {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px 40px;
  align-items: start;
}

/* The division sub-head: the group's name links its STANDINGS page (the one
   cross-surface pointer this page carries), the count is quiet metadata. */
html.pub-league .pub-teams-divhead {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pub-accent);
  margin: 0 0 4px;
}

html.pub-league .pub-teams-divhead a {
  color: inherit;
  text-decoration: none;
}

html.pub-league .pub-teams-divhead a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

html.pub-league .pub-teams-count {
  color: var(--muted-foreground);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

/* A directory row is still a BUTTON: 48px minimum, the whole row the link. */
html.pub-league .pub-team-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 7px 2px;
  border-bottom: 1px solid var(--border);
  color: var(--foreground);
  text-decoration: none;
  transition: background 0.15s ease;
}

html.pub-league .pub-teams-group .pub-team-row:last-child {
  border-bottom: 0;
}

html.pub-league a.pub-team-row:hover {
  background: var(--muted);
}

html.pub-league a.pub-team-row:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* THE CREST SITS BETWEEN THE FIXTURE CHIP AND THE OLD CARD CREST (32px
   against 22 and 44): big enough to read a real crest at, small enough for a
   directory row.

   IT CARRIES ITS OWN SURFACE, and that is not decoration. A club crest is an
   arbitrary PNG with a transparent ground: a white-on-transparent crest
   disappears on the white page of light mode, a dark one disappears on the
   dark page. A muted chip behind the image is a surface that differs from the
   page in BOTH modes, and the hairline round it means the box is visible even
   when the artwork inside it is not. */
html.pub-league .pub-team-crest {
  width: 32px;
  height: 32px;
  flex: none;
  padding: 3px;
  border-radius: 8px;
  background: var(--muted);
  border: 1px solid var(--border);
  object-fit: contain;
}

html.pub-league .pub-team-crest--chip {
  display: grid;
  place-items: center;
  color: var(--muted-foreground);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* TEAM NAMES ARE ATOMIC site-wide: a name never wraps inside itself, and one
   too long for the row is clipped with an ellipsis. */
html.pub-league .pub-team-name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 550;
  font-size: 16px;
}

/* "3W 1D 2L" -- right-aligned, tabular so the records of stacked rows line
   up, and a hair of tracking so the letters read as labels rather than as
   part of the numbers. It is NOT DRAWN AT ALL on a club with no played
   games. */
html.pub-league .pub-team-rec {
  margin-left: auto;
  color: var(--muted-foreground);
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* == RESPONSIVE =========================================================== */

@media (max-width: 720px) {
  html.pub-league .pub-fee-grid {
    grid-template-columns: 1fr;
  }

  /* ONE COLUMN OF DIVISION GROUPS, for the reason the match grid drops to
     one: two directories across a phone takes the club names down to a width
     that clips them, and a club a visitor cannot read is a directory that
     failed. */
  html.pub-league .pub-teams-dir {
    grid-template-columns: 1fr;
  }

  /* ONE COLUMN OF MATCH CARDS. Two cards side by side on a phone would take the
     team names down to a width that clips half of them, and the card is already
     the compact reading -- it does not need a second compression. */
  html.pub-league .pub-match-grid {
    grid-template-columns: 1fr;
  }

  html.pub-league .pub-hero .pub-wrap,
  html.pub-platform .pub-hero .pub-wrap {
    padding-top: 48px;
    padding-bottom: 48px;
  }

  /* THE TWO-LINE REFLOW. Time keeps its FIXED track; teams take the rest of
     line one and right-justify, so they align with the division tag under
     them; field and tag share line two. */
  html.pub-league .pub-fix,
  html.pub-platform .pub-fix {
    grid-template-columns: 3.4rem 1fr auto;
    grid-template-areas:
      "time teams teams"
      "field field divtag";
    row-gap: 5px;
  }

  html.pub-league .pub-fix--dated,
  html.pub-platform .pub-fix--dated {
    grid-template-columns: 6.5rem 1fr auto;
  }

  html.pub-league .pub-fix--week,
  html.pub-platform .pub-fix--week {
    grid-template-columns: 4.9rem 1fr auto;
  }

  /* THE SCHEDULE ROW ON A PHONE: date leads the first line (the weekday is
     dropped -- the date carries the fact), the clock opens the second beside
     the field and tag. Same two-line reflow as every fixture row. */
  html.pub-league .pub-fix--sched,
  html.pub-platform .pub-fix--sched {
    grid-template-columns: 4.4rem 1fr auto;
    grid-template-areas:
      "date teams teams"
      "time field divtag";
  }

  html.pub-league .pub-fix--sched .pub-fix-dow,
  html.pub-platform .pub-fix--sched .pub-fix-dow {
    display: none;
  }

  html.pub-league .pub-fix--sched .pub-fix-date,
  html.pub-platform .pub-fix--sched .pub-fix-date {
    grid-area: date;
  }

  html.pub-league .pub-fix-time,
  html.pub-platform .pub-fix-time {
    grid-area: time;
  }

  html.pub-league .pub-fix-teams,
  html.pub-platform .pub-fix-teams {
    grid-area: teams;
    justify-content: flex-end;
    text-align: right;
    font-size: 17px;
  }

  html.pub-league .pub-fix-field,
  html.pub-platform .pub-fix-field {
    grid-area: field;
    white-space: normal;
    overflow: visible;
  }

  html.pub-league .pub-fix-div,
  html.pub-league .pub-fix-status,
  html.pub-platform .pub-fix-div,
  html.pub-platform .pub-fix-status {
    grid-area: divtag;
    justify-self: end;
    align-self: center;
  }


  html.pub-league .pub-contact-role {
    width: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.pub-league .pub-div-row,
  html.pub-platform .pub-div-row {
    transition: none;
  }
}

/* The P4x in-page section menu is GONE (Bill, 2026-08-27): WP-C's site nav
   took its job, and beside it the strip read as a duplicated menu. */

/* The HOME/AWAY marker on a team's own schedule. It rides in the teams cell
   rather than claiming a fifth column: the grammar has four slots everywhere
   or it has four slots nowhere. */
html.pub-league .pub-fix-ha,
html.pub-platform .pub-fix-ha {
  color: var(--muted-foreground);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* == THE PLATFORM LANDING (P4z WP-E) ======================================
   Everything below is scoped to html.pub-platform and named pub-plat-*: it
   is the platform's marketing surface and NOTHING a league site renders
   reaches it. The one shared thing is the fixture-row grammar above, which
   the hero preview reuses through the pub-fix--sm modifier rather than
   through a fourth row shape.

   THE PLATFORM HERO IS NOT .pub-hero. A league's hero is a photo band with
   its own mode-independent tokens; this one is the page's own background
   with a two-column split on it, and painting it --pub-hero-bg would leave
   it near-black in light mode for no reason. */

html.pub-platform .pub-nav {
  display: flex;
  align-items: center;
  gap: 22px;
  font-size: 14.5px;
}

html.pub-platform .pub-nav a {
  color: var(--muted-foreground);
  text-decoration: none;
}

html.pub-platform .pub-nav a:hover {
  color: var(--foreground);
}

/* -- hero: the six-sport facility CAROUSEL (P4z amendment) ----------------
   SIX FRAMES, 24 SECONDS, FOUR SECONDS EACH, AND NO SCRIPT.

   FRAME ONE (soccer) IS NOT AN ANIMATED LAYER. It is the static background of
   the slides container, so it is what the hero shows before any animation has
   run a step, what it shows in the gaps of the cycle, and what it shows under
   prefers-reduced-motion. Frames two to six are five absolutely-positioned
   layers over it, each fading in as the one before it fades out.

   THE ARITHMETIC, once, so the percentages below are readable: the cycle is
   24s, a frame's window is 4s (16.667%) and a crossfade is 1s (4.167%). Frame
   n carries animation-delay (n-1)*4s, so it fades in exactly as frame n-1
   fades out, and the pair is opaque throughout the handover.

   THE IMAGE URLS ARE NOT IN THIS FILE. They come from the head <style> the
   layout emits (PublicChrome.HeadStyle), because they are embedded assets the
   HOST serves and their names are a build fact, not a design one. */

html.pub-platform .pub-plat-hero {
  position: relative;
  overflow: hidden;
  background: var(--pub-hero-bg);
}

html.pub-platform .pub-plat-slides {
  position: absolute;
  inset: 0;
  background-image: var(--pub-slide-1, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

html.pub-platform .pub-plat-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  animation: pub-plat-slide 24s linear infinite;
}

html.pub-platform .pub-plat-slide--2 {
  background-image: var(--pub-slide-2, none);
  animation-delay: 4s;
}

html.pub-platform .pub-plat-slide--3 {
  background-image: var(--pub-slide-3, none);
  animation-delay: 8s;
}

html.pub-platform .pub-plat-slide--4 {
  background-image: var(--pub-slide-4, none);
  animation-delay: 12s;
}

html.pub-platform .pub-plat-slide--5 {
  background-image: var(--pub-slide-5, none);
  animation-delay: 16s;
}

html.pub-platform .pub-plat-slide--6 {
  background-image: var(--pub-slide-6, none);
  animation-delay: 20s;
}

@keyframes pub-plat-slide {
  0% {
    opacity: 0;
  }

  4.1667% {
    opacity: 1;
  }

  16.6667% {
    opacity: 1;
  }

  20.8333% {
    opacity: 0;
  }

  100% {
    opacity: 0;
  }
}

/* THE BASE FRAME'S CREDIT rides the same clock from the other end: it is the
   only one that must be visible at t=0, so it starts opaque, leaves when frame
   two arrives, and comes back as frame six leaves. A credit is bound to its
   photograph or it is not a credit. */
html.pub-platform .pub-plat-credit--1 {
  animation: pub-plat-credit-base 24s linear infinite;
}

@keyframes pub-plat-credit-base {
  0% {
    opacity: 1;
  }

  16.6667% {
    opacity: 1;
  }

  20.8333% {
    opacity: 0;
  }

  95.8333% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

/* THE HERO WASH, at HALF the original strength (the amendment's ~.44 left,
   ~.21 right), with the hero's own text carrying a compensating shadow. The
   photographs earned the room; the type protects itself. */
html.pub-platform .pub-plat-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(8, 14, 11, 0.44) 0%, rgba(8, 14, 11, 0.3) 55%, rgba(8, 14, 11, 0.21) 100%);
}

/* REDUCED MOTION COLLAPSES TO FRAME ONE. Not "slower" and not "no crossfade":
   a visitor who asked for no motion gets one photograph, which is the soccer
   frame the container already paints. */
@media (prefers-reduced-motion: reduce) {
  html.pub-platform .pub-plat-slide {
    display: none;
  }

  html.pub-platform .pub-plat-credit--1 {
    animation: none;
    opacity: 1;
  }
}

html.pub-platform .pub-plat-hero .pub-wrap {
  position: relative;
  padding-top: 64px;
  padding-bottom: 56px;
}

html.pub-platform .pub-plat-hero-grid {
  display: grid;
  /* ASYMMETRIC, per the mockup: the pitch takes the wider half, the live
     card the narrower one. */
  grid-template-columns: 6fr 5fr;
  gap: 52px;
  align-items: center;
}

/* THE PHOTO-HERO CONTRAST LAW (P4z amendment): everything inside a photo hero
   is MODE-INVARIANT and self-protected. These are EXPLICIT COLORS, not tokens
   -- a photograph does not get lighter because the visitor's laptop did, so
   type over one must not change with the mode. The shadow is the other half of
   the law: it is what pays for the halved wash. */
html.pub-platform .pub-plat-h1 {
  font-size: clamp(2.1rem, 4.5vw, 3.2rem);
  line-height: 1.07;
  letter-spacing: -0.03em;
  font-weight: 700;
  margin: 0 0 18px;
  text-wrap: balance;
  color: #ffffff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.65), 0 1px 2px rgba(0, 0, 0, 0.5);
}

html.pub-platform .pub-plat-sub {
  color: rgba(255, 255, 255, 0.92);
  font-size: 1.1rem;
  max-width: 44ch;
  margin: 0 0 26px;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.45);
}

html.pub-platform .pub-plat-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
}

html.pub-platform .pub-plat-btn {
  display: inline-block;
  text-decoration: none;
  font-weight: 600;
  font-size: 15px;
  padding: 11px 22px;
  border-radius: 8px;
  transition: transform 0.15s ease, opacity 0.15s ease;
}

html.pub-platform .pub-plat-btn:active {
  transform: translateY(1px);
}

/* BOTH HERO BUTTONS ARE MODE-INVARIANT, for the reason the headline is: they
   sit on photographs. The primary keeps the floodlight green in BOTH modes
   (the light-mode deep green would read as a hole punched in a photo), and the
   GHOST GETS A TRANSLUCENT DARK FILL -- the amendment's explicit instruction.
   A one-pixel border over a photograph is not a button; a fill is. */
html.pub-platform .pub-plat-btn--primary {
  background: #46c46e;
  color: #08120b;
}

html.pub-platform .pub-plat-btn--primary:hover {
  opacity: 0.9;
}

html.pub-platform .pub-plat-btn--ghost {
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(8, 14, 11, 0.62);
  color: #ffffff;
}

html.pub-platform .pub-plat-btn--ghost:hover {
  border-color: rgba(255, 255, 255, 0.85);
  background: rgba(8, 14, 11, 0.74);
}

/* -- the live preview card ----------------------------------------------- */

html.pub-platform .pub-plat-preview {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* The fixture list inside the card is the SITE-WIDE list, minus its own box:
   two nested borders around the same rows is one border too many. */
html.pub-platform .pub-plat-preview .pub-fix-list {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}

html.pub-platform .pub-plat-preview--empty {
  padding: 22px;
  color: var(--muted-foreground);
  font-size: 14.5px;
}

html.pub-platform .pub-plat-preview--empty p {
  margin: 0;
  max-width: 40ch;
}

html.pub-platform .pub-plat-preview-head {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

html.pub-platform .pub-plat-preview-name {
  font-weight: 600;
  font-size: 14px;
}

html.pub-platform .pub-plat-preview-week {
  color: var(--muted-foreground);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}

html.pub-platform .pub-plat-preview-foot {
  padding: 10px 18px;
  background: var(--secondary);
  border-top: 1px solid var(--border);
  font-size: 13px;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

html.pub-platform .pub-plat-preview-foot a {
  color: var(--primary);
  text-decoration: none;
  font-weight: 600;
}

html.pub-platform .pub-plat-preview-foot a:hover {
  text-decoration: underline;
}

html.pub-platform .pub-plat-preview-more {
  color: var(--muted-foreground);
}

/* THE GRAMMAR'S SMALL VARIANT. Same four slots, same fixed first column,
   same mobile reflow -- one step down in size, for a card that sits beside a
   headline rather than under one. */
html.pub-platform .pub-fix--sm {
  padding: 10px 18px;
  font-size: 14px;
}

html.pub-platform .pub-fix--sm .pub-fix-time {
  font-size: 13px;
}

html.pub-platform .pub-fix--sm .pub-fix-clock {
  font-size: 12.5px;
}

html.pub-platform .pub-fix--sm .pub-fix-field {
  font-size: 13px;
}

/* -- capability rows ----------------------------------------------------- */

html.pub-platform .pub-plat-cap {
  display: grid;
  grid-template-columns: 5fr 6fr;
  gap: 44px;
  align-items: center;
  padding: 24px 0;
}

html.pub-platform .pub-plat-cap--flip {
  grid-template-columns: 6fr 5fr;
}

html.pub-platform .pub-plat-cap--flip .pub-plat-cap-text {
  order: 2;
}

html.pub-platform .pub-plat-cap h3 {
  margin: 0 0 8px;
  font-size: 1.15rem;
}

html.pub-platform .pub-plat-cap p {
  margin: 0;
  color: var(--muted-foreground);
  max-width: 48ch;
}

html.pub-platform .pub-plat-cap-visual {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
}

/* The EXAMPLE tag. It is on the page for one reason: these two cards are
   real data wherever there is any, so the one state where they are a shape
   rather than a fact has to say so. */
html.pub-platform .pub-plat-example {
  margin: 0 0 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

html.pub-platform .pub-plat-standing {
  display: grid;
  grid-template-columns: 2ch minmax(0, 1fr) repeat(4, 3ch);
  gap: 10px;
  padding: 7px 4px;
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--border);
}

html.pub-platform .pub-plat-standing:last-child {
  border-bottom: 0;
}

html.pub-platform .pub-plat-standing--head {
  color: var(--muted-foreground);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

html.pub-platform .pub-plat-standing-team {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

html.pub-platform .pub-plat-rsvp {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 9px 4px;
  border-bottom: 1px solid var(--border);
  font-size: 13.5px;
}

html.pub-platform .pub-plat-rsvp:last-child {
  border-bottom: 0;
}

html.pub-platform .pub-plat-rsvp-label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

html.pub-platform .pub-plat-rsvp-when {
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

html.pub-platform .pub-plat-chip {
  font-size: 11.5px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted-foreground);
  white-space: nowrap;
  flex: none;
}

html.pub-platform .pub-plat-chip--in {
  color: var(--primary);
  border-color: var(--primary);
}

/* -- directory cards + the closing bands --------------------------------- */

html.pub-platform .pub-plat-dir {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 460px));
  gap: 16px;
}

html.pub-platform .pub-plat-card {
  display: block;
  text-decoration: none;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px 22px;
  transition: border-color 0.15s ease;
}

html.pub-platform .pub-plat-card:hover {
  border-color: var(--primary);
}

html.pub-platform .pub-plat-card-name {
  display: block;
  font-weight: 650;
  font-size: 1.05rem;
  margin-bottom: 4px;
}

html.pub-platform .pub-plat-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  color: var(--muted-foreground);
  font-size: 13.5px;
}

/* The directory page's own title. It is an h1 (that page's subject) drawn at
   the band heading's size, so /l and the branding page's directory band read
   as the same thing at the same weight. */
html.pub-platform .pub-plat-title {
  font-size: 1.35rem;
  letter-spacing: -0.015em;
  margin: 0 0 6px;
  color: var(--foreground);
}

html.pub-platform .pub-plat-note {
  color: var(--muted-foreground);
  font-size: 14px;
  margin: 18px 0 0;
  max-width: 60ch;
}

html.pub-platform .pub-plat-organizers p {
  color: var(--muted-foreground);
  max-width: 62ch;
  margin: 0 0 14px;
}

html.pub-platform .pub-plat-organizers p:last-child {
  margin-bottom: 0;
}

html.pub-platform .pub-plat-organizers b {
  color: var(--foreground);
  font-weight: 600;
}

@media (max-width: 860px) {
  html.pub-platform .pub-plat-hero-grid,
  html.pub-platform .pub-plat-cap,
  html.pub-platform .pub-plat-cap--flip {
    grid-template-columns: 1fr;
    gap: 28px;
  }

  html.pub-platform .pub-plat-cap--flip .pub-plat-cap-text {
    order: 0;
  }

  html.pub-platform .pub-plat-hero .pub-wrap {
    padding-top: 40px;
    padding-bottom: 36px;
  }

  html.pub-platform .pub-plat-dir {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.pub-platform .pub-plat-btn,
  html.pub-platform .pub-plat-card {
    transition: none;
  }
}

/* == THE ACCOUNT PAGE (P5a) -- /my's forms, in the platform scope ==========
   The public design system had no form primitives before this: every public
   page until now was READ-ONLY, and the RSVP links are buttons on a signed
   URL. These are the smallest set that draws card 5 and card 6 -- a labelled
   input, a checkbox row, a read-only value with its state badge, a button --
   and they are token-only, so they follow the platform's three-state light/
   dark block with everything else on the page.

   PLATFORM SCOPE ONLY. /my is an account page, never a league's (RULING 2),
   so there is no html.pub-league twin below and there should not be one. */

html.pub-platform .pub-acct-notice {
  margin: 0 0 8px;
  padding: 11px 14px;
  border: 1px solid var(--success);
  border-radius: var(--radius);
  color: var(--foreground);
  background: var(--card);
  font-size: 0.95rem;
  max-width: 60ch;
}

html.pub-platform .pub-acct-notice--err {
  border-color: var(--destructive);
}

html.pub-platform .pub-acct-form {
  max-width: 760px;
}

html.pub-platform .pub-acct-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

html.pub-platform .pub-acct-field {
  display: block;
  margin: 0 0 16px;
}

html.pub-platform .pub-acct-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--foreground);
  margin: 0 0 6px;
}

html.pub-platform .pub-acct-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font: inherit;
  font-size: 15px;
  color: var(--foreground);
  background: var(--card);
  border: 1px solid var(--input);
  border-radius: 8px;
}

html.pub-platform .pub-acct-input:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 1px;
  border-color: var(--ring);
}

/* The read-only value (email). A PRINTED VALUE, not a disabled input: a
   disabled input still looks like a control somebody could talk their way
   into, and this one is never editable here. */
html.pub-platform .pub-acct-ro {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0;
  padding: 10px 0;
  font-size: 15px;
  color: var(--foreground);
}

html.pub-platform .pub-acct-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--success);
  color: var(--success);
  font-size: 12px;
  font-weight: 600;
}

html.pub-platform .pub-acct-badge--warn {
  border-color: var(--destructive);
  color: var(--destructive);
}

html.pub-platform .pub-acct-hint {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--muted-foreground);
  max-width: 60ch;
}

html.pub-platform .pub-acct-sub {
  margin: 26px 0 4px;
  font-size: 15px;
  font-weight: 600;
  color: var(--foreground);
}

/* The whole row is the label, so the words are part of the hit target -- a
   consent checkbox is the last control on this page that should be fiddly. */
html.pub-platform .pub-acct-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 10px 0 0;
  max-width: 60ch;
  font-size: 14px;
  color: var(--foreground);
  cursor: pointer;
}

html.pub-platform .pub-acct-check input {
  margin: 2px 0 0;
  width: 16px;
  height: 16px;
  accent-color: var(--primary);
  flex: none;
}

html.pub-platform .pub-acct-actions {
  margin: 20px 0 0;
}

html.pub-platform .pub-acct-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 10px 0 0;
}

html.pub-platform .pub-acct-btn {
  display: inline-block;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  padding: 10px 18px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--foreground);
  cursor: pointer;
}

html.pub-platform .pub-acct-btn:hover {
  border-color: var(--ring);
}

html.pub-platform .pub-acct-btn--primary {
  border-color: var(--primary);
  background: var(--primary);
  color: var(--primary-foreground);
}

html.pub-platform .pub-acct-btn--primary:hover {
  opacity: 0.9;
}

@media (max-width: 640px) {
  html.pub-platform .pub-acct-row {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* == THE ACCOUNT PAGE'S RECORD CARDS (P5a WP-B) ===========================
   Cards 2, 3 and 4 -- seasons+teams, dues, waiver record -- are LISTS OF
   RECORDS rather than forms, so they need a second small set of primitives
   beside the form ones above: an item, its head line with a state pill, a
   meta line, and the three small run-ons (money, payment history, the
   collapsed waiver text).

   TOKEN-ONLY AND PLATFORM SCOPE ONLY, exactly as the form set is: /my is an
   account page, never a league's, so there is no html.pub-league twin. */

html.pub-platform .pub-acct-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  max-width: 760px;
}

html.pub-platform .pub-acct-item {
  padding: 14px 0;
  border-top: 1px solid var(--border);
}

html.pub-platform .pub-acct-item:first-child {
  border-top: 0;
}

html.pub-platform .pub-acct-item-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0;
}

html.pub-platform .pub-acct-item-name {
  font-size: 16px;
  font-weight: 600;
  color: var(--foreground);
  text-decoration: none;
}

html.pub-platform a.pub-acct-item-name:hover {
  text-decoration: underline;
}

html.pub-platform .pub-acct-item-meta {
  margin: 3px 0 0;
  font-size: 13px;
  color: var(--muted-foreground);
}

/* The state pill. Neutral by default -- "rostered" is the ordinary case and
   should not shout; the modifiers carry the exceptions. */
html.pub-platform .pub-acct-state {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 600;
}

html.pub-platform .pub-acct-state--invited,
html.pub-platform .pub-acct-state--owing {
  border-color: var(--destructive);
  color: var(--destructive);
}

html.pub-platform .pub-acct-state--paid {
  border-color: var(--success);
  color: var(--success);
}

html.pub-platform .pub-acct-state--released {
  border-style: dashed;
}

/* The register prompt sits INSIDE card 2, under the lines it is the answer
   to: "here is where you stand, and here is the season you may join". */
html.pub-platform .pub-acct-prompt {
  margin: 18px 0 0;
  padding: 14px 16px;
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  background: var(--card);
  max-width: 760px;
}

html.pub-platform .pub-acct-prompt-text {
  margin: 0;
  font-size: 15px;
  color: var(--foreground);
}

html.pub-platform .pub-acct-money {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 8px 0 0;
  font-size: 14px;
  color: var(--foreground);
}

html.pub-platform .pub-acct-sublist {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  font-size: 13px;
  color: var(--muted-foreground);
}

html.pub-platform .pub-acct-sublist li {
  margin: 2px 0;
}

html.pub-platform .pub-acct-details {
  margin: 8px 0 0;
  font-size: 14px;
}

html.pub-platform .pub-acct-details summary {
  cursor: pointer;
  color: var(--muted-foreground);
  font-size: 13px;
}

/* The frozen waiver text, in a scrolling well: it is evidence, so it renders
   in full, but it must not push the rest of the page off the screen. */
html.pub-platform .pub-acct-waiver {
  margin: 10px 0 0;
  padding: 12px 14px;
  max-height: 24rem;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--muted);
  color: var(--foreground);
  font-size: 14px;
  line-height: 1.6;
}

/* == THE AVAILABILITY CARD (P5a WP-C) =====================================
   Card 1 of /my: the site's own fixture row with a control cluster BESIDE it.

   NOTHING HERE TOUCHES .pub-fix. The fixture grammar is site-wide law and
   this card is a consumer of it, not a variant: the row keeps its four
   tracks and its own rules, and everything this page adds sits outside it.

   TOKEN-ONLY AND PLATFORM SCOPE ONLY, as the rest of the account set is --
   /my is an account page, never a league's, so there is no pub-league twin.

   THE CLUSTER WRAPS BELOW THE ROW ON A NARROW SCREEN rather than squeezing
   it: this is the control a player uses one-handed on a phone, and a Yes/No
   pair that has been crushed into a 40px column is a control that gets
   mis-tapped. */

html.pub-platform .pub-acct-avail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 16px;
  border-bottom: 1px solid var(--border);
}

html.pub-platform .pub-acct-avail > .pub-fix {
  flex: 1 1 26rem;
  min-width: 0;
  border-bottom: 0;
}

html.pub-platform .pub-acct-avail-ctl {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  /* The right inset matches .pub-fix's own, so the buttons stop where the
     row's division tag stops instead of running to the card's edge. */
  padding: 0 18px 10px 0;
  margin: 0;
}

/* Which of this player's teams the fixture is theirs through. A LABEL, not a
   link: the team's public page is one row up, inside the fixture row. */
html.pub-platform .pub-acct-avail-team {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted-foreground);
}

/* The compact button, for a pair that sits inline with a row rather than
   standing alone under a form. */
html.pub-platform .pub-acct-btn--sm {
  padding: 5px 14px;
  font-size: 14px;
}

/* The card's last row closes the list, exactly as .pub-fix:last-child does on
   a plain fixture list -- the section's own edge is the closing line there. */
html.pub-platform .pub-fix-list > .pub-acct-avail:last-child {
  border-bottom: 0;
}
