/* ==========================================================================
   GoodTip — Design System
   Footy tipping, for good.
   Brand kept faithful to client: deep forest + electric lime + cream.
   Signature: the "ladder" (footy ladder + live charity tally) and the Wall.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* The hidden attribute must always win.
   The browser's own [hidden] { display: none } is a UA rule, so ANY author
   rule that sets display on the same element silently beats it — and every
   panel we toggle with el.hidden = true is display-typed (grid, flex).
   That produced a dialog which opened and then could not be closed: setting
   .hidden ran, and .fxh-modal { display: grid } kept it on screen. Declaring
   it once here removes the whole class of bug rather than one instance. */
[hidden] { display: none !important; }

:root {
  /* ---- Brand palette (from client) ----

     THE GREEN, RESATURATED — Sep 2026. Client: "I do not know why the green in
     the pages looks worn out and not bright and sharp green, looks docile to
     me ... let's make sure our green is bright and stands out."

     Right, and the cause was CHROMA, not brightness. These four sat at 51-69%
     saturation, which at this darkness is close enough to neutral that a large
     area of it reads as charcoal with a green cast — you see grey and conclude
     the screen is dim. They are now 86%, which is the difference between a
     colour and a shade.

     AT THE SAME LUMINANCE, TO THE FOURTH DECIMAL. That is the whole trick, and
     the first attempt got it wrong: pushing saturation the obvious way also
     lifted these a couple of points, and two points of lightness under a
     backdrop this dark dropped thirty-eight pairs of low-opacity cream text
     through 4.5:1 at once. Every value here was solved for the luminance the
     colour it replaces already had, so nothing written on them moved at all —
     cream on the deep ground is 11.50:1 before and after.

     This is the direction to push for "brighter" on a dark palette. Lightening
     would have made the page paler rather than greener, and taken the contrast
     with it. */
  --deep:    #072F1E;   /* deep forest */
  --deep-2:  #042316;   /* darker panel */
  --panel:   #052C1A;   /* raised panel on deep */
  --panel-2: #083E25;   /* highlighted panel on deep */
  /* THE GREEN AS TYPE. #2D7A3A for two years, and 4.25:1 on the pale-lime
     and pale-green chips it is most often written on — under the 4.5:1 body
     text needs, which is the "shade that is not easy to see" the client kept
     finding. Three points darker clears every one of them (38 rules) and is
     indistinguishable side by side; it also lifts white-on-forest from 5.3:1
     to 5.6:1, because this token is a fill as well as an ink.
     scripts/check_contrast.py is what says so, and will say so again. */
  --forest:  #2A7337;   /* mid green */
  /* The spark, lifted with them. It is the thing the eye lands on, so a
     resaturated ground with the old lime on it would have read as the lime
     going flat rather than the green coming up. */
  --lime:    #CFF93A;   /* electric lime — the spark */
  --lime-dk: #B2DE22;   /* lime pressed */
  /* THE VIVID GREEN, and what it is NOT for.

     --forest is the green that body text is written in, and it cannot get any
     brighter: at #2A7337 it clears 4.5:1 on the cream page by six hundredths,
     and every candidate two shades up fails it. That is not a colour choice,
     it is the floor.

     So the brightness goes somewhere it costs nothing — a second green for
     things that are SHAPES rather than sentences: bars, rings, icon fills,
     the loaders. It takes deep green as its ink, never white (white on it is
     3.1:1), and it must never be used for a line of text. */
  --green:   #22B352;   /* vivid green — fills and graphics only */
  --cream:   #E8E4D5;   /* warm paper */
  --cream-2: #F2EFE6;   /* lighter paper */
  --white:   #FFFFFF;
  --ink:     #0F2E22;   /* body text on light */
  /* Sage grey, darkened Sep 2026. At #6B7A6E this cleared 4.5:1 on a white
     card and nothing else: 4.04:1 on the cream sheet and 3.56:1 on the cream
     page, which is most of the public site and every supporting line under a
     heading on it. It is only ever used on a light ground — 279 rules, not one
     of them on a dark panel, because dark panels use cream at an opacity
     instead — so darkening it has no other side. Now 4.5:1 on the cream page,
     5.1 on the sheet, 5.7 on a card. */
  --muted:   #5C6A5A;   /* sage grey */
  --line:    rgba(15,46,34,0.12);
  --line-d:  rgba(255,255,255,0.12);

  /* ---- Type ---- */
  --display: 'Archivo Black', system-ui, sans-serif;     /* headlines */
  --body:    'Archivo', system-ui, sans-serif;           /* prose */
  --mark:    'Barlow Condensed', system-ui, sans-serif;  /* wordmark / labels */
  --score:   'Barlow Semi Condensed', system-ui, sans-serif; /* scoreboard numerals */

  /* ---- Type scale ---- */
  /* THE SECTION LADDER, INVERTED — client, 22 Sep 2026.

     "On the public pages we lack hierarchy. The one that comes first should be
     much bigger than what follows. So for example 'Who it's for' / 'Your
     community already tips' — the 'Who it's for', since it comes first, must
     be the bigger one."

     He is right that there was no hierarchy, and the cause was that the two
     were the wrong way round. Every section opened with a 12px all-caps label
     above a 46px headline, so the label was decoration and the headline was
     the title — which meant a reader scanning the page got the SENTENCE first
     and had to read it to find out what the section was. Naming the section
     first, and naming it loudest, is what lets somebody skim.

     So --fs-eyebrow is now the section title and --fs-h2 is the statement
     under it. The names are kept because 43 eyebrows and 37 headings use them;
     what changed is which one is bigger. --fs-h1 is untouched: it belongs to
     the page's own headline in the hero and still outranks everything. */
  --fs-eyebrow: clamp(26px, 3.6vw, 44px);   /* the section title */
  --fs-small:   13px;
  --fs-body:    16px;
  --fs-lead:    17px;
  --fs-h3:      22px;
  --fs-h2:      clamp(19px, 2.1vw, 27px);   /* the statement under it */
  --fs-h1:      clamp(46px, 7.5vw, 86px);   /* the page headline, in the hero */
  /* The old label size, still wanted for genuine labels — the ones that name a
     control or a column rather than opening a section. */
  --fs-label:   12px;

  /* ---- Space ---- */
  --gutter: 5vw;
  --section-y: clamp(64px, 9vw, 112px);
  --maxw: 1180px;

  /* ---- State ----
     "Something is wrong" as a token rather than a literal. The five places
     that spelled a red inline (#C0392B, #9a2b2b) all sat on cream and stopped
     being legible the moment the surface under them could go dark. */
  --danger: #B3261E;

  /* ---- Misc ---- */
  --radius: 2px;          /* angular, sporty — almost no rounding */
  --ease: cubic-bezier(.22,.61,.36,1);
}

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--body);
  background: var(--cream);
  color: var(--ink);
  line-height: 1.6;
  font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

.wrap { max-width: var(--maxw); margin: 0 auto; }

/* Tabular figures everywhere numbers carry meaning */
.tnum, .stat-num, .ladder *, .price-num, .tally-num, .step-num-big {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ==========================================================================
   NAV
   ========================================================================== */
.nav {
  position: sticky; top: 0; z-index: 100;
  background: var(--deep);
  border-bottom: 1px solid var(--line-d);
}
.nav-inner {
  max-width: var(--maxw); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--gutter); height: 66px;
}
.wordmark {
  font-family: var(--mark); font-weight: 900;
  font-size: 27px; letter-spacing: .01em; line-height: 1;
  color: var(--white); text-decoration: none; text-transform: uppercase;
  display: inline-flex; align-items: baseline; gap: 1px;
}
.wordmark span { color: var(--lime); }
.wordmark .dot { color: var(--lime); }

.nav-links { display: flex; align-items: center; gap: 30px; list-style: none; }
.nav-links a {
  color: var(--cream); text-decoration: none;
  font-size: 14px; font-weight: 600; opacity: .82;
  transition: opacity .15s var(--ease); position: relative; padding: 4px 0;
}
.nav-links a:hover, .nav-links a:focus-visible { opacity: 1; }
.nav-links a[aria-current="page"] { opacity: 1; }
.nav-links a[aria-current="page"]::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: 5px;
  height: 2px; background: var(--lime);
}
.btn-nav {
  background: var(--lime); color: var(--deep) !important; opacity: 1 !important;
  font-family: var(--display); font-size: 13px; padding: 11px 20px;
  text-decoration: none; letter-spacing: .03em; text-transform: uppercase;
  transition: filter .15s var(--ease), transform .1s var(--ease);
  border-radius: var(--radius);
}
.btn-nav:hover { filter: brightness(1.06); transform: translateY(-1px); }

.nav-toggle {
  display: none; background: none; border: 0; cursor: pointer; padding: 8px;
}
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--cream); margin: 5px 0; transition: .2s var(--ease); }

/* 1120px, not 860 — the switch is set by where the link row actually runs out
   of room, and with nine links plus two auth actions that happens well before
   a phone. Below it the drawer takes over. */
@media (max-width: 1120px) {
  .nav-toggle { display: block; }
  .nav-links {
    position: absolute; top: 66px; left: 0; right: 0;
    background: var(--deep); flex-direction: column; align-items: stretch;
    gap: 0; padding: 8px var(--gutter) 20px;
    border-bottom: 1px solid var(--line-d);
    transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: .2s var(--ease);
  }
  .nav-links a { padding: 14px 0; border-bottom: 1px solid var(--line-d); }
  .nav-links .btn-nav { text-align: center; margin-top: 12px; }
  .nav[data-open="true"] .nav-links { transform: none; opacity: 1; pointer-events: auto; }
  .nav[data-open="true"] .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav[data-open="true"] .nav-toggle span:nth-child(2) { opacity: 0; }
  .nav[data-open="true"] .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* ==========================================================================
   BUTTONS / PILLS / EYEBROWS
   ========================================================================== */
.btn-primary, .btn-ghost, .btn-dark {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--display); font-weight: 900; font-size: 15px;
  letter-spacing: .04em; text-transform: uppercase; text-decoration: none;
  padding: 16px 30px; border: 0; cursor: pointer; border-radius: var(--radius);
  transition: filter .15s var(--ease), transform .1s var(--ease), background .15s var(--ease);
}
.btn-primary { background: var(--lime); color: var(--deep); }
.btn-primary:hover { filter: brightness(1.07); transform: translateY(-2px); }
.btn-primary:active { transform: translateY(0); background: var(--lime-dk); }
.btn-ghost { background: transparent; color: var(--cream); border: 1.5px solid rgba(232,228,213,.4); }
.btn-ghost:hover { border-color: var(--cream); transform: translateY(-2px); }
.btn-dark { background: var(--deep); color: var(--lime); }
.btn-dark:hover { filter: brightness(1.25); transform: translateY(-2px); }
.on-dark .btn-ghost { color: var(--cream); }

.pill {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgba(200,241,53,.12); border: 1px solid rgba(200,241,53,.32);
  color: var(--lime); font-size: 13px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; padding: 7px 16px;
  border-radius: 100px;
}
.pill .dot { width: 7px; height: 7px; background: var(--lime); border-radius: 50%; box-shadow: 0 0 0 0 rgba(200,241,53,.6); animation: ping 2.4s var(--ease) infinite; }
@keyframes ping { 0% { box-shadow: 0 0 0 0 rgba(200,241,53,.5);} 70%,100% { box-shadow: 0 0 0 9px rgba(200,241,53,0);} }

/* THE SECTION TITLE. It reads first and it is now sized to be read first.
   Display face rather than the body's bold, because at 44px a 700-weight
   Archivo next to an Archivo Black headline in the hero looks like the same
   font failing to load. Tracking drops from .14em to .015em: letter-spacing
   that wide is a device for making 12px type legible, and at this size it
   just pulls the words apart. */
.eyebrow {
  font-family: var(--display);
  font-size: var(--fs-eyebrow); font-weight: 900; letter-spacing: .015em;
  line-height: 1.02; text-transform: uppercase; color: var(--forest);
  margin-bottom: 14px; max-width: 20ch;
}
/* Full strength on dark. At .8 it was a label being quiet; as the title of the
   section it is the thing the eye is meant to land on. */
.on-dark .eyebrow { color: var(--lime); opacity: 1; }
/* A genuine small label — a control's name, a column head — keeps the old
   treatment. Use this, not .eyebrow, when the thing is not a section title. */
.eyebrow-sm {
  font-family: var(--body);
  font-size: var(--fs-label); font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--forest); margin-bottom: 14px;
  max-width: none; line-height: 1.4;
}
.on-dark .eyebrow-sm { color: var(--lime); opacity: .85; }

/* ==========================================================================
   SECTION SCAFFOLD
   ========================================================================== */
.section { padding: var(--section-y) var(--gutter); }
.section > .wrap { max-width: var(--maxw); margin: 0 auto; }
.section.on-dark { background: var(--deep); color: var(--cream); }
.section.on-cream { background: var(--cream); }
.section.on-paper { background: var(--cream-2); }

/* THE STATEMENT, second in the ladder. Body face at 700 rather than the
   display black: two consecutive blocks of Archivo Black compete, and the
   title above has to win. Sentence case, because it is a sentence. */
.section-heading {
  font-family: var(--body); font-weight: 700; font-size: var(--fs-h2);
  letter-spacing: -.01em; line-height: 1.24; color: var(--ink);
  max-width: 720px; margin-bottom: 14px;
}
.on-dark .section-heading { color: var(--white); }
.section-heading em { color: var(--forest); font-style: normal; }
.on-dark .section-heading em { color: var(--lime); }
/* Third and smallest. */
.section-sub {
  font-size: var(--fs-lead); font-weight: 400; color: var(--muted);
  max-width: 600px; line-height: 1.62; margin-bottom: 40px;
}
.on-dark .section-sub { color: var(--cream); opacity: .82; }

/* ==========================================================================
   HERO
   ========================================================================== */
.hero {
  background:
    radial-gradient(1200px 480px at 50% -10%, rgba(200,241,53,.10), transparent 60%),
    var(--deep);
  padding: clamp(72px,11vw,116px) var(--gutter) clamp(56px,8vw,84px);
  text-align: center; position: relative; overflow: hidden;
}
.hero .pill { margin-bottom: 34px; }
.hero h1 {
  font-family: var(--display); font-size: var(--fs-h1); line-height: .98;
  color: var(--white); letter-spacing: -.02em; max-width: 880px; margin: 0 auto 26px;
}
.hero h1 em { color: var(--lime); font-style: normal; }
.hero-sub {
  font-size: clamp(16px,2vw,20px); color: var(--cream); opacity: .86;
  max-width: 560px; margin: 0 auto 40px; line-height: 1.6;
}
.hero-ctas { display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; margin-bottom: 14px; }
.cta-subtext { font-size: 13px; color: var(--lime); opacity: .72; letter-spacing: .02em; }

/* Stat scoreboard */
.hero-stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  margin-top: 64px; border-top: 1px solid var(--line-d); padding-top: 42px;
  max-width: 760px; margin-left: auto; margin-right: auto;
}
.stat { padding: 0 14px; }
.stat + .stat { border-left: 1px solid var(--line-d); }
.stat-num {
  font-family: var(--score); font-weight: 900; font-size: clamp(34px,5vw,52px);
  color: var(--lime); line-height: 1; letter-spacing: -.01em;
}
.stat-label { font-size: var(--fs-small); color: var(--cream); opacity: .72; margin-top: 8px; }

@media (max-width: 560px) {
  .hero-stats { grid-template-columns: 1fr; gap: 22px; }
  .stat + .stat { border-left: 0; border-top: 1px solid var(--line-d); padding-top: 22px; }
}

/* ==========================================================================
   THE LADDER — signature element (footy ladder + live charity tally)
   ========================================================================== */
.ladder-band { background: var(--deep); color: var(--cream); padding: var(--section-y) var(--gutter); }
.ladder-shell {
  max-width: var(--maxw); margin: 0 auto;
  display: grid; grid-template-columns: 1.15fr .85fr; gap: 28px; align-items: stretch;
}
.ladder {
  background: var(--panel); border: 1px solid var(--line-d); border-radius: var(--radius);
  overflow: hidden;
}
.ladder-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 1px solid var(--line-d);
  background: var(--deep-2);
}
.ladder-head .label { font-family: var(--mark); font-weight: 900; letter-spacing: .08em; text-transform: uppercase; font-size: 15px; color: var(--white); }
.ladder-head .round { font-size: 12px; color: var(--lime); letter-spacing: .08em; text-transform: uppercase; }
.ladder-row {
  display: grid; grid-template-columns: 34px 1fr auto auto;
  align-items: center; gap: 14px; padding: 13px 20px;
  border-bottom: 1px solid rgba(255,255,255,.06);
  transition: background .15s var(--ease);
}
.ladder-row:hover { background: rgba(255,255,255,.03); }
.ladder-row .rank { font-family: var(--score); font-weight: 900; font-size: 20px; color: var(--muted); }
.ladder-row.top .rank { color: var(--lime); }
.ladder-row .who { font-weight: 600; color: var(--cream); }
.ladder-row .who small { display: block; font-weight: 400; font-size: 12px; color: var(--muted); }
.ladder-row .form { display: flex; gap: 3px; }
.ladder-row .form i { width: 8px; height: 8px; border-radius: 2px; background: rgba(255,255,255,.18); }
.ladder-row .form i.w { background: var(--lime); }
.ladder-row .pts { font-family: var(--score); font-weight: 900; font-size: 20px; color: var(--white); min-width: 42px; text-align: right; }

.tally {
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid rgba(200,241,53,.25); border-radius: var(--radius);
  padding: 28px; display: flex; flex-direction: column; justify-content: center;
}
.tally .eyebrow { color: var(--lime); opacity: .8; }
.tally-num {
  font-family: var(--score); font-weight: 900; font-size: clamp(48px,7vw,76px);
  color: var(--lime); line-height: 1; letter-spacing: -.02em;
}
.tally-cap { font-size: var(--fs-small); color: var(--cream); opacity: .8; margin-top: 10px; line-height: 1.6; }
.tally-bar { height: 8px; background: rgba(255,255,255,.1); border-radius: 100px; margin-top: 22px; overflow: hidden; }
.tally-bar > i { display: block; height: 100%; width: 0; background: var(--lime); border-radius: 100px; transition: width 1.4s var(--ease); }
.tally-meta { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); margin-top: 10px; }

@media (max-width: 820px) { .ladder-shell { grid-template-columns: 1fr; } }

/* ==========================================================================
   ILLUSTRATIVE FIGURES — saying so, next to the number
   --------------------------------------------------------------------------
   Every dollar amount on the marketing pages that is NOT real money wears one
   of these (client, Sep 2026: "Add disclaimers on every non-real dollar
   figure ... label clearly as illustrative, not actual funds raised").

   A chip beside the figure and a line under it, rather than a footnote at the
   bottom of the section: a reader who scans a big green $8,420 and moves on
   has to meet the word EXAMPLE in the same glance, or the disclaimer has not
   disclaimed anything.

   Both take their colour from the text around them (currentColor), so the same
   two classes work on the dark ladder band, the auth aside's veiled photo and
   a cream news card without a variant per surface.
   ========================================================================== */
.illus-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mark); font-size: 10px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 100px; vertical-align: middle;
  border: 1px solid currentColor; color: currentColor; opacity: .75;
  white-space: nowrap;
}
/* Beside a very large number the chip should sit with the baseline, not float
   up level with the digits' caps. */
.illus-tag.is-beside { margin-left: 10px; transform: translateY(-.35em); }
.illus-note {
  display: block; margin-top: 8px;
  font-size: 12px; line-height: 1.5; color: currentColor; opacity: .7;
}



/* ==========================================================================
   COMPARISON (them vs us)
   ========================================================================== */
.compare-frame { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.compare-col { padding: 30px 28px; }
.col-them { background: var(--cream-2); }
.col-us { background: var(--deep); color: var(--cream); }
.compare-header { margin-bottom: 18px; }
.compare-header .lab { font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.col-us .compare-header .lab { color: var(--lime); }
.compare-header h3 { font-family: var(--display); font-size: var(--fs-h3); line-height: 1.1; margin-top: 6px; color: var(--ink); }
.col-us .compare-header h3 { color: var(--white); }
.compare-cell { display: flex; align-items: flex-start; gap: 12px; padding: 11px 0; font-size: 15px; border-top: 1px solid var(--line); }
.col-us .compare-cell { border-top: 1px solid rgba(255,255,255,.08); color: var(--cream); }
.cell-icon { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 900; margin-top: 1px; }
.icon-no { background: rgba(15,46,34,.08); color: var(--muted); }
.icon-yes { background: var(--lime); color: var(--deep); }
.compare-callout { margin-top: 28px; background: var(--deep); color: var(--cream); padding: 30px 32px; border-radius: var(--radius); display: flex; gap: 18px; align-items: flex-start; }
.compare-callout .mark { font-family: var(--display); font-size: 56px; line-height: .6; color: var(--lime); }
.compare-callout p { font-size: var(--fs-lead); line-height: 1.6; }
.compare-callout strong { color: var(--lime); }
@media (max-width: 700px) { .compare-frame { grid-template-columns: 1fr; } }

/* ==========================================================================
   STEPS (real sequence — numbering justified)
   ========================================================================== */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.step { background: var(--cream-2); padding: 32px 28px; }
.step-num { font-family: var(--mark); font-weight: 900; letter-spacing: .12em; text-transform: uppercase; font-size: 13px; color: var(--forest); }
.step-icon { width: 46px; height: 46px; border-radius: 50%; background: var(--lime); display: grid; place-items: center; margin: 18px 0 16px; }
.step h3 { font-family: var(--display); font-size: var(--fs-h3); line-height: 1.12; margin-bottom: 10px; color: var(--ink); }
.step p { font-size: 15px; color: var(--muted); }
@media (max-width: 760px) { .steps { grid-template-columns: 1fr; } }

/* big numbered step blocks (how-it-works page) */
.step-block { display: grid; grid-template-columns: 110px 1fr; gap: 28px; padding: 36px 0; border-top: 1px solid var(--line); }
.step-block:first-of-type { border-top: 0; }
.step-num-big { font-family: var(--score); font-weight: 900; font-size: 64px; color: var(--lime); line-height: .9; -webkit-text-stroke: 1px var(--forest); }
.step-tag { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--forest); }
.step-block h2 { font-family: var(--display); font-size: clamp(24px,3vw,34px); line-height: 1.06; margin: 8px 0 12px; color: var(--ink); }
.step-detail { font-size: 15px; color: var(--muted); max-width: 620px; }
@media (max-width: 600px) { .step-block { grid-template-columns: 1fr; gap: 8px; } .step-num-big { font-size: 48px; } }

/* ==========================================================================
   CARD GRIDS (who it's for / supporters / charities)
   ========================================================================== */
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 20px; }
.grid-4 { display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; }
@media (max-width: 900px) { .grid-3 { grid-template-columns: 1fr; } .grid-4 { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 520px) { .grid-4 { grid-template-columns: 1fr; } }

.for-card { background: var(--cream-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 28px; display: flex; flex-direction: column; }
.for-tag { align-self: flex-start; font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--deep); background: var(--lime); padding: 5px 12px; border-radius: 100px; margin-bottom: 16px; }
.for-card h3 { font-family: var(--display); font-size: var(--fs-h3); line-height: 1.12; margin-bottom: 10px; }
.for-card p { font-size: 14px; color: var(--muted); margin-bottom: 18px; }
.for-link { margin-top: auto; color: var(--forest); font-weight: 700; text-decoration: none; font-size: 14px; }
.for-link:hover { color: var(--deep); }

.amb-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.amb-photo { aspect-ratio: 1/1; background: var(--deep); display: grid; place-items: center; }
.amb-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.amb-photo .ph { text-align: center; color: rgba(200,241,53,.55); }
.amb-photo .ph .ring { width: 64px; height: 64px; border-radius: 50%; border: 2px dashed rgba(200,241,53,.4); margin: 0 auto 10px; }
.amb-photo .ph span { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; }
.amb-body { padding: 18px 20px; }
.amb-body .name { font-family: var(--display); font-size: 16px; color: var(--ink); margin-bottom: 4px; }
.amb-body .role { font-size: 12px; color: var(--forest); font-weight: 700; margin-bottom: 8px; }
.amb-body .quote { font-size: 13px; color: var(--muted); line-height: 1.5; }

.charity-card { background: var(--cream-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; text-align: center; }
.charity-icon { width: 44px; height: 44px; border-radius: 50%; background: var(--forest); margin: 0 auto 12px; display: grid; place-items: center; color: var(--lime); font-family: var(--display); font-size: 16px; }
.charity-icon.alt { background: var(--deep); }
.charity-card h4 { font-size: 14px; color: var(--ink); margin-bottom: 2px; }
.charity-card p { font-size: 12px; color: var(--muted); }
.charity-card .tagx { display: inline-block; margin-top: 8px; font-size: 11px; color: var(--forest); font-weight: 700; }
.cat-label { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--forest); margin: 8px 0 16px; }

/* ==========================================================================
   QUOTE BREAK
   ========================================================================== */
.quote-break { background: var(--lime); padding: clamp(48px,7vw,84px) var(--gutter); text-align: center; }
.quote-break blockquote { font-family: var(--display); font-size: clamp(26px,4vw,46px); line-height: 1.08; color: var(--deep); max-width: 880px; margin: 0 auto; letter-spacing: -.02em; }
.quote-break blockquote em { font-style: normal; text-decoration: underline; text-decoration-thickness: 3px; text-underline-offset: 5px; }
.quote-attr { margin-top: 18px; font-family: var(--mark); font-weight: 900; letter-spacing: .12em; text-transform: uppercase; font-size: 14px; color: var(--forest); }

/* ==========================================================================
   PRICING
   ========================================================================== */
.pricing-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; align-items: stretch; }
@media (max-width: 980px) { .pricing-grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 520px) { .pricing-grid { grid-template-columns: 1fr; } }
.price-card { background: var(--cream-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 28px 24px; display: flex; flex-direction: column; position: relative; }
.price-card.featured { background: var(--deep); color: var(--cream); border-color: var(--lime); }
.price-badge { position: absolute; top: -12px; left: 24px; background: var(--lime); color: var(--deep); font-family: var(--display); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; padding: 5px 12px; border-radius: 100px; }
.price-tier { font-family: var(--display); font-size: 17px; color: var(--ink); margin-bottom: 4px; }
.price-card.featured .price-tier { color: var(--white); }
.price-size { font-size: 12px; color: var(--muted); margin-bottom: 16px; }
.price-card.featured .price-size { color: var(--cream); opacity: .7; }
.price-num { font-family: var(--score); font-weight: 900; font-size: 46px; color: var(--ink); line-height: 1; }
.price-card.featured .price-num { color: var(--lime); }
.price-period { font-size: 13px; color: var(--muted); margin: 2px 0 4px; }
.price-per { font-size: 12px; color: var(--forest); font-weight: 700; margin-bottom: 16px; }
.price-card.featured .price-per { color: var(--lime); opacity: .85; }
.price-desc { font-size: 13px; color: var(--muted); line-height: 1.6; }
.price-card.featured .price-desc { color: var(--cream); opacity: .82; }
.pricing-footnote { margin-top: 28px; font-size: 14px; color: var(--muted); }
.pricing-footnote a { color: var(--forest); font-weight: 700; }

/* tier table (pricing page) */
.tier-table { width: 100%; border-collapse: collapse; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.tier-table th, .tier-table td { padding: 18px 18px; text-align: left; border-bottom: 1px solid var(--line); }
.tier-table thead th { background: var(--deep); color: var(--cream); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; }
.tier-table tbody tr:hover { background: var(--cream-2); }
.tier-table .tname { font-family: var(--display); font-size: 16px; color: var(--ink); }
.tier-table .tsize { font-size: 12px; color: var(--muted); }
.tier-table .tprice { font-family: var(--score); font-weight: 900; font-size: 24px; color: var(--ink); }
.tier-table .tprice span { font-size: 12px; color: var(--muted); font-family: var(--body); font-weight: 400; }
.tier-table .tper { font-size: 12px; color: var(--forest); font-weight: 700; }
/* ---- the groups tag, on a price CARD ----
   The table on /pricing/ marks these rows with .tier-badge--groups; the cards
   needed the same mark and had none (client, 22 Sep 2026). Same palette, so
   the two pages read as one product, but its own class because it sits under
   the price rather than beside a name, and because it has to survive landing
   on the dark featured card. */
.price-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 12px; }
.price-tag {
  font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 100px;
  background: rgba(200,241,53,.22); color: var(--forest);
  border: 1px solid rgba(45,122,58,.42);
}
/* On --deep, forest-on-translucent-lime measures 2.4:1. The lime itself is
   9.9:1 there, so the dark card takes the colours the other way round. */
.price-card.featured .price-tag {
  background: rgba(200,241,53,.16); color: var(--lime); border-color: rgba(200,241,53,.45);
}

.tier-badge { display: inline-block; margin-left: 8px; background: var(--lime); color: var(--deep); font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; padding: 2px 8px; border-radius: 100px; }

/* feature grid (no gates) */
.feature-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 18px; }
@media (max-width: 820px){ .feature-grid { grid-template-columns: 1fr; } }
.feature { padding: 22px; background: var(--cream-2); border: 1px solid var(--line); border-radius: var(--radius); }
.feature .tick { width: 24px; height: 24px; border-radius: 50%; background: var(--lime); color: var(--deep); display: grid; place-items: center; font-weight: 900; margin-bottom: 12px; }
.feature h3 { font-size: 16px; color: var(--ink); margin-bottom: 6px; }
.feature p { font-size: 13px; color: var(--muted); }

/* ==========================================================================
   MONEY-FLOW (numbered, on dark)
   ========================================================================== */
.flow { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; background: rgba(255,255,255,.06); max-width: 920px; }
@media (max-width: 760px){ .flow { grid-template-columns: 1fr; } }
.flow-cell { background: var(--panel); padding: 34px 28px; }
.flow-cell.hot { background: var(--panel-2); }
.flow-num { font-family: var(--score); font-weight: 900; font-size: 38px; color: var(--lime); line-height: 1; margin-bottom: 12px; }
.flow-cell h4 { font-size: 15px; font-weight: 700; color: var(--white); margin-bottom: 10px; }
.flow-cell p { font-size: 13px; color: var(--cream); opacity: .72; line-height: 1.7; }
.flow-note { margin-top: 36px; max-width: 700px; border-left: 3px solid var(--lime); padding-left: 26px; font-size: var(--fs-lead); color: var(--cream); line-height: 1.7; }

/* ==========================================================================
   FOUNDING BAND
   ========================================================================== */
.founding { background: var(--lime); padding: clamp(44px,6vw,72px) var(--gutter); }
.founding-inner { max-width: var(--maxw); margin: 0 auto; display: grid; grid-template-columns: 1.4fr 1fr; gap: 36px; align-items: center; }
@media (max-width: 820px){ .founding-inner { grid-template-columns: 1fr; } }
.founding .eyebrow { color: var(--deep); opacity: .65; }
.founding h2 { font-family: var(--display); font-size: clamp(28px,4vw,44px); line-height: 1.04; color: var(--deep); letter-spacing: -.02em; margin-bottom: 14px; }
.founding p { color: var(--deep); opacity: .82; font-size: 15px; max-width: 560px; }
.founding-cta { display: flex; flex-direction: column; gap: 12px; }
.founding-deadline { font-size: 13px; color: var(--deep); font-weight: 700; }

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq-list { max-width: 820px; }
.faq-item { border-top: 1px solid var(--line); }
.faq-item:last-child { border-bottom: 1px solid var(--line); }
.faq-q { width: 100%; text-align: left; background: none; border: 0; cursor: pointer; font-family: var(--body); font-weight: 700; font-size: 17px; color: var(--ink); padding: 20px 0; display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.faq-q::after { content: "+"; font-family: var(--display); color: var(--forest); font-size: 22px; transition: transform .2s var(--ease); }
.faq-item[data-open="true"] .faq-q::after { transform: rotate(45deg); }
.faq-a { font-size: 15px; color: var(--muted); line-height: 1.7; max-height: 0; overflow: hidden; transition: max-height .28s var(--ease), padding .28s var(--ease); }
.faq-item[data-open="true"] .faq-a { max-height: 320px; padding-bottom: 20px; }

/* ==========================================================================
   CONTACT
   ========================================================================== */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; max-width: 880px; }
@media (max-width: 760px){ .contact-grid { grid-template-columns: 1fr; gap: 28px; } }
.contact-info { color: var(--cream); font-size: 15px; line-height: 2; opacity: .8; }
.contact-info .dot { color: var(--lime); }
.form-col { display: flex; flex-direction: column; gap: 12px; }
.form-col input, .form-col select, .form-col textarea {
  width: 100%; padding: 14px 16px; background: rgba(255,255,255,.07);
  border: 1px solid rgba(255,255,255,.15); color: var(--white);
  font-family: var(--body); font-size: 14px; border-radius: var(--radius); outline: none;
}
.form-col input::placeholder, .form-col textarea::placeholder { color: rgba(255,255,255,.5); }
.form-col input:focus, .form-col select:focus, .form-col textarea:focus { border-color: var(--lime); }
.form-col select { -webkit-appearance: none; appearance: none; color: rgba(255,255,255,.7); }
.form-col button { background: var(--lime); color: var(--deep); font-family: var(--display); font-size: 14px; font-weight: 900; letter-spacing: .06em; text-transform: uppercase; padding: 16px 28px; border: 0; cursor: pointer; border-radius: var(--radius); transition: filter .15s var(--ease); }
.form-col button:hover { filter: brightness(1.07); }

/* ==========================================================================
   THE WALL — net-new social layer (the competitive gap)
   ========================================================================== */
.wall-shell { display: grid; grid-template-columns: 280px 1fr 300px; gap: 24px; align-items: start; }
@media (max-width: 1040px){ .wall-shell { grid-template-columns: 1fr; } .wall-side { display: none; } }

.wall-side { position: sticky; top: 90px; display: flex; flex-direction: column; gap: 16px; }
.wall-card { background: var(--panel); border: 1px solid var(--line-d); border-radius: var(--radius); padding: 18px; }
.wall-card h4 { font-family: var(--mark); font-weight: 900; letter-spacing: .08em; text-transform: uppercase; font-size: 14px; color: var(--white); margin-bottom: 12px; }
.mini-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-top: 1px solid rgba(255,255,255,.06); }
.mini-row:first-of-type { border-top: 0; }
.mini-row .r { font-family: var(--score); font-weight: 900; color: var(--lime); width: 22px; }
.mini-row .n { font-size: 14px; color: var(--cream); flex: 1; }
.mini-row .p { font-family: var(--score); font-weight: 900; color: var(--white); }

.composer { background: var(--panel); border: 1px solid var(--line-d); border-radius: var(--radius); padding: 16px 18px; display: flex; gap: 12px; align-items: flex-start; }
.composer .av { width: 40px; height: 40px; border-radius: 50%; background: var(--forest); color: var(--lime); display: grid; place-items: center; font-family: var(--display); font-size: 15px; flex: none; }
.composer .grow { flex: 1; }
.composer textarea { width: 100%; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12); color: var(--white); border-radius: var(--radius); padding: 10px 12px; font-family: var(--body); font-size: 14px; resize: none; outline: none; }
.composer textarea::placeholder { color: rgba(255,255,255,0.49); }
.composer-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; }
.composer-actions .hint { font-size: 12px; color: var(--muted); }

.post { background: var(--panel); border: 1px solid var(--line-d); border-radius: var(--radius); padding: 18px; margin-top: 16px; }
.post-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.post-head .av { width: 38px; height: 38px; border-radius: 50%; background: var(--deep-2); color: var(--lime); display: grid; place-items: center; font-family: var(--display); font-size: 14px; }
.post-head .meta { line-height: 1.2; }
.post-head .meta b { color: var(--white); font-size: 14px; }
.post-head .meta small { color: var(--muted); font-size: 12px; }
.post-head .tag { margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--deep); background: var(--lime); padding: 3px 10px; border-radius: 100px; }
.post-head .tag.sys { background: rgba(200,241,53,.15); color: var(--lime); }
.post-body { font-size: 15px; color: var(--cream); line-height: 1.6; }
.post-body .pick { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1); padding: 3px 10px; border-radius: 100px; font-size: 13px; font-weight: 600; color: var(--lime); margin: 0 2px; }
.post-foot { display: flex; gap: 8px; margin-top: 14px; }
.react { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); color: var(--cream); border-radius: 100px; padding: 6px 12px; font-size: 13px; cursor: pointer; transition: .15s var(--ease); user-select: none; }
.react:hover { background: rgba(200,241,53,.14); border-color: rgba(200,241,53,.4); }
.react[data-on="true"] { background: var(--lime); color: var(--deep); border-color: var(--lime); }
.react .c { font-variant-numeric: tabular-nums; }

.wall-tally { background: linear-gradient(180deg, var(--panel-2), var(--panel)); border: 1px solid rgba(200,241,53,.25); border-radius: var(--radius); padding: 20px; text-align: center; }
.wall-tally .tally-num { font-size: clamp(34px,5vw,46px); }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.footer { background: var(--deep-2); color: var(--cream); padding: clamp(48px,6vw,72px) var(--gutter) 32px; }
.footer-top { max-width: var(--maxw); margin: 0 auto; display: grid; grid-template-columns: 1.2fr 2fr; gap: 40px; }
@media (max-width: 760px){ .footer-top { grid-template-columns: 1fr; } }
/* The footer mark is the nav's wordmark, same markup, so it inherits .wordmark
   wholesale and only needs its size settling for the smaller context. It used
   to be an <img> of a second, lime-block version of the logo — hence the
   line-height:0 and overflow:hidden that lived here, both of which would flatten
   type. */
.footer-logo.wordmark { font-size: 30px; display: inline-flex; }
.footer-brand p { font-size: 14px; color: var(--cream); opacity: .7; margin-top: 12px; }
.footer-links { display: grid; grid-template-columns: repeat(3,1fr); gap: 24px; }
@media (max-width: 520px){ .footer-links { grid-template-columns: 1fr 1fr; } }
.footer-col h4 { font-family: var(--mark); font-weight: 900; letter-spacing: .08em; text-transform: uppercase; font-size: 13px; color: var(--lime); margin-bottom: 12px; }
.footer-col ul { list-style: none; }
.footer-col li { margin-bottom: 8px; }
.footer-col a { color: var(--cream); opacity: .75; text-decoration: none; font-size: 14px; }
.footer-col a:hover { opacity: 1; color: var(--lime); }
.footer-bottom { max-width: var(--maxw); margin: 36px auto 0; padding-top: 24px; border-top: 1px solid var(--line-d); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
.footer-legal { font-size: 12px; color: var(--cream); opacity: .6; line-height: 1.6; }
.footer-disclaimer { font-size: 12px; color: var(--lime); opacity: .7; }

/* ==========================================================================
   SCROLL REVEAL + MOTION
   ========================================================================== */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* `*:not(.shot)`, not `*`. The blanket version used `!important`, which beats
     even an animation's own declarations, so it silently killed every calm,
     deliberately-reduced alternative the rest of this file defines — including
     the scene crossfade and the alternating zoom, both of which have explicit
     reduced-motion variants written for exactly this case. The result was that
     anyone with the setting on saw photos hard-cut with no movement at all,
     while the code read as though it had been handled.

     A slow scale and a fade are not the kind of movement this query exists to
     suppress; travel, parallax and spin are, and those are all still caught. */
  /* Split in two because the descendant selector below cannot reach <html>
     itself, and smooth scrolling is set on <html> — folding it into the
     blanket would have quietly left smooth-scroll on for exactly the people
     who asked for less movement. */
  html:not(.gt-motion) { scroll-behavior: auto !important; }
  /* `.reveal` joins `.shot` in the exemption. Both have deliberate, calmer
     reduced-motion variants written for them a few thousand lines down, and a
     blanket !important here silently overrode every one of them — which is
     how the wave came to do nothing at all rather than doing less. */
  /* `.busy-run` marks the spinners that report a submitted form is still
     running. Those are status, not decoration — a frozen "signing you in" is
     worse than none — and the comment beside them claimed they survived this
     block. They did not: `animation: none !important` here outranks the
     `animation-duration` override written down there, so every busy indicator
     on the site was already dead for reduced-motion users. They are slowed
     instead, which keeps the meaning without the flicker. */
  /* `.lanim` marks the loader scenes' moving parts, and it replaced `.ball`
     and `.posts` here — which were the OLD single loader's classes and went on
     sitting in this list after the six competition scenes took over. The
     effect was total and silent: every scene frozen on any machine with the
     setting on, and State of Origin, whose shield draws itself with
     stroke-dashoffset, frozen at offset 420 — which is to say invisible. The
     splash showed a wordmark over an empty box.

     Like `.busy-run`, these are not decoration: the loader exists to say
     something is happening, and a still one says the opposite. They are
     SLOWED instead, in the reduced-motion block beside the scenes themselves.
     The class goes on the element and never on a wrapper, because `:not()`
     here matches elements, not ancestry. */
  html:not(.gt-motion) *:not(.shot):not(.reveal):not(.busy-run):not(.lanim) { animation: none !important; transition: none !important; }
  .reveal { opacity: 1; transform: none; }
  .pill .dot { animation: none; }
}

/* focus visibility */
:focus-visible { outline: 3px solid var(--lime); outline-offset: 2px; }

/* small helpers */
.center { text-align: center; }
.mt-s { margin-top: 14px; } .mt-m { margin-top: 28px; } .mt-l { margin-top: 48px; }
.note { font-size: 14px; color: var(--muted); }
.note a { color: var(--forest); font-weight: 700; text-decoration: none; }

/* ==========================================================================
   ============ v2 ENHANCEMENTS — "push it to a 10" =========================
   Sport loader · animated hero · interactive 3D cards · dark Wall · motion
   ========================================================================== */

@property --ang { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

/* ---------- PAGE LOADER (sport-themed, one scene per competition) ----------
   The scenes, their six motions and their reduced-motion variants live in
   "THE LOADER SCENES" near the end of this file, beside the markup that uses
   them (templates/partials/_loader_scenes.html).

   What was here was the SINGLE loader those replaced — .pitch, .ball, .ground,
   the kick and its calm variant, the post flash. It was dead the moment the
   three base templates started including the scene partial, and it was worse
   than dead: `.ball` and `.posts` were still named in the blanket
   reduced-motion exemption above, so the exemption protected markup that no
   longer existed while the scenes that replaced it were frozen. Removed
   together, which is the only way that stays true. */
.loader {
  position: fixed; inset: 0; z-index: 9999;
  display: grid; place-items: center;
  background:
    radial-gradient(900px 500px at 50% 12%, rgba(207,249,58,.10), transparent 60%),
    var(--deep-2);
  transition: opacity .55s var(--ease), visibility .55s var(--ease);
}
.loader.out { opacity: 0; visibility: hidden; }
html:not(.js) .loader { display: none; }
body.loading { overflow: hidden; }

.loader-inner { text-align: center; transform: translateY(-6px); }
.loader-word { font-family: var(--mark); font-weight: 900; font-size: 34px; letter-spacing: .02em; color: var(--white); text-transform: uppercase; }
.loader-word span { color: var(--lime); }
.loader-sport { font-family: var(--mark); font-weight: 600; letter-spacing: .28em; text-transform: uppercase; font-size: 12px; color: var(--lime); opacity: .85; margin-top: 8px; }
.loader-bar { width: 180px; height: 5px; background: rgba(255,255,255,.12); border-radius: 100px; margin: 22px auto 0; overflow: hidden; }
/* The fill is driven from gt-loader.js against real load progress, not a fixed
   keyframe. A keyframe always took the same 2.9s whether the page was ready in
   80ms or four seconds, so the bar was decoration that happened to look like a
   progress meter — and it reached 100% while the page was still fetching. This
   width is only ever moved by something actually completing. */
.loader-bar > i {
  display: block; height: 100%; width: 0; background: var(--lime);
  border-radius: 100px; transition: width .3s var(--ease);
  box-shadow: 0 0 10px rgba(200,241,53,.55);
}

/* ---------- HERO: ambient motion + staggered intro ---------- */
.hero::after {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 140%;
  background:
    radial-gradient(420px 220px at 18% 24%, rgba(200,241,53,.10), transparent 60%),
    radial-gradient(520px 260px at 84% 10%, rgba(45,122,58,.30), transparent 60%);
  filter: blur(2px); animation: meshDrift 16s ease-in-out infinite alternate; pointer-events: none;
}
.hero > * { position: relative; z-index: 1; }
@keyframes meshDrift { 0% { transform: translate3d(0,0,0) scale(1); } 100% { transform: translate3d(-3%,2%,0) scale(1.06); } }

.js .hero .pill,
.js .hero h1,
.js .hero .hero-sub,
.js .hero .hero-ctas,
.js .hero .cta-subtext,
.js .hero .hero-stats { opacity: 0; transform: translateY(16px); }
.js body.ready .hero .pill,
.js body.ready .hero h1,
.js body.ready .hero .hero-sub,
.js body.ready .hero .hero-ctas,
.js body.ready .hero .cta-subtext,
.js body.ready .hero .hero-stats {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.js body.ready .hero h1 { transition-delay: .08s; }
.js body.ready .hero .hero-sub { transition-delay: .18s; }
.js body.ready .hero .hero-ctas { transition-delay: .28s; }
.js body.ready .hero .cta-subtext { transition-delay: .36s; }
.js body.ready .hero .hero-stats { transition-delay: .46s; }
.hero .stat-num { transition: transform .3s var(--ease); }
.hero .stat:hover .stat-num { transform: translateY(-3px) scale(1.04); }

/* ---------- BUTTONS: shine sweep ---------- */
.btn-primary { position: relative; overflow: hidden; }
.btn-primary::after {
  content: ""; position: absolute; top: 0; left: -120%; width: 60%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.55), transparent);
  transform: skewX(-18deg); transition: left .55s var(--ease);
}
.btn-primary:hover::after { left: 150%; }

/* ---------- INTERACTIVE 3D CARDS (applied via JS: .card-pro) ---------- */
.card-pro {
  position: relative; isolation: isolate;
  transition: transform .28s var(--ease), box-shadow .28s var(--ease), border-color .28s var(--ease);
  transform: perspective(1000px) rotateX(0) rotateY(0);
}
.card-pro::before {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: inherit; opacity: 0; transition: opacity .28s var(--ease);
  background: radial-gradient(360px circle at var(--mx,50%) var(--my,50%), rgba(200,241,53,.16), transparent 42%);
}
.card-pro > * { position: relative; z-index: 1; }
@media (hover: hover) and (pointer: fine) {
  .card-pro:hover {
    transform: perspective(1000px) rotateX(var(--rx,0)) rotateY(var(--ry,0)) translateY(-6px);
    box-shadow: 0 26px 60px -28px rgba(0,0,0,.55);
    border-color: rgba(200,241,53,.5);
    z-index: 3;
  }
  .card-pro:hover::before { opacity: 1; }
  /* animated top accent */
  .card-pro::after {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px; z-index: 3;
    background: var(--lime); transform: scaleX(0); transform-origin: left; transition: transform .3s var(--ease);
  }
  .card-pro:hover::after { transform: scaleX(1); }
  .for-card:hover .for-tag, .feature:hover .tick, .step:hover .step-icon { transform: translateY(-2px) scale(1.06); }
  .for-tag, .tick, .step-icon { transition: transform .25s var(--ease); }
  .charity-card:hover .charity-icon { transform: rotate(-6deg) scale(1.1); transition: transform .25s var(--ease); }
}

/* featured pricing: rotating conic glow border */
.price-card.featured { overflow: visible; }
.price-card.featured::after {
  content: ""; position: absolute; inset: -1.5px; z-index: 0; border-radius: inherit;
  background: conic-gradient(from var(--ang), transparent 0 12%, var(--lime) 22%, rgba(200,241,53,.2) 32%, transparent 48% 100%);
  animation: spinBorder 5s linear infinite; transform: none !important; opacity: .9;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; padding: 1.5px;
}
.price-card.featured > * { position: relative; z-index: 1; }
@keyframes spinBorder { to { --ang: 360deg; } }

/* count-up emphasis */
.tally-num, .stat-num { transition: text-shadow .3s var(--ease); }
.ladder-band .tally:hover .tally-num { text-shadow: 0 0 26px rgba(200,241,53,.45); }

/* ---------- THE WALL — true dark app surface ---------- */
.wall-stage {
  background:
    radial-gradient(700px 360px at 84% -8%, rgba(200,241,53,.08), transparent 60%),
    radial-gradient(600px 320px at 8% 4%, rgba(45,122,58,.16), transparent 60%),
    linear-gradient(180deg, #070908, #0a0d0b);
  position: relative; color: var(--cream);
}
.wall-stage::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image: linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px);
  background-size: 44px 44px; mask-image: radial-gradient(800px 600px at 50% 0%, #000, transparent 75%);
}
.wall-stage .section-heading { color: var(--white); }
.wall-stage .post, .wall-stage .composer, .wall-stage .wall-card, .wall-stage .wall-tally {
  background: linear-gradient(180deg, #11161300, #0000) , #0e1311;
  border: 1px solid rgba(255,255,255,.08);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 16px 36px -24px rgba(0,0,0,.9);
  border-radius: 10px;
}
.wall-stage .wall-tally { background: linear-gradient(180deg, #12180f, #0d120f); border-color: rgba(200,241,53,.22); }
.wall-stage .post { transition: transform .25s var(--ease), border-color .25s var(--ease), box-shadow .25s var(--ease); }
.wall-stage .post:hover { transform: translateY(-3px); border-color: rgba(200,241,53,.35); box-shadow: 0 24px 50px -28px rgba(0,0,0,.9); }
.wall-stage .composer textarea, .wall-stage .composer { background: #0c100e; }
.wall-stage .react { background: #141a17; border-color: rgba(255,255,255,.1); }
.wall-stage .react:hover { background: rgba(200,241,53,.14); }
.wall-stage .mini-row { border-top-color: rgba(255,255,255,.06); }
.wall-stage .wall-card h4 { color: #fff; }

/* new-post entrance + tally pulse + reaction pop */
.post.new { animation: postIn .5s var(--ease); }
@keyframes postIn { from { opacity: 0; transform: translateY(-10px) scale(.99); } to { opacity: 1; transform: none; } }
.post.flash { box-shadow: 0 0 0 1px var(--lime), 0 0 30px -6px rgba(200,241,53,.5); }
.tally-num.bump { animation: bump .5s var(--ease); }
@keyframes bump { 30% { transform: scale(1.08); color: #e9ff6b; } }
.react { transition: transform .12s var(--ease), background .15s var(--ease), border-color .15s var(--ease); }
.react:active { transform: scale(.92); }
.react.pop { animation: pop .32s var(--ease); }
@keyframes pop { 40% { transform: scale(1.18); } }
.float-heart { position: absolute; pointer-events: none; font-size: 16px; animation: floatUp .9s var(--ease) forwards; }
@keyframes floatUp { to { transform: translateY(-34px) scale(1.3); opacity: 0; } }

/* typing indicator */
.typing { display: inline-flex; gap: 4px; align-items: center; color: var(--muted); font-size: 13px; }
.typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--lime); opacity: .6; animation: blink 1s infinite; }
.typing i:nth-child(2){ animation-delay: .15s; } .typing i:nth-child(3){ animation-delay: .3s; }
@keyframes blink { 0%,100% { opacity: .25; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-3px); } }

/* live dot */
.live-dot { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--lime); font-weight: 700; }
.live-dot::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--lime); box-shadow: 0 0 0 0 rgba(200,241,53,.7); animation: ping 1.8s var(--ease) infinite; }

/* ---------- REDUCED MOTION: calm everything ---------- */
@media (prefers-reduced-motion: reduce) {
  /* .loader-bar > i is deliberately NOT in this list. It used to be, which left
     the bar frozen at width:0 — a progress meter that never moved, for exactly
     the users least able to tell a slow page from a broken one. It is now a
     width transition driven by real progress, which is feedback rather than
     decoration, so it stays. The loader's scenes are off this list for the
     same reason and are exempted as `.lanim` in the blanket rule above:
     freezing one leaves a still picture on a screen whose whole job is to say
     something is happening. They get slowed variants instead — see the
     reduced-motion block beside the scenes themselves. */
  .hero::after, .price-card.featured::after,
  .post.new, .tally-num.bump, .react.pop, .typing i, .live-dot::before { animation: none !important; }
  .js .hero .pill, .js .hero h1, .js .hero .hero-sub, .js .hero .hero-ctas,
  .js .hero .cta-subtext, .js .hero .hero-stats { opacity: 1 !important; transform: none !important; }
  .card-pro { transform: none !important; }
}

/* ==========================================================================
   ============ v3 — card colours · scoreboard · scroll · images ============
   ========================================================================== */

:root {
  /* tinted card surfaces (greener than cream, low-sat, harmonised) */
  --card-1: #E3EBDA;  --acc-1: #2D7A3A;   /* sage / forest    */
  --card-2: #D9E8E1;  --acc-2: #1F7A63;   /* mint / teal      */
  --card-3: #DFE6CE;  --acc-3: #6E8B2E;   /* stone / olive    */
  --card-4: #DEE7E7;  --acc-4: #2E6E7A;   /* sky-sage / teal-blue */
}

/* ---------- TINTED CARDS (replace the flat cream) ---------- */
.for-card, .price-card, .feature, .charity-card, .step {
  background: var(--card-1);
  border-color: rgba(15,46,34,.10);
  border-left: 3px solid var(--acc-1);
}
/* rotate tints + accents across grid children for gentle variety */
.grid-3 > *:nth-child(3n+1), .feature-grid > *:nth-child(3n+1), .steps > *:nth-child(3n+1) { background: var(--card-1); border-left-color: var(--acc-1); }
.grid-3 > *:nth-child(3n+2), .feature-grid > *:nth-child(3n+2), .steps > *:nth-child(3n+2) { background: var(--card-2); border-left-color: var(--acc-2); }
.grid-3 > *:nth-child(3n+3), .feature-grid > *:nth-child(3n+3), .steps > *:nth-child(3n+3) { background: var(--card-3); border-left-color: var(--acc-3); }
.grid-4 > *:nth-child(4n+1) { background: var(--card-1); border-left-color: var(--acc-1); }
.grid-4 > *:nth-child(4n+2) { background: var(--card-2); border-left-color: var(--acc-2); }
.grid-4 > *:nth-child(4n+3) { background: var(--card-3); border-left-color: var(--acc-3); }
.grid-4 > *:nth-child(4n+4) { background: var(--card-4); border-left-color: var(--acc-4); }
/* EVERY POSITION GETS A TINT, and the dark one follows .featured rather than a
   position. This used to leave nth-child(2) deliberately empty because the
   featured card was the second one; the client moved Most popular to Workplace
   (18 Sep 2026), which is the third, so the second card lost its tint and the
   third fought the override for its background. Keying the dark card off the
   class it already carries means the badge can move again without this block
   knowing. */
.pricing-grid > *:nth-child(1) { background: var(--card-1); border-left-color: var(--acc-1); }
.pricing-grid > *:nth-child(2) { background: var(--card-2); border-left-color: var(--acc-2); }
.pricing-grid > *:nth-child(3) { background: var(--card-3); border-left-color: var(--acc-3); }
.pricing-grid > *:nth-child(4) { background: var(--card-4); border-left-color: var(--acc-4); }
.pricing-grid > *:nth-child(5) { background: var(--card-1); border-left-color: var(--acc-1); }
.price-card.featured { background: var(--deep) !important; border-left-color: var(--lime) !important; }

/* category chips inherit the card's accent so each card "owns" a colour */
.for-card .for-tag { background: var(--acc-2); color: #fff; }
.grid-3 > *:nth-child(3n+1) .for-tag { background: var(--acc-1); }
.grid-3 > *:nth-child(3n+3) .for-tag { background: var(--acc-3); }
.charity-card .charity-icon { background: var(--acc-1); color: #fff; }
.grid-4 > *:nth-child(4n+2) .charity-icon { background: var(--acc-2); }
.grid-4 > *:nth-child(4n+3) .charity-icon { background: var(--acc-3); }
.grid-4 > *:nth-child(4n+4) .charity-icon { background: var(--acc-4); }
.charity-card .charity-icon.alt { background: var(--deep); color: var(--lime); }

/* ---------- WALL chat cards: tinted, not flat black ---------- */
.wall-stage .post { background: #111814; border-left: 3px solid rgba(200,241,53,.35); }
.wall-stage .post:has(.tag.sys) { background: linear-gradient(180deg, #14180c, #10140b); border-left-color: var(--lime); }
.wall-stage .post:has(.tag.sys.is-ladder) { background: linear-gradient(180deg, #0e1717, #0c1413); border-left-color: var(--acc-4); }
.wall-stage .composer { background: #0f1512; border-left: 3px solid var(--acc-2); }
.wall-stage .wall-card { background: #0f1513; }
.wall-stage .post-head .av { background: var(--acc-1); color: #fff; }
.wall-stage .post:has(.tag.sys) .post-head .av { background: rgba(200,241,53,.18); color: var(--lime); }

/* ---------- ANIMATED SCOREBOARD (replaces basic hero stats) ---------- */
.scoreboard {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: rgba(200,241,53,.16); border: 1px solid rgba(200,241,53,.22);
  border-radius: 16px; overflow: hidden; max-width: 720px; margin: 58px auto 0;
  box-shadow: 0 30px 70px -40px rgba(0,0,0,.8);
}
.sb-cell { background: rgba(7,20,14,.72); backdrop-filter: blur(8px); padding: 26px 16px 22px; text-align: center; }
.sb-label { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--cream); opacity: .68; margin-top: 12px; }
.sb-num { font-family: var(--score); font-weight: 900; font-size: clamp(32px,4.6vw,48px); color: var(--lime); line-height: 1; }
.sb-ring { width: 86px; height: 86px; margin: 0 auto; position: relative; }
.sb-ring svg { width: 86px; height: 86px; transform: rotate(-90deg); }
.sb-ring .track { fill: none; stroke: rgba(255,255,255,.12); stroke-width: 7; }
.sb-ring .bar { fill: none; stroke: var(--lime); stroke-width: 7; stroke-linecap: round; stroke-dasharray: 226.2; stroke-dashoffset: 226.2; transition: stroke-dashoffset 1.5s var(--ease); }
.sb-ring.lit .bar { stroke-dashoffset: 0; }
.sb-ring .pct { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--score); font-weight: 900; font-size: 21px; color: var(--lime); }
.sb-pips { display: flex; gap: 3px; justify-content: center; margin-top: 14px; height: 16px; align-items: center; }
.sb-pips i { width: 5px; height: 8px; background: rgba(255,255,255,.16); border-radius: 2px; transition: height .25s var(--ease), background .25s var(--ease); }
.sb-pips i.on { background: var(--lime); height: 16px; }
.sb-chips { display: flex; gap: 5px; justify-content: center; flex-wrap: wrap; margin-top: 14px; min-height: 22px; }
.sb-chip { font-family: var(--mark); font-weight: 600; font-size: 11px; letter-spacing: .06em; padding: 3px 9px; border: 1px solid rgba(200,241,53,.4); color: var(--lime); border-radius: 100px; opacity: 0; transform: translateY(6px); }
.sb-chip.lit { opacity: 1; transform: none; transition: opacity .35s var(--ease), transform .35s var(--ease); }
@media (max-width: 560px) { .scoreboard { grid-template-columns: 1fr; } }

/* ---------- HERO image backdrop + crossfade ---------- */
.hero.has-shots { background: var(--deep-2); }
.hero.has-shots::after { display: none; }            /* drop the mesh; photo carries texture */
.hero-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero-bg .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 35%;
  opacity: 0; transition: opacity 1.6s var(--ease); transform: scale(1.06);
}
.hero-bg .shot.active { opacity: 1; }
/* A shot can hold a <video> instead of only a background-image (see the hero
   collage's right pane). The background-image is the poster frame, so the
   shot reads as an ordinary photo in the rotation from the first paint; the
   video sits on top and only shows once gt-video.js has it ready to play,
   at which point it fades in over the poster already there. Sized to match
   the shot's own cover/position contract so nothing shifts when it arrives. */
.shot > video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center 35%;
  opacity: 0; transition: opacity .6s var(--ease);
}
.shot > video.ready { opacity: 1; }
.hero.has-shots .hero-veil {
  position: absolute; inset: 0; z-index: 0;
  background:
    radial-gradient(900px 480px at 50% -10%, rgba(200,241,53,.12), transparent 60%),
    linear-gradient(180deg, rgba(8,21,14,.82), rgba(8,21,14,.74) 55%, rgba(8,21,14,.92));
}
/* The 14s infinite-alternate ken-burns that used to live here is gone. It set
   `animation` on `.hero-bg .shot.active` — three classes — which outranked the
   two-class `.shot.zoom-in` / `.shot.zoom-out` rules that drive the alternating
   zoom, so on every hero the intended effect never ran. One engine owns the
   transform now; see "v25 — one zoom, everywhere". */
.js .hero .scoreboard { opacity: 0; transform: translateY(16px); }
.js body.ready .hero .scoreboard { opacity: 1; transform: none; transition: opacity .7s var(--ease) .5s, transform .7s var(--ease) .5s; }

/* ---------- IMAGE BAND (players) ---------- */
.image-band { position: relative; min-height: 360px; display: grid; place-items: center; padding: var(--section-y) var(--gutter); overflow: hidden; color: var(--white); }
.image-band .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 30%;
  transform: scale(1.04); opacity: 0; transition: opacity 1.8s var(--ease);
}
.image-band .shot:first-of-type, .image-band .shot.active { opacity: 1; }
/* 16s ken-burns removed for the same reason as the hero's — it outranked and
   silently replaced the alternating zoom. */
.image-band .veil { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(8,21,14,.92), rgba(8,21,14,.55) 60%, rgba(8,21,14,.3)); }
.image-band .wrap { position: relative; z-index: 1; max-width: var(--maxw); margin: 0 auto; width: 100%; }
.image-band blockquote { font-family: var(--display); font-size: clamp(24px,3.4vw,40px); line-height: 1.08; max-width: 640px; letter-spacing: -.02em; }
.image-band .src { margin-top: 14px; font-family: var(--mark); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; font-size: 12px; color: var(--lime); }

/* supporter photo cover */
.amb-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }

/* ---------- NAV: hover underline + scroll-reactive ---------- */
.nav { transition: background .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease); }
.nav .nav-inner { transition: height .3s var(--ease); }
.nav.scrolled { background: rgba(9,18,13,.72); backdrop-filter: blur(14px) saturate(1.2); border-bottom-color: rgba(200,241,53,.28); box-shadow: 0 12px 34px -20px rgba(0,0,0,.85); }
.nav.scrolled .nav-inner { height: 56px; }
@media (min-width: 1121px) {
  /* Inset to the text, not the box. These links gained horizontal padding when
     the nav was respaced; left:0/right:0 then stretched the underline across
     the whole hit area and left it floating 3px clear of the label.
     .nav-login is excluded outright — it is a bordered button now, and an
     underline hanging beneath a button is just a stray line. */
  .nav-links a:not(.btn-nav):not(.nav-login) { position: relative; }
  .nav-links a:not(.btn-nav):not(.nav-login)::before {
    content: ""; position: absolute; left: 12px; right: 12px; bottom: 5px; height: 2px;
    background: var(--lime); transform: scaleX(0); transform-origin: left; transition: transform .26s var(--ease);
  }
  .nav-links a:not(.btn-nav):not(.nav-login):hover { color: var(--lime); opacity: 1; }
  .nav-links a:not(.btn-nav):not(.nav-login):hover::before { transform: scaleX(1); }
}

/* ---------- SCROLL PROGRESS BAR ---------- */
.scroll-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 9998; pointer-events: none; }
.scroll-progress > i { display: block; height: 100%; width: 0; background: linear-gradient(90deg, var(--forest), var(--lime)); box-shadow: 0 0 12px rgba(200,241,53,.55); transition: width .08s linear; }

/* ---------- STAGGERED CARD REVEAL ON SCROLL ---------- */
.stagger > * { opacity: 0; transform: translateY(24px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
.stagger.in > * { opacity: 1; transform: none; }
.stagger.in > *:nth-child(1) { transition-delay: 0s; }
.stagger.in > *:nth-child(2) { transition-delay: .07s; }
.stagger.in > *:nth-child(3) { transition-delay: .14s; }
.stagger.in > *:nth-child(4) { transition-delay: .21s; }
.stagger.in > *:nth-child(5) { transition-delay: .28s; }
.stagger.in > *:nth-child(6) { transition-delay: .35s; }
.stagger.in > *:nth-child(7) { transition-delay: .42s; }
.stagger.in > *:nth-child(8) { transition-delay: .49s; }

@media (prefers-reduced-motion: reduce) {
  .hero-bg .shot { transition: none; }
  .sb-ring .bar { transition: none; stroke-dashoffset: 0; }
  .stagger > * { opacity: 1 !important; transform: none !important; }
  .scroll-progress > i { transition: none; }
  .js .hero .scoreboard { opacity: 1 !important; transform: none !important; }
}

/* supporter spotlight layout */
.amb-spot { display: grid; grid-template-columns: 300px 1fr; gap: 40px; align-items: center; }
.amb-spot .amb-card { max-width: 300px; border-left: 3px solid var(--acc-2); }
.amb-spot .section-heading em { color: var(--forest); }
@media (max-width: 760px) { .amb-spot { grid-template-columns: 1fr; } .amb-spot .amb-card { max-width: 360px; } }

/* ==========================================================================
   ============ v4 — flows · carousels · split heroes · elegance ===========
   ========================================================================== */

/* refined reveals with direction modifiers */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.from-left  { transform: translateX(-30px); }
.reveal.from-right { transform: translateX(30px); }
.reveal.zoom       { transform: scale(.95); }
.reveal.in { opacity: 1; transform: none; }

/* icon helpers */
.cic { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.sic { width: 20px; height: 20px; fill: currentColor; }

/* ---------- SPLIT HERO (other pages) ---------- */
.hero-split { display: grid; grid-template-columns: 1.05fr .95fr; min-height: clamp(440px, 62vh, 620px); background: var(--deep); }
.hero-split .hs-left {
  padding: clamp(40px,6vw,76px) clamp(28px,5vw,72px);
  display: flex; flex-direction: column; justify-content: center;
  background: radial-gradient(700px 360px at 16% 0%, rgba(200,241,53,.10), transparent 60%), var(--deep);
  position: relative;
}
.hero-split .hs-left h1 { font-family: var(--display); font-size: clamp(36px,5vw,62px); line-height: .98; color: var(--white); letter-spacing: -.02em; margin-bottom: 20px; }
.hero-split .hs-left h1 em { color: var(--lime); font-style: normal; }
.hero-split .hs-left .hero-sub { margin: 0 0 30px; max-width: 460px; }
.hero-split .hs-right { position: relative; overflow: hidden; min-height: 320px; }
.hero-split .hero-bg .shot { background-position: center 40%; }
.hero-split .hs-veil { position: absolute; inset: 0; z-index: 1; background: linear-gradient(90deg, var(--deep) 0%, rgba(15,46,34,.35) 22%, transparent 55%); }
.hero-split .hs-tagstrip { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 14px 22px; background: linear-gradient(0deg, rgba(7,18,12,.88), transparent); display: flex; align-items: center; gap: 10px; }
.hero-split .hs-tagstrip .live-dot { font-size: 11px; }
@media (max-width: 820px) {
  .hero-split { grid-template-columns: 1fr; }
  .hero-split .hs-right { min-height: 240px; order: -1; }
  .hero-split .hs-veil { background: linear-gradient(0deg, var(--deep) 0%, transparent 60%); }
}

/* ---------- SUPPORTER CAROUSEL ---------- */
.amb-carousel { position: relative; }
.amb-stage { position: relative; min-height: 320px; }
.amb-slide {
  position: absolute; inset: 0; display: grid; grid-template-columns: 300px 1fr; gap: 40px; align-items: center;
  opacity: 0; visibility: hidden; transform: translateY(14px); transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.amb-slide.on { opacity: 1; visibility: visible; transform: none; position: relative; }
.amb-portrait { border-radius: 14px; overflow: hidden; border: 1px solid var(--line); aspect-ratio: 4/5; background: linear-gradient(145deg, var(--panel-2), var(--deep)); position: relative; }
.amb-portrait img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 18%; }
.amb-portrait .pinitials { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--display); font-size: 64px; color: rgba(200,241,53,.55); }
.amb-portrait .pbadge { position: absolute; left: 14px; bottom: 14px; background: var(--lime); color: var(--deep); font-family: var(--mark); font-weight: 700; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; padding: 4px 11px; border-radius: 100px; }
.amb-quote { }
.amb-quote .qmark { font-family: var(--display); font-size: 60px; line-height: .5; color: var(--lime); }
.amb-quote blockquote { font-family: var(--display); font-size: clamp(20px,2.6vw,30px); line-height: 1.16; color: var(--ink); letter-spacing: -.01em; margin: 12px 0 18px; }
.on-dark .amb-quote blockquote { color: var(--white); }
.amb-quote .who { font-weight: 700; color: var(--ink); }
.on-dark .amb-quote .who { color: var(--white); }
.amb-quote .role { font-size: 13px; color: var(--forest); font-weight: 700; }
.amb-controls { display: flex; align-items: center; gap: 14px; margin-top: 26px; }
.amb-dots { display: flex; gap: 8px; }
.amb-dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(15,46,34,.18); border: 0; cursor: pointer; padding: 0; transition: .2s var(--ease); }
.on-dark .amb-dot { background: rgba(255,255,255,.2); }
.amb-dot.on { background: var(--forest); width: 26px; border-radius: 100px; }
.on-dark .amb-dot.on { background: var(--lime); }
.amb-pausebtn { display: inline-flex; align-items: center; gap: 7px; background: none; border: 1px solid var(--line); color: var(--muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; padding: 7px 13px; border-radius: 100px; cursor: pointer; }
.on-dark .amb-pausebtn { border-color: var(--line-d); color: var(--cream); }
.amb-pausebtn:hover { border-color: var(--forest); color: var(--forest); }
.on-dark .amb-pausebtn:hover { border-color: var(--lime); color: var(--lime); }
.amb-hint { font-size: 12px; color: var(--muted); margin-left: auto; }
@media (max-width: 700px) {
  .amb-slide { grid-template-columns: 1fr; gap: 22px; }
  .amb-portrait { max-width: 220px; }
}

/* ---------- CHARITY ICON CARDS ---------- */
.charity-icon svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.charity-card { transition: transform .25s var(--ease), box-shadow .25s var(--ease); }
.charity-card .charity-icon { width: 48px; height: 48px; }
.charity-card .cgive { display: inline-block; margin-top: 10px; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--forest); opacity: 0; transform: translateY(4px); transition: .25s var(--ease); }
.charity-card:hover .cgive { opacity: 1; transform: none; }

/* ---------- ANIMATED DONATION FLOW (left -> right, expand/contract) ---------- */
.flow-seq { display: flex; gap: 14px; align-items: stretch; }
.flow-seq .flow-arrow { align-self: center; color: rgba(255,255,255,.25); flex: 0 0 auto; transition: color .4s var(--ease), transform .4s var(--ease); }
.flow-step {
  flex: 1 1 0; min-width: 0; position: relative; border-radius: 14px; overflow: hidden; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line-d);
  transition: flex-grow .6s var(--ease), box-shadow .45s var(--ease), border-color .45s var(--ease), transform .45s var(--ease);
}
.flow-step .fs-img { height: 0; opacity: 0; background-size: cover; background-position: center; transition: height .55s var(--ease), opacity .55s var(--ease); }
.flow-step .fs-body { padding: 26px 24px; position: relative; z-index: 1; }
.flow-step .fs-num { display: inline-grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: rgba(200,241,53,.14); color: var(--lime); font-family: var(--score); font-weight: 900; font-size: 20px; margin-bottom: 14px; }
.flow-step h4 { font-family: var(--display); font-size: 18px; color: var(--white); margin-bottom: 10px; }
.flow-step .fs-lead { font-size: 14px; color: var(--cream); opacity: .8; }
.flow-step .fs-detail { max-height: 0; opacity: 0; overflow: hidden; transition: max-height .55s var(--ease), opacity .45s var(--ease), margin .45s var(--ease); }
.flow-step .fs-detail p { font-size: 13px; color: var(--cream); opacity: .72; line-height: 1.7; }
/* colour-coded steps */
.flow-step.c1 { border-top: 3px solid var(--acc-1); } .flow-step.c1 .fs-num { background: rgba(45,122,58,.22); color: #8fe07a; }
.flow-step.c2 { border-top: 3px solid var(--acc-2); } .flow-step.c2 .fs-num { background: rgba(31,122,99,.22); color: #6fe0c0; }
.flow-step.c3 { border-top: 3px solid var(--lime); } .flow-step.c3 .fs-num { background: rgba(200,241,53,.18); color: var(--lime); }
/* active (expanded) */
.flow-step.active { flex-grow: 2.4; box-shadow: 0 30px 70px -34px rgba(0,0,0,.8); border-color: rgba(200,241,53,.45); transform: translateY(-4px); }
.flow-step.active .fs-img { height: 150px; opacity: 1; }
.flow-step.active .fs-detail { max-height: 220px; opacity: 1; margin-top: 14px; }
.flow-step.active .fs-num { box-shadow: 0 0 0 6px rgba(200,241,53,.10); }
.flow-seq .flow-arrow.lit { color: var(--lime); transform: translateX(3px); }
.flow-progress { display: flex; gap: 6px; justify-content: center; margin-top: 22px; }
.flow-progress i { width: 28px; height: 4px; border-radius: 100px; background: rgba(255,255,255,.16); transition: background .3s var(--ease); }
.flow-progress i.on { background: var(--lime); }
.flow-hint { text-align: center; font-size: 12px; color: var(--muted); margin-top: 12px; }
.on-dark .flow-hint { color: var(--cream); opacity: .6; }
@media (max-width: 820px) {
  .flow-seq { flex-direction: column; }
  .flow-seq .flow-arrow { transform: rotate(90deg); align-self: center; }
  .flow-seq .flow-arrow.lit { transform: rotate(90deg) translateX(3px); }
  .flow-step.active { flex-grow: 1; }
}

/* ---------- PREMIUM FOUNDING BAND ---------- */
.founding-pro { position: relative; overflow: hidden; background: var(--deep-2); color: var(--cream); padding: clamp(54px,7vw,90px) var(--gutter); }
.founding-pro::before { content: ""; position: absolute; inset: 0; background: radial-gradient(800px 420px at 88% 12%, rgba(200,241,53,.14), transparent 58%), radial-gradient(600px 360px at 0% 100%, rgba(45,122,58,.30), transparent 60%); }
.founding-pro::after { content: "2026"; position: absolute; right: 2%; bottom: -6%; font-family: var(--display); font-size: clamp(140px,22vw,300px); color: rgba(255,255,255,.03); letter-spacing: -.04em; pointer-events: none; }
.fp-inner { position: relative; z-index: 1; max-width: var(--maxw); margin: 0 auto; display: grid; grid-template-columns: 1.3fr 1fr; gap: 48px; align-items: center; }
@media (max-width: 860px){ .fp-inner { grid-template-columns: 1fr; gap: 30px; } }
.fp-badge { display: inline-flex; align-items: center; gap: 9px; background: rgba(200,241,53,.12); border: 1px solid rgba(200,241,53,.4); color: var(--lime); font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; padding: 8px 16px; border-radius: 100px; margin-bottom: 22px; }
.fp-badge .live-dot::before { width: 7px; height: 7px; }
.founding-pro h2 { font-family: var(--display); font-size: clamp(30px,4.2vw,52px); line-height: 1.02; color: var(--white); letter-spacing: -.02em; margin-bottom: 16px; }
.founding-pro h2 em { color: var(--lime); font-style: normal; }
.founding-pro > .fp-inner p.lead { font-size: var(--fs-lead); color: var(--cream); opacity: .82; max-width: 560px; }
.fp-benefits { list-style: none; margin: 24px 0 0; display: grid; gap: 12px; }
.fp-benefits li { display: flex; gap: 12px; align-items: flex-start; font-size: 15px; color: var(--cream); }
.fp-benefits .bk { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%; background: var(--lime); color: var(--deep); display: grid; place-items: center; font-weight: 900; font-size: 12px; }
.fp-card { background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.02)); border: 1px solid rgba(200,241,53,.25); border-radius: 16px; padding: 30px; backdrop-filter: blur(6px); }
.fp-deadline { font-family: var(--score); font-weight: 900; font-size: 40px; color: var(--lime); line-height: 1; }
.fp-card .dl-label { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--cream); opacity: .7; margin-bottom: 8px; }
.fp-card .dl-sub { font-size: 13px; color: var(--cream); opacity: .72; margin: 14px 0 22px; line-height: 1.6; }

/* ---------- FAQ v2 ---------- */
.faq-grid { display: grid; grid-template-columns: 320px 1fr; gap: 48px; align-items: start; }
@media (max-width: 820px){ .faq-grid { grid-template-columns: 1fr; gap: 22px; } }
.faq-aside { position: sticky; top: 96px; }
.faq-item { border: 1px solid var(--line); border-radius: 12px; margin-bottom: 12px; overflow: hidden; background: var(--card-1); transition: border-color .25s var(--ease), background .25s var(--ease); }
.faq-item:nth-child(3n+2){ background: var(--card-2); } .faq-item:nth-child(3n){ background: var(--card-3); }
.faq-item[data-open="true"] { border-color: var(--forest); }
.faq-q { width: 100%; text-align: left; background: none; border: 0; cursor: pointer; font-family: var(--body); font-weight: 700; font-size: 16px; color: var(--ink); padding: 18px 20px; display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.faq-q .qn { font-family: var(--score); font-weight: 900; color: var(--forest); margin-right: 4px; }
.faq-q .qx { position: relative; width: 18px; height: 18px; flex: none; }
.faq-q .qx::before, .faq-q .qx::after { content: ""; position: absolute; background: var(--forest); border-radius: 2px; transition: transform .25s var(--ease), opacity .25s var(--ease); }
.faq-q .qx::before { left: 0; right: 0; top: 8px; height: 2px; }
.faq-q .qx::after { top: 0; bottom: 0; left: 8px; width: 2px; }
.faq-item[data-open="true"] .qx::after { transform: scaleY(0); opacity: 0; }
.faq-a { font-size: 15px; color: var(--muted); line-height: 1.7; max-height: 0; overflow: hidden; transition: max-height .3s var(--ease), padding .3s var(--ease); padding: 0 20px; }
.faq-item[data-open="true"] .faq-a { max-height: 320px; padding: 0 20px 20px; }

/* ---------- CONTACT v2 + SOCIALS ---------- */
.contact-shell { display: grid; grid-template-columns: .9fr 1.1fr; gap: 48px; align-items: start; }
@media (max-width: 820px){ .contact-shell { grid-template-columns: 1fr; gap: 32px; } }
.email-cta { display: inline-flex; align-items: center; gap: 14px; background: rgba(255,255,255,.05); border: 1px solid rgba(200,241,53,.3); border-radius: 14px; padding: 18px 22px; text-decoration: none; transition: border-color .2s var(--ease), transform .2s var(--ease), background .2s var(--ease); margin-bottom: 12px; }
.email-cta:hover { border-color: var(--lime); transform: translateY(-2px); background: rgba(200,241,53,.08); }
.email-cta .ec-ic { width: 46px; height: 46px; border-radius: 12px; background: var(--lime); color: var(--deep); display: grid; place-items: center; flex: none; }
.email-cta .ec-ic svg { width: 24px; height: 24px; }
.email-cta .ec-txt small { display: block; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--lime); opacity: .8; }
.email-cta .ec-txt b { font-size: 18px; color: var(--white); font-family: var(--body); }
.contact-meta { font-size: 14px; color: var(--cream); opacity: .8; line-height: 1.9; margin-bottom: 26px; }
.socials { display: flex; flex-wrap: wrap; gap: 12px; }
/* White fill, same reasoning as everywhere else socials appear on the site:
   a brand-colour glyph reads instantly on white and disappears into one flat
   tone on a dark or tinted pill. Default state carries the platform's own
   colour on both icon and label; hover swaps it — the pill fills with that
   colour and the glyph/label flip white — so the two states read as the
   same button, colour and background trading places. */
.social-btn {
  display: inline-flex; align-items: center; gap: 11px;
  background: #fff; border: 1px solid transparent; color: var(--deep);
  border-radius: 100px; padding: 13px 22px; text-decoration: none;
  font-size: 14.5px; font-weight: 600;
  transition: transform .2s var(--ease), background .2s var(--ease), color .2s var(--ease), box-shadow .2s var(--ease);
}
.social-btn svg { width: 19px; height: 19px; fill: currentColor; }
.social-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 18px -8px rgba(0,0,0,.4); }
.social-btn.s-email          { color: var(--forest); }
.social-btn.s-linkedin       { color: #0A66C2; }
.social-btn.s-facebook       { color: #1877F2; }
.social-btn.s-instagram      { color: #E1306C; }
.social-btn.s-youtube        { color: #FF0000; }
.social-btn.s-whatsapp       { color: #25D366; }
.social-btn.s-tiktok         { color: #111; }
.social-btn.s-email:hover    { background: var(--lime); color: var(--deep); }
.social-btn.s-linkedin:hover { background: #0A66C2; color: #fff; }
.social-btn.s-facebook:hover { background: #1877F2; color: #fff; }
.social-btn.s-instagram:hover{ background: #E1306C; color: #fff; }
.social-btn.s-youtube:hover  { background: #FF0000; color: #fff; }
.social-btn.s-whatsapp:hover { background: #25D366; color: #042; }
.social-btn.s-tiktok:hover   { background: #111; color: #fff; }
.social-note { font-size: 12px; color: var(--muted); margin-top: 12px; }

/* elegant form: floating labels */
.field { position: relative; }
.field input, .field select, .field textarea {
  width: 100%; padding: 22px 16px 10px; background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.14); color: var(--white); font-family: var(--body);
  font-size: 15px; border-radius: 12px; outline: none; transition: border-color .2s var(--ease), background .2s var(--ease);
}
.field textarea { min-height: 110px; resize: vertical; }
.field label { position: absolute; left: 16px; top: 16px; color: rgba(255,255,255,.55); font-size: 15px; pointer-events: none; transition: .18s var(--ease); }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--lime); background: rgba(200,241,53,.05); }
.field input:focus + label, .field input:not(:placeholder-shown) + label,
.field textarea:focus + label, .field textarea:not(:placeholder-shown) + label,
.field select:focus + label, .field select[data-filled="true"] + label { top: 7px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--lime); }
/* gt-ui.js replaces this select with the site's own `ddx` dropdown, so what is
   below is the no-JS fallback rather than the normal appearance. It still
   matters: the option list is drawn by the OS on a white background and
   inherits the select's white text, so untouched, every choice was there but
   painted white on white. */
.field select {
  color: var(--white);
  -webkit-appearance: none; appearance: none;
  /* appearance:none also removes the arrow, so the field gave no sign it was a
     dropdown. A real chevron, not two stacked gradient triangles. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C8F135' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-position: right 16px center;
  background-size: 17px 17px;
  background-repeat: no-repeat;
  padding-right: 46px;
  cursor: pointer;
}
.field select option { background: var(--deep-2); color: var(--cream); }
.field select option[value=""] { color: rgba(232,228,213,0.56); }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-grid .col-span { grid-column: 1 / -1; }
.form-submit { background: var(--lime); color: var(--deep); font-family: var(--display); font-size: 15px; font-weight: 900; letter-spacing: .04em; text-transform: uppercase; padding: 16px 30px; border: 0; cursor: pointer; border-radius: 12px; transition: filter .15s var(--ease), transform .1s var(--ease); width: 100%; position: relative; overflow: hidden; }
.form-submit:hover { filter: brightness(1.07); transform: translateY(-2px); }
@media (max-width: 520px){ .form-grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   ============ v5 — pricing tiers · how-it-works flow · scoring ===========
   ========================================================================== */

/* shaded tier cards (replace the table) */
.tier-cards { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; align-items: stretch; }
@media (max-width: 1040px){ .tier-cards { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 560px){ .tier-cards { grid-template-columns: 1fr; } }
.tier {
  position: relative; border-radius: 14px; padding: 26px 20px; display: flex; flex-direction: column;
  border: 1px solid rgba(15,46,34,.10); border-top: 4px solid var(--acc-1); overflow: hidden;
}
/* graduated shades that match the cream backdrop, deepening across the row */
.tier:nth-child(1){ background:#F0EDDF; border-top-color:#6E8B2E; }
.tier:nth-child(2){ background:#E7EDD8; border-top-color:#4E8A3A; }
.tier:nth-child(3){ background:#DDEAD7; border-top-color:#2D7A3A; }
.tier:nth-child(4){ background:#D3E6D3; border-top-color:#1F7A63; }
.tier:nth-child(5){ background:#C7E0CE; border-top-color:#176B57; }
.tier.featured { background: var(--deep); border: 1px solid var(--lime); border-top: 4px solid var(--lime); color: var(--cream); }
.tier-name { font-family: var(--display); font-size: 17px; color: var(--ink); }
.tier.featured .tier-name { color: #fff; }
.tier-for { font-size: 12px; color: var(--muted); margin: 4px 0 16px; }
.tier.featured .tier-for { color: var(--cream); opacity: .75; }
.tier-price { font-family: var(--score); font-weight: 900; font-size: 40px; color: var(--ink); line-height: 1; }
.tier.featured .tier-price { color: var(--lime); }
.tier-price span { font-size: 13px; font-family: var(--body); font-weight: 400; color: var(--muted); }
.tier.featured .tier-price span { color: var(--cream); opacity: .7; }
/* The founding rate, and the list price it replaced. On its own line under
   the number rather than beside it: at 40px the price has no room for a second
   figure on the same baseline without one of them shrinking to unreadable. */
.tier-was {
  display: block; margin-top: 6px;
  font-family: var(--body); font-size: 12px; font-weight: 700;
  color: var(--forest); letter-spacing: .01em;
}
.tier-was s { color: var(--muted); font-weight: 400; margin-right: 6px; }
.tier.featured .tier-was { color: var(--lime); }
.tier.featured .tier-was s { color: var(--cream); opacity: .6; }
/* The one-line "what this plan does not include" note. Quiet: it is a fact
   about the plan, not a warning about the customer's choice. */
.tier-note {
  margin: 0 0 14px; font-size: 12px; line-height: 1.5; color: var(--muted);
}
.tier.featured .tier-note { color: var(--cream); opacity: .62; }
.tier-per { font-size: 12px; font-weight: 700; color: var(--forest); margin: 6px 0 16px; }
.tier.featured .tier-per { color: var(--lime); opacity: .85; }
.tier-cta { margin-top: auto; text-align: center; font-family: var(--display); font-size: 12px; letter-spacing: .04em; text-transform: uppercase; padding: 11px; border-radius: 8px; text-decoration: none; background: rgba(15,46,34,.08); color: var(--deep); transition: .2s var(--ease); }
.tier-cta:hover { background: var(--forest); color: #fff; }
.tier.featured .tier-cta { background: var(--lime); color: var(--deep); }
.tier.featured .tier-cta:hover { filter: brightness(1.08); }
.tier-flag { position: absolute; top: 14px; right: -30px; transform: rotate(45deg); background: var(--lime); color: var(--deep); font-family: var(--mark); font-weight: 700; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; padding: 4px 36px; }

/* feature v2 */
.feature-grid2 { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; }
@media (max-width: 820px){ .feature-grid2 { grid-template-columns: 1fr; } }
.feat2 { position: relative; padding: 26px 24px 26px; border-radius: 14px; border: 1px solid rgba(15,46,34,.10); border-left: 3px solid var(--acc-1); overflow: hidden; transition: transform .25s var(--ease), box-shadow .25s var(--ease); }
.feat2:nth-child(6n+1){ background: var(--card-1); border-left-color: var(--acc-1); } .feat2:nth-child(6n+1) .feat-ic{ color: var(--acc-1); }
.feat2:nth-child(6n+2){ background: var(--card-2); border-left-color: var(--acc-2); } .feat2:nth-child(6n+2) .feat-ic{ color: var(--acc-2); }
.feat2:nth-child(6n+3){ background: var(--card-3); border-left-color: var(--acc-3); } .feat2:nth-child(6n+3) .feat-ic{ color: var(--acc-3); }
.feat2:nth-child(6n+4){ background: var(--card-4); border-left-color: var(--acc-4); } .feat2:nth-child(6n+4) .feat-ic{ color: var(--acc-4); }
.feat2:nth-child(6n+5){ background: var(--card-1); border-left-color: var(--forest); } .feat2:nth-child(6n+5) .feat-ic{ color: var(--forest); }
.feat2:nth-child(6n+6){ background: var(--card-2); border-left-color: var(--acc-2); } .feat2:nth-child(6n+6) .feat-ic{ color: var(--acc-2); }
.feat-ic { width: 44px; height: 44px; border-radius: 12px; background: rgba(255,255,255,.6); display: grid; place-items: center; margin-bottom: 16px; }
.feat-ic svg { width: 24px; height: 24px; }
.feat2 h3 { font-size: 16px; color: var(--ink); margin-bottom: 6px; }
.feat2 p { font-size: 13px; color: var(--muted); }

/* ---------- HOW-IT-WORKS vertical flow timeline ---------- */
.timeline { position: relative; max-width: 880px; margin: 0 auto; }
.timeline::before { content: ""; position: absolute; left: 31px; top: 20px; bottom: 20px; width: 2px; background: linear-gradient(var(--forest), var(--lime)); opacity: .5; }
@media (max-width: 620px){ .timeline::before { left: 23px; } }
.tl-step { position: relative; display: grid; grid-template-columns: 64px 1fr; gap: 24px; padding: 18px 0; }
.tl-marker { width: 64px; height: 64px; border-radius: 16px; display: grid; place-items: center; font-family: var(--score); font-weight: 900; font-size: 26px; color: #fff; position: relative; z-index: 1; box-shadow: 0 12px 28px -14px rgba(0,0,0,.4); }
.tl-step:nth-child(1) .tl-marker { background: linear-gradient(145deg, #3a8f49, var(--acc-1)); }
.tl-step:nth-child(2) .tl-marker { background: linear-gradient(145deg, #2a9079, var(--acc-2)); }
.tl-step:nth-child(3) .tl-marker { background: linear-gradient(145deg, var(--lime), var(--lime-dk)); color: var(--deep); }
.tl-card { border-radius: 14px; padding: 26px 28px; border: 1px solid rgba(15,46,34,.10); border-left: 3px solid var(--acc-1); }
.tl-step:nth-child(1) .tl-card { background: var(--card-1); border-left-color: var(--acc-1); }
.tl-step:nth-child(2) .tl-card { background: var(--card-2); border-left-color: var(--acc-2); }
.tl-step:nth-child(3) .tl-card { background: var(--card-3); border-left-color: var(--acc-3); }
.tl-tag { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--forest); }
.tl-card h2 { font-family: var(--display); font-size: clamp(22px,3vw,30px); line-height: 1.05; margin: 8px 0 12px; color: var(--ink); }
.tl-card p { font-size: 15px; color: var(--muted); max-width: 560px; }
@media (max-width: 620px){ .tl-step { grid-template-columns: 48px 1fr; gap: 16px; } .tl-marker { width: 48px; height: 48px; font-size: 20px; border-radius: 12px; } }

/* scoring cards (separated, distinct) */
.score-cards { display: grid; grid-template-columns: repeat(3,1fr); gap: 18px; }
@media (max-width: 760px){ .score-cards { grid-template-columns: 1fr; } }
.score-card { border-radius: 16px; padding: 30px 26px; position: relative; overflow: hidden; color: #fff; min-height: 200px; display: flex; flex-direction: column; justify-content: flex-end; }
.score-card:nth-child(1){ background: linear-gradient(160deg, #2f8a49, #1c5e30); }
.score-card:nth-child(2){ background: linear-gradient(160deg, #2a9079, #134f42); }
.score-card:nth-child(3){ background: linear-gradient(160deg, #b9e72e, #7fa019); color: var(--deep); }
.score-pts { position: absolute; top: 18px; right: 22px; font-family: var(--score); font-weight: 900; font-size: clamp(56px,8vw,84px); line-height: .8; opacity: .35; }
.score-card h3 { font-family: var(--display); font-size: 20px; margin-bottom: 8px; position: relative; }
.score-card p { font-size: 14px; opacity: .9; position: relative; }
.score-card .pts-lab { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; opacity: .8; margin-bottom: auto; position: relative; }

/* roles & ladder distinct */
.roles-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 700px){ .roles-2 { grid-template-columns: 1fr; } }
.role-card { border-radius: 16px; padding: 30px; border: 1px solid var(--line-d); position: relative; overflow: hidden; background: var(--panel); color: var(--cream); }
.role-card.r1 { border-top: 3px solid var(--lime); }
.role-card.r2 { border-top: 3px solid var(--acc-2); }
.role-ic { width: 50px; height: 50px; border-radius: 14px; display: grid; place-items: center; color: var(--lime); background: rgba(200,241,53,.12); margin-bottom: 16px; }
.role-card.r2 .role-ic { color: #6fe0c0; background: rgba(31,122,99,.18); }
.role-ic svg { width: 26px; height: 26px; }
.role-card h3 { font-family: var(--display); font-size: 20px; color: #fff; margin-bottom: 8px; }
.role-card p { font-size: 14px; color: var(--cream); opacity: .8; }
.role-tag { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--deep); background: var(--lime); padding: 4px 12px; border-radius: 100px; margin-bottom: 14px; }
.role-card.r2 .role-tag { background: #6fe0c0; }

.ladder-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; }
@media (max-width: 820px){ .ladder-3 { grid-template-columns: 1fr; } }
.ladder-card { border-radius: 14px; padding: 26px; border: 1px solid rgba(15,46,34,.10); position: relative; overflow: hidden; }
.ladder-card:nth-child(1){ background: var(--card-1); border-left: 3px solid var(--acc-1); }
.ladder-card:nth-child(2){ background: var(--card-2); border-left: 3px solid var(--acc-2); }
.ladder-card:nth-child(3){ background: var(--card-4); border-left: 3px solid var(--acc-4); }
.ladder-card .lrank { font-family: var(--score); font-weight: 900; font-size: 40px; color: rgba(15,46,34,.12); line-height: 1; }
.ladder-card h3 { font-family: var(--display); font-size: 18px; color: var(--ink); margin: 6px 0 8px; }
.ladder-card p { font-size: 14px; color: var(--muted); }

/* big closing CTA */
.cta-pro { position: relative; overflow: hidden; background: var(--lime); padding: clamp(48px,7vw,84px) var(--gutter); text-align: center; }
.cta-pro::before { content: ""; position: absolute; inset: 0; background: radial-gradient(600px 300px at 50% 120%, rgba(15,46,34,.18), transparent 60%); }
.cta-pro .wrap { position: relative; z-index: 1; }
.cta-pro h2 { font-family: var(--display); font-size: clamp(30px,4.6vw,56px); line-height: 1.02; color: var(--deep); letter-spacing: -.02em; margin-bottom: 14px; }
.cta-pro p { color: var(--deep); opacity: .8; font-size: var(--fs-lead); max-width: 560px; margin: 0 auto 28px; }
.cta-pro .btn-dark { background: var(--deep); color: var(--lime); }

@media (prefers-reduced-motion: reduce) {
  .flow-step .fs-img, .flow-step .fs-detail { transition: none; }
  .amb-slide { transition: none; }
}

/* flow-step icon illustration (no stock photos needed) */
.flow-step .fs-illus { height: 0; opacity: 0; display: grid; place-items: center; transition: height .55s var(--ease), opacity .55s var(--ease); }
.flow-step.active .fs-illus { height: 132px; opacity: 1; }
.flow-step .fs-illus svg { width: 54px; height: 54px; stroke: rgba(255,255,255,.92); fill: none; stroke-width: 1.5; }
.flow-step.c1 .fs-illus { background: linear-gradient(140deg, #2f8a49, #1c5e30); }
.flow-step.c2 .fs-illus { background: linear-gradient(140deg, #2a9079, #134f42); }
.flow-step.c3 .fs-illus { background: linear-gradient(140deg, #b9e72e, #7fa019); }
.flow-step.c3 .fs-illus svg { stroke: var(--deep); }

/* small icon chip for homepage price tiers */
.price-ic { width: 40px; height: 40px; border-radius: 11px; background: rgba(15,46,34,.07); display: grid; place-items: center; color: var(--forest); margin-bottom: 14px; }
.price-card.featured .price-ic { background: rgba(200,241,53,.14); color: var(--lime); }
/* fill:none AND a stroke, like every other icon rule in this file.

   Client, 22 Sep 2026, screenshot of the pricing cards: Starter's icon was an
   empty grey square, Workplace's a solid black rectangle, Organisation's a
   solid black circle. Not missing symbols — the sprite in
   public/partials/_icons.html is drawn as OUTLINES, with no presentation
   attributes of its own, so it relies on the caller for fill and stroke. This
   rule sized the box and set neither, so every path took SVG's default fill of
   black and no stroke at all: ic-spark is eight open line segments and filled
   to nothing, ic-doc's page outline filled to a rectangle, ic-globe's circle
   filled to a disc. The one that looked fine, ic-users, only did so by
   accident — a filled head and shoulders still reads as a person.

   .charity-icon svg and .card-lift .cl-ic svg have had the right three
   properties all along; this rule is the odd one out and now matches them. */
.price-ic svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* neutralise the legacy FAQ "+" pseudo (v2 uses the .qx morph instead) */
.faq-q::after { content: none !important; }

/* ==========================================================================
   ============ v6 — The Good List (public leaderboard) ====================
   ========================================================================== */

.preseason { display: inline-flex; gap: 8px; align-items: center; background: rgba(200,241,53,.12); border: 1px solid rgba(200,241,53,.4); color: var(--lime); font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; padding: 7px 14px; border-radius: 100px; }
.preseason .live-dot::before { width: 7px; height: 7px; }

/* hero figure block */
.lb-herostat { margin-top: 30px; }
.lb-herostat .big { font-family: var(--score); font-weight: 900; font-size: clamp(44px,7vw,72px); color: var(--lime); line-height: 1; }
.lb-herostat .lab { font-size: 13px; color: var(--cream); opacity: .7; margin-top: 8px; }

/* ---------- TABS ---------- */
.tabs { position: sticky; top: 0; z-index: 40; background: rgba(232,228,213,.88); backdrop-filter: blur(10px); border-bottom: 1px solid rgba(15,46,34,.12); }
.tabs-inner { max-width: var(--maxw); margin: 0 auto; display: flex; gap: 6px; padding: 12px var(--gutter); overflow-x: auto; scrollbar-width: none; }
.tabs-inner::-webkit-scrollbar { display: none; }
.tab-btn { border: 1px solid transparent; background: none; font-family: var(--body); font-weight: 600; font-size: 14px; color: var(--muted); padding: 10px 20px; border-radius: 100px; cursor: pointer; white-space: nowrap; transition: color .2s var(--ease), background .2s var(--ease), transform .1s var(--ease); }
.tab-btn:hover { color: var(--deep); background: rgba(15,46,34,.06); }
.tab-btn.active { color: var(--deep); background: var(--lime); box-shadow: 0 8px 20px -10px rgba(200,241,53,.7); }
.tab-panel { display: none; }
.tab-panel.active { display: block; animation: panelIn .45s var(--ease); }
@keyframes panelIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* ---------- PODIUM (top 3) ---------- */
.podium { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; align-items: end; margin-bottom: 20px; }
.pod { border-radius: 18px; padding: 28px 22px; text-align: center; position: relative; border: 1px solid rgba(15,46,34,.10); overflow: hidden; }
.pod .medal { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; font-family: var(--score); font-weight: 900; font-size: 20px; margin: 0 auto 14px; }
/* podium avatars + metallic rank badges */
.pod .pod-avwrap { position: relative; width: 58px; height: 58px; margin: 0 auto 12px; }
.pod .pod-av { width: 100%; height: 100%; border-radius: 50%; display: grid; place-items: center; font-family: var(--display); font-size: 22px; background: var(--card-1); color: var(--forest); border: 2px solid rgba(15,46,34,.15); overflow: hidden; }
.pod .pod-av.has-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pod .pod-rank { position: absolute; right: -6px; bottom: -6px; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; font-family: var(--score); font-weight: 900; font-size: 13px; border: 2px solid var(--app-card); box-shadow: 0 4px 10px -4px rgba(0,0,0,.4); }
.pod .pod-rank.gold   { background: linear-gradient(145deg, #FFE985, #E4B33C 60%, #B8860B); color: #4a3405; }
.pod .pod-rank.silver { background: linear-gradient(145deg, #F4F4F4, #C9CDD3 60%, #9AA0A8); color: #3d434b; }
.pod .pod-rank.bronze { background: linear-gradient(145deg, #F0C39B, #C88A4E 60%, #96602C); color: #45280e; }
.pod .pod-pts { font-family: var(--score); font-weight: 900; font-size: clamp(24px,3vw,34px); line-height: 1; margin-top: 10px; color: var(--ink); }
.pod .pod-pts small { font-family: var(--body); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-left: 5px; }
.pod.first .pod-pts { color: var(--lime); } .pod.first .pod-pts small { color: rgba(245,241,230,.6); }
.pod.first .pod-avwrap { width: 68px; height: 68px; }
.pod.first .pod-av { border-color: var(--lime); background: rgba(200,241,53,.14); color: var(--lime); font-size: 26px; }
.pod.first .pod-rank { border-color: var(--deep); }
/* leaderboard tipper cell */
.lb-tipper { display: inline-flex; align-items: center; gap: 10px; }
.lb-av { width: 30px; height: 30px; border-radius: 50%; display: inline-grid; place-items: center; flex: none; font-family: var(--display); font-size: 13px; background: var(--card-1); color: var(--forest); border: 1px solid rgba(15,46,34,.14); overflow: hidden; }
.lb-av.has-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pod .pod-name { font-family: var(--display); font-size: 17px; color: var(--ink); line-height: 1.1; }
.pod .pod-meta { font-size: 12px; color: var(--muted); margin-top: 4px; }
.pod .pod-chip { display: inline-block; margin-top: 12px; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 4px 12px; border-radius: 100px; background: rgba(15,46,34,.08); color: var(--forest); }
.pod .pod-amt { font-family: var(--score); font-weight: 900; font-size: clamp(26px,3.4vw,38px); margin-top: 16px; color: var(--ink); line-height: 1; }
.pod.first  { order: 2; background: var(--deep); color: var(--cream); border-color: var(--lime); padding: 40px 24px 30px; box-shadow: 0 34px 70px -34px rgba(0,0,0,.55); }
.pod.first .pod-name { color: #fff; } .pod.first .pod-amt { color: var(--lime); }
.pod.first .pod-chip { background: rgba(200,241,53,.14); color: var(--lime); }
.pod.first .medal { background: var(--lime); color: var(--deep); }
.pod.first .crown { position: absolute; top: 14px; left: 50%; transform: translateX(-50%); color: var(--lime); }
.pod.first .crown svg { width: 22px; height: 22px; }
.pod.second { order: 1; background: var(--card-2); } .pod.second .medal { background: var(--acc-2); color: #fff; }
.pod.third  { order: 3; background: var(--card-3); } .pod.third .medal { background: var(--acc-3); color: #fff; }
@media (max-width: 720px){ .podium { grid-template-columns: 1fr; } .pod, .pod.first, .pod.second, .pod.third { order: 0; } }

/* ---------- LEADERBOARD ROWS ---------- */
.lb { display: flex; flex-direction: column; gap: 8px; }
/* yellow cards — must read against the cream section behind them */
.lb-row { display: grid; align-items: center; gap: 16px; padding: 16px 20px; border-radius: 12px; background: linear-gradient(180deg, #F1F6C3, #E9F0A9); border: 1px solid rgba(169,209,28,.45); transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease); }
.lb--orgs .lb-row   { grid-template-columns: 46px 1fr auto auto; }
.lb--metric .lb-row { grid-template-columns: 46px 1fr 90px 130px; }
.lb-row:hover { transform: translateX(4px); border-color: rgba(200,241,53,.6); box-shadow: 0 16px 34px -24px rgba(0,0,0,.4); }
.lb-row.t3 { background: linear-gradient(90deg, rgba(200,241,53,.72), rgba(200,241,53,.30)); border-color: rgba(169,209,28,.8); }
.lb-rank { font-family: var(--score); font-weight: 900; font-size: 18px; color: var(--muted); text-align: center; }
.lb-row.t3 .lb-rank { color: var(--forest); }
.lb-name b { font-weight: 700; color: var(--ink); font-size: 15px; }
.lb-name small { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.lb-chip { justify-self: start; font-size: 12px; font-weight: 700; color: var(--forest); background: rgba(255,255,255,.6); padding: 5px 12px; border-radius: 100px; white-space: nowrap; }
.lb-count { font-family: var(--score); font-weight: 800; font-size: 16px; color: var(--muted); text-align: right; }
.lb-amt { font-family: var(--score); font-weight: 900; font-size: 18px; color: var(--ink); text-align: right; white-space: nowrap; }
.lb-row.t3 .lb-amt { color: var(--forest); }
@media (max-width: 620px){
  .lb--orgs .lb-row { grid-template-columns: 38px 1fr auto; }
  .lb--orgs .lb-chip { display: none; }
  .lb--metric .lb-row { grid-template-columns: 38px 1fr auto; }
  .lb--metric .lb-count { display: none; }
}

/* ---------- FEATURED SPOTLIGHT ---------- */
.lb-featured { display: grid; grid-template-columns: 1fr 1.15fr; border-radius: 18px; overflow: hidden; background: var(--deep); color: var(--cream); border: 1px solid var(--line-d); margin-bottom: 40px; }
.lb-shot { position: relative; min-height: 300px; background-size: cover; background-position: center; }
.lb-shot .veil { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,21,14,.3), rgba(8,21,14,.75)); }
.lb-shot .spot-badge { position: absolute; left: 20px; bottom: 20px; z-index: 1; background: var(--lime); color: var(--deep); font-family: var(--mark); font-weight: 700; font-size: 11px; letter-spacing: .05em; text-transform: uppercase; padding: 6px 13px; border-radius: 100px; }
.lb-copy { padding: 42px 46px; display: flex; flex-direction: column; justify-content: center; }
.lb-copy .eyebrow { color: var(--lime); opacity: .85; }
.lb-copy h3 { font-family: var(--display); color: #fff; font-size: clamp(22px,3vw,32px); margin: 10px 0 12px; }
.lb-copy p { color: var(--cream); opacity: .8; font-size: 15px; max-width: 52ch; }
.lb-figure { font-family: var(--score); font-weight: 900; color: var(--lime); font-size: clamp(30px,4vw,44px); margin-top: 20px; line-height: 1; }
.lb-figure small { display: block; font-family: var(--body); font-weight: 400; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--cream); opacity: .6; margin-top: 8px; }
@media (max-width: 760px){ .lb-featured { grid-template-columns: 1fr; } .lb-shot { min-height: 200px; } }

/* section head row */
.lb-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 20px; flex-wrap: wrap; margin-bottom: 22px; }
.lb-head h2 { font-family: var(--display); font-size: clamp(22px,2.8vw,30px); color: var(--ink); background: linear-gradient(180deg, transparent 60%, rgba(200,241,53,.65) 60%, rgba(200,241,53,.65) 94%, transparent 94%); padding: 0 6px; margin-left: -6px; }
.lb-head .note { font-family: var(--body); font-size: 15px; font-weight: 500; color: var(--ink); opacity: .78; line-height: 1.55; max-width: 52ch; }
.lb-colhead { display: grid; gap: 16px; padding: 0 20px 8px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.lb--orgs .lb-colhead   { grid-template-columns: 46px 1fr auto auto; }
.lb--metric .lb-colhead { grid-template-columns: 46px 1fr 90px 130px; }
.lb-colhead .r-amt { text-align: right; }
@media (max-width: 620px){ .lb-colhead { display: none; } }

/* ==========================================================================
   ============ v7 — nav auth · calmer flow · wave · pricing table · mobile =
   ========================================================================== */

/* ---------- KILL HORIZONTAL SCROLL (no stray side-space on mobile) -------- */
html, body { overflow-x: clip; }              /* clip keeps sticky nav working */
img, svg, video { max-width: 100%; }
.wrap, .nav-inner, .section > .wrap { width: 100%; }

/* ---------- NAV: login link + sign-up, tidy on mobile --------------------- */
.nav-login { font-weight: 700 !important; }
@media (min-width: 1121px) {
  .nav-auth { margin-left: 6px; padding-left: 16px; border-left: 1px solid var(--line-d); }
}
@media (max-width: 1120px) {
  /* make sure every item — including Home / Log in / Sign up — is visible */
  .nav-links { max-height: calc(100vh - 66px); overflow-y: auto; }
  .nav-auth { border-top: 1px solid var(--line-d); }
}

/* ---------- CALMER DONATION FLOW (large cards, gentle pop, no shake) ------- */
.flow-seq { align-items: stretch; gap: 16px; }
/* equal widths at all times → sibling text never reflows → nothing "shakes" */
.flow-step { flex: 1 1 0; transition: box-shadow .4s var(--ease), border-color .4s var(--ease), transform .4s var(--ease); }
/* illustration + detail are always shown, so cards stay a fixed large size */
.flow-step .fs-illus,
.flow-step.active .fs-illus { height: 92px; opacity: 1; }
.flow-step .fs-detail,
.flow-step.active .fs-detail { max-height: none; opacity: 1; overflow: visible; margin-top: 14px; }
/* the "flow" is just a gentle highlight sweeping left → right */
.flow-step.active {
  flex-grow: 1;                 /* cancel the old 2.4 width jump */
  transform: scale(1.035); z-index: 2;
  box-shadow: 0 26px 58px -30px rgba(0,0,0,.7);
  border-color: rgba(200,241,53,.5);
}
@media (max-width: 820px) { .flow-step.active { transform: scale(1.02); } }
@media (prefers-reduced-motion: reduce) { .flow-step.active { transform: none; } }

/* ---------- WAVING HAND (next to "Email us") ------------------------------ */
.wave { display: inline-block; transform-origin: 70% 70%; animation: wave 2.5s var(--ease) infinite; }
@keyframes wave {
  0%,65%,100% { transform: rotate(0); }
  10% { transform: rotate(14deg); } 20% { transform: rotate(-8deg); }
  30% { transform: rotate(14deg); } 40% { transform: rotate(-4deg); }
  50% { transform: rotate(10deg); } 60% { transform: rotate(0); }
}
@media (prefers-reduced-motion: reduce) { .wave { animation: none; } }

/* ---------- HOME "HOW IT WORKS": left → right connected flow --------------- */
.step-flow { display: flex; align-items: stretch; gap: 0; }
.step-flow .step { flex: 1 1 0; min-width: 0; border-radius: 14px; border-left: 3px solid var(--acc-1); }
.step-flow .step.s1 { background: var(--card-1); border-left-color: var(--acc-1); }
.step-flow .step.s2 { background: var(--card-2); border-left-color: var(--acc-2); }
.step-flow .step.s3 { background: var(--card-3); border-left-color: var(--acc-3); }
.step-flow .flow-conn { flex: 0 0 auto; align-self: center; display: grid; place-items: center; width: 44px; color: var(--forest); }
.step-flow .flow-conn svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 760px) {
  .step-flow { flex-direction: column; }
  .step-flow .flow-conn { width: auto; height: 34px; transform: rotate(90deg); }
}

/* ==========================================================================
   PRICING TABLE — shaded columns that blend with the cream section
   ========================================================================== */
.tier-table-wrap { border-radius: 16px; overflow: hidden; border: 1px solid rgba(15,46,34,.14); box-shadow: 0 24px 56px -38px rgba(0,0,0,.4); }
.tier-table { width: 100%; border-collapse: collapse; background: var(--cream-2); }
.tier-table thead th {
  background: var(--deep); color: var(--cream);
  font-family: var(--mark); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  font-size: 12px; padding: 16px 22px; text-align: left; white-space: nowrap;
}
.tier-table thead th.ta-r { text-align: right; }
.tier-table td { padding: 22px; vertical-align: top; border-top: 1px solid rgba(15,46,34,.10); font-size: 14px; color: var(--muted); line-height: 1.6; }
/* the four columns, each its own shade — deepening left → right, all keyed to cream */
.tier-table td:nth-child(1) { background: #EFEBDC; }
.tier-table td:nth-child(2) { background: #E7ECDB; }
.tier-table td:nth-child(3) { background: #DFEAD9; }
.tier-table td:nth-child(4) { background: #D6E6D6; }
/* gentle row highlight that keeps the column tints underneath */
.tier-table tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(200,241,53,.07); }
/* the popular row gets a lime edge */
.tier-table tr.is-popular td:first-child { box-shadow: inset 4px 0 0 var(--lime); }
.tier-table tr.is-popular:hover td:first-child { box-shadow: inset 4px 0 0 var(--lime), inset 0 0 0 999px rgba(200,241,53,.07); }
/* cell contents */
.tt-tier .tt-name { display: block; font-family: var(--display); font-size: 16px; color: var(--ink); line-height: 1.15; }
.tt-tier .tt-sub { display: block; margin-top: 5px; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--forest); }
.tier-table .tier-badge { display: inline-block; margin-left: 6px; background: var(--lime); color: var(--deep); font-family: var(--mark); font-weight: 700; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; padding: 2px 8px; border-radius: 100px; vertical-align: middle; }
/* The Groups tag. Outlined rather than filled so it cannot be mistaken for
   "Most popular", which is filled and sits beside it on the same row — two
   solid lime pills in a row read as one two-word label. Forest type on a pale
   lime wash clears AA on the column tints underneath it. */
.tier-table .tier-badge--groups { background: rgba(200,241,53,.22); color: var(--forest); border: 1px solid rgba(45,122,58,.42); }

/* ---- WHERE THE TABLE SPLITS IN TWO --------------------------------------
   Client, 19 Sep 2026: "people won't realise it's a big step up ... unless
   there is another colour coding that can help distinguish it from the non
   group packs."

   The tag names it; this is what makes it visible from across the table. A
   lime edge runs down the full height of every plan that carries groups, so
   the five rows read as two blocks — the shared-ladder plans, then the ones
   with a ladder per team — before any of the words have been.

   On the LAST cell rather than the first, because the first already carries
   the popular row's edge and two bars on one cell is a stripe, not a signal.
   Paired with the tier name going full-strength ink, which is the difference
   the eye actually lands on when scanning the left column. */
.tier-table tr.has-groups td:last-child { box-shadow: inset -4px 0 0 var(--lime); }
.tier-table tr.has-groups:hover td:last-child {
  box-shadow: inset -4px 0 0 var(--lime), inset 0 0 0 999px rgba(200,241,53,.07);
}
.tier-table tr.has-groups .tt-name { font-weight: 700; }

/* Stacked on a phone the table is five cards, so a right-hand edge on the last
   cell is an edge under the price and means nothing. The whole card gets the
   lime border there instead, which is the same statement in the shape the
   layout has become. */
@media (max-width: 800px) {
  .tier-table tr.has-groups td:last-child { box-shadow: none; }
  .tier-table tr.has-groups:hover td:last-child { box-shadow: inset 0 0 0 999px rgba(200,241,53,.07); }
  .tier-table tr.has-groups { border-top-color: var(--lime); }
}
.tt-priced { text-align: right; white-space: nowrap; }
.tt-priced .tt-price { display: block; font-family: var(--score); font-weight: 900; font-size: 28px; color: var(--ink); line-height: 1; }
.tt-priced .tt-price span { font-size: 12px; font-weight: 400; font-family: var(--body); color: var(--muted); }
.tt-priced .tt-per { display: block; margin-top: 4px; font-size: 12px; font-weight: 700; color: var(--forest); white-space: normal; }

/* responsive: each tier becomes its own stacked card (no side-scroll) */
@media (max-width: 800px) {
  .tier-table, .tier-table tbody, .tier-table tr, .tier-table td { display: block; width: 100%; }
  .tier-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .tier-table tr { border-top: 4px solid var(--forest); }
  .tier-table tr:first-child { border-top: 0; }
  .tier-table td { border-top: 1px solid rgba(15,46,34,.10); padding: 12px 20px; display: flex; gap: 16px; justify-content: space-between; align-items: baseline; }
  .tier-table td::before { content: attr(data-label); flex: 0 0 38%; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--forest); }
  .tier-table td.tt-tier, .tier-table td.tt-priced { text-align: right; }
  /* per-tier shade on mobile so each card still has its own tone */
  .tier-table tbody tr:nth-of-type(1) td { background: #EFEBDC; }
  .tier-table tbody tr:nth-of-type(2) td { background: #E7ECDB; }
  .tier-table tbody tr:nth-of-type(3) td { background: #DFEAD9; }
  .tier-table tbody tr:nth-of-type(4) td { background: #D8E7D7; }
  .tier-table tbody tr:nth-of-type(5) td { background: #CFE3D0; }
  .tt-priced, .tt-priced .tt-per { text-align: right; }
}

/* ==========================================================================
   ================= APP (authenticated product) — shell + UI ==============
   The marketing site's calmer working cousin: same palette/type, denser.
   ========================================================================== */
:root { --app-bg:#EBE7DA; --app-card:#FCFBF5; --app-card-2:#F4F1E6; --app-line:rgba(15,46,34,.12); --app-line-2:rgba(15,46,34,.07); }

.app { background: var(--app-bg); min-height: 100vh; color: var(--ink); font-family: var(--body); }
.app-main { max-width: 1160px; margin: 0 auto; padding: 26px 20px 72px; }
.app-h1 { font-family: var(--display); font-size: clamp(22px,2.8vw,30px); color: var(--ink); letter-spacing: -.01em; }
.app-sub { color: var(--muted); font-size: 15px; margin-top: 4px; }
.app-eyebrow { font-family: var(--mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; font-size: 12px; color: var(--forest); }
.num { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; }
.mono { font-variant-numeric: tabular-nums; }

/* charity strip */
.charity-strip { background: var(--deep-2); color: var(--cream); }
.charity-strip .cs-inner { max-width: 1160px; margin: 0 auto; display: flex; align-items: center; gap: 14px; padding: 8px 20px; font-size: 12px; }
.charity-strip .cs-label { display: flex; align-items: center; gap: 8px; font-family: var(--mark); font-weight: 600; letter-spacing: .07em; text-transform: uppercase; white-space: nowrap; }
.charity-strip svg { width: 15px; height: 15px; fill: var(--lime); flex: none; }
.charity-strip .cs-amt { font-family: var(--score); font-weight: 800; color: #fff; }
.charity-strip .cs-bar { flex: 1; max-width: 240px; height: 5px; border-radius: 100px; background: rgba(255,255,255,.14); overflow: hidden; }
.charity-strip .cs-bar > i { display: block; height: 100%; background: var(--lime); border-radius: 100px; }
.charity-strip .cs-right { margin-left: auto; opacity: .65; letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }
.charity-strip.nopledge .cs-amt { color: var(--lime); }
@media (max-width: 680px){ .charity-strip .cs-bar { display: none; } .charity-strip .cs-right { display: none; } }

/* app nav */
/* the whole topbar (charity strip + nav) sticks together — no mid-scroll split */
/* only the menu sticks; the charity strip lives under it and scrolls away */
.app-nav { position: sticky; top: 0; z-index: 50; }
.app-nav { background: var(--deep); border-bottom: 1px solid rgba(255,255,255,.08); }
.app-nav .an-inner { max-width: 1160px; margin: 0 auto; height: 60px; display: flex; align-items: center; gap: 16px; padding: 0 20px; }
.an-brand { font-family: var(--mark); font-weight: 900; font-size: 20px; color: #fff; text-transform: uppercase; letter-spacing: .02em; text-decoration: none; }
.an-brand span { color: var(--lime); }
/* ---- Where you are: the context chip in the nav ---- */
/* flex:none, or the nav's flex layout shrinks this to whatever is left after
   the link row has taken what it wants — which measured 149px against a
   max-width of 460px, so the org and group names were ellipsised by a
   constraint that had nothing to do with either of them. The names are the
   whole point of the control; the links can give way instead. */
.an-ctx { margin-left: 10px; flex: none; }
/* Was max-width:320px at 13px, which an org name plus a group name plus the
   chevron ran straight past — "masterclass › IT" arrived as "mastercl… › I…",
   i.e. the two things the control exists to tell you were the two things
   truncated. Roomier, and a size the name can actually be read at. */
.an-ctx-btn {
  display: inline-flex; align-items: center; gap: 8px; max-width: 460px;
  font-size: 14px; color: var(--cream); padding: 7px 12px;
  border-radius: 11px; border: 1px solid rgba(255,255,255,.1);
  background: rgba(255,255,255,.04);
}
.an-ctx-btn:hover { border-color: rgba(200,241,53,.4); background: rgba(200,241,53,.08); }
/* The org gets a floor on how small it may be squeezed to; the group half is
   allowed to give way first, since the org name is the stable landmark. */
.an-ctx-org { opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 4.5em; }
/* The group is the half that changes which ladder a tip lands on, so it is the
   half that is legible. */
.an-ctx-group { font-weight: 700; color: var(--lime); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.an-ctx-sep { opacity: .45; flex: none; }
/* This whole block is painted for the DARK dropdown panel it lives in
   (.an-menu-panel, a deep-green gradient). It was written with the light-card
   palette — --ink rows, --muted headings, --forest links and tick — which on
   that panel is dark-on-dark: the group list was there the whole time and
   simply could not be read. */
.an-ctx-head {
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(232,228,213,.62); padding: 12px 14px 6px; font-weight: 800;
}
.an-ctx-row {
  display: flex; width: 100%; text-align: left; align-items: center; gap: 10px;
  background: none; border: 0; cursor: pointer; padding: 11px 13px;
  border-radius: 11px;
  font-family: var(--body); color: var(--cream);
}
/* The rows carry <b> name / <i> descriptor like the panel's anchors do, but
   they are <button>s inside a <form> — so `.an-menu-panel a b` never reached
   them and both lines fell back to the inherited colour. */
.an-ctx-row .mi-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.an-ctx-row .mi-txt b { font-size: 14.5px; font-weight: 700; color: #fff; line-height: 1.25; }
.an-ctx-row .mi-txt i { font-style: normal; font-size: 12px; color: rgba(232,228,213,.58); line-height: 1.3; }
.an-ctx-row:hover { background: rgba(200,241,53,.14); }
.an-ctx-row.on { background: linear-gradient(90deg, rgba(200,241,53,.26), rgba(200,241,53,.07)); }
.an-ctx-row.on .mi-txt b { color: var(--lime); }
.an-ctx-row.on::after { content: "✓"; margin-left: auto; font-weight: 900; color: var(--lime); }
.an-ctx-more {
  display: block; padding: 11px 14px; font-size: 12.5px; font-weight: 800;
  color: var(--lime); text-decoration: none;
}
.an-ctx-more:hover { text-decoration: underline; }
@media (max-width: 900px) { .an-ctx { display: none; } }

/* Switch-organisation list, capped once an account runs several businesses
   through GoodTip — the same self-scrolling idea as .gt-scroll, but built
   fresh rather than reused: .gt-scroll's fade-to-cream and scrollbar tokens
   are tuned for the cream list beds it normally sits on, and this list lives
   inside the dark, rounded .an-menu-panel gradient instead. */
.an-ctx-listwrap { position: relative; }
.an-ctx-scroll {
  max-height: 280px; overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin; scrollbar-color: rgba(200,241,53,.30) transparent;
  overscroll-behavior: contain;
}
.an-ctx-scroll::-webkit-scrollbar { width: 8px; }
.an-ctx-scroll::-webkit-scrollbar-track { background: transparent; }
.an-ctx-scroll::-webkit-scrollbar-thumb { background-color: rgba(200,241,53,.28); border-radius: 100px; }
.an-ctx-listwrap::after {
  content: ""; position: absolute; left: 0; right: 8px; bottom: 0; height: 22px;
  background: linear-gradient(180deg, transparent, var(--deep-2));
  pointer-events: none; opacity: 1; transition: opacity .2s var(--ease);
}
.an-ctx-listwrap.at-end::after { opacity: 0; }
.an-ctx-filter-wrap { padding: 0 13px 6px; }
.an-ctx-filter {
  width: 100%; font-family: var(--body); font-size: 13px; color: var(--cream);
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
  border-radius: 9px; padding: 8px 10px;
}
.an-ctx-filter::placeholder { color: rgba(232,228,213,0.56); }
.an-ctx-filter:focus { outline: none; border-color: rgba(200,241,53,.5); background: rgba(255,255,255,.09); }

/* Which organisation, and which group inside it, as two columns rather than
   one long stack — they are different questions, and reading down a single
   list gave no visual cue where "switch org" stopped and "switch group"
   began. The org column is unpadded-outer / padded-per-column so the divider
   between columns can run the panel's full height. */
.an-menu-panel.an-ctx-panel { display: flex; align-items: stretch; padding: 0; min-width: 480px; }
.an-ctx-col { flex: 1 1 0; min-width: 220px; max-width: 280px; padding: 7px; }
.an-ctx-col-groups { border-left: 1px solid rgba(255,255,255,.10); }
.an-ctx-empty {
  padding: 14px 13px 4px; font-size: 13px; line-height: 1.4;
  color: rgba(232,228,213,.55);
}
/* The current organisation's own row in its column — shown, not a switch
   target, so it neither submits a form nor takes the hover a real option gets. */
.an-ctx-row-static { cursor: default; }
.an-ctx-row-static:hover { background: linear-gradient(90deg, rgba(200,241,53,.26), rgba(200,241,53,.07)); }
/* ---- WHERE THE CONTEXT PANEL OPENS -------------------------------------
   Reported by the client: the organisation/group dropdown "is messed up and
   overlaps with the dashboard".

   Two separate faults, both from this panel inheriting .an-menu-panel, which
   was written for the Manage menu at the RIGHT-HAND end of the bar:

   1. `right: 0` anchors the panel's right edge to the button's right edge and
      lets it grow leftwards. That is correct for a control sitting at the end
      of the row and wrong for one sitting at the start — a 480px-wide panel
      hanging off a chip 150px from the left edge runs off the side of the
      window, so the organisation column was half off-screen and the page had
      to be scrolled sideways to read it.

   2. Nothing capped its height. An account in a dozen organisations, each with
      groups, produced a panel taller than the viewport with no way to scroll
      it — it simply ran down over the dashboard beneath.

   So this one opens LEFT-anchored, is capped to what is actually on screen,
   and scrolls inside itself. `right: auto` has to be said explicitly: `left`
   alone does not unset the inherited `right`, and an element with both is
   stretched between them. */
.an-ctx .an-menu-panel { left: 0; right: auto; }
/* Never wider than the window it opens into, whatever the org names are. */
.an-menu-panel.an-ctx-panel {
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 84px);
  overflow: auto; overscroll-behavior: contain;
}
/* Above the page, not merely above the bar. The nav is a stacking context of
   its own (position:sticky + z-index), so this number only competes with the
   bar's own children — but sheets, veils and the charity strip all paint into
   the same layer as the page, and the panel has to win against them. */
.an-ctx .an-menu-panel { z-index: 120; }

/* The chip itself. 460px was set when it was the only thing beside the
   wordmark; with the full link row next to it, a long organisation name took
   enough of the bar that the links had nowhere to go and ran under the
   right-hand controls. It gives way now — but never below a width the name is
   readable at, which is what `clamp` is for. */
.an-ctx-btn { max-width: clamp(160px, 24vw, 360px); }

@media (max-width: 1100px) {
  .an-ctx-panel { min-width: 0; }
  .an-ctx-col { max-width: none; }
}
/* Below ~1000px the two columns cannot both fit inside the capped width, so
   they stack — with the groups column's left border becoming a top one, or
   the divider ends up running across the panel in the wrong direction. */
@media (max-width: 1000px) {
  .an-menu-panel.an-ctx-panel { flex-direction: column; }
  .an-ctx-col-groups { border-left: 0; border-top: 1px solid rgba(255,255,255,.10); }
}

.an-org { font-size: 13px; color: var(--cream); opacity: .7; padding-left: 14px; border-left: 1px solid rgba(255,255,255,.15); white-space: nowrap; }
.an-links { display: flex; gap: 2px; margin-left: 10px; }
.an-links a { font-size: 14px; font-weight: 600; color: var(--cream); opacity: .72; padding: 8px 14px; border-radius: 8px; text-decoration: none; transition: opacity .15s var(--ease), background .15s var(--ease); }
.an-links a:hover { opacity: 1; background: rgba(255,255,255,.06); }
.an-links a.on { color: var(--lime); opacity: 1; background: rgba(200,241,53,.10); }
.an-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.an-avatar { width: 34px; height: 34px; border-radius: 50%; background: var(--lime); color: var(--deep); display: grid; place-items: center; font-weight: 800; font-family: var(--mark); font-size: 15px; text-decoration: none; }
.an-logout { color: var(--cream); opacity: .65; display: inline-grid; place-items: center; }
.an-logout:hover { opacity: 1; color: var(--lime); }
.an-logout svg { width: 19px; height: 19px; }
.an-burger { display: none; background: none; border: 0; color: var(--cream); cursor: pointer; padding: 6px; }
.an-burger svg { width: 24px; height: 24px; }
@media (max-width: 760px){
  .an-links, .an-org { display: none; }
  .an-burger { display: inline-grid; }
}
/* mobile sheet */
.app-sheet { position: fixed; inset: 0; z-index: 60; background: rgba(7,18,12,.6); opacity: 0; visibility: hidden; transition: .25s var(--ease); }
.app-sheet.open { opacity: 1; visibility: visible; }
.app-sheet .sheet-panel { position: absolute; top: 0; right: 0; bottom: 0; width: min(300px,84vw); background: var(--deep); padding: 22px; transform: translateX(100%); transition: transform .28s var(--ease); display: flex; flex-direction: column; gap: 6px; }
.app-sheet.open .sheet-panel { transform: none; }
.app-sheet a { color: var(--cream); text-decoration: none; padding: 13px 14px; border-radius: 10px; font-weight: 600; }
.app-sheet a:hover { background: rgba(255,255,255,.06); color: var(--lime); }

/* flash */
.flash { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-radius: 10px; font-size: 14px; font-weight: 600; margin-bottom: 14px; border: 1px solid; }
.flash.success { background: rgba(45,122,58,.12); border-color: rgba(45,122,58,.4); color: #1c5e30; }
.flash.error { background: rgba(178,52,52,.10); border-color: rgba(178,52,52,.4); color: #9a2b2b; }
.flash.info { background: rgba(46,110,122,.10); border-color: rgba(46,110,122,.4); color: #245b66; }
.flash-close { flex: none; margin-left: auto; background: none; border: none; padding: 4px; display: grid; place-items: center; cursor: pointer; color: inherit; opacity: .55; border-radius: 6px; transition: opacity .15s var(--ease), background .15s var(--ease); }
.flash-close:hover { opacity: 1; background: rgba(0,0,0,.06); }

/* On a dark sheet these pastel, low-opacity flash colours pick up the green
   backdrop showing through them, and their dark text nearly disappears
   against it — "Started again" was there, just unreadable. Opaque and
   light-on-dark here instead of fighting the ambient colour.

   Each state also has to be told apart AT A GLANCE, which a green-tinted
   panel on a green page never managed: "created" and "that didn't work"
   both just read as green. So the state is carried by a bright accent rail
   down the left edge and a matching icon chip, against one neutral near-black
   panel — the panel stays out of the way and the colour does the talking.
   Lime is the success colour because it is the brand's own "yes", and it is
   the one hue on this page the background cannot mute. */
.page-deep .flash {
  position: relative;
  align-items: flex-start;
  gap: 13px;
  padding: 15px 16px 15px 20px;
  background: linear-gradient(180deg, rgba(14,38,28,.97), rgba(9,27,20,.97));
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 13px;
  color: var(--cream);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.5;
  overflow: hidden;
  box-shadow: 0 20px 44px -22px rgba(0,0,0,.9), 0 0 0 1px rgba(255,255,255,.03);
}
/* The accent rail. ::before rather than a border-left so the radius stays
   round and the colour reads as a solid bar, not a hairline. */
.page-deep .flash::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--flash-accent, rgba(255,255,255,.3));
}
.page-deep .flash .flash-ic {
  flex: none; width: 26px; height: 26px; border-radius: 8px; margin-top: -1px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--flash-accent) 18%, transparent);
  color: var(--flash-accent);
}
.page-deep .flash .flash-ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.4; }
.page-deep .flash > span:not(.flash-ic) { padding-top: 3px; }

.page-deep .flash.success { --flash-accent: #C8F135; }
.page-deep .flash.error   { --flash-accent: #FF7A6B; }
.page-deep .flash.info    { --flash-accent: #6FD2E8; }

.page-deep .flash-close { margin-top: 1px; opacity: .5; }
.page-deep .flash-close:hover { opacity: 1; background: rgba(255,255,255,.12); }

/* Arrives rather than just being there — a message that appears on the same
   frame as a full page render is easy to miss entirely. */
@media (prefers-reduced-motion: no-preference) {
  .page-deep .flash { animation: flash-in .34s var(--ease) both; }
}
@keyframes flash-in {
  from { opacity: 0; transform: translateY(-7px); }
  to   { opacity: 1; transform: none; }
}

/* cards */
.card { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 14px; padding: 22px; }
.card.pad-lg { padding: 28px; }
.card-h { font-family: var(--display); font-size: 17px; color: var(--ink); margin-bottom: 4px; }
.card-lead { color: var(--muted); font-size: 14px; }
.grid-app { display: grid; gap: 16px; }
.g-2 { grid-template-columns: 1fr 1fr; } .g-3 { grid-template-columns: repeat(3,1fr); }
@media (max-width: 820px){ .g-2, .g-3 { grid-template-columns: 1fr; } }

/* stat cards */
.stat-card { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 14px; padding: 20px; position: relative; overflow: hidden; }
.stat-card .s-lab { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.stat-card .s-num { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: clamp(30px,4vw,40px); color: var(--ink); line-height: 1; margin-top: 8px; }
.stat-card .s-sub { font-size: 13px; color: var(--muted); margin-top: 6px; }
.stat-card.accent { background: var(--deep); border-color: var(--deep); }
.stat-card.accent .s-lab { color: var(--cream); opacity: .7; } .stat-card.accent .s-num { color: var(--lime); } .stat-card.accent .s-sub { color: var(--cream); opacity: .7; }
/* watermark icon, positioned behind the number so the stat still reads first */
.stat-card .s-ic { position: absolute; top: 16px; right: 14px; width: 40px; height: 40px; stroke: var(--ink); fill: none; stroke-width: 1.6; opacity: .1; }
.stat-card.accent .s-ic { stroke: var(--cream); opacity: .16; }

/* app buttons (compact) */
.abtn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-family: var(--body); font-weight: 700; font-size: 14px; padding: 11px 18px; border-radius: 10px; border: 1px solid transparent; cursor: pointer; text-decoration: none; transition: transform .1s var(--ease), filter .15s var(--ease), background .15s var(--ease), border-color .15s; white-space: nowrap; }
.abtn:active { transform: translateY(1px); }
.abtn-primary { background: var(--lime); color: var(--deep); }
.abtn-primary:hover { filter: brightness(1.06); }
.abtn-dark { background: var(--deep); color: var(--lime); }
.abtn-dark:hover { filter: brightness(1.12); }
.abtn-ghost { background: transparent; border-color: var(--app-line); color: var(--ink); }
.abtn-ghost:hover { border-color: var(--forest); color: var(--forest); }
.abtn-sm { padding: 8px 13px; font-size: 13px; }
.abtn-block { width: 100%; }

/* pills / badges */
.pill-role { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 3px 10px; border-radius: 100px; background: rgba(15,46,34,.08); color: var(--forest); }
.pill-role.owner { background: var(--lime); color: var(--deep); }
.pill-role.manager { background: rgba(45,122,58,.16); color: #1c5e30; }
.pill-role.captain { background: rgba(46,110,122,.16); color: #245b66; }
.badge-live { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--forest); }
.badge-live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--forest); animation: ping 1.8s var(--ease) infinite; }
.tag-need { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #9a2b2b; background: rgba(178,52,52,.10); padding: 4px 10px; border-radius: 100px; }

/* progress */
.pbar { height: 8px; border-radius: 100px; background: rgba(15,46,34,.10); overflow: hidden; }
.pbar > i { display: block; height: 100%; background: linear-gradient(90deg,var(--forest),var(--lime)); border-radius: 100px; }
.pbar.sm { height: 6px; }

/* app forms */
.fld { margin-bottom: 16px; }
.fld > label { display: block; font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.fld input, .fld select, .fld textarea { width: 100%; padding: 12px 14px; background: #fff; border: 1px solid var(--app-line); border-radius: 10px; font-family: var(--body); font-size: 15px; color: var(--ink); outline: none; transition: border-color .15s var(--ease), box-shadow .15s var(--ease); }
.fld input:focus, .fld select:focus, .fld textarea:focus { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }
.fld .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.fld .err { font-size: 12px; color: #9a2b2b; margin-top: 6px; font-weight: 600; }
.fld-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 520px){ .fld-row { grid-template-columns: 1fr; } }
.pw-meter { height: 5px; border-radius: 100px; background: rgba(15,46,34,.10); overflow: hidden; margin-top: 8px; }
.pw-meter > i { display: block; height: 100%; width: 40%; background: var(--lime); border-radius: 100px; transition: width .2s var(--ease); }

/* live password-requirements checklist */
.pw-checks { list-style: none; margin: 10px 0 0; padding: 10px 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 6px 14px; background: rgba(15,46,34,.04); border: 1px solid var(--app-line); border-radius: 10px; transition: border-color .2s var(--ease), background .2s var(--ease); }
.pw-checks li { display: flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; color: var(--muted); transition: color .2s var(--ease); }
.pw-checks .pwc-ic { flex: none; width: 15px; height: 15px; border-radius: 50%; border: 1.5px solid rgba(15,46,34,.25); background: #fff; display: inline-flex; align-items: center; justify-content: center; transition: background .2s var(--ease), border-color .2s var(--ease); }
.pw-checks .pwc-ic svg { width: 9px; height: 9px; fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 12; stroke-dashoffset: 12; }
.pw-checks li.ok { color: #1c5e30; }
.pw-checks li.ok .pwc-ic { background: var(--forest); border-color: var(--forest); animation: pwc-pop .28s var(--ease); }
.pw-checks li.ok .pwc-ic svg { stroke-dashoffset: 0; transition: stroke-dashoffset .25s var(--ease) .08s; }
.pw-checks.all-ok { border-color: rgba(45,122,58,.45); background: rgba(45,122,58,.08); }
@keyframes pwc-pop { 0% { transform: scale(.7); } 60% { transform: scale(1.18); } 100% { transform: scale(1); } }
@media (max-width: 400px){ .pw-checks { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce){ .pw-checks li.ok .pwc-ic { animation: none; } .pw-checks .pwc-ic svg { transition: none; } }

/* app tables / lists */
.atable { width: 100%; border-collapse: separate; border-spacing: 0 6px; }
.atable th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; background: var(--deep); color: var(--cream); padding: 12px 16px; }
.atable th:first-child { border-radius: 10px 0 0 10px; }
.atable th:last-child { border-radius: 0 10px 10px 0; }
.atable td { background: var(--app-card); padding: 14px 16px; border-top: 1px solid rgba(15,46,34,.12); border-bottom: 1px solid rgba(15,46,34,.12); vertical-align: middle; }
.atable td:first-child { border-left: 1px solid rgba(15,46,34,.12); border-radius: 10px 0 0 10px; }
.atable td:last-child { border-right: 1px solid rgba(15,46,34,.12); border-radius: 0 10px 10px 0; }
/* one clean card tone per row — a single forest accent, no rainbow bands */
.atable td:first-child { box-shadow: inset 3px 0 0 var(--forest); }
.atable tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(200,241,53,.12); }
.atable tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--forest), inset 0 0 0 999px rgba(200,241,53,.12); }
.atable tr.me td { background: #DFF0A2; border-color: rgba(169,209,28,.7); }
.atable .r-rank { font-family: var(--score); font-weight: 900; font-size: 17px; color: var(--muted); width: 56px; white-space: nowrap; }
.atable tr.me .r-rank { color: var(--forest); }
.atable .r-num { font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.atable .r-pts { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; text-align: right; color: var(--ink); white-space: nowrap; }
/* column discipline — content lines up instead of drifting per row */
.atable th.num, .atable td.num { text-align: right; }
.atable th.fit, .atable td.fit { width: 1%; white-space: nowrap; }
.atable td .team-cell { white-space: nowrap; }
/* My Tips columns: match + pick get the room, result/outcome stay tight */
.tips-table th:nth-child(1) { width: 1%; }
.tips-table th:nth-child(2) { width: 42%; }
.tips-table td:nth-child(3) { white-space: nowrap; }
.tips-table td:nth-child(4) { white-space: nowrap; text-align: right; width: 1%; }
.tips-table .pick-mini { flex-wrap: nowrap; }
.tips-table .pm-btn > span { white-space: nowrap; }
/* Match column: home | v | away on one line. Each team track is at least as
   wide as the longest club name, so names never wrap onto a second line and
   the "v" chip lines up down the whole round. */
.tips-table td.match-td { width: 1%; }
.match-cell { display: grid; grid-template-columns: minmax(215px, 1fr) 26px minmax(215px, 1fr); align-items: center; gap: 12px; }
.match-cell .team-cell { white-space: nowrap; }
.mc-vs { width: 26px; height: 26px; border-radius: 50%; background: rgba(15,46,34,.08); color: var(--forest); font-family: var(--score); font-weight: 900; font-size: 12px; display: grid; place-items: center; text-transform: lowercase; }
@media (max-width: 860px) {
  .tips-table th:nth-child(2) { width: auto; }
  .tips-table .pick-mini { flex-wrap: wrap; }
}

/* ---------- browse-all groups list (find your group) ---------- */
.bl-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; padding: 6px 0; }
.bl-row.bl-child { margin-left: 22px; padding-left: 14px; border-left: 2px solid rgba(15,46,34,.10); }
/* flex:1 so the actions stay pinned right once the icon tile takes the left */
.bl-main { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.bl-name { font-weight: 700; color: var(--ink); font-size: 15px; }
.bl-meta { font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.bl-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Tile icon per group. flex:none stops it squashing when a long group name
   competes for the row; the child rows keep their indent rule instead, so the
   parent/child hierarchy still reads without a second tile size. */
.bl-ic {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 12px;
  background: rgba(15,46,34,.06); color: var(--forest);
}
.bl-ic svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.8; }

/* ---------- browse header: count chip + sort ---------- */
.browse-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.browse-title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.browse-count {
  font-size: 12px; font-weight: 700; color: var(--forest);
  background: rgba(45,122,58,.12); padding: 4px 11px; border-radius: 100px;
}
.browse-sort { display: flex; align-items: center; gap: 9px; margin: 0; }
.browse-sort label { font-size: 13px; color: var(--muted); font-weight: 600; }
/* The chevron is drawn by us, so the native arrow is removed and right padding
   leaves room for it. */
.browse-sel { position: relative; display: inline-flex; align-items: center; }
.browse-sel select {
  appearance: none; -webkit-appearance: none;
  font-family: var(--body); font-size: 14px; font-weight: 700; color: var(--ink);
  background: #fff; border: 1px solid var(--app-line); border-radius: 10px;
  padding: 9px 36px 9px 14px; cursor: pointer;
}
.browse-sel select:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
.browse-sel svg { position: absolute; right: 12px; width: 16px; height: 16px; color: var(--muted); pointer-events: none; }

/* leading icon inside a button (e.g. + Create a group) */
.abtn-ic { width: 17px; height: 17px; flex: none; }

/* ---------- election stats (charity vote) ----------
   Single-hue viz on the app palette: forest carries magnitude, the winner
   gets the solid step, tracks stay recessive. Numbers wear ink, not the
   series colour. */
.vz-live { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-bottom: 10px; }
.vz-tile { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 12px; padding: 12px 14px; }
.vz-tile b { display: block; font-family: var(--score); font-weight: 900; font-size: 21px; color: var(--ink); line-height: 1.15; }
.vz-tile small { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.vz-meter { height: 6px; border-radius: 100px; background: rgba(15,46,34,.08); overflow: hidden; }
.vz-meter i { display: block; height: 100%; background: var(--forest); border-radius: 100px; }
.vz-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.vz-card { padding: 18px 20px; }
.vz-span { grid-column: 1 / -1; }
.vz-h { font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; }
.vz-bars { display: grid; gap: 10px; }
.vz-row { display: grid; grid-template-columns: minmax(150px, 230px) 1fr 92px; align-items: center; gap: 12px; }
.vz-lab { font-weight: 600; font-size: 14px; color: var(--ink); }
.vz-track { height: 18px; border-radius: 4px; background: rgba(15,46,34,.06); overflow: hidden; }
.vz-track i { display: block; height: 100%; background: rgba(45,122,58,.55); border-radius: 0 4px 4px 0; min-width: 2px; }
.vz-track i.win { background: var(--forest); }
.vz-num { font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums; text-align: right; color: var(--ink); white-space: nowrap; }
.vz-num small { color: var(--muted); font-family: var(--body); font-weight: 600; }
.vz-donut { position: relative; width: 132px; height: 132px; margin: 4px auto 10px; border-radius: 50%; background: conic-gradient(var(--forest) var(--deg), rgba(15,46,34,.08) 0); display: grid; place-items: center; }
.vz-donut::before { content: ""; position: absolute; inset: 14px; border-radius: 50%; background: var(--app-card); }
.vz-donut b { position: relative; font-family: var(--score); font-weight: 900; font-size: 26px; color: var(--ink); }
.vz-cap { text-align: center; font-size: 13px; color: var(--muted); }
.vz-days { display: flex; align-items: stretch; gap: 12px; height: 150px; }
.vz-day { flex: 1; max-width: 72px; display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; }
.vz-daybar { flex: 1 1 auto; width: 100%; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px; }
.vz-daybar i { display: block; width: 100%; max-width: 34px; background: rgba(45,122,58,.7); border-radius: 4px 4px 0 0; min-height: 3px; }
.vz-day .vz-count { font-family: var(--score); font-weight: 800; font-size: 13px; color: var(--ink); }
.vz-day small { font-size: 11px; color: var(--muted); white-space: nowrap; }
@media (max-width: 720px) {
  .vz-grid { grid-template-columns: 1fr; }
  .vz-row { grid-template-columns: 1fr; gap: 6px; }
  .vz-num { text-align: left; }
}

/* countdown */
.countdown { display: inline-flex; gap: 8px; }
.cd-cell { background: var(--deep); color: var(--lime); border-radius: 8px; padding: 8px 10px; text-align: center; min-width: 52px; }
.cd-cell b { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 22px; display: block; line-height: 1; }
.cd-cell small { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--cream); opacity: .6; }

/* tip pick control */
.match-row { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 14px; padding: 16px 18px; margin-bottom: 12px; }
.match-meta { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--muted); margin-bottom: 12px; }
.match-meta .venue { margin-left: auto; }
.pick { display: grid; grid-template-columns: 1fr 44px 1fr; gap: 10px; align-items: stretch; }
.pick .team { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border: 1.5px solid var(--app-line); border-radius: 12px; background: #fff; cursor: pointer; font-weight: 700; color: var(--ink); transition: border-color .15s var(--ease), background .15s var(--ease), transform .1s var(--ease); user-select: none; }
.pick .team:hover { border-color: var(--forest); }
.pick .team.away { flex-direction: row-reverse; text-align: right; }
.pick .team.sel { border-color: var(--lime); background: rgba(200,241,53,.12); box-shadow: 0 0 0 3px rgba(200,241,53,.14); }
.pick .team .crest { width: 34px; height: 34px; border-radius: 9px; background: var(--card-1); display: grid; place-items: center; font-family: var(--mark); font-weight: 700; font-size: 13px; color: var(--forest); flex: none; }
.pick .vs { display: grid; place-items: center; font-family: var(--mark); font-weight: 700; font-size: 12px; color: var(--muted); }
.saved-tick { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--forest); margin-top: 10px; opacity: 0; transition: opacity .2s var(--ease); }
.saved-tick.show { opacity: 1; }
.saved-tick svg { width: 15px; height: 15px; }
.locked-banner { display: flex; align-items: center; gap: 10px; background: rgba(15,46,34,.06); border: 1px solid var(--app-line); border-radius: 12px; padding: 13px 16px; font-size: 14px; color: var(--muted); font-weight: 600; margin-bottom: 18px; }

/* result badges (my tips) */
.res { font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; padding: 4px 10px; border-radius: 100px; }
.res.correct { background: rgba(45,122,58,.15); color: #1c5e30; }
.res.incorrect { background: rgba(178,52,52,.12); color: #9a2b2b; }
.res.pending { background: rgba(15,46,34,.08); color: var(--muted); }

/* section head with filter */
.app-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.inline-select { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); }
.inline-select select { padding: 9px 12px; border-radius: 9px; border: 1px solid var(--app-line); background: #fff; font-family: var(--body); font-size: 14px; color: var(--ink); }

/* auth form bits — the layout itself lives in the AUTH section further down.
   The old .auth-shell / .auth-brand / .auth-main / .auth-card rules were
   dropped along with the 50/50 page split they described. */
.fld .flink { color: var(--forest); font-weight: 700; text-decoration: none; }
.fld .flink:hover { text-decoration: underline; }
.fld-labelrow { display: flex; justify-content: space-between; align-items: baseline; }

/* donation breakdown */
.dbreak { display: grid; gap: 10px; margin: 16px 0; }
.dbreak .row { display: flex; justify-content: space-between; font-size: 14px; }
.dbreak .row b { font-variant-numeric: tabular-nums; font-family: var(--score); }
.dbreak .row.total { border-top: 1px solid var(--app-line); padding-top: 10px; font-weight: 800; }

/* ==========================================================================
   ================= APP v2 — stronger picks, news, ladder, dropdowns ======
   ========================================================================== */

/* ---- STRONG selected-team state (was too light) ---- */
.pick .team { border-width: 2px; }
.pick .team.sel {
  background: linear-gradient(135deg, #2f8a49, #1a5a2f);
  border-color: #123f24;
  color: #fff; box-shadow: 0 14px 30px -14px rgba(20,70,40,.65);
}
.pick .team.sel span { color: #fff; }
.pick .team.sel .crest { background: var(--lime); color: var(--deep); }
.pick .team { position: relative; }
.pick .team.sel::before { content: ""; position: absolute; inset: 0; border-radius: 9px; box-shadow: inset 0 0 0 2px rgba(200,241,53,.55); pointer-events: none; }
.match-row:has(.team.sel) { border-color: rgba(45,122,58,.4); }

/* ---- Custom dropdown (replaces native select look) ---- */
.dd { position: relative; display: inline-block; min-width: 170px; }
.dd-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.dd-btn { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; background: #fff; border: 1px solid var(--app-line); border-radius: 10px; padding: 10px 14px; font-family: var(--body); font-size: 14px; font-weight: 600; color: var(--ink); cursor: pointer; transition: border-color .15s var(--ease), box-shadow .15s var(--ease); }
.dd-btn:hover { border-color: var(--forest); }
.dd.open .dd-btn { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }
.dd-btn .dd-chev { width: 16px; height: 16px; stroke: var(--muted); fill: none; stroke-width: 2; transition: transform .2s var(--ease); }
.dd.open .dd-btn .dd-chev { transform: rotate(180deg); }
.dd-menu { position: absolute; top: calc(100% + 6px); left: 0; right: 0; background: #fff; border: 1px solid var(--app-line); border-radius: 12px; box-shadow: 0 22px 48px -22px rgba(0,0,0,.4); padding: 6px; z-index: 30; opacity: 0; visibility: hidden; transform: translateY(-6px); transition: opacity .16s var(--ease), transform .16s var(--ease), visibility .16s; max-height: 280px; overflow: auto; }
.dd.open .dd-menu { opacity: 1; visibility: visible; transform: none; }
.dd-opt { display: flex; align-items: center; justify-content: space-between; width: 100%; text-align: left; background: none; border: 0; padding: 10px 12px; border-radius: 8px; font-family: var(--body); font-size: 14px; color: var(--ink); cursor: pointer; transition: background .12s var(--ease); }
.dd-opt:hover { background: var(--card-1); }
.dd-opt.on { background: rgba(200,241,53,.18); font-weight: 700; }
.dd-opt.on::after { content: "✓"; color: var(--forest); font-weight: 900; }

/* The ESPN-style news feed this file used to carry — .news-lead, .news-list,
   .news-item, .ntag, .hub-grid — has gone. Both story lists render the deck
   card now (partials/_news_deck_card.html), so there is one story card in
   the product rather than three that had to be kept looking alike by hand. */

/* ---------- news article page ---------- */
.article { max-width: 760px; margin: 0 auto; }
.art-back { display: inline-block; margin-bottom: 16px; font-size: 13px; font-weight: 700; color: var(--forest); text-decoration: none; }
.art-hero { height: clamp(220px, 34vw, 380px); border-radius: 16px; background-size: cover; background-position: center 30%; position: relative; border: 1px solid var(--app-line); overflow: hidden; margin-bottom: 22px; }
.art-hero .sport-tag { position: absolute; top: 14px; left: 14px; background: var(--lime); color: var(--deep); font-family: var(--mark); font-weight: 700; font-size: 12px; letter-spacing: .05em; text-transform: uppercase; padding: 5px 13px; border-radius: 100px; }
.art-hero.noimg { background: radial-gradient(560px 280px at 82% -20%, rgba(200,241,53,.35), transparent 60%), var(--deep); display: grid; place-items: center; }
.art-hero.noimg .noimg-mark { font-family: var(--display); font-size: 40px; color: rgba(242,239,230,.5); }
.art-hero.noimg .noimg-mark span { color: rgba(200,241,53,.6); }
.art-title { font-family: var(--display); font-size: clamp(26px, 4vw, 38px); line-height: 1.08; color: var(--ink); margin-bottom: 10px; }
.art-meta { font-size: 13px; color: var(--muted); font-weight: 600; margin-bottom: 18px; }
.art-lead { font-size: 18px; line-height: 1.55; color: var(--ink); font-weight: 600; margin-bottom: 14px; }
.art-body { font-size: 16px; line-height: 1.7; color: var(--ink); }
.art-body p { margin-bottom: 14px; }
.hub-h { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.hub-h .k { font-family: var(--mark); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; font-size: 12px; color: var(--forest); }
.hub-h a { font-size: 13px; color: var(--forest); font-weight: 700; text-decoration: none; }

/* ---- Team ladder / standings widget ---- */
.standings { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 14px; padding: 16px 16px 12px; }
.st-head { display: grid; grid-template-columns: 26px 1fr 42px 46px auto; gap: 8px; padding: 0 10px 8px; font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.st-row { display: grid; grid-template-columns: 26px 1fr 42px 46px auto; gap: 8px; align-items: center; padding: 8px 10px; border-radius: 8px; font-size: 13px; }
.st-row:nth-child(even) { background: var(--app-card-2); }
.st-row.finals { position: relative; }
.st-rank { font-family: var(--score); font-weight: 900; color: var(--muted); }
.st-row.finals .st-rank { color: var(--forest); }
.st-team { font-weight: 700; color: var(--ink); font-family: var(--mark); letter-spacing: .02em; }
.st-pts { font-family: var(--score); font-weight: 900; text-align: right; }
.st-fa { font-family: var(--score); font-weight: 700; text-align: right; color: var(--muted); font-size: 12px; }
.streak { display: flex; gap: 3px; justify-content: flex-end; }
.streak i { width: 9px; height: 9px; border-radius: 2px; }
.streak i.w { background: var(--forest); } .streak i.l { background: rgba(15,46,34,.16); }
.st-divider { height: 0; border-top: 1px dashed rgba(200,241,53,.6); margin: 4px 10px; position: relative; }
.st-divider::after { content: "Finals line"; position: absolute; right: 10px; top: -8px; font-size: 9px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--forest); background: var(--app-card); padding: 0 6px; }

/* ---- per-comp mini stats (ESPN-style) ---- */
.comp-stats { display: flex; gap: 22px; margin-top: 12px; flex-wrap: wrap; }
.comp-stats .cs .k { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 700; }
.comp-stats .cs .v { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 17px; color: var(--ink); }
.comp-stats .cs .v small { font-size: 12px; color: var(--muted); font-weight: 400; }

/* ---- dashboard hero banner polish ---- */
.dash-banner { position: relative; overflow: hidden; border-radius: 16px; padding: 26px 28px; color: var(--cream); margin-bottom: 18px; }
.dash-banner .bg { position: absolute; inset: 0; background-size: cover; background-position: center 35%; }
.dash-banner .veil { position: absolute; inset: 0; background: linear-gradient(100deg, rgba(11,35,25,.94) 30%, rgba(11,35,25,.6) 70%, rgba(11,35,25,.35)); }
.dash-banner > * { position: relative; z-index: 1; }

/* The banner's three text lines used to carry their colours as inline styles
   on the elements themselves, which no stylesheet can override — so the light
   theme could not reach them. Same values, on classes, so both themes can. */
.dash-banner .db-eyebrow { color: var(--lime); opacity: .85; }
.dash-banner .db-h1 { color: #fff; }
.dash-banner .db-rank { color: var(--cream); opacity: .8; margin-top: 4px; font-size: 14px; }

/* ==========================================================================
   ================= APP v3 — banner crossfade, nav ========================
   ========================================================================== */

/* Auth submit buttons: big, unmissable, tuned to each mood. Ported from the
   old .auth-card rules when the auth layout moved to .auth-panel / .ap-form. */
.ap-form .abtn-block {
  padding: 17px 22px; font-size: 17px; font-weight: 800; letter-spacing: .01em;
  border-radius: 14px; margin-top: 12px;
  background: var(--lime); color: var(--deep); border: 2px solid var(--deep);
  box-shadow: 0 18px 34px -16px rgba(15,46,34,.55);
}
.ap-form .abtn-block:hover { transform: translateY(-1px); background: var(--lime-dk); filter: none; }
.mood-lime .auth-foot-note svg { fill: var(--deep); }

/* dashboard banner crossfade */
.dash-banner .bg { position: absolute; inset: 0; }
.dash-banner .bg .shot { position: absolute; inset: 0; background-size: cover; background-position: center 35%; opacity: 0; transition: opacity 1.5s var(--ease); }
.dash-banner .bg .shot.on { opacity: 1; }

/* nav: clearer profile + logout */
.an-profile { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; padding: 4px 6px 4px 4px; border-radius: 100px; transition: background .15s var(--ease); }
.an-profile:hover { background: rgba(255,255,255,.07); }
.an-avatar { box-shadow: 0 0 0 2px rgba(200,241,53,.4); }
.an-plabel { color: var(--cream); font-size: 13px; font-weight: 600; }
.an-divider { width: 1px; height: 22px; background: rgba(255,255,255,.16); }
.an-logout { display: inline-flex; align-items: center; gap: 7px; color: var(--cream); opacity: .95; border: 1px solid rgba(255,255,255,.22); border-radius: 100px; padding: 7px 13px; font-size: 13px; font-weight: 600; text-decoration: none; transition: .15s var(--ease); }
.an-logout:hover { border-color: var(--lime); color: var(--lime); background: rgba(200,241,53,.08); opacity: 1; }
.an-logout svg { width: 17px; height: 17px; stroke: currentColor; fill: none; }
@media (max-width: 760px){ .an-plabel, .an-llabel { display: none; } .an-logout { border: 0; padding: 6px; } .an-divider { display: none; } }

/* auto-generate / suggest button */
.gen-btn { background: rgba(45,122,58,.10); color: var(--forest); border: 1px solid rgba(45,122,58,.28); border-radius: 100px; font-size: 12px; font-weight: 700; padding: 4px 11px; cursor: pointer; transition: .15s var(--ease); display: inline-flex; align-items: center; gap: 5px; }
.gen-btn:hover { background: var(--forest); color: #fff; border-color: var(--forest); }

/* ---------- password generator UI + flash stack (integration additions) ---------- */
.flash-stack { margin-bottom: 6px; }
.fld-labelrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gen-row { display: flex; gap: 8px; }
.pw-toggle { background: none; border: 0; cursor: pointer; color: var(--muted); font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 0; }
.pw-toggle.on { color: var(--forest); }

/* ---------- password field with an in-field reveal ----------
   The eye sits inside the input rather than up in the label row, so the row
   above carries only the label (and Forgot? on login). Input padding-right
   reserves the space so a long password never runs under the icon. */
.pw-field { position: relative; display: block; }
.pw-field > input { width: 100%; padding-right: 46px; }
.pw-eye {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0; border-radius: 8px;
  background: none; color: var(--muted); cursor: pointer;
  transition: color .15s var(--ease), background .15s var(--ease);
}
.pw-eye:hover { color: var(--forest); background: rgba(15,46,34,.06); }
.pw-eye.on { color: var(--forest); }
.pw-eye:focus-visible { outline: 2px solid var(--forest); outline-offset: 1px; }
.pw-eye svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 1.9; }

/* Generate gets its own line under the meter instead of sharing the label row */
.pw-genrow { display: flex; justify-content: flex-start; margin-top: 10px; }
.pw-gen-out { display: none; align-items: center; gap: 10px; margin-top: 10px; padding: 10px 12px; border-radius: 10px; background: rgba(45,122,58,.10); border: 1px solid rgba(45,122,58,.32); }
.pw-gen-out.show { display: flex; }
.pw-gen-out .pwo-lab { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: #1c5e30; flex: none; }
.pw-gen-out .pwo-val { font-family: var(--score); font-weight: 700; font-size: 15px; color: var(--ink); letter-spacing: .04em; word-break: break-all; flex: 1; }
.pw-gen-out .pwo-copy { background: var(--forest); color: #fff; border: 0; cursor: pointer; font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; padding: 6px 12px; border-radius: 7px; flex: none; }
.pw-gen-out .pwo-copy:hover { filter: brightness(1.08); }
.fld-err { color: #9a2b2b; font-size: 12.5px; font-weight: 600; margin-top: 6px; }

/* ---------- generated club crests (monogram badges in club colours) ---------- */
.crest { width: 30px; height: 30px; border-radius: 8px; display: inline-grid; place-items: center;
  font-family: var(--mark); font-weight: 700; font-size: 12px; letter-spacing: .01em; flex: none;
  background: var(--crest-bg, var(--card-1)); color: var(--crest-fg, var(--forest)); }
.team-cell { display: inline-flex; align-items: center; gap: 10px; font-weight: 600; }
/* let picks use each team's colours, but keep the selected-state override winning */
.pick .team .crest { width: 34px; height: 34px; border-radius: 9px; font-size: 13px;
  background: var(--crest-bg, var(--card-1)); color: var(--crest-fg, var(--forest)); }
.pick .team.sel .crest { background: var(--lime); color: var(--deep); }
/* real club logos: white plate so dark marks read on any row colour */
.crest.has-logo { background: #fff; border: 1px solid rgba(15,46,34,.12); padding: 3px; overflow: hidden; }
.crest.has-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.pick .team .crest.has-logo, .pick .team.sel .crest.has-logo { background: #fff; }
.cd-locked { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mark); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; font-size: 13px; color: var(--muted); }

/* My Tips: change a pick inline while the match is still open */
.pick-mini { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pm-btn { display: inline-flex; align-items: center; gap: 8px; background: #fff; border: 1.5px solid rgba(15,46,34,.22);
  border-radius: 100px; padding: 4px 14px 4px 5px; font-family: var(--body); font-size: 13px; font-weight: 700;
  color: var(--ink); cursor: pointer; transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease); }
.pm-btn .crest { width: 24px; height: 24px; border-radius: 50%; font-size: 9px; }
.pm-btn:hover { border-color: var(--forest); }
.pm-btn.on { background: var(--deep); color: #fff; border-color: var(--deep); }
.pm-saved { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--forest); }

/* ---------- misc app integration bits ---------- */
.sample-tag { display: inline-block; margin-left: 8px; font-family: var(--body); font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); background: rgba(15,46,34,.08); padding: 2px 8px; border-radius: 100px; vertical-align: middle; }
.vs-mini { color: var(--muted); font-weight: 700; margin: 0 6px; font-size: 12px; }
.atable td .team-cell { font-size: 14px; }

/* ==========================================================================
   ================= APP v4 — onboarding, billing, admin ===================
   ========================================================================== */

/* selectable option cards (checkbox / radio) */
.opt-grid { display: grid; gap: 10px; }
.opt-grid.cols2 { grid-template-columns: 1fr 1fr; }
.opt-grid.cols3 { grid-template-columns: repeat(3,1fr); }
@media (max-width: 620px){ .opt-grid.cols2, .opt-grid.cols3 { grid-template-columns: 1fr; } }
.opt-card { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border: 1.5px solid var(--app-line); border-radius: 12px; background: #fff; cursor: pointer; transition: border-color .15s var(--ease), background .15s var(--ease), box-shadow .15s var(--ease); position: relative; }
/* In dark/deep contexts the hardcoded #fff background makes card text invisible.
   Override to the app card surface so text colours stay readable. */
.page-deep .opt-card,
html[data-theme="dark"] .gt-app .opt-card { background: var(--app-card); }
.page-deep .opt-card:has(input:checked),
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) { background: rgba(200,241,53,.10); }
.page-deep .opt-card .oc-title,
html[data-theme="dark"] .gt-app .opt-card .oc-title { color: var(--ink); }
.page-deep .opt-card .oc-sub,
html[data-theme="dark"] .gt-app .opt-card .oc-sub { color: var(--muted); }

/* ---- WHICH ONE IS CHOSEN, ON THE DARK GROUND -----------------------------
   CLIENT, 17 SEP 2026: "in dark mode the charity that is clicked on is hard
   to see ... that is when picking a charity."

   The light-mode treatment is right and does not survive being inverted. Its
   three signals are a lime border, a ten-per-cent lime wash and a FOREST GREEN
   tick box — and on a deep green card the tick box is dark green on dark
   green, which is the part that actually says "this one". The wash is close to
   nothing at 10% over a dark surface, and a 1.5px border carries the whole
   message on its own.

   So on the dark ground the roles are swapped rather than reused: the tick box
   takes the lime and the tick itself goes deep green, which is the same pairing
   every other affirmative control in the app uses here. The wash and the ring
   are lifted enough to be visible against the card they sit on, and the chosen
   card's name goes to full-strength cream — a scan down the column lands on it
   without having to hunt for the border. */
.page-deep .opt-card:has(input:checked),
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) {
  background: rgba(200,241,53,.16);
  border-color: var(--lime);
  box-shadow: 0 0 0 3px rgba(200,241,53,.22);
}
.page-deep .opt-card:has(input:checked) .oc-box,
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) .oc-box {
  background: var(--lime); border-color: var(--lime);
}
.page-deep .opt-card:has(input:checked) .oc-box svg,
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) .oc-box svg {
  stroke: var(--deep);
}
.page-deep .opt-card:has(input:checked) .oc-title,
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) .oc-title,
.page-deep .opt-card:has(input:checked) .ch-name,
html[data-theme="dark"] .gt-app .opt-card:has(input:checked) .ch-name {
  color: var(--cream);
}
/* The unchosen box is a 2px outline of --app-line, which on this ground is
   faint enough that the row reads as having nothing to press. */
.page-deep .opt-card .oc-box,
html[data-theme="dark"] .gt-app .opt-card .oc-box {
  border-color: rgba(255,255,255,.34);
}
/* A charity is picked from a grid of cards rather than a list of labels, so
   the chosen one also brightens its logo tile — the tiles are what the eye is
   moving between, and a dimmed one beside a lit one is the fastest possible
   read of which is which. */
.page-deep .ch-picker .opt-card .ch-tile,
html[data-theme="dark"] .gt-app .ch-picker .opt-card .ch-tile { opacity: .82; }
.page-deep .ch-picker .opt-card:has(input:checked) .ch-tile,
html[data-theme="dark"] .gt-app .ch-picker .opt-card:has(input:checked) .ch-tile { opacity: 1; }

/* The create wizard is a cream sheet standing on a deep page, so the dark-ground
   treatment above (cream name, lime tick box) turns the chosen option's name
   into cream on cream. On that sheet the chosen card keeps the light-mode
   signals: dark name, forest tick box, white tick. */
.page-deep .wiz-card .opt-card:has(input:checked) .oc-title,
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) .oc-title,
.page-deep .wiz-card .opt-card:has(input:checked) .ch-name,
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) .ch-name { color: var(--deep); }
.page-deep .wiz-card .opt-card:has(input:checked) .oc-sub,
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) .oc-sub { color: #3f5648; }
.page-deep .wiz-card .opt-card:has(input:checked) .oc-box,
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.page-deep .wiz-card .opt-card:has(input:checked) .oc-box svg,
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) .oc-box svg { stroke: #fff; }
.page-deep .wiz-card .opt-card:has(input:checked),
html[data-theme="dark"] .gt-app .wiz-card .opt-card:has(input:checked) { background: rgba(200,241,53,.22); }
.opt-card:hover { border-color: var(--forest); }
.opt-card:has(input:checked) { border-color: var(--lime); background: rgba(200,241,53,.10); box-shadow: 0 0 0 3px rgba(200,241,53,.14); }
.opt-card input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt-card .oc-box { width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--app-line); flex: none; display: grid; place-items: center; margin-top: 1px; transition: .15s var(--ease); }
.opt-card.radio .oc-box { border-radius: 50%; }
.opt-card:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.opt-card .oc-box svg { width: 14px; height: 14px; stroke: #fff; fill: none; stroke-width: 3; opacity: 0; }
.opt-card:has(input:checked) .oc-box svg { opacity: 1; }
.opt-card .oc-title { display: block; font-weight: 700; color: var(--ink); font-size: 15px; }
.opt-card .oc-sub { display: block; font-size: 13px; color: var(--muted); margin-top: 2px; }
.opt-card .oc-right { margin-left: auto; text-align: right; }
.opt-card .oc-series { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.opt-card .oc-series-chip {
  font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
  padding: 3px 9px; border-radius: 100px;
  background: rgba(45,122,58,.09); color: var(--forest);
  border: 1px solid rgba(45,122,58,.2);
}
.opt-card:has(input:checked) .oc-series-chip { background: rgba(200,241,53,.2); border-color: rgba(200,241,53,.45); color: var(--deep); }

/* copy-link field */
.copy-field { display: flex; gap: 8px; }
.copy-field input { flex: 1; padding: 12px 14px; background: var(--app-card-2); border: 1px solid var(--app-line); border-radius: 10px; font-family: var(--body); font-size: 14px; color: var(--ink); }
.copy-field .abtn { flex: none; }

/* amount input with $ */
.amount-input { position: relative; }
.amount-input::before { content: "$"; position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--muted); font-weight: 700; z-index: 1; }
.amount-input input { padding-left: 26px; }

/* conditional blocks */
[data-when] { display: none; }
[data-when].show { display: block; }

/* vote card + result */
.vote-card { border: 1.5px solid var(--app-line); border-radius: 12px; padding: 16px 18px; background: #fff; }
.vote-card.win { border-color: var(--lime); background: rgba(200,241,53,.10); }
.vote-result { margin-top: 10px; }
.vote-bar { height: 10px; border-radius: 100px; background: rgba(15,46,34,.10); overflow: hidden; }
.vote-bar > i { display: block; height: 100%; background: linear-gradient(90deg,var(--forest),var(--lime)); }

/* plan "current" marker (reuses marketing .tier) */
.tier.current { outline: 2px solid var(--forest); outline-offset: 2px; }
.tier .tier-current { position: absolute; top: 14px; left: 14px; background: var(--forest); color: #fff; font-family: var(--mark); font-weight: 700; font-size: 10px; letter-spacing: .05em; text-transform: uppercase; padding: 3px 9px; border-radius: 100px; }
.tier-feats { list-style: none; margin: 14px 0; display: grid; gap: 8px; }
.tier-feats li { display: flex; gap: 8px; font-size: 13px; color: var(--ink); }
.tier.featured .tier-feats li { color: var(--cream); }
.tier-feats .tk { color: var(--forest); flex: none; font-weight: 900; }
.tier.featured .tier-feats .tk { color: var(--lime); }

/* receipt */
.receipt { max-width: 640px; margin: 0 auto; background: #fff; border: 1px solid var(--app-line); border-radius: 14px; padding: clamp(24px,4vw,40px); }
.receipt-head { display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 1px solid var(--app-line); padding-bottom: 20px; margin-bottom: 20px; gap: 16px; flex-wrap: wrap; }
.receipt-head .r-brand { font-family: var(--mark); font-weight: 900; font-size: 22px; color: var(--deep); }
.receipt-head .r-brand span { color: var(--forest); }
.receipt-meta { text-align: right; font-size: 13px; color: var(--muted); }
.receipt-row { display: flex; justify-content: space-between; padding: 9px 0; font-size: 14px; color: var(--ink); }
.receipt-row .k { color: var(--muted); }
.receipt-total { border-top: 2px solid var(--deep); margin-top: 14px; padding-top: 16px; display: flex; justify-content: space-between; align-items: baseline; }
.receipt-total .num { font-family: var(--score); font-weight: 900; font-size: 26px; color: var(--deep); }
.receipt-note { margin-top: 20px; font-size: 12px; color: var(--muted); }
@media print { .charity-strip, .app-nav, .no-print { display: none !important; } .app-main { padding: 0; } }

/* ---- ADMIN sub-layout ---- */
.admin { display: grid; grid-template-columns: 224px 1fr; min-height: 100vh; background: var(--app-bg); }
.admin-side { background: var(--deep-2); color: var(--cream); padding: 22px 16px; display: flex; flex-direction: column; gap: 4px; }
.admin-side .as-brand { font-family: var(--mark); font-weight: 900; font-size: 18px; color: #fff; text-transform: uppercase; letter-spacing: .02em; margin-bottom: 6px; }
.admin-side .as-brand span { color: var(--lime); }
.admin-side .as-tag { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--cream); opacity: .5; margin-bottom: 18px; }
.admin-side a { display: flex; align-items: center; gap: 10px; color: var(--cream); opacity: .75; padding: 11px 13px; border-radius: 9px; text-decoration: none; font-weight: 600; font-size: 14px; transition: .15s var(--ease); }
.admin-side a:hover { opacity: 1; background: rgba(255,255,255,.06); }
.admin-side a.on { background: rgba(200,241,53,.12); color: var(--lime); opacity: 1; }
.admin-side a svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.admin-side .as-foot { margin-top: auto; font-size: 12px; }
.admin-side .as-foot a { opacity: .6; }
.admin-main { padding: 26px 30px 60px; max-width: 1100px; }
.admin-h1 { font-family: var(--display); font-size: 26px; color: var(--ink); }
@media (max-width: 820px){
  .admin { grid-template-columns: 1fr; }
  .admin-side { flex-direction: row; align-items: center; gap: 4px; overflow-x: auto; padding: 12px; }
  .admin-side .as-brand, .admin-side .as-tag, .admin-side .as-foot { display: none; }
  .admin-side a { white-space: nowrap; }
}
.tiles { display: grid; grid-template-columns: repeat(4,1fr); gap: 14px; }
@media (max-width: 820px){ .tiles { grid-template-columns: 1fr 1fr; } }
.tile { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 12px; padding: 18px; }
.tile .t-num { font-family: var(--score); font-weight: 900; font-size: 30px; color: var(--ink); }
.tile .t-lab { font-size: 12px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); font-weight: 700; margin-top: 2px; }
.atable.compact td { padding: 10px 14px; }
.mini-form { background: var(--app-card); border: 1px solid var(--app-line); border-radius: 12px; padding: 20px; }
.status-pill { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; padding: 3px 9px; border-radius: 100px; }
.status-pill.open { background: rgba(45,122,58,.15); color: var(--ad-ok-fg, #1c5e30); }
.status-pill.locked { background: var(--ad-quiet-bg, rgba(15,46,34,.1)); color: var(--muted); }
.status-pill.draft { background: rgba(178,120,40,.14); color: var(--ad-gold-fg, #8a5a12); }

/* ==========================================================================
   ============ v8 — password toggle · copy feedback · ADMIN polish ========
   ========================================================================== */

/* copied-button feedback (invite links etc.) */
.abtn.is-copied { background: var(--forest) !important; color: #fff !important; border-color: var(--forest) !important; }

/* ---- ADMIN: richer tiles + shaded-column tables ---- */
.admin-main { max-width: 1120px; }

.tiles .tile { border-left: 4px solid var(--forest); position: relative; overflow: hidden; transition: transform .18s var(--ease), box-shadow .18s var(--ease); }
.tiles .tile:hover { transform: translateY(-3px); box-shadow: 0 18px 38px -24px rgba(0,0,0,.4); }
.tiles .tile::after { content: ""; position: absolute; right: -18px; top: -18px; width: 72px; height: 72px; border-radius: 50%; background: rgba(45,122,58,.08); }
.tiles .tile:nth-child(1){ background:#EFEBDC; border-left-color:#6E8B2E; }
.tiles .tile:nth-child(2){ background:#E7EDDB; border-left-color:#2D7A3A; }
.tiles .tile:nth-child(3){ background:#DEEADB; border-left-color:#1F7A63; }
.tiles .tile:nth-child(4){ background:#D8E7DB; border-left-color:#2E6E7A; }
.tiles .tile .t-num { color: var(--deep); position: relative; }
.tiles .tile .t-lab { color: var(--forest); opacity: .85; position: relative; }

.admin .card { box-shadow: 0 16px 38px -30px rgba(0,0,0,.32); }
.admin .card > .card-h { padding-bottom: 10px; border-bottom: 1px solid var(--app-line-2); }
.admin .atable { border-spacing: 0 7px; }
.admin .atable thead th { color: var(--forest); font-weight: 800; letter-spacing: .07em; padding-bottom: 8px; }
.admin .atable td { background: #F4F1E6; border-color: rgba(15,46,34,.08); }
/* graduated column shades — blend cream → sage across each row */
.admin .atable td:nth-child(1){ background:#F1EEDF; box-shadow: inset 3px 0 0 var(--forest); }
.admin .atable td:nth-child(2){ background:#EAEFDD; }
.admin .atable td:nth-child(3){ background:#E2ECDB; }
.admin .atable td:nth-child(4){ background:#DBE8DA; }
.admin .atable td:nth-child(5){ background:#D5E5D8; }
.admin .atable tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(200,241,53,.12); }
.admin .atable tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--forest), inset 0 0 0 999px rgba(200,241,53,.12); }
.admin .atable td b, .admin .atable td a[style*="forest"] { color: var(--deep); }

/* ---- Sync provider status cards ---- */
.sync-providers { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 20px; max-width: 720px; }
@media (max-width: 640px){ .sync-providers { grid-template-columns: 1fr; } }
.prov { display: flex; align-items: flex-start; gap: 12px; padding: 16px 18px; border-radius: 12px; border: 1px solid var(--app-line); background: var(--app-card); border-left: 3px solid var(--forest); }
.prov .prov-ic { width: 38px; height: 38px; border-radius: 10px; background: rgba(45,122,58,.12); color: var(--forest); display: grid; place-items: center; flex: none; }
.prov .prov-ic svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.prov h4 { font-size: 14px; color: var(--ink); margin: 0; }
.prov p { font-size: 12px; color: var(--muted); margin: 3px 0 0; line-height: 1.5; }
.prov .status-pill { margin-left: auto; flex: none; }

/* ==========================================================================
   ============ v9 — UI refresh: CTAs · loader copy · lime forms ·
   ============ elegant dropdowns · side scenes · admin & pricing presence ==
   ========================================================================== */

/* ---------- BUTTONS: lime that turns green on hover ---------- */
.abtn-primary { transition: background .18s var(--ease), color .18s var(--ease), transform .1s var(--ease), box-shadow .18s var(--ease); }
.abtn-primary:hover { background: var(--forest); color: #fff; filter: none; box-shadow: 0 14px 28px -16px rgba(28,94,48,.65); }
.abtn-primary:active { background: #1c5e30; color: #fff; }
.abtn-lg { padding: 14px 26px; font-size: 15px; border-radius: 12px; font-weight: 800; }

/* ---------- LOADER: tell the user what's happening ---------- */
.loader-task { margin-top: 16px; font-size: 18px; font-weight: 700; color: var(--cream); opacity: .95; letter-spacing: .03em; }
.loader-task::after { content: ""; display: inline-block; width: 1.2em; text-align: left; animation: taskDots 1.2s steps(4, end) infinite; }
@keyframes taskDots { 0% { content: ""; } 25% { content: "."; } 50% { content: ".."; } 75% { content: "..."; } }
@media (prefers-reduced-motion: reduce) { .loader-task::after { content: "…"; animation: none; } }

/* ---------- ELEGANT DROPDOWNS (ddx — replaces native selects) ---------- */
.ddx { position: relative; display: block; min-width: 0; }
.ddx select { position: absolute !important; width: 1px; height: 1px; opacity: 0; pointer-events: none; margin: 0; padding: 0; border: 0; }
.ddx-btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: linear-gradient(180deg, #fff, #FAF8EE);
  border: 1px solid rgba(15,46,34,.16); border-radius: 12px;
  padding: 12px 16px; font-family: var(--body); font-size: 15px; font-weight: 600; color: var(--ink);
  cursor: pointer; text-align: left;
  box-shadow: 0 2px 6px -3px rgba(15,46,34,.18), inset 0 1px 0 #fff;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease), background .16s var(--ease);
}
.ddx-btn:hover { border-color: var(--forest); box-shadow: 0 6px 16px -8px rgba(28,94,48,.35), inset 0 1px 0 #fff; }
.ddx.open .ddx-btn { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(200,241,53,.35), 0 6px 16px -8px rgba(28,94,48,.35); }
.ddx-btn.is-disabled { opacity: .55; cursor: not-allowed; }
.ddx-val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ddx-val.is-placeholder { color: var(--muted); font-weight: 500; }
.ddx-chev { width: 18px; height: 18px; color: var(--forest); flex: none; background: rgba(45,122,58,.10); border-radius: 6px; padding: 2px; transition: transform .2s var(--ease), background .2s var(--ease), color .2s var(--ease); }
.ddx.open .ddx-chev { transform: rotate(180deg); background: var(--forest); color: #fff; }
.ddx-menu {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 60;
  background: #FFFDF6; border: 1px solid rgba(15,46,34,.14); border-radius: 14px;
  box-shadow: 0 26px 60px -24px rgba(15,46,34,.45), 0 4px 14px -8px rgba(15,46,34,.25);
  padding: 7px; max-height: 300px; overflow: auto;
  opacity: 0; visibility: hidden; transform: translateY(-8px) scale(.985);
  transition: opacity .18s var(--ease), transform .18s var(--ease), visibility .18s;
}
.ddx.open .ddx-menu { opacity: 1; visibility: visible; transform: none; }
/* The open menu, lifted out of a clipping ancestor — see gt-ui.js. Left, top
   and width are written by the script in viewport coordinates; all this rule
   does is take the menu out of flow and put it above the page. z-index is the
   nav's plus one: a dropdown opened under the sticky bar reads as broken. */
.ddx-menu.is-fixed { position: fixed; z-index: 220; }
.ddx-opt {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  text-align: left; background: none; border: 0; padding: 11px 13px; border-radius: 9px;
  font-family: var(--body); font-size: 14px; font-weight: 600; color: var(--ink); cursor: pointer;
  transition: background .12s var(--ease), color .12s var(--ease);
}
.ddx-opt:hover { background: rgba(45,122,58,.10); color: var(--forest); }
.ddx-opt.on { background: linear-gradient(90deg, rgba(200,241,53,.35), rgba(200,241,53,.12)); color: var(--deep); }
.ddx-opt.on::after { content: "✓"; font-weight: 900; color: var(--forest); }
.ddx-menu::-webkit-scrollbar { width: 8px; }
.ddx-menu::-webkit-scrollbar-thumb { background: rgba(15,46,34,.18); border-radius: 100px; }

/* ---------- SIDE SCENES: imagery living inside the cream ---------- */
.page-scenes { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.page-scenes .scene { position: absolute; top: 0; bottom: 0; width: clamp(150px, 15vw, 260px); overflow: hidden; }
.page-scenes .scene.left { left: 0; }
.page-scenes .scene.right { right: 0; }
.page-scenes .shot { position: absolute; inset: 0; background-size: cover; background-position: center; opacity: 0; transition: opacity 2s var(--ease); }
.page-scenes .shot.on { opacity: 1; }
/* clear photos — just a hairline where they meet the content, no mist */
.page-scenes .scene.left { border-right: 1px solid rgba(15,46,34,.10); }
.page-scenes .scene.right { border-left: 1px solid rgba(15,46,34,.10); }
.app-main, .admin-main { position: relative; z-index: 1; }
@media (max-width: 1180px) { .page-scenes { display: none; } }

/* ---------- CREATE COMPETITION: centered lime form ---------- */
.app-head.head-center { display: block; text-align: center; margin-bottom: 28px; }
.app-head.head-center .app-eyebrow { font-size: 15px; letter-spacing: .22em; }
.app-head.head-center .app-h1 { font-size: clamp(30px, 4.4vw, 44px); margin-top: 6px; }
.app-head.head-center .app-sub { max-width: 52ch; margin: 8px auto 0; }
.form-centered { max-width: 760px; margin: 0 auto; }
.form-centered > .abtn { display: flex; width: 100%; }
/* concentrated GoodTip yellow — the button lime, not a washed-out tint */
.form-lime > .card {
  background: linear-gradient(180deg, #D9F059, var(--lime) 55%, #BCE72B);
  border: 1px solid rgba(15,46,34,.3);
  box-shadow: 0 30px 60px -34px rgba(15,46,34,.6), inset 0 1px 0 rgba(255,255,255,.5);
}
.form-lime .card .card { box-shadow: none; }
.form-lime .fld > label { color: var(--deep); }
.form-lime .fld input, .form-lime .fld textarea { background: #fff; border-color: rgba(15,46,34,.3); }
.form-lime .fld input:focus, .form-lime .fld textarea:focus { border-color: var(--deep); box-shadow: 0 0 0 3px rgba(15,46,34,.18); }
.form-lime .opt-card { background: rgba(255,255,255,.85); border-color: rgba(15,46,34,.28); }
.form-lime .hint, .form-lime .app-sub { color: rgba(15,46,34,.72); }
/* a lime button disappears on the lime card — go deep green there */
.form-lime .abtn-primary { background: var(--deep); color: var(--lime); border: 2px solid var(--deep); padding: 16px 22px; font-size: 16px; }
.form-lime .abtn-primary:hover { background: var(--deep-2); filter: none; }

/* ---------- ADMIN: presence ---------- */
.admin-main { margin: 0 auto; width: 100%; }
.admin .page-scenes .scene.left { left: 224px; }
@media (max-width: 820px) { .admin .page-scenes { display: none; } }

/* back-to-app: visible, at the top of the sidebar */
.admin-side .as-back {
  display: flex; align-items: center; gap: 8px; margin: 0 0 16px;
  background: rgba(200,241,53,.14); border: 1px solid rgba(200,241,53,.45);
  color: var(--lime) !important; opacity: 1 !important; font-weight: 700; font-size: 13px;
  padding: 10px 13px; border-radius: 9px;
}
.admin-side .as-back:hover { background: var(--lime); color: var(--deep) !important; }
@media (max-width: 820px) { .admin-side .as-back { display: inline-flex; margin: 0; white-space: nowrap; } }

/* stat tiles: rich gradients, felt presence */
.tiles .tile { border: 0; border-left: 0; padding: 22px 20px; border-radius: 14px; box-shadow: 0 20px 40px -24px rgba(15,46,34,.55); }
.tiles .tile:nth-child(1) { background: linear-gradient(150deg, #35844A, #1C5E30); border-left: 0; }
.tiles .tile:nth-child(2) { background: linear-gradient(150deg, #2A9079, #134F42); border-left: 0; }
.tiles .tile:nth-child(3) { background: linear-gradient(150deg, #8FAF33, #5F7A1B); border-left: 0; }
.tiles .tile:nth-child(4) { background: linear-gradient(150deg, #3B8296, #20505E); border-left: 0; }
.tiles .tile::after { background: rgba(255,255,255,.10); width: 96px; height: 96px; right: -24px; top: -24px; }
.tiles .tile .t-num { color: #fff; font-size: 34px; }
.tiles .tile .t-lab { color: rgba(255,255,255,.85); opacity: 1; }
.tiles .tile:hover { transform: translateY(-4px); box-shadow: 0 26px 50px -26px rgba(15,46,34,.7); }

/* admin cards: tinted, framed, no more wallflower */
.admin .card {
  background: linear-gradient(180deg, #F7F4E2, #EDEFD2);
  border: 1px solid rgba(15,46,34,.16); border-top: 4px solid var(--forest);
  box-shadow: 0 22px 46px -30px rgba(15,46,34,.5);
}
.admin .mini-form {
  background: linear-gradient(180deg, #F3F9CB, #E7F3A6);
  border: 1px solid rgba(140,175,25,.5); border-top: 4px solid var(--forest);
  box-shadow: 0 22px 46px -30px rgba(15,46,34,.5);
}
.admin .prov { box-shadow: 0 16px 34px -26px rgba(15,46,34,.45); border: 1px solid rgba(15,46,34,.14); border-left: 4px solid var(--forest); }
.admin .prov:nth-child(1) { background: linear-gradient(180deg, #EDF2DA, #E0EACA); }
.admin .prov:nth-child(2) { background: linear-gradient(180deg, #F2EEDA, #EAE2C6); }

/* admin tables: deep header band + saturated column shades */
.admin .atable thead th { background: var(--deep); color: var(--cream); padding: 12px 16px; letter-spacing: .09em; }
.admin .atable thead th:first-child { border-radius: 10px 0 0 10px; }
.admin .atable thead th:last-child { border-radius: 0 10px 10px 0; }
.admin .atable td { border-color: rgba(15,46,34,.14); }
.admin .atable td:nth-child(1) { background: #EFE9C8; box-shadow: inset 4px 0 0 var(--forest); }
.admin .atable td:nth-child(2) { background: #E4EDBB; }
.admin .atable td:nth-child(3) { background: #D5E6AF; }
.admin .atable td:nth-child(4) { background: #C5DEA8; }
.admin .atable td:nth-child(5) { background: #B7D6A6; }
.admin .atable tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(200,241,53,.18); }
.admin .atable tbody tr:hover td:first-child { box-shadow: inset 4px 0 0 var(--forest), inset 0 0 0 999px rgba(200,241,53,.18); }

/* ---------- PRICING TABLE: columns that own the room ---------- */
.tier-table td { color: #46543f; }
.tier-table td:nth-child(1) { background: #F2ECC5; }
.tier-table td:nth-child(2) { background: #E5EEB6; }
.tier-table td:nth-child(3) { background: #D3E6AB; }
.tier-table td:nth-child(4) { background: #BFDDA3; }
.tier-table tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(200,241,53,.14); }
.tt-priced .tt-price { color: var(--deep); }
@media (max-width: 800px) {
  .tier-table tbody tr:nth-of-type(1) td { background: #F2ECC5; }
  .tier-table tbody tr:nth-of-type(2) td { background: #E5EEB6; }
  .tier-table tbody tr:nth-of-type(3) td { background: #D3E6AB; }
  .tier-table tbody tr:nth-of-type(4) td { background: #C7E0A6; }
  .tier-table tbody tr:nth-of-type(5) td { background: #BFDDA3; }
}

/* ---------- GOOD LIST: filter bar + warm empty states ---------- */
.gl-filterbar {
  display: flex; gap: 12px; flex-wrap: wrap; align-items: center;
  background: linear-gradient(180deg, #F6F3E4, #EFEFDA);
  border: 1px solid rgba(15,46,34,.13); border-radius: 16px;
  padding: 14px 16px; margin-bottom: 24px;
  box-shadow: 0 18px 40px -30px rgba(15,46,34,.4);
}
.gl-filterbar .ddx { flex: 1 1 220px; min-width: 200px; }
.gl-filterbar > select { flex: 1 1 220px; min-width: 200px; padding: 12px 16px; border: 1px solid rgba(15,46,34,.16); border-radius: 12px; background: #fff; font-family: var(--body); font-size: 15px; font-weight: 600; color: var(--ink); }
.gl-empty {
  max-width: 620px; margin: 12px auto; text-align: center; padding: 52px 34px;
  background: radial-gradient(420px 260px at 50% -20%, rgba(200,241,53,.25), transparent 65%), linear-gradient(165deg, #16402C, var(--deep-2));
  border: 1px solid rgba(200,241,53,.35); border-radius: 20px;
  box-shadow: 0 28px 56px -30px rgba(0,0,0,.6);
}
.gl-empty .ge-ic { width: 58px; height: 58px; border-radius: 50%; background: linear-gradient(150deg, var(--lime), var(--lime-dk)); display: grid; place-items: center; margin: 0 auto 18px; box-shadow: 0 14px 28px -14px rgba(140,175,25,.7); }
.gl-empty .ge-ic svg { width: 28px; height: 28px; stroke: var(--deep); fill: none; stroke-width: 2; }
.gl-empty h3 { font-family: var(--display); font-size: 21px; color: #fff; margin-bottom: 10px; }
.gl-empty .note { max-width: 46ch; margin: 0 auto; color: var(--cream); opacity: .85; }

/* ---------- PROFILE: avatar upload ---------- */
.an-avatar.has-img { overflow: hidden; padding: 0; background: var(--deep); }
.an-avatar.has-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.avatar-edit { position: relative; display: inline-block; cursor: pointer; }
.avatar-edit .av-face { width: 84px; height: 84px; border-radius: 50%; background: var(--lime); color: var(--deep); display: grid; place-items: center; font-family: var(--mark); font-weight: 800; font-size: 32px; box-shadow: 0 0 0 3px rgba(200,241,53,.4); overflow: hidden; }
.avatar-edit .av-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-edit .av-badge { position: absolute; right: -2px; bottom: -2px; width: 30px; height: 30px; border-radius: 50%; background: var(--deep); color: var(--lime); display: grid; place-items: center; border: 2px solid var(--app-bg); transition: background .15s var(--ease), color .15s var(--ease), transform .15s var(--ease); }
.avatar-edit .av-badge svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; }
.avatar-edit:hover .av-badge { background: var(--forest); color: #fff; transform: scale(1.08); }
.avatar-remove { background: none; border: 0; cursor: pointer; font-size: 12px; font-weight: 700; color: #9a2b2b; padding: 0; margin-top: 8px; }
.avatar-remove:hover { text-decoration: underline; }

/* ==========================================================================
   ======== DASHBOARD v2 — comp picker, fixtures slate, locking strip =======
   ========================================================================== */

/* control bar: pick a comp, or start one — the two top-level actions */
.dash-ctl { display: flex; align-items: center; gap: 14px; margin: 0 0 18px; flex-wrap: wrap; }
.dash-ctl .dc-pick { flex: 1 1 360px; display: flex; align-items: center; gap: 12px; min-width: 0; }
.dash-ctl .dc-lab { font-family: var(--mark); font-weight: 900; font-size: 13px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); flex: none; }
.dash-ctl .dc-pick .ddx, .dash-ctl .dc-pick > select { flex: 1 1 auto; min-width: 220px; }
.dash-ctl .dc-pick > select { padding: 13px 16px; border: 1px solid rgba(15,46,34,.2); border-radius: 12px; background: #fff; font-family: var(--body); font-size: 15px; font-weight: 600; color: var(--ink); }
/* the competition picker deserves more presence than a stock control */
.dash-ctl .dc-pick .ddx .ddx-btn { padding: 14px 18px; border-radius: 12px; font-size: 15px; font-weight: 700; border-color: rgba(15,46,34,.24); box-shadow: 0 10px 24px -18px rgba(15,46,34,.5); }
.dash-ctl .dc-pick .ddx .ddx-menu { border-radius: 14px; padding: 8px; }
.dash-ctl .dc-pick .ddx .ddx-opt { padding: 12px 14px; font-size: 14.5px; }

/* unmissable create button */
.abtn-xl { padding: 17px 28px; font-size: 17px; font-weight: 800; letter-spacing: .01em; border-radius: 14px; border: 2px solid var(--deep); box-shadow: 0 18px 36px -16px rgba(15,46,34,.55); }
.abtn-xl:hover { transform: translateY(-1px); }

/* the round's slate of games */
.fixtures { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
.fx { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 14px; padding: 13px 16px; background: #fff; border: 1px solid var(--app-line); border-radius: 12px; transition: border-color .15s var(--ease), transform .15s var(--ease); }
.fx:hover { border-color: rgba(169,209,28,.6); transform: translateX(3px); }
.fx-teams { font-weight: 700; color: var(--ink); font-size: 15px; min-width: 0; }
.fx-teams i { font-style: normal; color: var(--muted); font-weight: 700; margin: 0 9px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
.fx-meta { font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.fx-pill { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; padding: 5px 11px; border-radius: 100px; background: rgba(15,46,34,.06); color: var(--muted); border: 1px solid var(--app-line); white-space: nowrap; }
.fx.done .fx-pill { background: rgba(200,241,53,.4); color: var(--deep); border-color: rgba(169,209,28,.55); }
@media (max-width: 680px){ .fx { grid-template-columns: 1fr auto; } .fx-meta { display: none; } }

/* ---------- dashboard tip slip: pick teams inline, confirm at the top ---------- */
.slip-bar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; background: var(--deep); border-radius: 14px; padding: 14px 18px; margin: 18px 0 12px; }
.slip-bar .slip-info { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.slip-bar .slip-info b { color: #fff; font-size: 15px; }
.slip-bar .slip-info span { color: var(--cream); opacity: .75; font-size: 12.5px; line-height: 1.4; }
.slip-bar .slip-meter { font-family: var(--score); font-weight: 800; font-size: 13px; color: var(--cream); white-space: nowrap; }
.slip-bar .slip-meter b { font-size: 20px; color: var(--lime); margin-right: 2px; }
.slip-bar.locked { background: rgba(15,46,34,.08); border: 1px solid var(--app-line); }
.slip-bar.locked .slip-info b { color: var(--ink); }
.slip-bar.locked .slip-info span { color: var(--muted); opacity: 1; }
.pbar.slim { height: 6px; }

/* fixture rows with tappable teams (SportPesa-style slate) */
.fxp { background: #fff; border: 1px solid var(--app-line); border-radius: 12px; padding: 12px 14px; transition: border-color .15s var(--ease), box-shadow .15s var(--ease); }
.fxp + .fxp { margin-top: 0; }
.fxp-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; font-size: 12px; color: var(--muted); }
.fxp-meta .fxp-date { font-weight: 700; color: var(--ink); opacity: .75; white-space: nowrap; }
.fxp-meta .fxp-venue { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.fxp-meta .fxp-state { margin-left: auto; font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; padding: 4px 10px; border-radius: 100px; background: rgba(15,46,34,.06); color: var(--muted); border: 1px solid var(--app-line); white-space: nowrap; }
.fxp.done .fxp-state, .fxp.picked .fxp-state { background: rgba(200,241,53,.4); color: var(--deep); border-color: rgba(169,209,28,.55); }
.fxp.done { border-color: rgba(169,209,28,.5); }
.fxp.picked { border-color: rgba(169,209,28,.8); box-shadow: 0 0 0 3px rgba(200,241,53,.18); }
/* compact team buttons inside the slate */
.pick.pick-sm .team { padding: 10px 12px; font-size: 14px; gap: 9px; border-radius: 10px; }
.pick.pick-sm .team .crest { width: 30px; height: 30px; font-size: 12px; }
.pick.pick-sm .vs { font-size: 11px; }
@media (max-width: 560px){
  .pick.pick-sm { grid-template-columns: 1fr 30px 1fr; gap: 6px; }
  .pick.pick-sm .team { flex-direction: column; text-align: center; gap: 6px; padding: 9px 6px; font-size: 12.5px; }
  .pick.pick-sm .team.away { flex-direction: column; }
}

/* ---------- find-a-group autocomplete ---------- */
.ac { position: relative; max-width: 640px; }
.ac-box { display: flex; align-items: center; gap: 10px; background: #fff; border: 1.5px solid rgba(15,46,34,.22); border-radius: 14px; padding: 4px 16px; transition: border-color .15s var(--ease), box-shadow .15s var(--ease); }
.ac-box:focus-within { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.14); }
.ac-box .ac-ic { width: 20px; height: 20px; color: var(--muted); flex: none; }
.ac-box input { flex: 1; border: 0; outline: 0; background: none; padding: 13px 0; font-family: var(--body); font-size: 16px; font-weight: 600; color: var(--ink); min-width: 0; }
.ac-box input::-webkit-search-cancel-button { -webkit-appearance: none; }
.ac-spin { width: 16px; height: 16px; border: 2px solid rgba(15,46,34,.2); border-top-color: var(--forest); border-radius: 50%; animation: acspin .7s linear infinite; flex: none; }
@keyframes acspin { to { transform: rotate(360deg); } }
.ac-panel { position: absolute; top: calc(100% + 8px); left: 0; right: 0; background: #fff; border: 1px solid var(--app-line); border-radius: 14px; box-shadow: 0 26px 54px -22px rgba(0,0,0,.4); padding: 6px; z-index: 40; max-height: 340px; overflow: auto; }
.ac-opt { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; width: 100%; text-align: left; background: none; border: 0; padding: 11px 13px; border-radius: 10px; cursor: pointer; font-family: var(--body); transition: background .12s var(--ease); }
.ac-opt:hover, .ac-opt.hi { background: rgba(200,241,53,.18); }
.ac-opt .ac-name { font-weight: 700; font-size: 15px; color: var(--ink); grid-column: 1; }
.ac-opt .ac-meta { font-size: 12px; color: var(--muted); grid-column: 1; }
.ac-opt .ac-chip { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; padding: 4px 10px; border-radius: 100px; }
.ac-opt .ac-chip.member { background: rgba(45,122,58,.14); color: var(--forest); }
.ac-opt .ac-chip.pending { background: rgba(154,107,31,.14); color: #875D1A; }
.ac-empty { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 14px 16px; font-size: 14px; color: var(--muted); }

/* other comps about to lock — a notification strip, not a wall of cards */
/* The dashboard's "Locking soon in your other comps" strip used to live
   here. Removed Aug 2026 at the client's request: on an account in six
   organisations it repeated the same round six times under the fixtures
   the member had actually come to tip. The organisation switcher in the
   nav is how you reach another comp. */

/* ==========================================================================
   ===== CREATE PAGE — deep green stage (no cream), imagery through glass ===
   ========================================================================== */
.app.page-deep { background: radial-gradient(900px 560px at 78% -10%, rgba(200,241,53,.12), transparent 60%), radial-gradient(720px 520px at -10% 112%, rgba(45,122,58,.4), transparent 62%), var(--deep); }
.app.page-deep .app-head .app-h1 { color: #fff; }
.app.page-deep .app-head .app-eyebrow { color: var(--lime); }
.app.page-deep .app-head .app-sub { color: var(--cream); opacity: .82; }
.app.page-deep .page-scenes .scene.left::after { background: linear-gradient(90deg, rgba(15,46,34,.06), rgba(15,46,34,.32) 55%, rgba(15,46,34,.85)); border-right-color: rgba(200,241,53,.25); }
.app.page-deep .page-scenes .scene.right::after { background: linear-gradient(270deg, rgba(15,46,34,.06), rgba(15,46,34,.32) 55%, rgba(15,46,34,.85)); border-left-color: rgba(200,241,53,.25); }

/* ==========================================================================
   ============ v10 — manage area rides the dashboard design system ========
   ========================================================================== */
.manage-app .dash-banner { margin-bottom: 22px; }
.manage-app .admin-h1 { font-size: 26px; margin-bottom: 18px; }
.manage-app .an-links a.on { color: var(--lime); }
@media (max-width: 1180px) { .manage-app .page-scenes { display: none; } }
/* manage backdrop: the cream gives way to a full-bleed image under a green
   glass wash — with the two side scenes that makes three image views. The
   wash stays strong enough that cards and tables read exactly as before. */
.manage-app { background: var(--deep-2); }
/* overflow:hidden because the shots inside are scaled and panned past their
   own box — every other stack container clips for the same reason, and this
   was the one that relied on being viewport-sized to get away with it. */
.page-backdrop { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.page-backdrop .shot { position: absolute; inset: 0; background-size: cover; background-position: center; opacity: 0; transition: opacity 2s var(--ease); }
.page-backdrop .shot.on { opacity: 1; }
.page-backdrop .wash { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(15,46,34,.88), rgba(20,74,52,.80) 45%, rgba(11,35,25,.92)); }
.manage-app .admin-h1 { color: var(--cream); }
.manage-app .app-sub, .manage-app .app-eyebrow { color: rgba(245,241,230,.75); }

/* ---------- TELL THE BOSS ---------- */
.boss-stats { display: flex; gap: 28px; margin-top: 34px; flex-wrap: wrap; }
.boss-stats > div { display: flex; flex-direction: column; gap: 2px; }
.boss-stats b { font-family: var(--score); font-weight: 900; font-size: 30px; color: var(--deep); line-height: 1; }
.boss-stats span { font-size: 12.5px; color: var(--muted); font-weight: 600; letter-spacing: .02em; }
.boss-note { max-width: 680px; margin: 36px auto 0; position: relative; }

/* the letter, styled as an unrolled scroll: cylinder bars top and bottom,
   ruled paper between, letter set in a serif so it reads as written, not UI */
.bn-scroll { position: relative; filter: drop-shadow(0 34px 44px rgba(15,46,34,.30)); }
.bn-roll {
  position: relative; z-index: 2; height: 30px; margin: 0 -16px; border-radius: 15px;
  background: linear-gradient(180deg, #D9D0B2 0%, #FFFDF4 44%, #EBE3C6 76%, #C9BE9A 100%);
  border: 1px solid rgba(15,46,34,.20);
  box-shadow: 0 5px 12px -5px rgba(15,46,34,.40);
}
.bn-roll::before, .bn-roll::after {
  content: ""; position: absolute; top: 3px; bottom: 3px; width: 13px; border-radius: 50%;
  background: radial-gradient(circle at 38% 45%, #C4B992, #948A64 72%);
  box-shadow: inset 0 0 0 2px rgba(15,46,34,.14);
}
.bn-roll::before { left: 2px; }
.bn-roll::after { right: 2px; }
.bn-paper {
  position: relative; z-index: 1; margin: -15px 0;
  background:
    repeating-linear-gradient(180deg, transparent 0 31px, rgba(45,122,58,.08) 31px 32px),
    linear-gradient(180deg, #FFFDF4, #FAF4DF);
  border-left: 1px solid rgba(15,46,34,.16); border-right: 1px solid rgba(15,46,34,.16);
  padding: 46px 46px 42px;
}
.bn-paper p { margin: 0 0 15px; font-family: Georgia, 'Times New Roman', serif; font-size: 17px; line-height: 1.85; color: var(--ink); }
.bn-paper p:first-child { font-weight: 700; }
.bn-paper .bn-sign { margin-bottom: 0; font-style: italic; font-weight: 700; font-size: 19px; color: var(--forest); }
.boss-note .bn-actions { display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 20px; }
.boss-note .bn-hint { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; color: var(--muted); }

/* let-us-send-it form, same paper card language as the coming-soon form */
.bn-send {
  margin-top: 30px; background: #FFFDF4; border: 1px solid rgba(15,46,34,.16);
  border-radius: 16px; padding: 28px 32px; border-top: 6px solid var(--lime);
  box-shadow: 0 30px 60px -38px rgba(15,46,34,.55);
}
.bn-send h3 { font-family: var(--display); font-size: 21px; color: var(--deep); margin: 0 0 16px; }
.bn-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px; }
.bn-fields label:last-child { grid-column: 1 / -1; }
.bn-fields label { display: block; font-weight: 700; font-size: 13.5px; color: var(--ink); }
.bn-fields .bn-opt { font-weight: 400; color: var(--muted); }
.bn-fields input {
  display: block; width: 100%; margin-top: 6px; padding: 12px 14px;
  border: 1.5px solid rgba(15,46,34,.22); border-radius: 10px; background: #fff;
  font-family: var(--body); font-size: 15px; color: var(--ink);
}
.bn-fields input:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.15); }
.bn-send-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 18px; }
.bn-send-row .bn-hint { text-transform: none; letter-spacing: 0; font-weight: 600; }
.bn-error { margin: 0 0 14px; font-weight: 700; color: #9a2b2b; }
.bn-sent { text-align: center; }
.bn-sent h3 { color: var(--forest); margin-bottom: 8px; }
.bn-sent p { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.6; }
@media (max-width: 640px) {
  .bn-paper { padding: 34px 24px 30px; }
  .bn-fields { grid-template-columns: 1fr; }
  .bn-roll { margin: 0 -8px; }
}
.cmp-list { list-style: none; margin: 14px 0 0; padding: 0; }
.cmp-list li { position: relative; padding: 9px 0 9px 30px; line-height: 1.5; border-top: 1px solid rgba(255,255,255,.08); }
.cmp-list li:first-child { border-top: 0; }
.cmp-list li::before { position: absolute; left: 2px; top: 9px; font-weight: 900; }
.cmp-list.bad li::before { content: "\2715"; color: #d98c8c; }
.cmp-list.good li::before { content: "\2713"; color: var(--lime); }
.cta-pro .btn-dark + .btn-dark { margin-left: 10px; }

/* ---------- COMING SOON: lock-in form ---------- */
.soon-form { max-width: 520px; margin: 34px auto 0; }
.soon-form form, .soon-form .soon-done {
  background: #FFFDF4; border: 1px solid rgba(15,46,34,.16); border-radius: 16px;
  padding: 30px 32px; box-shadow: 0 30px 60px -38px rgba(15,46,34,.55);
  border-top: 6px solid var(--lime);
}
.soon-form label { display: block; font-weight: 700; font-size: 13.5px; color: var(--ink); margin-bottom: 16px; }
.soon-form label .opt { font-weight: 400; color: var(--muted); }
.soon-form input, .soon-form select {
  display: block; width: 100%; margin-top: 6px; padding: 12px 14px;
  border: 1.5px solid rgba(15,46,34,.22); border-radius: 10px; background: #fff;
  font-family: var(--body); font-size: 15px; color: var(--ink);
}
.soon-form input:focus, .soon-form select:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.15); }
.soon-form .soon-note { margin: 12px 0 0; text-align: center; font-size: 12.5px; color: var(--muted); }
.soon-form .soon-error { margin: 0 0 14px; font-weight: 700; color: #9a2b2b; }
.soon-form .soon-done { text-align: center; }
.soon-form .soon-done h3 { font-family: var(--display); font-size: 24px; color: var(--forest); margin: 0 0 8px; }

/* ---------- NOTIFICATIONS: bell, panel, popup ---------- */
.an-bell { position: relative; display: flex; align-items: center; }
.bell-btn { position: relative; background: none; border: 0; cursor: pointer; color: var(--cream); opacity: .8; padding: 6px; border-radius: 8px; display: grid; place-items: center; transition: .15s var(--ease); }
.bell-btn:hover { opacity: 1; background: rgba(255,255,255,.08); }
.bell-btn svg { width: 19px; height: 19px; }
.bell-badge { position: absolute; top: 0; right: 0; min-width: 16px; height: 16px; border-radius: 100px; background: var(--lime); color: var(--deep); font-size: 10px; font-weight: 900; display: grid; place-items: center; padding: 0 4px; }
.bell-panel { position: absolute; top: calc(100% + 12px); right: -8px; width: 340px; max-height: 420px; overflow: auto; background: #fff; border: 1px solid var(--app-line); border-radius: 14px; box-shadow: 0 26px 54px -22px rgba(0,0,0,.45); padding: 6px; display: none; z-index: 60; }
.bell-panel.open { display: block; }
.bell-panel .bp-head { font-family: var(--mark); font-weight: 700; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); padding: 10px 12px 6px; }
.bell-panel .bp-item { display: block; padding: 10px 12px; border-radius: 10px; text-decoration: none; transition: background .12s var(--ease); }
.bell-panel .bp-item:hover { background: rgba(200,241,53,.16); }
.bell-panel .bp-item.unread { background: rgba(200,241,53,.10); box-shadow: inset 3px 0 0 var(--lime); }
.bell-panel .bp-title { display: block; font-weight: 700; font-size: 13.5px; color: var(--ink); }
.bell-panel .bp-msg { display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px; line-height: 1.45; }
.bell-panel .bp-meta { display: block; font-size: 11px; color: var(--muted); opacity: .75; margin-top: 4px; }
.bell-panel .bp-empty { padding: 18px 12px; font-size: 13px; color: var(--muted); text-align: center; }
@media (max-width: 680px) { .bell-panel { position: fixed; top: 64px; left: 12px; right: 12px; width: auto; } }

/* THE BELL TICKER — one notification at a time, beside the bell that holds
   the rest.

   What this replaces: .note-pop, a card pinned bottom-right showing the newest
   undismissed notification until somebody closed it. It had two faults. One
   card is one notification, so the second and third waited behind it unseen;
   and a card that must be closed is a chore laid over the page before the page
   can be used.

   The teaser is deliberately one line. It carries the organisation and the
   title and stops — enough to decide whether to look. Anyone who wants the
   rest clicks, and the message body, the timestamp and the whole list are in
   the panel directly above it. It is a hook, not a delivery.

   Sized off the bell rather than the viewport, and pinned to the same edge as
   .bell-panel, so the thing that buzzed and the thing that appeared read as one
   object. Timing lives in the JS (app_base.html). */
.bell-tick {
  position: absolute; top: calc(100% + 10px); right: -8px; z-index: 59;
  display: flex; align-items: center; gap: 9px;
  width: max-content; max-width: min(320px, calc(100vw - 32px));
  padding: 9px 8px 9px 11px;
  background: #fff; border: 1px solid var(--app-line); border-radius: 12px;
  box-shadow: 0 18px 38px -20px rgba(0,0,0,.45);
  cursor: pointer;
  opacity: 0; transform: translateY(-8px) scale(.97); transform-origin: 90% 0;
  transition: opacity .24s var(--ease), transform .24s var(--ease);
}
.bell-tick.in { opacity: 1; transform: none; }
/* display:flex above outranks the browser's own [hidden]{display:none}, so
   without this the teaser is never actually hidden between turns — it just
   sits there at opacity 0, over the top of anything under it. */
.bell-tick[hidden] { display: none; }
.bell-tick:hover { border-color: rgba(45,122,58,.35); }
.bell-tick .bt-ic {
  flex: none; width: 26px; height: 26px; border-radius: 8px;
  background: rgba(200,241,53,.34); display: grid; place-items: center;
}
.bell-tick .bt-ic svg { width: 14px; height: 14px; stroke: var(--forest); fill: none; stroke-width: 1.9; }
.bell-tick .bt-txt { min-width: 0; display: block; }
.bell-tick .bt-eyebrow {
  display: block; font-family: var(--mark); font-weight: 700; font-size: 9.5px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--forest);
}
/* One line, always. A title long enough to wrap would make the teaser a card
   again, which is the thing being got rid of. */
.bell-tick .bt-title {
  display: block; font-weight: 700; font-size: 13px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.bell-tick .bt-x {
  flex: none; background: none; border: 0; cursor: pointer; padding: 2px 4px;
  font-size: 17px; line-height: 1; color: var(--muted);
}
.bell-tick .bt-x:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .bell-tick { transition: none; transform: none; }
}

/* The buzz before the teaser: the same shake the bell already does when
   something lands live, three times over, so it reads as the same bell doing
   the same thing rather than a second animation vocabulary. */
.an-bell.buzz .bell-btn { animation: gt-ring .66s var(--ease) 3; transform-origin: 50% 12%; }
@media (prefers-reduced-motion: reduce) { .an-bell.buzz .bell-btn { animation: none; } }

/* ---------- ELECTION SETUP: the when picker ---------- */
.election-when { display: block; width: 100%; padding: 13px 16px; border: 1.5px solid rgba(15,46,34,.22); border-radius: 12px; background: #fff; font-family: var(--body); font-size: 15px; font-weight: 600; color: var(--ink); }
.election-when:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.15); }
.when-presets { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* ---------- PUBLIC NEWS ---------- */
.pnews-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 32px; }
@media (max-width: 900px) { .pnews-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .pnews-grid { grid-template-columns: 1fr; } }
.pnews-card { display: flex; flex-direction: column; background: #FFFDF4; border: 1px solid rgba(15,46,34,.14); border-radius: 16px; overflow: hidden; text-decoration: none; transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease); }
.pnews-card:hover { transform: translateY(-4px); box-shadow: 0 24px 48px -28px rgba(15,46,34,.5); border-color: rgba(15,46,34,.28); }
.pnews-card .pn-thumb { position: relative; height: 170px; background-size: cover; background-position: center; }
.pnews-card .pn-thumb.noimg { background: linear-gradient(150deg, var(--deep), var(--deep-2)); }
.pn-tag { position: absolute; left: 12px; top: 12px; background: var(--lime); color: var(--deep); font-size: 10.5px; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; padding: 4px 10px; border-radius: 100px; }
.pnews-card .pn-body { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.pnews-card h3 { font-family: var(--display); font-size: 18px; line-height: 1.3; color: var(--ink); margin: 0; }
.pnews-card p { font-size: 13.5px; line-height: 1.55; color: var(--muted); margin: 0; }
.pnews-card .pn-date { margin-top: auto; font-size: 12px; color: var(--muted); opacity: .8; }
.pnews-article { max-width: 720px; margin: 0 auto; }
.pnews-article .pn-back { display: inline-block; font-size: 13px; font-weight: 700; color: var(--forest); text-decoration: none; margin-bottom: 18px; }
.pnews-article .pn-hero { position: relative; height: clamp(220px, 38vw, 380px); border-radius: 18px; background-size: cover; background-position: center; margin-bottom: 24px; }
.pnews-article h1 { font-family: var(--display); font-size: clamp(26px, 4.4vw, 40px); line-height: 1.15; color: var(--ink); margin: 0 0 10px; }
.pnews-article .pn-meta { font-size: 13px; color: var(--muted); margin-bottom: 18px; }
.pnews-article .pn-lead { font-size: 18px; line-height: 1.6; font-weight: 600; color: var(--ink); margin: 0 0 14px; }
.pnews-article .pn-prose { font-size: 16px; line-height: 1.75; color: var(--ink); }
.pnews-article .pn-prose p { margin: 0 0 16px; }
.pnews-more { max-width: 980px; margin: 44px auto 0; }

/* ==========================================================================
   VINTAGE BOSS LETTER — aged parchment, quill, red wax seal
   ========================================================================== */
.bn-paper.aged {
  background:
    radial-gradient(420px 260px at 12% 8%, rgba(133,94,44,.10), transparent 65%),
    radial-gradient(380px 300px at 88% 92%, rgba(133,94,44,.14), transparent 60%),
    radial-gradient(600px 420px at 50% 45%, rgba(255,250,230,.55), transparent 75%),
    repeating-linear-gradient(180deg, transparent 0 31px, rgba(107,79,38,.07) 31px 32px),
    linear-gradient(160deg, #F4E9C8, #EAD9A8 55%, #E2CE96);
  border-left: 1px solid rgba(93,64,24,.28);
  border-right: 1px solid rgba(93,64,24,.28);
  box-shadow: inset 0 0 70px rgba(120,85,40,.22), inset 0 0 8px rgba(120,85,40,.14);
  padding-top: 92px;                          /* the inkwell stands here */
  padding-bottom: 118px;                      /* the feather sleeps here, seal beside it */
}
.bn-paper.aged p {
  font-family: Georgia, 'Times New Roman', serif;
  color: #3B2A16; text-shadow: 0 .4px 0 rgba(255,248,225,.6);
}
.bn-paper.aged p:first-of-type {
  font-size: 21px; font-style: italic; font-weight: 700;
}
.bn-paper.aged .bn-sign { color: #6B2B1E; font-size: 22px; }

/* coffee-ring style stains */
.bn-stain { position: absolute; border-radius: 50%; pointer-events: none; }
.bn-stain.s1 {
  width: 110px; height: 104px; right: 34px; top: 118px;
  border: 3px solid rgba(133,90,38,.12);
  box-shadow: inset 0 0 18px rgba(133,90,38,.10);
  transform: rotate(-8deg) scaleX(1.06);
}
.bn-stain.s2 {
  width: 60px; height: 58px; left: 26px; bottom: 150px;
  border: 2px solid rgba(133,90,38,.10);
  box-shadow: inset 0 0 10px rgba(133,90,38,.08);
}

/* the inkwell, standing top-right where the ink was kept — spare quills in it */
.bn-inkwell {
  position: absolute; top: -52px; right: 30px; width: 116px; height: auto;
  filter: drop-shadow(-6px 12px 10px rgba(59,42,22,.35));
  pointer-events: none; z-index: 3;
  transform: perspective(600px) rotateX(4deg);
}
/* the working feather, asleep on the page next to the signature */
.bn-feather {
  position: absolute; left: 26px; bottom: 16px; width: 250px; height: auto;
  transform: rotate(-4deg);
  filter: drop-shadow(0 6px 5px rgba(59,42,22,.28));
  pointer-events: none; z-index: 3;
}
@media (prefers-reduced-motion: no-preference) {
  .bn-feather { animation: featherBreathe 6s ease-in-out infinite; }
  @keyframes featherBreathe {
    0%, 100% { transform: rotate(-4deg) translateY(0); }
    50% { transform: rotate(-4.8deg) translateY(-2px); }
  }
}
@media (max-width: 640px) {
  .bn-inkwell { width: 80px; top: -34px; right: 16px; }
  .bn-feather { width: 170px; left: 14px; bottom: 12px; }
  .bn-paper.aged { padding-top: 70px; padding-bottom: 96px; }
}

/* red wax seal — small, tucked bottom-right, still unmistakably a seal */
.bn-seal {
  position: absolute; right: 24px; bottom: 20px;
  width: 68px; height: 68px; z-index: 4;
  filter: drop-shadow(0 5px 9px rgba(80,10,6,.45));
  transform: rotate(-11deg);
}
.bn-seal svg { width: 100%; height: 100%; }
.bn-seal .seal-ring {
  font-family: var(--mark); font-size: 10.5px; font-weight: 700;
  letter-spacing: .14em; fill: #F3D9C8; opacity: .92;
}
.bn-seal .seal-mono {
  font-family: var(--display); font-size: 34px; font-weight: 900;
  fill: #EFC9B4; letter-spacing: .02em;
  text-shadow: 0 1px 0 rgba(60,8,5,.8);
}
/* stamp-down entrance when it scrolls into view */
.js .bn-seal.reveal { opacity: 0; transform: rotate(-11deg) scale(1.7); transition: opacity .45s var(--ease), transform .45s cubic-bezier(.2,1.4,.4,1); }
.js .bn-seal.reveal.in { opacity: 1; transform: rotate(-11deg) scale(1); }
@media (max-width: 640px) { .bn-seal { width: 54px; height: 54px; right: 14px; bottom: 12px; } }

/* members-only send panel */
.bn-lock { text-align: center; border-top-color: var(--forest); }
.bn-lock-badge {
  width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(45,122,58,.12); border: 1px solid rgba(45,122,58,.3);
}
.bn-lock-badge svg { width: 24px; height: 24px; stroke: var(--forest); fill: none; }
.bn-lock p { max-width: 480px; margin: 0 auto 18px; color: var(--muted); font-size: 15px; line-height: 1.65; }
.bn-lock-ctas { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.bn-lock-ctas .btn-ghost { border-color: rgba(15,46,34,.25); color: var(--deep); }

/* ==========================================================================
   ROTATING COPY LINES (Wall teaser etc.)
   ========================================================================== */
.rotate-lines { display: grid; min-height: 3.2em; }
.rotate-lines .rline {
  grid-area: 1 / 1; opacity: 0; transform: translateY(8px);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.rotate-lines .rline.on { opacity: 1; transform: none; }
.no-js .rotate-lines .rline:first-child { opacity: 1; }

/* ==========================================================================
   PUBLIC NEWS — filter chips + lead story
   ========================================================================== */
.news-filters { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 26px; }
.news-filters .fchip {
  font-family: var(--mark); font-size: 12px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; text-decoration: none;
  padding: 9px 18px; border-radius: 100px; color: var(--deep);
  border: 1.5px solid rgba(15,46,34,.25); background: transparent;
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease), transform .1s var(--ease);
}
.news-filters .fchip:hover { border-color: var(--deep); transform: translateY(-1px); }
.news-filters .fchip.on { background: var(--deep); color: var(--lime); border-color: var(--deep); }

.pnews-card.lead { grid-column: 1 / -1; flex-direction: row; min-height: 280px; }
.pnews-card.lead .pn-thumb { height: auto; flex: 1.2; min-height: 280px; }
.pnews-card.lead .pn-body { flex: 1; justify-content: center; padding: 30px 34px; gap: 12px; }
.pnews-card.lead h3 { font-size: clamp(22px, 3vw, 30px); }
.pnews-card.lead p { font-size: 15px; }
.pn-lead-flag {
  align-self: flex-start; font-family: var(--mark); font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--forest);
  border-bottom: 2px solid var(--lime); padding-bottom: 3px;
}
@media (max-width: 720px) {
  .pnews-card.lead { flex-direction: column; }
  .pnews-card.lead .pn-thumb { min-height: 190px; width: 100%; }
}
.news-empty {
  margin-top: 32px; padding: 44px 30px; text-align: center;
  border: 1.5px dashed rgba(15,46,34,.25); border-radius: 16px; background: rgba(255,253,244,.6);
}
.news-empty .ne-big { font-family: var(--display); font-size: 22px; color: var(--deep); margin: 0 0 8px; }

/* ==========================================================================
   WELCOME PHOTO STEP (after signup)
   ========================================================================== */
.wp-stage { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 26px 0 30px; }
.wp-avatar {
  position: relative; width: 148px; height: 148px; border-radius: 50%;
  display: grid; place-items: center; overflow: hidden;
  background: linear-gradient(150deg, var(--deep), var(--deep-2));
}
.wp-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.wp-initials { font-family: var(--display); font-size: 44px; font-weight: 900; color: var(--lime); letter-spacing: .02em; }
.wp-ring { position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--lime); opacity: .85; pointer-events: none; }
.wp-avatar[data-empty="true"] .wp-ring { border-style: dashed; opacity: .5; }
.wp-pick { cursor: pointer; }
.wp-hint { font-size: 12.5px; color: var(--muted); margin: 0; text-align: center; }
.wp-actions { display: flex; flex-direction: column; gap: 10px; align-items: stretch; }
/* Skipping is one of the two real choices on this page, not fine print. As a
   13.5px muted underline it was read as a footnote and missed on first glance,
   which pushed people into an upload they had not decided to do. It is now a
   proper secondary button: same width and hit area as the primary, clearly
   subordinate by weight and colour rather than by being small. */
.wp-skip {
  cursor: pointer; font-size: 15px; font-weight: 700;
  text-decoration: none; letter-spacing: .01em;
}
.wp-skip-note { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); text-align: center; }
.abtn[disabled] { opacity: .45; cursor: not-allowed; }

/* ==========================================================================
   HERO COLLAGE — one full image that splits into two, images keep changing
   ========================================================================== */
.hero-bg.collage { display: flex; }
/* `flex: 1 1 0`, not `1 1 50%` — this is what makes the full-image state work.
   With a 50% basis the two panes already fill the row, so free space is zero
   and `flex-grow` has nothing to distribute: the "closed" pane stayed at half
   width no matter what grow value it was given. With a zero basis the whole
   row IS free space, split 1:1 while both are open, and ~1000:1 the moment one
   closes — so the open pane really does take the frame. */
.hero-bg.collage .pane {
  position: relative; flex: 1 1 0; min-width: 0; min-height: 0; overflow: hidden;
  transition: flex-grow 1.1s var(--ease);
}
.hero-bg.collage .pane.closed { flex-grow: 0.0001; }
/* lime seam between the two panes, only while split */
.hero-bg.collage .pane + .pane { border-left: 0 solid rgba(200,241,53,0); transition: flex-grow 1.1s var(--ease), border-color .6s var(--ease), border-width .6s var(--ease); }
.hero-bg.collage.split .pane + .pane:not(.closed) { border-left: 3px solid rgba(200,241,53,.45); }
/* shots inside a pane keep the standard crossfade + ken burns */
.hero-bg.collage .pane .shot { background-position: center 35%; }

/* Wall hero: one big post at a time, rotating — reads like the live feed */
.hero-comments { display: grid; margin: 0 0 30px; max-width: 520px; min-height: 172px; }
.hero-comments .rline {
  grid-area: 1 / 1; align-self: start;
  opacity: 0; transform: translateY(14px) scale(.985);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
  pointer-events: none;
}
.hero-comments .rline.on { opacity: 1; transform: none; pointer-events: auto; }
.hero-comments .post { margin-top: 0; padding: 20px 22px; box-shadow: 0 26px 48px -30px rgba(0,0,0,.7); }
.hero-comments .post-body { font-size: 18px; line-height: 1.55; }
.hero-comments .post-head .av { width: 44px; height: 44px; font-size: 16px; }
.hero-comments .post-head .meta b { font-size: 15.5px; }
@media (max-width: 860px) { .hero-comments { max-width: none; min-height: 190px; } .hero-comments .post-body { font-size: 16.5px; } }

/* ==========================================================================
   THE WALL (in-app, members only)
   ========================================================================== */
.gwall-private {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--forest); background: rgba(45,122,58,.1);
  border: 1px solid rgba(45,122,58,.3); border-radius: 100px; padding: 6px 12px;
}
.gwall-av {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; overflow: hidden;
  background: var(--deep); color: var(--lime);
  font-family: var(--display); font-size: 14px; font-weight: 900;
}
.gwall-av.has-img img { width: 100%; height: 100%; object-fit: cover; }
.gwall-av.sys { background: rgba(200,241,53,.2); color: var(--deep); border: 1px solid rgba(15,46,34,.2); }

.gwall-composer { margin-bottom: 18px; }
.gwall-composer .gwc-row { display: flex; gap: 12px; align-items: flex-start; }
.gwall-composer textarea {
  flex: 1; resize: vertical; min-height: 54px;
  border: 1.5px solid var(--app-line); border-radius: 10px; padding: 12px 14px;
  font-family: var(--body); font-size: 15px; color: var(--ink); background: #fff;
}
.gwall-composer textarea:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }
.gwc-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 12px; padding-left: 52px; }
.gwc-pick { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--muted); }
.gwc-pick select { max-width: 320px; }
.gwc-hint { font-size: 12.5px; color: var(--muted); }

.gwall-feed { display: flex; flex-direction: column; gap: 12px; }
.gwall-post { padding: 16px 18px; }
.gwall-post.is-recap { border-left: 4px solid var(--lime); background: linear-gradient(120deg, rgba(200,241,53,.10), transparent 55%); }
.gwall-post.is-system { border-left: 4px solid rgba(45,122,58,.35); }
.gwp-head { display: flex; align-items: center; gap: 12px; }
.gwp-meta { line-height: 1.2; }
.gwp-meta b { display: block; font-size: 14.5px; color: var(--ink); }
.gwp-meta small { font-size: 12px; color: var(--muted); }
.gwp-tag {
  margin-left: auto; font-size: 10.5px; font-weight: 900; letter-spacing: .08em;
  text-transform: uppercase; color: var(--deep); background: var(--lime);
  padding: 3px 10px; border-radius: 100px;
}
.gwp-remove { margin-left: auto; }
.gwp-tag + .gwp-remove { margin-left: 10px; }
.gwp-remove button {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: 18px; line-height: 1; padding: 4px 6px; border-radius: 6px;
}
.gwp-remove button:hover { color: #9a2b2b; background: rgba(154,43,43,.08); }
.gwp-pick {
  display: inline-flex; align-items: center; gap: 6px; margin: 12px 0 0;
  font-size: 13px; font-weight: 700; color: var(--forest);
  background: rgba(45,122,58,.08); border: 1px solid rgba(45,122,58,.25);
  padding: 5px 12px; border-radius: 100px;
}
.gwp-pick .hit { color: var(--forest); }
.gwp-pick .miss { color: #9a2b2b; }
.gwp-body { margin: 10px 0 0; font-size: 15px; line-height: 1.6; color: var(--ink); overflow-wrap: anywhere; }
.gwall-post.is-recap .gwp-body { font-size: 16px; }
.gwp-reacts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.gwp-react {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  font-size: 14px; padding: 5px 11px; border-radius: 100px;
  background: transparent; border: 1.5px solid var(--app-line); color: var(--ink);
  transition: background .15s var(--ease), border-color .15s var(--ease), transform .1s var(--ease);
}
.gwp-react:hover { border-color: var(--forest); transform: translateY(-1px); }
.gwp-react.on { background: rgba(200,241,53,.35); border-color: var(--deep); }
.gwp-react .c { font-size: 12.5px; font-weight: 800; color: var(--deep); }
.gwall-empty { text-align: center; padding: 44px 24px; }
.gwall-empty h3 { font-family: var(--display); font-size: 22px; color: var(--deep); margin: 0 0 8px; }
.gwall-empty p { margin: 0 auto; max-width: 440px; color: var(--muted); font-size: 14.5px; line-height: 1.6; }
@media (max-width: 640px) { .gwc-actions { padding-left: 0; } }

/* ==========================================================================
   CLIENT UI — the look from the approved mockups (Overview, Orgs, Sync,
   News, My Tips, Leaderboard, The Wall).

   Shape of it: a full-bleed stadium photo behind everything, one centred
   cream "sheet" carrying the content, and inside that a rhythm of
   dark-forest panels (hero, stat tiles, forms, trust strips) against white
   cards. Pages opt in with `gt-v2` on <body>, so anything not yet
   redesigned keeps its current styling untouched.
   ========================================================================== */

.gt-v2 {
  --gt-sheet:  #F4F2EA;   /* the cream page sheet */
  --gt-card:   #FFFFFF;
  --gt-deep:   #0C2417;   /* darkest — nav, hero base */
  --gt-deep-2: #123A25;   /* raised dark panel */
  --gt-deep-3: #1B4A2C;   /* active pill / hover on dark */
  --gt-hair:   rgba(15,46,34,.10);
  --gt-hair-d: rgba(255,255,255,.10);
  --gt-r:      18px;      /* card radius */
  --gt-r-sm:   12px;
  --gt-wide:   1300px;
}

/* ---- the sheet ---------------------------------------------------------
   Content sits on cream; the photo shows through at the left and right
   margins only. On narrow screens the sheet takes the full width and the
   imagery drops away. */
.gt-sheet .app-main {
  position: relative; z-index: 1;
  max-width: var(--gt-wide);
  margin: 0 auto;
  padding: 30px clamp(20px, 3vw, 44px) 64px;
  background: var(--gt-sheet);
  min-height: calc(100vh - 68px);
  box-shadow: 0 0 90px -20px rgba(0,0,0,.55);
}
.gt-sheet .page-scenes .scene { width: clamp(120px, 12vw, 210px); }
@media (max-width: 1180px) { .gt-sheet .app-main { box-shadow: none; } }

/* ---- nav ---------------------------------------------------------------
   Same bar, wearing the mockup's proportions: taller, wider, and an active
   link that reads as a filled pill rather than a tint. */
.gt-v2 .app-nav { background: var(--gt-deep); border-bottom: 0; }
.gt-v2 .app-nav .an-inner { max-width: var(--gt-wide); height: 68px; padding: 0 clamp(20px, 3vw, 44px); }
.gt-v2 .an-brand { font-size: 23px; letter-spacing: .01em; }
.gt-v2 .an-org { font-size: 14px; opacity: .55; border-left: 0; padding-left: 0; margin-left: 4px; }
.gt-v2 .an-links { gap: 4px; margin-left: 18px; }
.gt-v2 .an-links a { padding: 9px 16px; border-radius: 10px; font-size: 14.5px; opacity: .8; }
.gt-v2 .an-links a.on { background: var(--gt-deep-3); color: var(--lime); opacity: 1; }
.gt-v2 .an-right { gap: 14px; }
/* "Back to app" reads as a bordered pill, per the admin mockups */
.gt-v2 .an-backpill {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid rgba(255,255,255,.22); border-radius: 100px;
  padding: 9px 18px; font-size: 14px; font-weight: 600;
  color: var(--cream); text-decoration: none; white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.gt-v2 .an-backpill:hover { background: rgba(255,255,255,.08); border-color: rgba(200,241,53,.5); color: #fff; }
/* profile chip: avatar + name + caret */
.gt-v2 .an-chip { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; color: var(--cream); }
.gt-v2 .an-chip .an-avatar { width: 38px; height: 38px; border: 2px solid rgba(200,241,53,.55); background: var(--gt-deep-3); color: var(--lime); overflow: hidden; }
.gt-v2 .an-chip .an-avatar.has-img img { width: 100%; height: 100%; object-fit: cover; }
.gt-v2 .an-chip .cn { line-height: 1.15; }
.gt-v2 .an-chip .cn b { display: block; font-size: 14px; font-weight: 600; color: #fff; }
.gt-v2 .an-chip .cn small { font-size: 11.5px; opacity: .6; }
@media (max-width: 900px) { .gt-v2 .an-chip .cn, .gt-v2 .an-backpill span { display: none; } }

/* ---- hero banner -------------------------------------------------------
   Dark forest panel, stadium photo fading in from the right, lime eyebrow,
   display title, and the signed-in admin on the far right. */
.gt-hero {
  position: relative; overflow: hidden;
  border-radius: var(--gt-r);
  background: linear-gradient(90deg, var(--gt-deep) 0%, var(--gt-deep) 34%, rgba(12,36,23,.55) 100%), var(--gt-deep-2);
  padding: clamp(26px, 3.4vw, 42px) clamp(24px, 3vw, 40px);
  margin-bottom: 22px;
  display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap;
  isolation: isolate;
}
.gt-hero .gh-shot {
  position: absolute; inset: 0; z-index: -2;
  background-size: cover; background-position: center 38%;
}
.gt-hero .gh-veil {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(90deg, rgba(10,32,20,.97) 0%, rgba(10,32,20,.92) 30%, rgba(10,32,20,.55) 68%, rgba(10,32,20,.42) 100%);
}
.gt-hero .gh-eyebrow {
  font-family: var(--mark); font-weight: 600; font-size: 12.5px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--lime);
}
.gt-hero h1 {
  font-family: var(--display); color: #fff; line-height: 1.05;
  font-size: clamp(28px, 3.6vw, 42px); margin: 8px 0 0; letter-spacing: -.015em;
}
.gt-hero p { color: var(--cream); opacity: .78; font-size: 15px; margin: 10px 0 0; max-width: 46ch; }
/* the admin identity card */
.gt-hero .gh-who {
  display: inline-flex; align-items: center; gap: 13px;
  background: rgba(255,255,255,.07); border: 1px solid rgba(200,241,53,.28);
  border-radius: 100px; padding: 10px 20px 10px 10px; backdrop-filter: blur(3px);
}
.gt-hero .gh-who .av {
  width: 46px; height: 46px; border-radius: 50%; flex: none; overflow: hidden;
  display: grid; place-items: center; background: rgba(200,241,53,.16);
  color: var(--lime); font-family: var(--display); font-size: 17px;
  border: 2px solid rgba(200,241,53,.5);
}
.gt-hero .gh-who .av img { width: 100%; height: 100%; object-fit: cover; }
.gt-hero .gh-who b { display: block; font-family: var(--mark); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; font-size: 13.5px; color: #fff; line-height: 1.2; }
.gt-hero .gh-who small { font-size: 12px; color: var(--cream); opacity: .65; }
@media (max-width: 700px) { .gt-hero .gh-who { display: none; } }

/* ---- stat tiles --------------------------------------------------------
   Four dark tiles: circular icon badge, number, label, one line of context.
   The arrow is a link affordance, not a trend. */
.gt-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
@media (max-width: 960px) { .gt-stats { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .gt-stats { grid-template-columns: 1fr; } }
.gt-stat {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 16px;
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.14);
  border-radius: var(--gt-r); padding: 22px 20px;
  text-decoration: none; color: inherit;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
a.gt-stat:hover { transform: translateY(-3px); border-color: rgba(200,241,53,.4); box-shadow: 0 24px 44px -26px rgba(0,0,0,.85); }
.gt-stat::after {
  content: ""; position: absolute; right: -30px; bottom: -40px;
  width: 130px; height: 130px; border-radius: 50%;
  background: radial-gradient(circle, rgba(200,241,53,.10), transparent 70%);
}
.gt-stat .gs-ic {
  width: 56px; height: 56px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: rgba(200,241,53,.13); color: var(--lime);
}
.gt-stat .gs-ic svg { width: 25px; height: 25px; stroke: currentColor; fill: none; stroke-width: 1.7; }
/* four quiet hue steps so the row reads as a set, not a rainbow */
.gt-stat:nth-child(1) .gs-ic { background: rgba(90,168,96,.20);  color: #9BE0A0; }
.gt-stat:nth-child(2) .gs-ic { background: rgba(46,150,128,.20); color: #7FD9C2; }
.gt-stat:nth-child(3) .gs-ic { background: rgba(160,200,60,.20); color: #CDE86F; }
.gt-stat:nth-child(4) .gs-ic { background: rgba(64,140,168,.20); color: #8FCDE4; }
.gt-stat .gs-body { min-width: 0; position: relative; flex: 1; }
.gt-stat .gs-num { display: block; font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 40px; line-height: 1; color: #fff; }
.gt-stat .gs-lab { display: block; font-family: var(--mark); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; font-size: 12px; color: var(--cream); opacity: .9; margin-top: 6px; }
.gt-stat .gs-sub { display: block; font-size: 12.5px; color: var(--cream); opacity: .55; margin-top: 4px; }
.gt-stat .gs-go { align-self: flex-end; flex: none; color: var(--cream); opacity: .5; }
.gt-stat .gs-go svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
a.gt-stat:hover .gs-go { opacity: 1; color: var(--lime); }

/* ---- white panel -------------------------------------------------------
   The workhorse card: round-badge icon, title, optional action on the right. */
.gt-panel {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r); padding: clamp(20px, 2.4vw, 28px);
}
.gt-panel + .gt-panel { margin-top: 20px; }
.gt-panel-head { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; flex-wrap: wrap; }
.gt-panel-head .gp-ic {
  width: 44px; height: 44px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--gt-deep-2); color: var(--lime);
}
.gt-panel-head .gp-ic svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-panel-head h2 { font-family: var(--display); font-size: 21px; color: var(--ink); line-height: 1.15; }
.gt-panel-head .gp-sub { font-size: 13.5px; color: var(--muted); margin-top: 2px; }
.gt-panel-head .gp-right { margin-left: auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* pill link used as a panel action */
.gt-pill {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--gt-hair); border-radius: 100px;
  padding: 9px 18px; font-size: 13.5px; font-weight: 700;
  color: var(--ink); background: var(--gt-card); text-decoration: none; cursor: pointer;
  transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.gt-pill:hover { border-color: var(--forest); color: var(--forest); }
.gt-pill svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.9; }
.gt-pill.solid { background: var(--gt-deep); border-color: var(--gt-deep); color: var(--lime); }
.gt-pill.solid:hover { background: var(--gt-deep-3); color: var(--lime); }

/* ---- data table --------------------------------------------------------
   Dark header bar, white rows, hairline separators, an icon per row and a
   chevron on the right. Wide tables scroll inside their own box. */
.gt-tablewrap { overflow-x: auto; border-radius: var(--gt-r-sm); }
.gt-table { width: 100%; border-collapse: collapse; min-width: 640px; }
.gt-table thead th {
  background: var(--gt-deep); color: var(--cream);
  font-family: var(--mark); font-weight: 700; font-size: 11.5px;
  letter-spacing: .12em; text-transform: uppercase; text-align: left;
  padding: 14px 18px; white-space: nowrap;
}
.gt-table thead th:first-child { border-radius: var(--gt-r-sm) 0 0 var(--gt-r-sm); }
.gt-table thead th:last-child { border-radius: 0 var(--gt-r-sm) var(--gt-r-sm) 0; }
.gt-table tbody td { padding: 15px 18px; border-bottom: 1px solid var(--gt-hair); vertical-align: middle; font-size: 14.5px; }
.gt-table tbody tr:last-child td { border-bottom: 0; }
.gt-table tbody tr { transition: background .15s var(--ease); }
.gt-table tbody tr:hover { background: rgba(200,241,53,.10); }
.gt-table .gt-rowic {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: inline-grid; place-items: center;
  background: rgba(45,122,58,.12); color: var(--forest);
}
.gt-table .gt-rowic svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-table .gt-namecell { display: flex; align-items: center; gap: 13px; }
.gt-table .gt-namecell a { color: var(--forest); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.gt-table .gt-namecell a:hover { color: var(--deep); }
.gt-table .gt-namecell b { color: var(--ink); font-weight: 700; }
.gt-table .gt-namecell small { display: block; font-size: 12px; color: var(--muted); font-weight: 400; margin-top: 2px; }
.gt-table .gt-with-ic { display: inline-flex; align-items: center; gap: 9px; white-space: nowrap; }
.gt-table .gt-with-ic svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; color: var(--muted); flex: none; }
.gt-table .gt-go { color: var(--muted); text-decoration: none; display: inline-grid; place-items: center; }
.gt-table .gt-go svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.gt-table .gt-go:hover { color: var(--forest); }
.gt-table .gt-empty { text-align: center; color: var(--muted); padding: 34px 18px; }
.gt-table td.num, .gt-table th.num { text-align: right; }
.gt-table td.fit, .gt-table th.fit { width: 1%; white-space: nowrap; }

/* ---- trust strip -------------------------------------------------------
   The closing band: three reassurances, divided, on dark. */
.gt-trust {
  display: grid; grid-template-columns: repeat(3, 1fr);
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.14);
  border-radius: var(--gt-r); margin-top: 22px; overflow: hidden;
}
.gt-trust.one { grid-template-columns: 1fr; }
@media (max-width: 820px) { .gt-trust { grid-template-columns: 1fr; } }
.gt-trust .gtr {
  display: flex; align-items: center; gap: 16px;
  padding: 24px clamp(20px, 2.4vw, 30px);
  border-right: 1px solid rgba(255,255,255,.08);
}
.gt-trust .gtr:last-child { border-right: 0; }
@media (max-width: 820px) {
  .gt-trust .gtr { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.08); }
  .gt-trust .gtr:last-child { border-bottom: 0; }
}
.gt-trust .gtr-ic {
  width: 50px; height: 50px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: rgba(200,241,53,.12); color: var(--lime);
}
.gt-trust .gtr-ic svg { width: 23px; height: 23px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-trust b { display: block; color: #fff; font-size: 15.5px; font-weight: 700; }
.gt-trust p { color: var(--cream); opacity: .62; font-size: 13.5px; line-height: 1.5; margin: 4px 0 0; }

/* ---- dark form panel ---------------------------------------------------
   Used by Sync and the Wall composer: fields on dark, lime submit. */
.gt-dark {
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.16);
  border-radius: var(--gt-r); padding: clamp(20px, 2.4vw, 28px);
  color: var(--cream);
}
.gt-dark .gd-head { display: flex; align-items: center; gap: 11px; margin-bottom: 20px; }
.gt-dark .gd-head svg { width: 22px; height: 22px; stroke: var(--lime); fill: none; stroke-width: 1.7; }
.gt-dark .gd-head span { font-family: var(--mark); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; font-size: 12.5px; color: var(--lime); }
.gt-dark label { display: block; font-size: 13.5px; font-weight: 600; color: var(--cream); opacity: .82; margin-bottom: 8px; }
.gt-dark .gd-fld { margin-bottom: 18px; }
.gt-dark select, .gt-dark input, .gt-dark textarea {
  width: 100%; padding: 13px 16px; font-family: var(--body); font-size: 15px;
  color: #fff; background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.16); border-radius: var(--gt-r-sm);
  outline: none; transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.gt-dark select option { background: var(--gt-deep); color: #fff; }
.gt-dark select:focus, .gt-dark input:focus, .gt-dark textarea:focus {
  border-color: var(--lime); box-shadow: 0 0 0 3px rgba(200,241,53,.18);
}
.gt-dark ::placeholder { color: rgba(232,228,213,0.56); }
/* label + narrow input on one line (round number) */
.gt-dark .gd-inline { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.gt-dark .gd-inline label { margin-bottom: 0; }
.gt-dark .gd-inline input { width: 110px; text-align: center; }
/* choice cards on dark */
.gt-choices { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 20px; }
@media (max-width: 560px) { .gt-choices { grid-template-columns: 1fr; } }
/* the choice card is itself a <label>, so it has to out-specify the generic
   `.gt-dark label` field-label rule above */
.gt-dark .gt-choice, .gt-choice {
  position: relative; display: flex; align-items: flex-start; gap: 14px; flex-wrap: nowrap;
  margin-bottom: 0;
  padding: 16px 18px; border-radius: var(--gt-r-sm); cursor: pointer;
  background: rgba(255,255,255,.05); border: 1.5px solid rgba(255,255,255,.14);
  transition: border-color .15s var(--ease), background .15s var(--ease), box-shadow .15s var(--ease);
}
.gt-choice:hover { border-color: rgba(200,241,53,.45); }
.gt-choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.gt-choice:has(input:checked) { border-color: var(--lime); background: rgba(200,241,53,.10); box-shadow: 0 0 0 3px rgba(200,241,53,.14); }
.gt-choice .gc-ic {
  width: 44px; height: 44px; border-radius: var(--gt-r-sm); flex: none;
  display: grid; place-items: center; background: rgba(255,255,255,.08); color: var(--cream);
}
.gt-choice .gc-ic svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-choice:has(input:checked) .gc-ic { background: rgba(200,241,53,.9); color: var(--gt-deep); }
.gt-choice .gc-txt { min-width: 0; flex: 1; }
.gt-choice .gc-txt b { display: flex; align-items: center; gap: 9px; color: #fff; font-size: 15.5px; font-weight: 700; }
.gt-choice .gc-txt p { color: var(--cream); opacity: .6; font-size: 13px; line-height: 1.5; margin: 4px 0 0; }
/* the radio dot that sits beside the choice title */
.gt-choice .gc-dot {
  width: 17px; height: 17px; border-radius: 50%; flex: none;
  border: 2px solid rgba(255,255,255,.35); display: inline-grid; place-items: center;
}
.gt-choice:has(input:checked) .gc-dot { border-color: var(--lime); }
.gt-choice:has(input:checked) .gc-dot::after { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--lime); }
/* lime submit */
.gt-go-btn {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--lime); color: var(--gt-deep); border: 0; cursor: pointer;
  font-family: var(--body); font-weight: 800; font-size: 15.5px;
  padding: 15px 22px; border-radius: var(--gt-r-sm);
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.gt-go-btn:hover { filter: brightness(1.06); }
.gt-go-btn:active { transform: translateY(1px); }
.gt-go-btn svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 2; }

/* ---- numbered "how it works" list -------------------------------------- */
.gt-steps { list-style: none; display: grid; gap: 22px; position: relative; }
.gt-step { display: flex; gap: 15px; position: relative; }
.gt-step .gst-ic {
  width: 42px; height: 42px; border-radius: 50%; flex: none; z-index: 1;
  display: grid; place-items: center; background: var(--gt-deep-2); color: var(--lime);
}
.gt-step .gst-ic svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.7; }
/* hairline connecting the steps */
.gt-step:not(:last-child)::before {
  content: ""; position: absolute; left: 21px; top: 46px; bottom: -22px;
  width: 1px; background: var(--gt-hair);
}
.gt-step b { display: block; font-size: 15px; color: var(--ink); font-weight: 700; }
.gt-step p { font-size: 13.5px; color: var(--muted); line-height: 1.55; margin: 4px 0 0; }

/* ---- provider status cards (Sync) -------------------------------------- */
.gt-provs { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 22px; }
@media (max-width: 820px) { .gt-provs { grid-template-columns: 1fr; } }
.gt-prov {
  display: grid; grid-template-columns: auto 1fr; gap: 16px;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r); padding: 22px;
}
.gt-prov .gpv-ic { width: 50px; height: 50px; border-radius: 50%; display: grid; place-items: center; grid-row: span 2; align-self: start; }
.gt-prov .gpv-ic svg { width: 23px; height: 23px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-prov.ok  .gpv-ic { background: rgba(45,122,58,.14); color: var(--forest); }
.gt-prov.wait .gpv-ic { background: rgba(178,120,40,.14); color: #8a5a12; }
.gt-prov .gpv-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.gt-prov h3 { font-size: 17px; color: var(--ink); font-family: var(--body); font-weight: 700; }
.gt-prov p { font-size: 13.5px; color: var(--muted); line-height: 1.55; margin: 6px 0 0; }
.gt-prov code { background: rgba(15,46,34,.07); padding: 2px 6px; border-radius: 5px; font-size: 12.5px; }
.gt-prov .gpv-state { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 600; margin-top: 12px; }
.gt-prov .gpv-state svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; }
.gt-prov.ok .gpv-state { color: var(--forest); }
.gt-prov.wait .gpv-state { color: #8a5a12; }
.gt-chip { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; padding: 4px 11px; border-radius: 100px; }
.gt-chip.ready { background: rgba(45,122,58,.15); color: var(--ad-ok-fg, #1c5e30); }
.gt-chip.needs { background: rgba(212,160,48,.22); color: var(--ad-gold-fg, #7a5410); }
.gt-chip.live  { background: rgba(45,122,58,.15); color: var(--ad-ok-fg, #1c5e30); }
.gt-chip.draft { background: var(--ad-quiet-bg, rgba(15,46,34,.09)); color: var(--muted); }
/* Scheduled wore the draft chip, so "nobody has published this" and "this is
   published and waiting on a clock" were the same grey pill. Amber, matching
   the scheduler card in the editor, so the two states read as two states. */
.gt-chip.sched { background: var(--ad-tint-gold, rgba(244,196,48,.18)); color: var(--ad-gold-fg, #7a5410); }

/* ==========================================================================
   MEMBER PAGES — My Tips, Leaderboard, The Wall
   ========================================================================== */

/* ---- page header -------------------------------------------------------
   Eyebrow, oversized display title, lead line, actions parked on the right. */
.gt-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 22px; flex-wrap: wrap; margin-bottom: 24px; }
.gt-head .gh-eyebrow { font-family: var(--mark); font-weight: 700; letter-spacing: .18em; text-transform: uppercase; font-size: 12.5px; color: var(--forest); }
.gt-head h1 { font-family: var(--display); font-size: clamp(34px, 5vw, 52px); color: var(--ink); line-height: 1.02; letter-spacing: -.02em; margin: 8px 0 0; }
.gt-head .gh-lead { color: var(--muted); font-size: 15px; margin-top: 10px; max-width: 50ch; }
.gt-head .gh-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-top: 6px; }
.gt-head .gh-actions .lbl { font-size: 13.5px; color: var(--muted); }
/* select styled to match the pills beside it */
.gt-select {
  appearance: none; -webkit-appearance: none;
  background: var(--gt-card) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F2E22' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 14px center / 16px;
  border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 11px 40px 11px 16px; font-family: var(--body); font-size: 14px; font-weight: 600;
  color: var(--ink); cursor: pointer; outline: none;
}
.gt-select:focus { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }

/* ---- two-column shell (content + sidebar) ------------------------------ */
.gt-shell { display: grid; grid-template-columns: minmax(0,1fr) 320px; gap: 22px; align-items: start; }
@media (max-width: 1080px) { .gt-shell { grid-template-columns: 1fr; } }
.gt-side { display: grid; gap: 18px; position: sticky; top: 84px; }
@media (max-width: 1080px) { .gt-side { position: static; } }
.gt-scard { background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: var(--gt-r); padding: 20px 22px; }
.gt-scard .gsc-head { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.gt-scard .gsc-head svg { width: 18px; height: 18px; stroke: var(--forest); fill: none; stroke-width: 1.8; flex: none; }
.gt-scard .gsc-head span { font-family: var(--mark); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; font-size: 12px; color: var(--ink); }
.gt-scard .gsc-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; font-size: 14px; color: var(--muted); }
.gt-scard .gsc-row + .gsc-row { border-top: 1px solid var(--gt-hair); }
.gt-scard .gsc-row b { font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums; font-size: 15.5px; color: var(--ink); }
.gt-scard .gsc-row b.lime { color: var(--forest); }
.gt-scard.dark { background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep)); border-color: rgba(200,241,53,.16); }
.gt-scard.dark .gsc-head svg { stroke: var(--lime); }
.gt-scard.dark .gsc-head span { color: var(--lime); }
.gt-scard.dark .gsc-row { color: var(--cream); opacity: .82; }
.gt-scard.dark .gsc-row + .gsc-row { border-top-color: rgba(255,255,255,.10); }
.gt-scard.dark .gsc-row b { color: #fff; opacity: 1; }
.gt-scard .gsc-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255,255,255,.12); color: var(--lime); font-size: 13.5px; font-weight: 700; text-decoration: none; }
.gt-scard .gsc-foot svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }

/* rank + percentile pair, with the shield between them */
.gt-rankrow { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; }
.gt-rankrow .rk { text-align: left; }
.gt-rankrow .rk.r { text-align: right; }
.gt-rankrow .rk small { display: block; font-family: var(--mark); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; font-size: 10.5px; color: var(--muted); }
.gt-rankrow .rk b { display: block; font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 30px; color: var(--ink); line-height: 1.1; margin-top: 3px; }
.gt-rankrow .rk i { display: block; font-style: normal; font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.gt-rankrow .shield { width: 54px; height: 54px; display: grid; place-items: center; border-radius: 50%; background: rgba(45,122,58,.10); color: var(--forest); }
.gt-rankrow .shield svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.6; }
.gt-scard .gsc-note { font-size: 13px; color: var(--muted); margin: 16px 0 8px; }

/* the cause card */
.gt-cause { display: flex; align-items: center; gap: 14px; }
.gt-cause .gcz-ic { width: 52px; height: 52px; border-radius: 50%; flex: none; display: grid; place-items: center; background: rgba(200,52,52,.10); color: #C0392B; }
.gt-cause .gcz-ic svg { width: 26px; height: 26px; stroke: currentColor; fill: currentColor; }
.gt-cause b { display: block; font-size: 16px; color: var(--ink); font-weight: 700; line-height: 1.2; }
.gt-cause small { font-size: 13px; color: var(--muted); }

/* the closing quote card */
.gt-quote {
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.16); border-radius: var(--gt-r);
  padding: 26px 24px 30px; text-align: center; position: relative;
}
.gt-quote .qm { font-family: var(--display); font-size: 40px; color: var(--lime); opacity: .5; line-height: .7; display: block; text-align: left; }
.gt-quote p { font-family: var(--display); color: #fff; font-size: 19px; line-height: 1.35; margin: 10px 0 16px; }
.gt-quote .swoosh { display: block; margin: 0 auto; width: 110px; height: 8px; }
.gt-quote .swoosh path { stroke: var(--lime); stroke-width: 3.5; fill: none; stroke-linecap: round; }

/* ---- My Tips -----------------------------------------------------------
   Round stepper: previous / current / next, reading as one control. */
.gt-rounder { display: flex; align-items: center; gap: 14px; }
.gt-rounder .rnd-btn {
  width: 48px; height: 48px; border-radius: 50%; flex: none; cursor: pointer;
  display: grid; place-items: center; border: 1px solid var(--gt-hair);
  background: var(--gt-card); color: var(--ink); text-decoration: none;
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.gt-rounder .rnd-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2; }
.gt-rounder .rnd-btn:hover { background: var(--gt-deep); border-color: var(--gt-deep); color: var(--lime); }
.gt-rounder .rnd-btn.solid { background: var(--gt-deep); border-color: var(--gt-deep); color: #fff; }
.gt-rounder .rnd-btn.off { opacity: .35; pointer-events: none; }
.gt-rounder .rnd-now {
  background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 12px 26px; text-align: center; min-width: 168px;
}
.gt-rounder .rnd-now b { display: block; font-family: var(--mark); font-weight: 900; letter-spacing: .1em; text-transform: uppercase; font-size: 19px; color: var(--ink); line-height: 1.1; }
.gt-rounder .rnd-now small { font-size: 12.5px; color: var(--muted); }
.gt-rounder .rnd-when { display: flex; align-items: center; justify-content: center; gap: 7px; font-size: 12.5px; color: var(--muted); margin-top: 8px; }
.gt-rounder .rnd-when svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 1.8; }

/* dark season strip: three figures with icon badges */
.gt-strip {
  display: grid; grid-template-columns: repeat(3, 1fr);
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.16); border-radius: var(--gt-r);
  margin-bottom: 20px; overflow: hidden;
}
@media (max-width: 720px) { .gt-strip { grid-template-columns: 1fr; } }
.gt-strip .gsp { display: flex; align-items: center; gap: 15px; padding: 20px clamp(18px, 2.2vw, 26px); border-right: 1px solid rgba(255,255,255,.09); }
.gt-strip .gsp:last-child { border-right: 0; }
@media (max-width: 720px) {
  .gt-strip .gsp { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.09); }
  .gt-strip .gsp:last-child { border-bottom: 0; }
}
.gt-strip .gsp-ic { width: 52px; height: 52px; border-radius: 50%; flex: none; display: grid; place-items: center; background: rgba(200,241,53,.13); color: var(--lime); }
.gt-strip .gsp-ic svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-strip .gsp-lab { font-family: var(--mark); font-weight: 700; letter-spacing: .13em; text-transform: uppercase; font-size: 11.5px; color: var(--cream); opacity: .8; }
.gt-strip .gsp-num { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 30px; color: #fff; line-height: 1.05; margin-top: 2px; }
.gt-strip .gsp-sub { font-size: 12.5px; color: var(--cream); opacity: .55; margin-top: 2px; }

/* match rows — one card per fixture */
.gt-fixtures { display: grid; gap: 12px; }
.gt-fix {
  display: grid; grid-template-columns: minmax(0,1fr) auto auto;
  align-items: center; gap: 18px;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-left: 4px solid var(--gt-hair);
  border-radius: var(--gt-r-sm); padding: 14px 18px;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease), transform .16s var(--ease);
}
.gt-fix:hover { border-left-color: var(--forest); box-shadow: 0 16px 34px -26px rgba(15,46,34,.5); }
.gt-fix.tipped { border-left-color: var(--forest); }
.gt-fix.correct { border-left-color: var(--forest); background: linear-gradient(90deg, rgba(200,241,53,.12), var(--gt-card) 55%); }
.gt-fix.missed { border-left-color: #C0392B; }
@media (max-width: 900px) { .gt-fix { grid-template-columns: 1fr; gap: 12px; } }
.gt-fix .gfx-teams { display: grid; grid-template-columns: minmax(0,1fr) 34px minmax(0,1fr); align-items: center; gap: 12px; }
@media (max-width: 560px) { .gt-fix .gfx-teams { grid-template-columns: 1fr; } .gt-fix .gfx-vs { display: none; } }
.gt-fix .gfx-vs {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(15,46,34,.06); color: var(--muted);
  font-family: var(--mark); font-weight: 800; font-size: 12px; letter-spacing: .05em; text-transform: uppercase;
}
.gt-fix .gfx-pick { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.gt-fix .gfx-when { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--muted); }
.gt-fix .gfx-when svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 1.8; flex: none; }
.gt-fix .gfx-state { display: flex; align-items: center; gap: 10px; justify-self: end; }
@media (max-width: 900px) { .gt-fix .gfx-state { justify-self: start; } }
.gt-fix .gfx-score { font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums; font-size: 14px; color: var(--muted); white-space: nowrap; }

/* the two pick buttons inside a fixture card */
.gt-pickpair { display: flex; gap: 8px; flex-wrap: wrap; }
.gt-pickbtn {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  background: var(--gt-card); border: 1.5px solid var(--gt-hair); border-radius: 9px;
  padding: 9px 14px; font-family: var(--body); font-size: 13.5px; font-weight: 700; color: var(--ink);
  transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease), transform .1s var(--ease);
}
.gt-pickbtn:hover { border-color: var(--forest); }
.gt-pickbtn:active { transform: translateY(1px); }
.gt-pickbtn.on { background: var(--gt-deep); border-color: var(--gt-deep); color: #fff; }
.gt-pickbtn .tick { width: 15px; height: 15px; flex: none; stroke: var(--lime); fill: none; stroke-width: 3; display: none; }
.gt-pickbtn.on .tick { display: block; }
.gt-pickbtn.locked { cursor: default; }
.gt-pickbtn.locked:hover { border-color: var(--gt-hair); }

/* ---- Leaderboard -------------------------------------------------------
   Podium: 2nd | 1st | 3rd, the winner raised and inverted. */
.gt-podium { display: grid; grid-template-columns: 1fr 1.08fr 1fr; gap: 18px; align-items: center; margin-bottom: 24px; }
@media (max-width: 820px) { .gt-podium { grid-template-columns: 1fr; } }
.gt-pod {
  position: relative; border-radius: var(--gt-r); padding: 30px 22px 26px;
  text-align: center; border: 1px solid var(--gt-hair); overflow: hidden;
}
.gt-pod.p2 { background: #E4EDE6; }
.gt-pod.p3 { background: #E7EFD8; }
.gt-pod.p1 { background: linear-gradient(160deg, var(--gt-deep-2), var(--gt-deep)); border-color: rgba(200,241,53,.3); padding: 36px 22px 32px; }
/* a soft glow behind the winner only — the podium's hierarchy comes from
   size and colour, so the others stay clean */
.gt-pod.p1::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 30%, rgba(200,241,53,.13), transparent 62%);
}
.gt-pod .crown { display: block; margin: 0 auto 10px; }
.gt-pod .crown svg { width: 24px; height: 24px; stroke: var(--lime); fill: none; stroke-width: 1.8; }
.gt-pod .gp-avwrap { position: relative; width: 58px; height: 58px; margin: 0 auto 14px; }
.gt-pod.p1 .gp-avwrap { width: 66px; height: 66px; }
.gt-pod .gp-av {
  width: 100%; height: 100%; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; font-family: var(--display); font-size: 21px;
  background: rgba(15,46,34,.07); color: var(--forest); border: 2px solid rgba(15,46,34,.14);
}
.gt-pod .gp-av img { width: 100%; height: 100%; object-fit: cover; }
.gt-pod.p1 .gp-av { background: rgba(200,241,53,.12); color: var(--lime); border-color: rgba(200,241,53,.6); }
.gt-pod .gp-rank {
  position: absolute; right: -4px; bottom: -4px; width: 25px; height: 25px; border-radius: 50%;
  display: grid; place-items: center; font-family: var(--score); font-weight: 900; font-size: 12.5px;
  border: 2px solid #fff;
}
.gt-pod.p1 .gp-rank { background: linear-gradient(145deg,#FFE985,#E4B33C 60%,#B8860B); color: #4a3405; border-color: var(--gt-deep); }
.gt-pod.p2 .gp-rank { background: linear-gradient(145deg,#F4F4F4,#C9CDD3 60%,#9AA0A8); color: #3d434b; }
.gt-pod.p3 .gp-rank { background: linear-gradient(145deg,#F0C39B,#C88A4E 60%,#96602C); color: #45280e; }
.gt-pod .gp-name { font-family: var(--display); font-size: 18px; color: var(--ink); line-height: 1.15; position: relative; }
.gt-pod.p1 .gp-name { color: #fff; font-size: 19px; }
.gt-pod .gp-pts { font-family: var(--score); font-weight: 900; font-size: 40px; color: var(--ink); line-height: 1; margin-top: 10px; position: relative; }
.gt-pod.p1 .gp-pts { color: var(--lime); font-size: 44px; }
.gt-pod .gp-pts small { font-family: var(--body); font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-left: 6px; }
.gt-pod.p1 .gp-pts small { color: rgba(232,228,213,.6); }
.gt-pod .gp-chip {
  display: inline-block; margin-top: 14px; position: relative;
  font-family: var(--mark); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; font-size: 11px;
  padding: 5px 14px; border-radius: 100px; background: rgba(15,46,34,.08); color: var(--forest);
}
.gt-pod.p1 .gp-chip { background: rgba(200,241,53,.16); color: var(--lime); }

/* ranked rows — the board proper */
.gt-board { display: grid; gap: 8px; }
.gt-board .gb-head {
  display: grid; grid-template-columns: 58px 1fr 110px 90px; gap: 14px; align-items: center;
  background: var(--gt-deep); color: var(--cream); border-radius: var(--gt-r-sm);
  padding: 14px 20px; font-family: var(--mark); font-weight: 700; font-size: 11.5px;
  letter-spacing: .12em; text-transform: uppercase;
}
.gt-board .gb-row {
  display: grid; grid-template-columns: 58px 1fr 110px 90px; gap: 14px; align-items: center;
  background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 13px 20px; transition: transform .16s var(--ease), border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.gt-board .gb-row:hover { transform: translateX(3px); border-color: rgba(45,122,58,.4); box-shadow: 0 14px 30px -24px rgba(15,46,34,.55); }
.gt-board .gb-row.me { background: #E9F2D2; border-color: rgba(169,209,28,.6); box-shadow: inset 4px 0 0 var(--forest); }
.gt-board .gb-rank {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  background: var(--gt-deep); color: #fff; font-family: var(--score); font-weight: 900; font-size: 14px;
}
.gt-board .gb-row.me .gb-rank { background: var(--forest); }
.gt-board .gb-who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.gt-board .gb-av {
  width: 34px; height: 34px; border-radius: 50%; flex: none; overflow: hidden;
  display: grid; place-items: center; background: rgba(15,46,34,.07); color: var(--forest);
  font-family: var(--display); font-size: 13px;
}
.gt-board .gb-av img { width: 100%; height: 100%; object-fit: cover; }
.gt-board .gb-name { font-size: 15px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gt-board .gb-correct { font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums; font-size: 15px; color: var(--forest); text-align: right; }
.gt-board .gb-correct span { color: var(--muted); }
.gt-board .gb-pts { font-family: var(--score); font-weight: 900; font-variant-numeric: tabular-nums; font-size: 16px; color: var(--ink); text-align: right; }
.gt-board .gb-empty { background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm); padding: 34px 20px; text-align: center; color: var(--muted); }
@media (max-width: 620px) {
  .gt-board .gb-head { grid-template-columns: 44px 1fr 70px; }
  .gt-board .gb-row  { grid-template-columns: 44px 1fr 70px; }
  .gt-board .gb-correct, .gt-board .gb-head .h-correct { display: none; }
}

/* the reassurance line that closes a list */
.gt-note {
  display: flex; align-items: center; gap: 11px; margin-top: 16px;
  background: rgba(45,122,58,.07); border: 1px solid rgba(45,122,58,.16);
  border-radius: var(--gt-r-sm); padding: 14px 18px;
  font-size: 13.5px; color: var(--muted);
}
.gt-note svg { width: 17px; height: 17px; stroke: var(--forest); fill: none; stroke-width: 1.8; flex: none; }


/* ---- Pages: the index of what the admin can rewrite ---- */
.gt-note.is-warn {
  background: rgba(212, 160, 23, .09);
  border-color: rgba(212, 160, 23, .3);
  align-items: flex-start;
  color: var(--ink);
}
.gt-note.is-warn b { display: block; margin-bottom: 3px; }

.pg-url {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; color: var(--muted);
  background: var(--ad-quiet-bg, rgba(15, 46, 34, .05));
  padding: 3px 7px; border-radius: 5px;
}

.pg-tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
.pg-tag.is-ok { background: rgba(45, 122, 58, .14); color: var(--forest); }
.pg-tag.is-warn { background: rgba(212, 160, 23, .18); color: var(--ad-gold-fg, #7E5D07); }

.pg-bullets { margin: 0; padding-left: 20px; color: var(--muted); font-size: 14px; }
.pg-bullets li { margin-bottom: 9px; line-height: 1.55; }
.pg-bullets b { color: var(--ink); }

/* Before and after, side by side, so a change reads as a change. */
.pe-cell { max-width: 340px; }
.pe-was { color: var(--muted); text-decoration: line-through; font-size: 13px; }
.pe-now { color: var(--ink); font-size: 13px; }
.pe-shot { max-width: 120px; border-radius: 8px; display: block; }

/* ---- The Wall ----------------------------------------------------------
   Composer on dark: write box left, house rules right, lime Post. */
.gt-composer { display: grid; grid-template-columns: minmax(0,1fr) 280px; gap: 0; padding: 0; overflow: hidden; }
@media (max-width: 900px) { .gt-composer { grid-template-columns: 1fr; } }
.gt-composer .gcm-main { padding: clamp(20px, 2.4vw, 28px); display: flex; gap: 16px; }
.gt-composer .gcm-av {
  width: 54px; height: 54px; border-radius: 50%; flex: none; overflow: hidden;
  display: grid; place-items: center; background: rgba(200,241,53,.14);
  color: var(--lime); font-family: var(--display); font-size: 18px;
  border: 2px solid rgba(200,241,53,.45);
}
.gt-composer .gcm-av img { width: 100%; height: 100%; object-fit: cover; }
.gt-composer .gcm-body { flex: 1; min-width: 0; }
.gt-composer .gcm-body h3 { font-family: var(--display); color: #fff; font-size: 20px; margin: 0; }
.gt-composer .gcm-body .gcm-lead { color: var(--cream); opacity: .68; font-size: 14.5px; margin: 4px 0 14px; }
.gt-composer textarea {
  width: 100%; min-height: 104px; resize: vertical;
  background: var(--gt-sheet); border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--gt-r-sm); padding: 14px 16px;
  font-family: var(--body); font-size: 15px; color: var(--ink); outline: none;
}
.gt-composer textarea::placeholder { color: var(--muted); }
.gt-composer textarea:focus { border-color: var(--lime); box-shadow: 0 0 0 3px rgba(200,241,53,.18); }
.gt-composer .gcm-count { text-align: right; font-size: 12px; color: var(--muted); margin-top: -28px; margin-right: 14px; position: relative; pointer-events: none; }
.gt-composer .gcm-pick { margin-top: 16px; }
.gt-composer .gcm-pick > span { display: block; font-size: 13.5px; font-weight: 600; color: var(--cream); opacity: .82; margin-bottom: 8px; }
.gt-composer .gcm-pick > span i { font-style: normal; opacity: .6; font-weight: 400; }
.gt-composer .gcm-pick select {
  width: 100%; appearance: none; -webkit-appearance: none;
  background: var(--gt-sheet) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F2E22' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 16px center / 16px;
  border: 1px solid rgba(255,255,255,.14); border-radius: var(--gt-r-sm);
  padding: 14px 44px 14px 16px; font-family: var(--body); font-size: 15px; color: var(--ink); cursor: pointer; outline: none;
}
.gt-composer .gcm-side {
  border-left: 1px solid rgba(255,255,255,.10);
  padding: clamp(20px, 2.4vw, 28px);
  display: flex; flex-direction: column; gap: 20px;
}
@media (max-width: 900px) { .gt-composer .gcm-side { border-left: 0; border-top: 1px solid rgba(255,255,255,.10); } }
.gt-composer .gcm-rule { display: flex; gap: 13px; }
.gt-composer .gcm-rule svg { width: 21px; height: 21px; stroke: var(--cream); fill: none; stroke-width: 1.7; flex: none; opacity: .75; margin-top: 1px; }
.gt-composer .gcm-rule b { display: block; color: #fff; font-size: 14.5px; font-weight: 700; }
.gt-composer .gcm-rule p { color: var(--cream); opacity: .6; font-size: 13px; line-height: 1.5; margin: 2px 0 0; }
.gt-composer .gcm-post { margin-top: auto; }
.gt-composer .gcm-post button {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--lime); color: var(--gt-deep); border: 0; cursor: pointer;
  font-family: var(--body); font-weight: 800; font-size: 15.5px;
  padding: 14px 22px; border-radius: var(--gt-r-sm);
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.gt-composer .gcm-post button:hover { filter: brightness(1.06); }
.gt-composer .gcm-post button:active { transform: translateY(1px); }
.gt-composer .gcm-post svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }

/* empty state */
.gt-empty {
  display: flex; align-items: center; gap: 20px; position: relative; overflow: hidden;
  background: var(--ad-empty-bg, #EDEBE1); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r); padding: 28px clamp(22px, 2.6vw, 30px);
}
.gt-empty .ge-ic { width: 58px; height: 58px; border-radius: 50%; flex: none; display: grid; place-items: center; background: var(--gt-deep); color: var(--lime); }
.gt-empty .ge-ic svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-empty h3 { font-family: var(--display); font-size: 21px; color: var(--ink); margin: 0; }
.gt-empty p { color: var(--muted); font-size: 14.5px; line-height: 1.6; margin: 6px 0 0; max-width: 56ch; }

/* wall posts */
.gt-feed { display: grid; gap: 14px; margin-top: 20px; }
.gt-post {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-left: 4px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 18px 20px; transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.gt-post:hover { box-shadow: 0 16px 34px -26px rgba(15,46,34,.5); }
.gt-post.is-recap { border-left-color: var(--lime); background: linear-gradient(110deg, rgba(200,241,53,.12), var(--gt-card) 52%); }
/* A RECAP WEARS ITS COMPETITION (Sep 2026, client: "a creative way of knowing
   and distinguishing this is for NRL, this is for NRLW ... let them have
   separate colours and tags"). The same [data-code] tokens the fixtures, the
   ladder and the story cards read, so NRL is amber here because it is amber
   everywhere. The edge takes the deep ink and the wash is the light tone at
   low strength, so the words on it are still reading off the card colour. */
.gt-post.is-recap[data-code] {
  border-left-color: var(--code-ink);
  background: var(--gt-card);
  background: linear-gradient(110deg, color-mix(in srgb, var(--code) 16%, var(--gt-card)), var(--gt-card) 55%);
}
.gt-post .gpt-tag.gpt-code { background: var(--code-ink, var(--deep)); color: var(--code-on, #fff); }
.gt-post .gpt-tag + .gpt-tag { margin-left: -4px; }

/* The recap filter above the feed: one chip per competition, each in its own
   colour, and a way to keep the choice. */
.gwall-recaps {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin-top: 18px; padding: 12px 14px; border-radius: var(--gt-r-sm);
  background: var(--gt-card); border: 1px solid var(--gt-hair);
}
.gwr-lab {
  font-family: var(--mark); font-weight: 700; font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.gwr-chips { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.gwr-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 100px;
  font-size: 13px; font-weight: 700; line-height: 1.2; text-decoration: none; color: var(--ink);
  border: 1px solid var(--gt-hair); background: transparent;
  transition: background .15s var(--ease), border-color .15s var(--ease), color .15s var(--ease);
}
.gwr-chip i { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--code, var(--lime)); }
.gwr-chip:hover { border-color: var(--code-ink, var(--forest)); }
.gwr-chip.on { background: var(--code-ink, var(--deep)); border-color: var(--code-ink, var(--deep)); color: var(--code-on, #fff); }
.gwr-chip.on i { background: var(--code-on, #fff); }
.gwr-chip:focus-visible { outline: 3px solid var(--lime); outline-offset: 2px; }
.gwr-save { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.gwr-note { font-size: 12.5px; font-weight: 700; color: var(--forest); }
.gwr-btn {
  font: inherit; font-size: 12.5px; font-weight: 700; line-height: 1.2; padding: 7px 13px;
  border-radius: 100px; cursor: pointer; background: transparent; color: var(--forest);
  border: 1px dashed var(--forest);
}
.gwr-btn:hover { background: color-mix(in srgb, var(--forest) 10%, transparent); }
@media (max-width: 620px) { .gwr-save { margin-left: 0; width: 100%; } }
.gt-post.is-system { border-left-color: rgba(45,122,58,.4); }
.gt-post .gpt-head { display: flex; align-items: center; gap: 12px; }
.gt-post .gpt-av {
  width: 42px; height: 42px; border-radius: 50%; flex: none; overflow: hidden;
  display: grid; place-items: center; background: rgba(15,46,34,.07);
  color: var(--forest); font-family: var(--display); font-size: 14px;
}
.gt-post .gpt-av img { width: 100%; height: 100%; object-fit: cover; }
.gt-post .gpt-av.sys { background: rgba(200,241,53,.2); color: var(--deep); }
.gt-post .gpt-meta b { display: block; font-size: 15px; color: var(--ink); }
.gt-post .gpt-meta small { font-size: 12.5px; color: var(--muted); }
.gt-post .gpt-tag {
  font-family: var(--mark); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  font-size: 10.5px; padding: 4px 11px; border-radius: 100px;
  background: rgba(200,241,53,.25); color: var(--deep);
}
.gt-post .gpt-x { margin-left: auto; }
.gt-post .gpt-x button {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: 20px; line-height: 1; padding: 4px 9px; border-radius: 8px;
}
.gt-post .gpt-x button:hover { color: #9a2b2b; background: rgba(154,43,43,.08); }
.gt-post .gpt-pick {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 13px;
  background: rgba(15,46,34,.05); border: 1px solid var(--gt-hair);
  border-radius: 100px; padding: 7px 15px; font-size: 13.5px; font-weight: 600; color: var(--ink);
}
.gt-post .gpt-pick .hit { color: var(--forest); font-weight: 800; }
.gt-post .gpt-pick .miss { color: #9a2b2b; font-weight: 800; }
.gt-post .gpt-body { margin: 13px 0 0; font-size: 15px; line-height: 1.62; color: var(--ink); overflow-wrap: anywhere; }

/* three-up encouragement band, on dark, closing the Wall */
.gt-band {
  display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 20px; overflow: hidden;
  background: linear-gradient(150deg, var(--gt-deep-2), var(--gt-deep));
  border: 1px solid rgba(200,241,53,.16); border-radius: var(--gt-r);
}
@media (max-width: 820px) { .gt-band { grid-template-columns: 1fr; } }
.gt-band .gbd { display: flex; gap: 16px; padding: 26px clamp(20px, 2.4vw, 28px); border-right: 1px solid rgba(255,255,255,.09); }
.gt-band .gbd:last-child { border-right: 0; }
@media (max-width: 820px) {
  .gt-band .gbd { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.09); }
  .gt-band .gbd:last-child { border-bottom: 0; }
}
.gt-band .gbd-ic {
  width: 52px; height: 52px; border-radius: 50%; flex: none; display: grid; place-items: center;
  border: 1px solid rgba(200,241,53,.3); color: var(--lime);
}
.gt-band .gbd-ic svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-band b { display: block; color: #fff; font-size: 16px; font-weight: 700; }
.gt-band p { color: var(--cream); opacity: .6; font-size: 13.5px; line-height: 1.5; margin: 5px 0 0; }

/* ---- news & blog rows -------------------------------------------------- */
.gt-newsthumb { width: 62px; height: 46px; border-radius: 9px; object-fit: cover; flex: none; }
.gt-newsthumb.ph { background: rgba(15,46,34,.07); display: grid; place-items: center; color: var(--muted); }
.gt-newsthumb.ph svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gt-actions { display: inline-flex; align-items: center; gap: 6px; }
.gt-actions form { display: inline; }
.gt-actions button, .gt-actions a {
  background: none; border: 1px solid var(--gt-hair); border-radius: 8px; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 700; padding: 6px 12px;
  color: var(--forest); text-decoration: none; white-space: nowrap;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.gt-actions button:hover, .gt-actions a:hover { border-color: var(--forest); background: rgba(45,122,58,.07); }
.gt-actions button.danger { color: var(--danger, #9a2b2b); }
.gt-actions button.danger:hover { border-color: var(--danger, #9a2b2b); background: rgba(154,43,43,.07); }

/* ---- search field with a leading icon ---------------------------------- */
.gt-search { position: relative; }
.gt-search svg { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); width: 17px; height: 17px; stroke: var(--muted); fill: none; stroke-width: 1.9; pointer-events: none; }
.gt-search input {
  width: 100%; min-width: 200px; padding: 11px 16px 11px 42px;
  border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  background: var(--gt-card); font-family: var(--body); font-size: 14px; color: var(--ink); outline: none;
}
.gt-search input:focus { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }

/* ---- light form fields inside a white panel ---------------------------- */
.gt-form .gf-fld { margin-bottom: 16px; }
.gt-form label { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.gt-form label i { font-style: normal; font-weight: 400; color: var(--muted); }
.gt-form input, .gt-form select, .gt-form textarea {
  width: 100%; padding: 13px 16px; background: var(--gt-card);
  border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  font-family: var(--body); font-size: 15px; color: var(--ink); outline: none;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.gt-form input:focus, .gt-form select:focus, .gt-form textarea:focus {
  border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12);
}
/* A placeholder is still text. At .8 of a mid sage it was 3.1:1. */
.gt-form ::placeholder { color: rgba(74,88,78,.86); }
/* a prefix icon sitting inside the control (charity URL) */
.gt-form .gf-with { position: relative; }
.gt-form .gf-with svg { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; stroke: var(--muted); fill: none; stroke-width: 1.8; pointer-events: none; }
.gt-form .gf-with input { padding-left: 42px; }

/* ---- responsive trims -------------------------------------------------- */
@media (max-width: 700px) {
  .gt-head h1 { font-size: 34px; }
  .gt-rounder .rnd-now { min-width: 140px; padding: 10px 18px; }
}

/* ---- backdrop under the sheet -----------------------------------------
   The photo only shows in the margins, so it wants a light darkening for
   contrast against the nav — not the heavy green wash the old full-width
   layout needed. */
.gt-sheet .page-backdrop .wash { background: linear-gradient(180deg, rgba(9,26,17,.55), rgba(9,26,17,.35) 40%, rgba(9,26,17,.60)); }
/* the old manage styling assumed dark type on a dark page; the sheet is cream */
.gt-v2.manage-app .admin-h1 { color: var(--ink); }
.gt-v2 .flash-stack { margin-bottom: 18px; }

/* ---- admin: list + create-form split ----------------------------------- */
.gt-shell.gt-shell-wide { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); }
@media (max-width: 1080px) { .gt-shell.gt-shell-wide { grid-template-columns: 1fr; } }
/* News & blog dropped its sidebar create-form for a full standalone editor
   page, so the list panel now runs the shell's full width on its own. */
.gt-shell.gt-shell-single { grid-template-columns: 1fr; }
/* the org row's name line: title and status chip share a line, meta below */
.gt-table .gt-nameline { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.gt-table .gt-nameline b { white-space: nowrap; }
/* org list: sport and links shrink to their content so the name column keeps
   the title and its status chip on one line */
.gt-table.gt-t-orgs { min-width: 560px; table-layout: fixed; }
.gt-table.gt-t-orgs th:nth-child(1) { width: 50%; }
.gt-table.gt-t-orgs th:nth-child(2) { width: 33%; }
.gt-table.gt-t-orgs th:nth-child(3) { width: 17%; }
.gt-table.gt-t-orgs td:nth-child(2) { overflow: hidden; }
.gt-table.gt-t-orgs td:nth-child(2) .gt-with-ic { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* stacked row links (Rounds / Members) */
.gt-linkstack { display: grid; gap: 4px; }
.gt-linkstack a {
  display: inline-flex; align-items: center; justify-content: space-between; gap: 14px;
  color: var(--ink); text-decoration: none; font-size: 13.5px; font-weight: 600;
  padding: 4px 0; transition: color .15s var(--ease);
}
.gt-linkstack a svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; opacity: .5; }
.gt-linkstack a:hover { color: var(--forest); }
.gt-linkstack a:hover svg { opacity: 1; }
/* the search + filter cluster in a panel head wraps cleanly on small screens */
.gt-panel-head form.gp-right { flex-wrap: wrap; }
@media (max-width: 620px) { .gt-panel-head form.gp-right { width: 100%; } .gt-panel-head .gt-search { flex: 1; } }

/* the hero can carry a glyph instead of the admin chip (Sync) */
.gt-hero .gh-glyph {
  width: 92px; height: 92px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(200,241,53,.10); border: 1px solid rgba(200,241,53,.28); color: var(--lime);
}
.gt-hero .gh-glyph svg { width: 44px; height: 44px; stroke: currentColor; fill: none; stroke-width: 1.4; }
@media (max-width: 700px) { .gt-hero .gh-glyph { display: none; } }

/* shared form partials still use `.fld`; inside a gt-form panel they should
   wear the same fields as everything else on the page */
.gt-form .fld { margin-bottom: 16px; }
.gt-form .fld > label { font-size: 13.5px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.gt-form .fld input, .gt-form .fld select, .gt-form .fld textarea {
  padding: 13px 16px; border-radius: var(--gt-r-sm); border-color: var(--gt-hair);
}
.gt-form .fld input[type="checkbox"] { width: auto; padding: 0; }

/* ---- app nav, client look ----------------------------------------------
   Profile chip and a bordered Log out pill, matching the member mockups. */
.gt-v2 .an-profile { padding: 4px 14px 4px 4px; }
.gt-v2 .an-profile .an-avatar { width: 38px; height: 38px; border: 2px solid rgba(200,241,53,.55); background: var(--gt-deep-3); color: var(--lime); overflow: hidden; }
.gt-v2 .an-profile .an-avatar.has-img img { width: 100%; height: 100%; object-fit: cover; }
.gt-v2 .an-plabel { font-size: 14px; }
.gt-v2 .an-divider { display: none; }
.gt-v2 .an-logout {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid rgba(255,255,255,.22); border-radius: 100px;
  padding: 9px 18px; color: var(--cream); opacity: 1;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.gt-v2 .an-logout:hover { background: rgba(255,255,255,.08); border-color: rgba(200,241,53,.5); color: #fff; }
.gt-v2 .an-llabel { font-size: 14px; font-weight: 600; }
@media (max-width: 900px) { .gt-v2 .an-plabel, .gt-v2 .an-llabel { display: none; } .gt-v2 .an-logout { padding: 9px; } }

/* charity strip sits directly under the nav on the dark, above the sheet */
.gt-v2 .charity-strip { background: var(--gt-deep-2); }
.gt-v2 .charity-strip .cs-inner { max-width: var(--gt-wide); padding: 10px clamp(20px, 3vw, 44px); }

/* club badge + name inside a fixture card */
.gt-fix .team-cell { display: flex; align-items: center; gap: 11px; font-size: 15px; font-weight: 600; color: var(--ink); min-width: 0; }
.gt-fix .team-cell .crest { flex: none; }
.gt-fix .gfx-pick { max-width: 340px; }
@media (max-width: 900px) { .gt-fix .gfx-pick { max-width: none; } }
.pm-saved { font-size: 12px; font-weight: 700; color: var(--forest); align-self: center; }

/* reaction bar inside a client-UI wall post */
.gt-post .gwp-reacts { margin-top: 14px; }
.gt-post .gwp-react { border-color: var(--gt-hair); border-radius: 100px; }
.gt-post .gwp-react.on { background: rgba(200,241,53,.3); border-color: var(--forest); }

/* nav and charity strip sit above the fixed backdrop imagery */
.gt-v2 .app-nav { position: sticky; z-index: 50; }
.gt-v2 .charity-strip { position: relative; z-index: 2; }

/* news list: headlines need the room, actions must not wrap mid-button */
.gt-table.gt-t-news { min-width: 0; }
.gt-table.gt-t-news th:first-child, .gt-table.gt-t-news td:first-child { width: 46%; }
.gt-table.gt-t-news td { padding: 14px 12px; }
.gt-table.gt-t-news th { padding: 14px 12px; }
.gt-table.gt-t-news .gt-namecell b { font-size: 14.5px; line-height: 1.35; }

/* ==========================================================================
   THE PUBLIC WALL — live cross-group feed
   Hero: copy on the green, a rotating post card over the imagery.
   Below: one wide column of real posts, each with its own reply thread.
   ========================================================================== */

/* --- hero side --- */
/* Deliberately shorter than the other split heroes: the point of this page
   is the feed underneath, so the hero introduces it and gets out of the way.
   The cards live on the green so the photograph beside them stays clear. */
.hero-split.wall-hero { min-height: clamp(420px, 56vh, 560px); align-items: stretch; }
.wall-hero .hs-left {
  justify-content: flex-start; padding-top: clamp(30px, 3.4vw, 44px);
  padding-bottom: clamp(26px, 3vw, 38px);
}
.wall-hero .hs-left h1 { font-size: clamp(30px, 3.6vw, 44px); margin-bottom: 18px; }

/* the image side: only enough veil to seat the type, never enough to hide it */
.wall-shot-side .hs-veil {
  background: linear-gradient(90deg, var(--deep) 0%, rgba(15,46,34,.34) 16%, transparent 44%);
}
@media (max-width: 860px) {
  .wall-shot-side .hs-veil { background: linear-gradient(0deg, var(--deep) 0%, transparent 62%); }
}

.wall-rotor {
  position: relative; display: flex; flex-direction: column; gap: 12px;
  margin-bottom: 22px; width: 100%;
}
/* every card sits in the same cell; one is visible at a time */
.wr-stack { display: grid; min-height: 232px; }
.wr-stack > * {
  grid-area: 1 / 1; align-self: start;
  opacity: 0; transform: translateY(14px) scale(.975);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  pointer-events: none;
}
.wr-stack > .on { opacity: 1; transform: none; pointer-events: auto; }
.wr-stack > .out { transform: translateY(-12px) scale(.975); }
.no-js .wr-stack > *:first-child { opacity: 1; transform: none; }

.wr-dots { display: flex; gap: 7px; }
.wr-dot {
  width: 7px; height: 7px; border-radius: 50%; cursor: pointer;
  background: rgba(255,253,244,.25); transition: background .3s var(--ease), width .3s var(--ease);
}
.wr-dot.on { background: var(--lime); width: 20px; border-radius: 100px; }

/* --- the card itself (hero + feed share it) --- */
.wr-card {
  background: #101713; border: 1px solid rgba(255,255,255,.09);
  border-left: 3px solid var(--lime); border-radius: 14px;
  padding: 20px 22px; box-shadow: 0 28px 54px -32px rgba(0,0,0,.85);
}
.wr-card.is-sample { border-left-color: rgba(200,241,53,.35); }
.wr-top { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.wr-av {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; background: var(--acc-1); color: #fff;
  font-family: var(--mark); font-size: 15px; font-weight: 800; letter-spacing: .02em;
}
.wr-av img { width: 100%; height: 100%; object-fit: cover; }
.wr-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.wr-who b { font-size: 15.5px; color: var(--white); font-weight: 700; }
.wr-who small {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--cream); opacity: .62;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wr-gic { width: 12px; height: 12px; stroke: var(--lime); fill: none; flex: 0 0 auto; }
.wr-when {
  flex: 0 0 auto; font-family: var(--mark); font-size: 10.5px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--cream); opacity: .45;
}
.wr-pick {
  display: inline-block; margin-bottom: 10px; padding: 5px 12px; border-radius: 100px;
  background: rgba(200,241,53,.12); border: 1px solid rgba(200,241,53,.3);
  font-size: 12.5px; font-weight: 700; color: var(--lime);
}
.wr-pick .hit { color: var(--lime); }
.wr-pick .miss { color: #e9a3a3; }
.wr-body { margin: 0; font-size: 16px; line-height: 1.6; color: var(--cream); }
.wr-foot { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.wr-chip {
  font-size: 12px; font-weight: 700; color: var(--cream);
  background: #171f1a; border: 1px solid rgba(255,255,255,.08);
  border-radius: 100px; padding: 5px 11px;
}
.wr-chip.ghost { background: transparent; opacity: .5; font-weight: 400; }

/* is-compact = the hero treatment: same card, scaled up so it reads as the
   headline act rather than a thumbnail of the feed below. */
.wr-card.is-compact { padding: 24px 26px; border-left-width: 4px; }
.wr-card.is-compact .wr-body { font-size: 19px; line-height: 1.55; }
.wr-card.is-compact .wr-av { width: 48px; height: 48px; font-size: 17px; }
.wr-card.is-compact .wr-who b { font-size: 17px; }
.wr-card.is-compact .wr-who small { font-size: 12.5px; }
.wr-card.is-compact .wr-pick { font-size: 13.5px; padding: 6px 14px; }
.wr-card.is-compact .wr-chip { font-size: 12.5px; padding: 6px 12px; }

@media (max-width: 860px) {
  .wr-stack { min-height: 210px; }
  .wr-card.is-compact { padding: 20px 20px; }
  .wr-card.is-compact .wr-body { font-size: 17px; }
}

/* --- the wide feed --- */
.wall-feed-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap; gap: 14px; margin-bottom: 26px; position: relative;
}
.wall-feed-head .section-heading { color: var(--white); }
.wallfeed {
  position: relative; display: flex; flex-direction: column; gap: 18px;
  max-width: 860px; margin: 0 auto;
}
.wallfeed.is-preview { opacity: .78; }
.wf-item {
  background: #0d1310; border: 1px solid rgba(255,255,255,.07);
  border-radius: 16px; padding: 6px; overflow: hidden;
}
.wf-item .wr-card { box-shadow: none; border-radius: 12px; }

.wf-thread { padding: 4px 18px 0; display: flex; flex-direction: column; gap: 2px; }
.wf-reply { display: flex; gap: 11px; padding: 12px 0; border-top: 1px solid rgba(255,255,255,.06); }
.wf-rav {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; background: var(--acc-4); color: #fff;
  font-family: var(--mark); font-size: 11px; font-weight: 800;
}
.wf-rav.guest { background: #2a3630; color: var(--cream); }
.wf-reply > div { min-width: 0; }
.wf-reply b { font-size: 13.5px; color: var(--white); }
.wf-guest {
  font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--cream); opacity: .5; margin-left: 4px;
}
.wf-reply small { font-size: 11.5px; color: var(--cream); opacity: .45; margin-left: 8px; }
.wf-reply p { margin: 4px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--cream); opacity: .88; }

.wf-replybox { padding: 14px 18px 16px; border-top: 1px solid rgba(255,255,255,.06); }
.wf-guestfields { display: flex; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.wf-guestfields input {
  flex: 1 1 180px; min-width: 0; padding: 10px 14px; border-radius: 10px;
  background: #141b17; border: 1px solid rgba(255,255,255,.1); color: var(--cream);
  font-family: var(--body); font-size: 14px;
}
.wf-guestfields input:focus { outline: none; border-color: rgba(200,241,53,.5); }
.wf-guestfields input::placeholder { color: rgba(255,253,244,0.49); }
.wf-note { margin: 0; font-size: 13px; color: var(--cream); opacity: .65; }
.wf-note b { color: var(--lime); }
/* honeypot: off-screen, never focusable by tabbing */
.wf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.wf-replyrow { display: flex; gap: 10px; align-items: flex-end; }
.wf-replyrow textarea {
  flex: 1; min-height: 44px; max-height: 160px; resize: none; padding: 12px 15px;
  border-radius: 10px; background: #141b17; border: 1px solid rgba(255,255,255,.1);
  color: var(--cream); font-family: var(--body); font-size: 14.5px; line-height: 1.5;
}
.wf-replyrow textarea:focus { outline: none; border-color: rgba(200,241,53,.5); }
.wf-replyrow textarea::placeholder { color: rgba(255,253,244,0.49); }
.wf-replyrow .btn-primary { flex: 0 0 auto; padding: 12px 22px; font-size: 13px; }
.wf-fine { margin: 9px 0 0; font-size: 11.5px; line-height: 1.5; color: var(--cream); opacity: .42; }

.wall-flash-stack { max-width: 860px; margin: 0 auto 18px; display: flex; flex-direction: column; gap: 8px; }
.wall-flash {
  padding: 13px 18px; border-radius: 12px; font-size: 14px; font-weight: 600;
  background: rgba(200,241,53,.12); border: 1px solid rgba(200,241,53,.35); color: var(--lime);
}
.wall-flash.bad { background: rgba(214,110,110,.14); border-color: rgba(214,110,110,.4); color: #f0b9b9; }

.wall-preview-note {
  max-width: 860px; margin: 0 auto 20px; padding: 16px 20px; border-radius: 12px;
  border: 1px dashed rgba(200,241,53,.3); background: rgba(200,241,53,.05);
  display: flex; flex-direction: column; gap: 4px;
}
.wall-preview-note b { color: var(--lime); font-size: 14.5px; }
.wall-preview-note span { font-size: 13.5px; color: var(--cream); opacity: .7; line-height: 1.55; }

@media (max-width: 620px) {
  .wf-replyrow { flex-direction: column; align-items: stretch; }
  .wf-replyrow .btn-primary { width: 100%; }
}

/* ==========================================================================
   THE WALL, IN-APP — global strip, pop-out composer, reply threads
   ========================================================================== */

/* --- hero split: cross-group posts on one side, imagery on the other --- */
.gwall-hero {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  margin: 0 0 24px; border-radius: 18px; overflow: hidden;
  min-height: 320px;
  background: linear-gradient(120deg, var(--deep) 0%, var(--deep-2) 62%, #0d1712 100%);
  box-shadow: 0 26px 52px -34px rgba(15,46,34,.6);
}
@media (max-width: 900px) {
  .gwall-hero { grid-template-columns: 1fr; min-height: 0; }
  .gwh-shots { order: -1; min-height: 170px; }
}

.gwh-feed {
  padding: clamp(20px, 2.4vw, 28px); display: flex; flex-direction: column; gap: 12px;
  justify-content: flex-start; min-width: 0;
}
.gwh-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gwh-eyebrow { display: flex; }
.gwh-link {
  text-decoration: none; font-family: var(--mark); font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--lime); opacity: .8;
}
.gwh-link:hover { opacity: 1; }

.gwh-stack { display: grid; min-height: 226px; }
.gwh-stack > * {
  grid-area: 1 / 1; align-self: start;
  opacity: 0; transform: translateY(14px) scale(.975);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
  pointer-events: none;
}
.gwh-stack > .on { opacity: 1; transform: none; pointer-events: auto; }
.gwh-stack > .out { transform: translateY(-12px) scale(.975); }
.no-js .gwh-stack > *:first-child { opacity: 1; transform: none; }
/* the card sits on the hero's own dark ground, so it drops its own panel */
.gwh-stack .wr-card { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.1); }

.gwh-dots { display: flex; gap: 6px; }
.gwh-dot {
  width: 6px; height: 6px; border-radius: 50%; cursor: pointer;
  background: rgba(255,253,244,.25); transition: background .3s var(--ease), width .3s var(--ease);
}
.gwh-dot.on { background: var(--lime); width: 18px; border-radius: 100px; }

.gwh-quiet { display: flex; flex-direction: column; gap: 8px; padding: 26px 0; }
.gwh-quiet h2 { font-family: var(--display); font-size: 24px; color: #fff; margin: 0; }
.gwh-quiet p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--cream); opacity: .68; }
.gwh-quiet p b { color: var(--lime); opacity: 1; }

.gwh-shots { position: relative; overflow: hidden; min-height: 260px; }
.gwh-shots .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 38%;
  opacity: 0; transition: opacity 1.2s var(--ease); transform: scale(1.04);
}
.gwh-shots .shot.on { opacity: 1; }
.gwh-veil {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--deep) 0%, rgba(15,46,34,.42) 26%, rgba(7,18,12,.18) 100%);
}
@media (max-width: 900px) {
  .gwh-veil { background: linear-gradient(0deg, var(--deep) 0%, rgba(15,46,34,.3) 70%); }
}

/* the group heading sits tighter under the hero */
.gt-head.gwall-head { margin-top: 0; }
/* "Write a post" reads as an action, not a form */
.gt-pill.gt-pill-go {
  cursor: pointer; background: var(--deep); color: var(--lime);
  border-color: var(--deep); font-family: var(--body);
}
.gt-pill.gt-pill-go svg { stroke: var(--lime); }
.gt-pill.gt-pill-go:hover { filter: brightness(1.12); }

/* --- the composer modal --- */
body.gwm-open { overflow: hidden; }
.gwall-modal { position: fixed; inset: 0; z-index: 400; display: grid; place-items: center; padding: 20px; }
.gwall-modal[hidden] { display: none; }
.gwm-scrim {
  position: absolute; inset: 0; background: rgba(7,18,12,.62);
  backdrop-filter: blur(3px); opacity: 0; transition: opacity .3s var(--ease);
}
.gwall-modal.in .gwm-scrim { opacity: 1; }
.gwm-panel {
  position: relative; z-index: 1; width: min(920px, 100%);
  max-height: calc(100vh - 40px); overflow: auto;
  opacity: 0; transform: translateY(24px) scale(.96);
  transition: opacity .34s var(--ease), transform .34s var(--ease);
  box-shadow: 0 50px 90px -40px rgba(0,0,0,.75);
}
.gwall-modal.in .gwm-panel { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .gwm-panel, .gwm-scrim { transition: none; }
}
.gwm-x {
  position: absolute; top: 12px; right: 14px; z-index: 2; cursor: pointer;
  width: 34px; height: 34px; border-radius: 50%; line-height: 1;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14);
  color: var(--cream); font-size: 22px;
}
.gwm-x:hover { background: rgba(255,255,255,.16); color: #fff; }

/* --- share-publicly switch inside the composer --- */
.gcm-share { margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,.1); }
.gcm-switch { display: flex; align-items: flex-start; gap: 13px; cursor: pointer; }
.gcm-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.gcm-track {
  flex: 0 0 auto; margin-top: 2px; width: 44px; height: 25px; border-radius: 100px;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.16);
  position: relative; transition: background .22s var(--ease), border-color .22s var(--ease);
}
.gcm-track i {
  position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 50%;
  background: var(--cream); transition: transform .22s var(--ease), background .22s var(--ease);
}
.gcm-switch input:checked + .gcm-track { background: rgba(200,241,53,.28); border-color: var(--lime); }
.gcm-switch input:checked + .gcm-track i { transform: translateX(19px); background: var(--lime); }
.gcm-switch input:focus-visible + .gcm-track { box-shadow: 0 0 0 3px rgba(200,241,53,.3); }
.gcm-slabel b { display: block; color: #fff; font-size: 14px; font-weight: 700; }
.gcm-slabel small { display: block; margin-top: 3px; color: var(--cream); opacity: .58; font-size: 12.5px; line-height: 1.5; }
.gcm-locked {
  display: flex; align-items: flex-start; gap: 10px; margin: 0;
  color: var(--cream); opacity: .6; font-size: 12.8px; line-height: 1.55;
}
.gcm-locked svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; flex: none; margin-top: 2px; }

/* --- public badge on a post that went wide --- */
.gt-post .gpt-tag.pub {
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(45,122,58,.12); color: var(--forest); border: 1px solid rgba(45,122,58,.3);
}
.gt-post .gpt-tag.pub svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 1.8; }

/* --- reply threads under a group post --- */
.gpt-thread { margin-top: 14px; padding-top: 4px; border-top: 1px solid rgba(15,46,34,.1); }
.gpt-reply { display: flex; gap: 11px; padding: 11px 0; }
.gpt-reply + .gpt-reply { border-top: 1px solid rgba(15,46,34,.07); }
.gpr-av {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; background: var(--forest); color: #fff;
  font-family: var(--mark); font-size: 11px; font-weight: 800;
}
.gpr-av img { width: 100%; height: 100%; object-fit: cover; }
.gpr-av.guest { background: #d9e2da; color: var(--deep); }
.gpr-av.mine { background: var(--deep); color: var(--lime); }
.gpr-body { flex: 1; min-width: 0; }
.gpr-body b { font-size: 13.5px; color: var(--ink); }
.gpr-body b i { font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); margin-left: 4px; }
.gpr-body small { font-size: 11.5px; color: var(--muted); margin-left: 8px; }
.gpr-body p { margin: 3px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--ink); opacity: .88; }
.gpr-x button {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: 17px; line-height: 1; padding: 2px 4px;
}
.gpr-x button:hover { color: #b4453f; }

.gpt-replybox { display: flex; align-items: flex-end; gap: 10px; margin-top: 12px; }
.gpt-replybox textarea {
  flex: 1; min-height: 42px; max-height: 150px; resize: none;
  padding: 11px 14px; border-radius: 10px; background: #fff;
  border: 1.5px solid rgba(15,46,34,.14); color: var(--ink);
  font-family: var(--body); font-size: 14.5px; line-height: 1.5;
}
.gpt-replybox textarea:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }
.gpt-replybox button { flex: 0 0 auto; }
@media (max-width: 560px) {
  /* Superseded by the .gpr-row rules further down: the box is a column now,
     and it is the row inside it that wraps. Left as a no-op would be a rule
     fighting the layout, so it states the row instead. */
  .gpt-replybox .gpr-row { flex-wrap: wrap; }
}

/* ==========================================================================
   LIVE NOTIFICATIONS — bell panel rows + the toast that slams in
   ========================================================================== */
.bell-panel .bp-head i {
  font-style: normal; color: var(--forest); background: rgba(200,241,53,.35);
  border-radius: 100px; padding: 2px 7px; margin-left: 4px; letter-spacing: .04em;
}
.bell-panel .bp-item { display: flex; gap: 10px; align-items: flex-start; }
.bell-panel .bp-ic {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 9px; margin-top: 1px;
  display: grid; place-items: center; background: rgba(45,122,58,.1);
}
.bell-panel .bp-ic svg { width: 15px; height: 15px; stroke: var(--forest); fill: none; stroke-width: 1.8; }
.bell-panel .bp-item.unread .bp-ic { background: rgba(200,241,53,.4); }
.bell-panel .bp-txt { min-width: 0; flex: 1; }

/* the bell gives a little shake when something lands */
.an-bell.ring .bell-btn { animation: gt-ring .8s var(--ease); transform-origin: 50% 12%; }
@keyframes gt-ring {
  0%, 100% { transform: rotate(0); }
  15% { transform: rotate(-13deg); }
  30% { transform: rotate(11deg); }
  45% { transform: rotate(-8deg); }
  60% { transform: rotate(6deg); }
  75% { transform: rotate(-3deg); }
}
@media (prefers-reduced-motion: reduce) { .an-bell.ring .bell-btn { animation: none; } }

.gt-toasts {
  position: fixed; top: 74px; right: 18px; z-index: 500;
  display: flex; flex-direction: column; gap: 10px;
  width: min(360px, calc(100vw - 36px)); pointer-events: none;
}
.gt-toast {
  pointer-events: auto; display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 14px 14px 16px; border-radius: 14px; text-decoration: none;
  background: linear-gradient(135deg, var(--deep), var(--deep-2));
  border: 1px solid rgba(200,241,53,.28); border-left: 3px solid var(--lime);
  box-shadow: 0 26px 54px -24px rgba(0,0,0,.7);
  opacity: 0; transform: translateX(28px) scale(.94);
  transition: opacity .34s var(--ease), transform .34s cubic-bezier(.2,.9,.3,1.3);
}
.gt-toast.in { opacity: 1; transform: none; }
.gt-toast.out { opacity: 0; transform: translateX(28px) scale(.96); transition-duration: .3s; }
a.gt-toast:hover { border-color: var(--lime); transform: translateY(-2px); }
.gtt-ic {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: 10px;
  display: grid; place-items: center; background: rgba(200,241,53,.16);
}
.gtt-ic svg { width: 17px; height: 17px; stroke: var(--lime); fill: none; stroke-width: 1.8; }
.gtt-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gtt-eyebrow {
  font-family: var(--mark); font-size: 10px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--lime); opacity: .85;
}
.gtt-title { font-size: 14px; font-weight: 700; color: #fff; line-height: 1.35; }
.gtt-msg {
  font-size: 12.5px; color: var(--cream); opacity: .7; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.gtt-msg:empty { display: none; }
.gtt-x {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer; line-height: 1;
  color: var(--cream); opacity: .45; font-size: 19px; padding: 0 2px;
}
.gtt-x:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .gt-toast { transition: opacity .2s linear; transform: none; } }
@media (max-width: 680px) { .gt-toasts { top: 66px; right: 12px; left: 12px; width: auto; } }

/* ==========================================================================
   DASHBOARD WELCOME TICKER — the banner talks to you, one line at a time
   ========================================================================== */
.dash-prompt { display: grid; margin-top: 10px; min-height: 46px; max-width: 640px; }
.dash-prompt .dp-line {
  grid-area: 1 / 1; align-self: center;
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  opacity: 0; transform: translateY(9px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  pointer-events: none;
}
.dash-prompt .dp-line.on { opacity: 1; transform: none; pointer-events: auto; }
.no-js .dash-prompt .dp-line:first-child { opacity: 1; transform: none; }

/* The greeting is slide one, so it carries the headline weight the h1 used to
   and the banner doesn't visibly shrink when it moves on to the first prompt.
   The taller min-height belongs to the whole stack so nothing below it jumps
   as lines of different lengths cycle through. */
.dash-prompt.has-greet { min-height: 58px; }
.dp-greet .dp-txt {
  font-family: var(--display); font-size: clamp(22px,2.8vw,30px);
  color: #fff; opacity: 1; letter-spacing: -.01em; line-height: 1.15;
}

/* Reduced motion: cross-fade only, no travel. */
@media (prefers-reduced-motion: reduce) {
  .dash-prompt .dp-line { transform: none; transition: opacity .5s linear; }
}

/* Available to assistive tech, invisible on screen. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.dp-ic {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 9px;
  display: grid; place-items: center;
  background: rgba(200,241,53,.16); border: 1px solid rgba(200,241,53,.28);
}
.dp-ic svg { width: 16px; height: 16px; stroke: var(--lime); fill: none; stroke-width: 1.8; }
.dp-txt { font-size: 14.5px; line-height: 1.45; color: var(--cream); opacity: .92; }
.dp-cta {
  display: inline-flex; align-items: center; gap: 5px; text-decoration: none;
  font-family: var(--mark); font-size: 11.5px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase; color: var(--deep);
  background: var(--lime); border-radius: 100px; padding: 7px 14px;
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.dp-cta:hover { filter: brightness(1.06); transform: translateY(-1px); }
.dp-cta svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2.2; }
.dp-dots { display: flex; gap: 5px; margin-top: 10px; }
.dp-dot {
  width: 6px; height: 6px; border-radius: 50%; cursor: pointer;
  background: rgba(255,253,244,.28); transition: background .3s var(--ease), width .3s var(--ease);
}
.dp-dot.on { background: var(--lime); width: 16px; border-radius: 100px; }
@media (max-width: 560px) {
  .dash-prompt { min-height: 76px; }
  .dp-txt { font-size: 13.5px; }
}

/* ============================================================================
   SCENE ROTATION — shared timing for every image stack (gt-scenes.js)
   ----------------------------------------------------------------------------
   One engine rotates all `.shot` stacks on a 15s dwell, so the crossfade needs
   to be quick enough to finish well inside that beat. The older per-component
   fades ran 1.6–2s, which on the 3s cycle this was written for meant an image
   was mid-fade more often
   than it was settled — it read as a permanent blur rather than a change.
   ~0.9s in, held, and a slow ken-burns push on whichever shot is showing.

   The engine marks the visible shot with BOTH `on` and `active` so the older
   component rules keep working; these rules key off both for the same reason.
   ========================================================================= */
.page-scenes .shot,
.page-backdrop .shot,
.hero-bg .shot,
.image-band .shot,
.auth-bg .shot,
.auth-aside .aa-bg .shot,
.dash-banner .bg .shot,
.gwh-shots .shot,
[data-shots] .shot {
  transition: opacity .9s var(--ease);
}

/* =========================================================================
   FOUR TRANSITION STYLES
   gt-scenes.js puts one of fx-fade / fx-slide / fx-zoom / fx-wipe on every
   stack — named in the markup, or assigned by position so the site varies
   without each template opting in.

   All four animate the ENTERING shot and let the outgoing one fade beneath
   it. That is deliberate: the engine only ever toggles `on`, so there is no
   "leaving" state to hook a second animation onto, and layering the new image
   over the old one is what keeps every style readable — no gap, no flash of
   background. The entering shot sits a layer up for the same reason.
   Each animation is comfortably shorter than the dwell, so an image is
   settled far more of the time than it is moving.
   ========================================================================= */
.fx-slide .shot.on, .fx-zoom .shot.on, .fx-wipe .shot.on { z-index: 1; }

@media (prefers-reduced-motion: no-preference) {
  /* 1. FADE — the classic. Crossfade plus the slow ken-burns push, which is
        handled by the existing per-component rules. Nothing to add. */

  /* 2. SLIDE — the new image pushes in from the right with a touch of scale,
        so it reads as a camera move rather than a panel sliding over. */
  .fx-slide .shot.on { animation: fxSlide 1s cubic-bezier(.22,.61,.36,1); }
  @keyframes fxSlide {
    from { transform: translateX(7%) scale(1.05); }
    to   { transform: none; }
  }

  /* 3. ZOOM — a focus pull: the image arrives slightly large and soft, then
        settles and sharpens. */
  .fx-zoom .shot.on { animation: fxZoom 1.15s cubic-bezier(.22,.61,.36,1); }
  @keyframes fxZoom {
    from { transform: scale(1.16); filter: blur(7px); }
    60%  { filter: blur(1.5px); }
    to   { transform: none; filter: blur(0); }
  }

  /* 4. WIPE — a diagonal reveal that sweeps the new image across the frame,
        the most overtly "broadcast" of the four. */
  .fx-wipe .shot.on { animation: fxWipe 1.05s cubic-bezier(.5,0,.2,1); }
  @keyframes fxWipe {
    from { clip-path: polygon(0 0, 0 0, -25% 100%, -25% 100%); }
    to   { clip-path: polygon(0 0, 125% 0, 100% 100%, -25% 100%); }
  }
}

/* Reduced motion keeps the crossfade (a fade is not the kind of movement that
   setting exists to suppress) but drops the travel, scale and wipe. */
@media (prefers-reduced-motion: reduce) {
  .fx-slide .shot.on, .fx-zoom .shot.on, .fx-wipe .shot.on { animation: none; }
}

/* No-JS fallback: the first shot in a stack stays visible, since nothing is
   going to come along and mark one active. Guarded on `no-js` so it can't
   pin an image under the rotation once the engine is running. */
html.no-js .page-scenes .shot:first-child,
html.no-js .page-backdrop .shot:first-child,
html.no-js .hero-bg .shot:first-child,
html.no-js .image-band .shot:first-child,
html.no-js .gwh-shots .shot:first-child,
html.no-js [data-shots] .shot:first-child { opacity: 1; }

/* The old rule pinned the band's first image on permanently, so it never
   faded out and every later shot stacked on top of it. */
.image-band .shot:first-of-type:not(.on):not(.active) { opacity: 0; }

/* The 18s ken-burns that covered the remaining stacks — side scenes, the admin
   backdrop, the auth aside, the dashboard banner, the Wall header — is gone
   too. Same specificity problem, same fix: the alternating zoom is the only
   thing that touches `transform` on a `.shot` now, so it runs everywhere
   rather than being outranked on every stack the site has. */

/* ============================================================================
   AUTH — centred split card over a dimmed two-panel backdrop
   ----------------------------------------------------------------------------
   The old layout was a hard 50/50 page split: imagery on the left, a solid
   colour field on the right holding the form. This keeps the two-panel split
   but demotes it to a backdrop — both panels rotate their shots and a heavy
   veil sits over them, so you read motion and atmosphere, not photographs.
   The card floats centred on top and is itself split: form on one side,
   rotating imagery plus the "how this works" steps on the other.
   ========================================================================= */
.auth-body { min-height: 100vh; background: var(--deep); }

.auth-stage {
  position: relative; min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: clamp(20px, 4vw, 56px) clamp(16px, 4vw, 40px);
  gap: 20px;
}

/* ---- backdrop ---- */
.auth-bg { position: fixed; inset: 0; z-index: 0; overflow: hidden; }
.auth-bg .ab-pane { position: absolute; top: 0; bottom: 0; width: 50%; overflow: hidden; }
.auth-bg .ab-pane.left { left: 0; }
.auth-bg .ab-pane.right { right: 0; }
/* A seam between the two panels, so the split still reads as deliberate
   through the veil rather than looking like one wide photo. */
.auth-bg .ab-pane.right { border-left: 1px solid rgba(200,241,53,.10); }
.auth-bg .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 40%;
  opacity: 0; transform: scale(1.06);
}
.auth-bg .shot.on, .auth-bg .shot.active { opacity: 1; }
/* Heavy enough that the form always wins the contrast fight, light enough
   that you can still see which picture is showing and when it changes. */
.auth-bg .ab-veil {
  position: absolute; inset: 0;
  background:
    radial-gradient(900px 520px at 50% -10%, rgba(200,241,53,.10), transparent 62%),
    linear-gradient(180deg, rgba(8,21,14,.84), rgba(8,21,14,.90) 55%, rgba(8,21,14,.94));
}

/* ---- the card ---- */
.auth-panel {
  position: relative; z-index: 1;
  width: 100%; max-width: 1000px;
  display: grid; grid-template-columns: 1.02fr .98fr;
  background: var(--white);
  border-radius: 22px; overflow: hidden;
  box-shadow: 0 40px 90px rgba(0,0,0,.46), 0 2px 0 rgba(255,255,255,.06);
}
/* The lime/deep hairline that used to sit on .auth-card, kept as the card's
   top edge so the two moods stay visually distinct. */
.auth-panel::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px; z-index: 3;
  background: var(--lime);
}
.auth-panel.mood-lime::before { background: var(--deep); }

/* ---------- solo panel: one centred column, no brand aside ----------
   Used by the verification step. Same card, same backdrop, just narrowed to
   a single column so the one thing being asked for sits in the middle of the
   split rather than off to one side. */
.auth-panel.solo { grid-template-columns: 1fr; max-width: 480px; }
.auth-panel.solo .ap-form { padding-top: 44px; padding-bottom: 40px; }

.otp-head { text-align: center; margin-bottom: 26px; }
.otp-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 54px; border-radius: 16px; margin-bottom: 16px;
  background: rgba(45,122,58,.12); color: var(--forest);
}
.otp-badge svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.otp-head h1 { margin-bottom: 6px; }
.otp-email { color: var(--forest); word-break: break-all; }

/* The code field is the whole page — sized and spaced so the digits read back
   at a glance when checking them against the email. */
.otp-input {
  width: 100%; text-align: center;
  font-family: var(--score, monospace); font-size: 30px; font-weight: 800;
  letter-spacing: .42em; text-indent: .42em;
  padding: 16px 12px;
}
.otp-input::placeholder { letter-spacing: .42em; opacity: .28; }

/* Says what the field is about to do on its own, so a page that submits
   itself does not look like a page that submitted by accident. */
.otp-auto-note {
  margin-top: 10px;
  text-align: center;
  font-size: 12.5px;
  opacity: .62;
}

.otp-resend { margin-top: 18px; text-align: center; }
.otp-resend .sub { font-size: 13px; }
/* A submit button that has to read as part of the sentence around it. */
.otp-resend-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: var(--forest); font-weight: 700; text-decoration: underline;
}

/* ---------- settings toggle switch ----------
   A real checkbox drives it, moved off-screen rather than display:none so it
   stays keyboard-reachable and announces its state. */
.tog-row { display: flex; align-items: center; gap: 13px; cursor: pointer; }
.tog-input { position: absolute; opacity: 0; width: 0; height: 0; }
.tog-track {
  flex: none; position: relative; width: 46px; height: 27px; border-radius: 100px;
  background: rgba(15,46,34,.18); transition: background .2s var(--ease);
}
.tog-knob {
  position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
  transition: transform .2s var(--ease);
}
.tog-input:checked + .tog-track { background: var(--forest); }
.tog-input:checked + .tog-track .tog-knob { transform: translateX(19px); }
.tog-input:focus-visible + .tog-track { outline: 2px solid var(--forest); outline-offset: 3px; }
.tog-text { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.tog-text span { font-size: 12.5px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .tog-track, .tog-knob { transition: none; }
}

.ap-form { padding: clamp(28px, 3.4vw, 46px); display: flex; flex-direction: column; }
.ap-mark {
  font-family: var(--mark); font-weight: 900; font-size: 21px; letter-spacing: .01em;
  color: var(--deep); text-transform: uppercase; text-decoration: none; margin-bottom: 22px;
}
.ap-mark span { color: var(--forest); }
.ap-mark .dot { color: var(--lime-dk); }

.ap-form h1 { font-family: var(--display); font-size: clamp(25px, 2.6vw, 31px); color: var(--ink); letter-spacing: -.01em; margin-bottom: 6px; }
.ap-form .sub { color: var(--muted); font-size: 15px; margin-bottom: 24px; }
.ap-form .alt { text-align: center; font-size: 14px; color: var(--muted); margin-top: 20px; }
.ap-form .alt a { color: var(--forest); font-weight: 700; text-decoration: none; }
.ap-form .alt a:hover { text-decoration: underline; }
.ap-form .flash { margin-bottom: 16px; }
.ap-form .fld input { background: #FBFAF6; border-radius: 12px; padding: 13px 15px; }
.ap-form .fld input:focus { background: #fff; border-color: var(--deep); box-shadow: 0 0 0 3px rgba(200,241,53,.55); }
.ap-form .abtn-block { margin-top: 4px; }
.mood-lime .ap-form .abtn-block { background: var(--deep); color: var(--lime); }
.mood-lime .ap-form .abtn-block:hover { background: var(--deep-2); }

/* ---- the card's own image side ---- */
.auth-aside { position: relative; overflow: hidden; min-height: 520px; color: var(--cream); }
.auth-aside .aa-bg { position: absolute; inset: 0; z-index: 0; }
.auth-aside .aa-bg .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 35%;
  opacity: 0; transform: scale(1.05);
}
.auth-aside .aa-bg .shot.on, .auth-aside .aa-bg .shot.active { opacity: 1; }
.auth-aside .aa-veil {
  position: absolute; inset: 0; z-index: 1;
  background:
    radial-gradient(520px 300px at 16% 8%, rgba(200,241,53,.16), transparent 62%),
    linear-gradient(180deg, rgba(8,21,14,.62), rgba(8,21,14,.86));
}
.auth-aside .aa-content {
  position: relative; z-index: 2; height: 100%;
  padding: clamp(28px, 3.2vw, 42px);
  display: flex; flex-direction: column; justify-content: center; gap: 18px;
}
.auth-aside h2 { font-family: var(--display); font-size: clamp(24px, 2.5vw, 31px); color: #fff; line-height: 1.06; letter-spacing: -.02em; }
.auth-aside h2 em { color: var(--lime); font-style: normal; }
.auth-aside .aa-sub { font-size: 14.5px; color: var(--cream); opacity: .84; max-width: 34ch; }

.aa-steps { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.aa-steps li { display: flex; align-items: flex-start; gap: 11px; }
.aa-steps .n {
  flex: 0 0 auto; width: 23px; height: 23px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--mark); font-weight: 800; font-size: 12px;
  background: var(--lime); color: var(--deep);
}
.aa-steps .t { font-size: 14px; line-height: 1.45; color: var(--cream); opacity: .9; }
.aa-steps .t b { color: #fff; font-weight: 700; opacity: 1; }

.aa-stat {
  display: inline-flex; align-items: baseline; gap: 10px; align-self: flex-start;
  background: rgba(255,255,255,.07); border: 1px solid rgba(200,241,53,.28);
  border-radius: 12px; padding: 13px 17px;
}
.aa-stat .n { font-family: var(--score); font-weight: 900; font-size: 27px; color: var(--lime); }
.aa-stat .t { font-size: 12.5px; color: var(--cream); opacity: .85; }

.auth-foot-note {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--cream); opacity: .62;
}
.auth-foot-note svg { width: 14px; height: 14px; fill: var(--lime); }

/* Below the split, the aside's copy still matters more than its picture, so it
   moves above the form as a compact banner rather than disappearing. */
@media (max-width: 900px) {
  .auth-panel { grid-template-columns: 1fr; max-width: 520px; }
  .auth-aside { min-height: 0; order: -1; }
  .auth-aside .aa-content { padding: 26px 24px; gap: 14px; }
  .auth-aside .aa-sub, .aa-steps, .aa-stat { display: none; }
  .auth-bg .ab-pane { width: 100%; }
  .auth-bg .ab-pane.right { display: none; }
}

/* ============================================================================
   TELL THE BOSS — two presentations of one note
   ----------------------------------------------------------------------------
   A: .ttb-postit — a sticky note, taped, tilted, lifting off the wall.
   B: .ttb-card   — the same words as a message card: lime header bar carrying
      the wordmark, white body, one line of provenance underneath.

   THE PAIR USED TO BE A PARCHMENT SCROLL AND A LINED CARD ON A DESK, with a
   fountain pen, a glass inkwell, ink splatter and a red wax seal drawn as
   shared SVG symbols. The client asked for "the right logo and the right name
   Good Tip — a more modern look, like a post-it note or the like", and the
   wax seal was the offender twice over: it was the most nineteenth-century
   object on the page, and the two lines pressed into it read GOOD / TIPS,
   which is not what the company is called. Both presentations now sign off
   with the site's own wordmark (.ttb-mark), from the same markup the nav bar
   uses.

   Both include the same letter body partial, so the wording lives in one
   place, and both are on screen at once as a hero split — two ways to put the
   same note in front of someone, side by side.
   ========================================================================= */

.boss-note { max-width: none; margin: 26px 0 0; position: relative; }

.ttb-centre { text-align: center; }
.ttb-lede {
  text-align: center; max-width: 52ch; margin: 12px auto 0;
  font-size: 15.5px; line-height: 1.6; color: var(--muted);
}

/* ---- the split ----
   Breaks out of .wrap so each half gets roughly half the viewport. The old
   pair needed the room for its desk props; this pair needs it because a
   sticky note that has to fit in a 430px column stops looking like a sticky
   note and starts looking like a coloured paragraph. */
.ttb-split {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(16px, 3vw, 48px);
  align-items: start;
  width: 100vw; max-width: 100vw;
  margin-left: calc(50% - 50vw);
  padding: 0 clamp(12px, 3vw, 44px);
}
/* A rule between the two, so it reads as a deliberate pair of options rather
   than one wide illustration. */
.ttb-split > .ttb-half + .ttb-half { border-left: 1px solid rgba(15,46,34,.12); padding-left: clamp(16px, 3vw, 48px); }

.ttb-half { min-width: 0; position: relative; }
.ttb-tag {
  display: block; text-align: center; margin-bottom: 4px;
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--forest); opacity: .7;
}

.ttb-stage { position: relative; }

/* ---- the wordmark, wherever a presentation signs off ----
   Same three-part markup as .wordmark in the bar. It is not simply given the
   .wordmark class because that one is sized for a 66px nav row and coloured
   for a dark bar; here it has to sit small on a yellow sticky and on a white
   card. Structure shared, scale and colour local. */
.ttb-mark {
  font-family: var(--mark); font-weight: 900; text-transform: uppercase;
  letter-spacing: .01em; line-height: 1;
  display: inline-flex; align-items: baseline; gap: 1px;
  color: var(--forest);
}
/* TIP and the stop take no colour of their own here. The bar's wordmark can
   set them lime because it is always on deep green; this one lands on a yellow
   sticky in one presentation and on a lime header bar in the other, and a
   single colour cannot be readable on both. So the base inherits, and the two
   presentations below each set a pair that passes on the ground it actually
   sits on — which is also what stops the contrast audit flagging a colour that
   is never painted on the surface it was measured against. */
.ttb-mark span,
.ttb-mark .dot { color: inherit; opacity: .72; }

/* ============ A · the post-it ============ */
.ttb-postit {
  --sticky: #FFF07A;
  --sticky-deep: #F4DF52;
  padding: clamp(26px, 4vw, 54px) 0 clamp(34px, 5vw, 64px);
  width: min(700px, 100%);
  margin: 0 auto;
  display: flex; justify-content: center;
}
.ttb-postit .pi-stack { position: relative; width: min(430px, 82%); }

/* The pad underneath. Peeks out on two edges only — a sticky offset evenly on
   all four sides reads as a border, not a second sheet. */
.ttb-postit .pi-behind {
  position: absolute; inset: 10px -12px -12px 6px;
  background: var(--sticky-deep); border-radius: 2px;
  transform: rotate(3.2deg);
  box-shadow: 0 12px 22px -12px rgba(60,54,10,.4);
}
.ttb-postit .pi-note {
  position: relative;
  transform: rotate(-1.8deg);
  padding: clamp(40px, 5vw, 58px) clamp(26px, 3.6vw, 42px) clamp(26px, 3.4vw, 38px);
  background: var(--sticky);
  border-radius: 2px;
  /* Tight at the top where it is glued, loose and wide at the bottom where it
     lifts. One even drop shadow makes it a rectangle floating in space. */
  box-shadow:
    0 2px 3px rgba(60,54,10,.28),
    0 20px 34px -16px rgba(60,54,10,.5),
    0 40px 46px -34px rgba(60,54,10,.45);
}
/* The glued band across the top. Barely darker — it is doing structural work,
   not decorative. */
.ttb-postit .pi-glue {
  position: absolute; inset: 0 0 auto 0; height: 34%;
  background: linear-gradient(180deg, rgba(190,168,30,.16), rgba(190,168,30,0));
  pointer-events: none; border-radius: 2px 2px 0 0;
}
/* One strip of washi tape, rotated AGAINST the note's own tilt so the two
   angles disagree the way they do when somebody actually sticks something up. */
.ttb-postit .pi-tape {
  position: absolute; top: -13px; left: 8%;
  width: clamp(76px, 22%, 118px); height: 26px;
  background: linear-gradient(180deg, rgba(255,255,255,.62), rgba(255,255,255,.34));
  border-left: 1px dashed rgba(255,255,255,.55);
  border-right: 1px dashed rgba(255,255,255,.55);
  transform: rotate(-6deg);
  box-shadow: 0 2px 5px rgba(60,54,10,.18);
  pointer-events: none;
}
.ttb-postit .pi-mark { margin-top: clamp(18px, 2.4vw, 26px); text-align: right; }
.ttb-postit .ttb-mark { font-size: clamp(15px, 1.6vw, 18px); color: #2C2A10; }
/* On #FFF07A this is 5.6:1 — dark enough to read, warm enough to read as ink
   on a sticky rather than as UI chrome dropped on top of one. */
.ttb-postit .ttb-mark span,
.ttb-postit .ttb-mark .dot { color: #6B5D05; opacity: 1; }

/* Note type on a sticky: the site's own sans, not a serif and not a joke
   handwriting face. Somebody is being asked to read and act on this. */
.ttb-postit .bn-body p {
  margin: 0 0 13px; font-size: clamp(15px, 1.5vw, 17px);
  line-height: 1.68; color: #2C2A10;
}
.ttb-postit .bn-body .bn-greet {
  font-family: var(--mark); font-weight: 900; text-transform: none;
  font-size: clamp(19px, 2vw, 23px); margin-bottom: 16px; color: #211F08;
}
.ttb-postit .bn-body .bn-sign {
  margin: 18px 0 0; font-weight: 800;
  font-size: clamp(16px, 1.7vw, 18.5px); color: #211F08;
}

/* ============ B · the message card ============ */
.ttb-card {
  padding: clamp(26px, 4vw, 54px) 0 clamp(34px, 5vw, 64px);
  width: min(700px, 100%);
  margin: 0 auto;
  display: flex; justify-content: center;
}
.ttb-card .ck-card {
  width: min(460px, 88%);
  background: var(--white, #fff);
  border: 1px solid rgba(15,46,34,.10);
  border-radius: 18px; overflow: hidden;
  box-shadow: 0 26px 48px -26px rgba(15,46,34,.42), 0 2px 6px rgba(15,46,34,.06);
}
.ttb-card .ck-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 20px; background: var(--lime);
}
.ttb-card .ck-bar .ttb-mark { font-size: 20px; color: var(--forest); }
/* On lime, a lime "TIP" would vanish. Deep forest at full strength is the only
   colour the mark can take here and still be the mark — the .55 alpha this
   started at came out at 3.5:1, which is under the bar and was caught by the
   contrast audit in goodtip/tests.py. GOOD stays --forest and TIP goes darker,
   so the two halves still read as two halves. */
.ttb-card .ck-bar .ttb-mark span,
.ttb-card .ck-bar .ttb-mark .dot { color: var(--deep); opacity: 1; }
.ttb-card .ck-bar-label {
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: rgba(15,46,34,.7);
}
.ttb-card .ck-body { padding: clamp(24px, 3.2vw, 34px) clamp(22px, 3.2vw, 32px); }
.ttb-card .bn-body p {
  margin: 0 0 13px; font-size: clamp(14.5px, 1.4vw, 16px);
  line-height: 1.72; color: var(--ink);
}
.ttb-card .bn-body .bn-greet {
  font-family: var(--mark); font-weight: 900; text-transform: none;
  font-size: clamp(17px, 1.8vw, 20px); margin-bottom: 15px; color: var(--forest);
}
.ttb-card .bn-body .bn-sign {
  margin: 18px 0 0; font-weight: 800;
  font-size: clamp(15px, 1.5vw, 17px); color: var(--forest);
}
.ttb-card .ck-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 13px 20px; border-top: 1px solid rgba(15,46,34,.08);
  background: var(--cream-2, #F6F4EA);
  font-size: 11.5px; letter-spacing: .01em; color: var(--muted);
}
.ttb-card .ck-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--lime); flex: none;
}

@media (prefers-reduced-motion: no-preference) {
  .ttb-postit .pi-stack,
  .ttb-card .ck-card { animation: ttbSettle .55s var(--ease) both; }
  /* Stagger the second half so the pair lands one after the other. */
  .ttb-half + .ttb-half .ck-card { animation-delay: .12s; }
}
@keyframes ttbSettle {
  from { opacity: 0; transform: translateY(10px) scale(.99); }
  to   { opacity: 1; }
}

/* Below ~1150px each half is narrow enough that the objects need most of it. */
@media (max-width: 1150px) {
  .ttb-postit .pi-stack { width: min(430px, 94%); }
  .ttb-card .ck-card { width: min(460px, 96%); }
}

/* Below ~860px two columns stop working at all — stack them, and take the
   tilt off so the note reads straight on a phone. The tape and the pad behind
   it stay: those are what make it a sticky, and they cost no width. */
@media (max-width: 860px) {
  .ttb-split { grid-template-columns: 1fr; gap: 34px; }
  .ttb-split > .ttb-half + .ttb-half {
    border-left: 0; padding-left: 0;
    border-top: 1px solid rgba(15,46,34,.12); padding-top: 28px;
  }
  .ttb-postit .pi-note { transform: none; }
  .ttb-postit .pi-behind { transform: rotate(2deg); inset: 8px -8px -10px 5px; }
}

/* ---- the same note, inside the send form ----
   The preview above the Send button on /tell-the-boss/ (members) and on the
   focused send page. Same card, shrunk: a preview that is a different shape
   from the thing it previews is not a preview. */
.bn-preview { margin: 0 0 18px; }
.bn-preview .ttb-card { padding: 0; width: 100%; }
.bn-preview .ttb-card .ck-card { width: 100%; box-shadow: 0 14px 26px -18px rgba(15,46,34,.4); }
.bn-preview-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 10px; flex-wrap: wrap;
}
.bn-preview-head b {
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--forest);
}
.bn-preview-head span { font-size: 12.5px; color: var(--muted); }
/* The recipient line — who this is about to go to, in their own words, above
   the note itself. */
.bn-preview-to {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; margin-bottom: 10px;
  border: 1px dashed rgba(15,46,34,.22); border-radius: 10px;
  font-size: 13px; color: var(--ink); background: rgba(255,255,255,.4);
}
.bn-preview-to .k {
  font-family: var(--mark); font-size: 10px; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.bn-preview-to .v { font-weight: 700; }
.bn-preview-to .v:empty::after { content: "—"; font-weight: 400; color: var(--muted); }

/* ============================================================================
   SELF-SCROLLING LISTS — a list scrolls inside itself, not by moving the page
   ----------------------------------------------------------------------------
   Long lists (fixtures, comments, org search results, ladders) used to grow the
   document, so reading item forty meant scrolling the header, stat strip and
   sidebar off screen and losing your place. .gt-scroll caps the region and
   scrolls within it, leaving the page furniture put.

   max-height is in vh so it always leaves room for the surrounding chrome, with
   a px floor so a short viewport still shows a few rows rather than a sliver.
   ========================================================================= */
/* The thumb is a wayfinding device — on a round of nine fixtures it is the
   only thing saying how much list is left — so it is drawn to be seen, in
   green, rather than tinted almost out of existence.

   It used to be lime at 30% alpha on the deep pages (.page-deep covers the
   dashboard, the ladder and the Wall). Lime is the brand's spark colour, but
   the fixtures list sits on a CREAM card even on those pages, and #C8F135 at
   .30 over cream is very nearly the cream: a yellow sliver you cannot find. */
.gt-scroll {
  --scroll-thumb:    rgba(63,158,85,.85);   /* mid green, actually seen */
  --scroll-thumb-hi: #3F9E55;
  overflow-y: auto; overflow-x: hidden;
  max-height: max(320px, 62vh);
  /* Room so the last card's shadow isn't clipped by the scroll edge. */
  padding-right: 4px;
  scrollbar-width: thin;
  scrollbar-color: var(--scroll-thumb) transparent;
  overscroll-behavior: contain;
}
/* 12px of gutter with a 2px inset leaves an 8px stick. The old 9px/2px left
   five, which is thin enough to read as a hairline rather than a control. */
.gt-scroll::-webkit-scrollbar { width: 12px; }
.gt-scroll::-webkit-scrollbar-track { background: transparent; }
.gt-scroll::-webkit-scrollbar-thumb {
  background-color: var(--scroll-thumb); border-radius: 100px;
  border: 2px solid transparent; background-clip: content-box;
}
.gt-scroll::-webkit-scrollbar-thumb:hover {
  background-color: var(--scroll-thumb-hi); background-clip: content-box;
}
/* One green for every surface, deliberately. .gt-scroll lists sit on cream
   cards in some places and straight on a deep panel in others, and which is
   which is not something the CSS can tell — the old rule guessed from
   .page-deep and guessed wrong for the fixtures, which are cream-carded on a
   deep page. #3F9E55 is light enough to separate from the deep panels
   (#152B1E) and dark enough to separate from the cream cards (#FCFBF5), so
   there is nothing left to get wrong. */
.gt-scroll.on-dark { --scroll-thumb: rgba(93,199,116,.75); --scroll-thumb-hi: #5DC774; }
/* Match cards (.fxc) run ~210px tall with the MatchReader row, so the generic
   floor above only ever fit about two before scrolling. Raised so roughly
   four sit in view first — same self-scrolling behaviour, just a bigger window. */
.fixtures.gt-scroll { max-height: max(880px, 74vh); }

/* A hint that there's more below, which fades out once you reach the end. */
.gt-listwrap { position: relative; }
.gt-listwrap::after {
  content: ""; position: absolute; left: 0; right: 9px; bottom: 0; height: 34px;
  background: linear-gradient(180deg, transparent, var(--app-bg, #FFFDF4));
  pointer-events: none; opacity: 1; transition: opacity .25s var(--ease);
}
.gt-listwrap.at-end::after { opacity: 0; }

/* ---- scoped loading state: only the list dims, never the whole page ---- */
.gt-listwrap[data-busy] { pointer-events: none; }
.gt-listwrap[data-busy] .gt-scroll { opacity: .45; transition: opacity .15s var(--ease); }
.gt-listwrap[data-busy]::before {
  content: ""; position: absolute; z-index: 4; top: 50%; left: 50%;
  width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border: 3px solid rgba(15,46,34,.16); border-top-color: var(--forest);
  border-radius: 50%; animation: gtSpin .7s linear infinite;
}
@keyframes gtSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .gt-listwrap[data-busy]::before { animation-duration: 1.6s; }
}

/* ============================================================================
   MATCH STATE — the betting-board line: clock, live score, venue
   ========================================================================= */
.ms { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: 8px; }
.ms-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 100px; white-space: nowrap;
}
.ms-badge svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2.2; }
.ms-badge.live { background: #C0261C; color: #fff; }
.ms-badge.done { background: rgba(15,46,34,.09); color: var(--forest); }
/* When and where is the first thing you look for on an upcoming fixture, so it
   is sized to be readable at a glance rather than as fine print: darker ink,
   heavier weight and a couple of points up on the muted 12px it shared with
   everything else in the row. */
.ms-badge.soon { background: rgba(15,46,34,.08); color: var(--ink); font-weight: 800; letter-spacing: .01em; text-transform: none; font-size: 14px; padding: 5px 11px; }
.ms-badge.soon svg { width: 14px; height: 14px; stroke-width: 2.4; }

/* The live pulse. It's the one thing here that should keep moving — it's the
   signal that the score on screen is not the final one. */
.ms-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
@media (prefers-reduced-motion: no-preference) {
  .ms-dot { animation: msPulse 1.5s ease-in-out infinite; }
}
@keyframes msPulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.72); } }

.ms-score { font-family: var(--score); font-weight: 900; font-size: 19px; color: var(--ink); letter-spacing: .01em; }
.ms-score.final { color: var(--forest); }
.ms-when, .ms-venue { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--muted); }
.ms-when svg, .ms-venue svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; }
/* The ground reads alongside the kickoff time on an upcoming fixture, so it is
   lifted to match rather than staying at the muted caption size. */
.ms-upcoming .ms-venue { font-size: 13.5px; font-weight: 700; color: var(--forest); }
.ms-upcoming .ms-venue svg { width: 14px; height: 14px; stroke-width: 2.3; }
.ms-prog { flex: 1 1 60px; min-width: 50px; height: 3px; border-radius: 100px; background: rgba(15,46,34,.12); overflow: hidden; }
.ms-prog > i { display: block; height: 100%; background: #C0261C; border-radius: 100px; transition: width .6s var(--ease); }

/* a live fixture gets a red edge so it's findable in a long list */
.gt-fix.phase-live { border-left: 4px solid #C0261C; }

/* The fixture card gained a live clock, score and venue. The original
   `1fr auto auto` single row couldn't absorb them — the auto columns took what
   they needed and the teams column collapsed until club names broke mid-word.
   So the match state gets its own full-width row underneath, which also reads
   better: it describes the match, not the pick. */
.gt-fix {
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1.15fr) minmax(0, .9fr);
  gap: 6px 14px;
}
.gt-fix .gfx-teams, .gt-fix .gfx-pick, .gt-fix .gfx-state { min-width: 0; }
.gt-fix .gfx-pick { max-width: none; }
/* Stacked content, so end-align the column rather than centring a flex row. */
.gt-fix .gfx-state {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 4px; text-align: right;
}
.gt-fix .ms {
  grid-column: 1 / -1;
  margin-top: 4px; padding-top: 10px;
  border-top: 1px solid var(--gt-hair);
}
@media (max-width: 900px) {
  .gt-fix { grid-template-columns: 1fr; }
  .gt-fix .gfx-state { align-items: flex-start; text-align: left; }
}

.gfx-picked { display: block; font-size: 12.5px; color: var(--muted); }
.gfx-picked b { color: var(--ink); font-weight: 700; }
.gfx-picked.none { opacity: .7; font-style: italic; }
.gfx-stamp { display: block; font-size: 11px; color: var(--muted); opacity: .75; margin-top: 3px; }
.res.live { background: #C0261C; color: #fff; }

/* ---- state filter tabs ---- */
.gt-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.gt-filter {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  font-family: var(--mark); font-size: 11.5px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--forest); background: rgba(15,46,34,.06);
  border: 1px solid rgba(15,46,34,.10);
  padding: 8px 13px; border-radius: 100px;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.gt-filter:hover { background: rgba(15,46,34,.11); }
.gt-filter i {
  font-style: normal; font-size: 11px; opacity: .75;
  background: rgba(15,46,34,.10); border-radius: 100px; padding: 1px 6px;
}
.gt-filter.on { background: var(--deep); border-color: var(--deep); color: var(--lime); }
.gt-filter.on i { background: rgba(255,255,255,.16); opacity: .95; }
.gt-filter.live .ms-dot { background: #C0261C; }
.gt-filter.live.on .ms-dot { background: var(--lime); }

/* Ladder rows moved inside a scroll container, so the row gap that .gt-board's
   own grid used to provide has to be restored here. The horizontal padding
   leaves room for .gb-row's 3px hover nudge, which overflow-x: hidden would
   otherwise shave off. */
.gt-board .gb-rows { display: grid; gap: 8px; padding: 2px 4px 2px 0; }
.gt-board.gt-listwrap { gap: 0; }
.gt-board .gb-head { margin-bottom: 8px; }

/* ============================================================================
   TIP REVIEW SHEET — "here's what you picked" before anything is saved
   ----------------------------------------------------------------------------
   Confirming a whole round used to post straight from the slip bar, with the
   fixtures scrolled somewhere above. This puts the full list of picks in front
   of you first, scrollable, with the ones you've missed called out, and only
   then submits. The panel sits inside the form so its confirm button is an
   ordinary submit.
   ========================================================================= */
body.sheet-open { overflow: hidden; }

.tipsheet { position: fixed; inset: 0; z-index: 120; display: grid; place-items: center; padding: 20px; }
.tipsheet[hidden] { display: none; }
.tipsheet .ts-veil { position: absolute; inset: 0; background: rgba(8,21,14,.68); backdrop-filter: blur(2px); }

.tipsheet .ts-panel {
  position: relative; z-index: 1;
  width: min(620px, 100%); max-height: min(88vh, 780px);
  display: flex; flex-direction: column;
  background: var(--white); border-radius: 18px; overflow: hidden;
  box-shadow: 0 40px 90px rgba(0,0,0,.45);
}
@media (prefers-reduced-motion: no-preference) {
  .tipsheet .ts-panel { animation: tsIn .22s var(--ease) both; }
}
@keyframes tsIn { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; } }

.tipsheet .ts-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
  padding: 20px 22px 14px; border-bottom: 1px solid var(--gt-hair);
}
.tipsheet .ts-head h3 { font-family: var(--display); font-size: 21px; color: var(--ink); }
.tipsheet .ts-sub { font-size: 13px; color: var(--muted); margin-top: 3px; }
.tipsheet .ts-sub b { color: var(--forest); font-weight: 800; }
.tipsheet .ts-x {
  border: 0; background: rgba(15,46,34,.07); color: var(--forest);
  width: 32px; height: 32px; border-radius: 50%; font-size: 21px; line-height: 1;
  cursor: pointer; flex: none;
}
.tipsheet .ts-x:hover { background: rgba(15,46,34,.14); }

/* The list is the reason the sheet exists — it takes the space that's left. */
.tipsheet .ts-list { flex: 1 1 auto; min-height: 0; max-height: none; padding: 12px 22px; }

.tipsheet .ts-item {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 11px 0; border-bottom: 1px solid var(--gt-hair);
}
.tipsheet .ts-item:last-child { border-bottom: 0; }
.tipsheet .tsi-match { min-width: 0; }
.tipsheet .tsi-match b { display: block; font-size: 14.5px; font-weight: 700; color: var(--ink); }
.tipsheet .tsi-match small { display: block; font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.tipsheet .tsi-pick { flex: none; }
.tipsheet .tsi-yes, .tipsheet .tsi-no {
  display: inline-block; font-family: var(--mark); font-size: 11.5px; font-weight: 800;
  letter-spacing: .04em; padding: 5px 11px; border-radius: 100px; white-space: nowrap;
}
.tipsheet .tsi-yes { background: var(--lime); color: var(--deep); }
.tipsheet .tsi-no { background: rgba(15,46,34,.08); color: var(--muted); font-style: italic; }
.tipsheet .ts-item.none .tsi-match b { color: var(--muted); }

.tipsheet .ts-miss {
  display: flex; align-items: flex-start; gap: 9px; margin: 0 22px 4px;
  padding: 11px 13px; border-radius: 11px;
  background: rgba(200,241,53,.16); border: 1px solid rgba(140,175,25,.4);
  font-size: 12.5px; color: var(--ink);
}
.tipsheet .ts-miss[hidden] { display: none; }
.tipsheet .ts-miss svg { width: 15px; height: 15px; stroke: var(--forest); fill: none; stroke-width: 2; flex: none; margin-top: 1px; }
.tipsheet .ts-miss b { font-weight: 800; }

.tipsheet .ts-foot {
  display: flex; gap: 10px; justify-content: flex-end;
  padding: 16px 22px 20px; border-top: 1px solid var(--gt-hair);
}
@media (max-width: 520px) {
  .tipsheet { padding: 0; }
  .tipsheet .ts-panel { width: 100%; max-height: 100vh; border-radius: 0; }
  .tipsheet .ts-foot { flex-direction: column-reverse; }
  .tipsheet .ts-foot .abtn { width: 100%; justify-content: center; }
}

/* ============================================================================
   NO GROUP YET — real fixtures, nothing to post to
   ----------------------------------------------------------------------------
   A member with no org still sees the next round on the dashboard. The teams
   are <button>s rather than radio labels because there is no form behind them:
   they open the "join a group first" dialog. These rules only undo the browser
   button defaults that .pick .team never had to fight when it was a <label>.
   ========================================================================= */
.pick button.team {
  width: 100%;
  font: inherit;
  font-weight: 700;
  text-align: left;
  -webkit-appearance: none;
  appearance: none;
}
.pick button.team.away { text-align: right; }

/* The dialog leads with the apology, so the emoji sits above the heading. */
.ng-panel .ng-emoji { font-size: 34px; line-height: 1; margin-bottom: 10px; }
.ng-panel .ts-head { align-items: flex-start; }
.ng-panel .ts-sub { max-width: 46ch; margin-top: 6px; }
.ng-panel .ts-foot { justify-content: flex-end; }

/* The password reset link sits under the password field, spelled out in full.
   Its own line and full size — the 12px "Forgot?" on the label row read as part
   of the label and people looked straight past it. */
.fld .flink-lg {
  display: inline-block;
  margin-top: 10px;
  font-size: 15px;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Join request review — the person, big enough to recognise before deciding. */
.jr-who { display: flex; align-items: center; gap: 16px; }
.jr-face {
  width: 64px; height: 64px; border-radius: 16px; flex: none;
  display: grid; place-items: center; overflow: hidden;
  background: var(--card-1); color: var(--forest);
  font-family: var(--mark); font-weight: 700; font-size: 26px;
}
.jr-face img { width: 100%; height: 100%; object-fit: cover; }

/* Count badge on an admin nav link — the Approvals queue advertises itself. */
.an-links a .an-count {
  display: inline-block; min-width: 18px; padding: 0 5px; margin-left: 5px;
  border-radius: 100px; background: var(--lime); color: var(--deep);
  font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
  vertical-align: middle;
}

/* ============================================================================
   CREATE-A-GROUP WIZARD
   ----------------------------------------------------------------------------
   One long form put people off part-way, so it's four steps with the answers
   saved after each. The rail shows where they are and what's already done.
   ========================================================================= */
/* The rail used to be a row of pills; the current one filled solid lime, which
   swallowed its own number circle. It is now circles joined by connectors —
   see the v11/v12 blocks near the end of this file, which are the live rules.
   Nothing is defined here on purpose: a `.wiz-step.current` declaration at this
   specificity beat the later `.wiz-step` rules and reinstated the pill. */

.wiz-nav { display: flex; gap: 10px; align-items: center; }
.wiz-nav .abtn-primary { flex: 1; justify-content: center; }
.wiz-summary th { color: var(--muted); font-weight: 600; padding-right: 18px; }

/* ---------- review step: icon + tint per row, not a bare table ---------- */
.wsum-list { display: flex; flex-direction: column; gap: 8px; }
.wsum-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 16px; border-radius: 12px;
}
.wsum-l { display: flex; align-items: center; gap: 12px; min-width: 0; }
.wsum-ic {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center;
  background: rgba(15,46,34,.09); color: var(--forest);
}
.wsum-ic svg { width: 16px; height: 16px; }
.wsum-label {
  font-size: 11.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--deep); white-space: nowrap;
}
.wsum-r { display: flex; align-items: center; gap: 12px; min-width: 0; }
.wsum-val { font-size: 14.5px; font-weight: 600; color: var(--ink); text-align: right; }
.wsum-check {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--forest); color: #fff;
}
.wsum-check svg { width: 11px; height: 11px; stroke: currentColor; fill: none; stroke-width: 3; }
/* Six pastel bands, one per field group — see _draft_summary's fixed mapping. */
.wsum-row.c1 { background: #E8F2E1; }
.wsum-row.c2 { background: #E3EFEA; }
.wsum-row.c3 { background: #E1EBF3; }
.wsum-row.c4 { background: #E9E7F3; }
.wsum-row.c5 { background: #F3E7EF; }
.wsum-row.c6 { background: #F7EAD9; }
@media (max-width: 520px) {
  .wsum-row { flex-wrap: wrap; }
  .wsum-val { text-align: left; }
}

.wiz-saved {
  margin: 14px 0 0; text-align: center;
  font-size: 13px; color: rgba(245,241,230,.6);
}
.wiz-restart {
  background: none; border: 0; padding: 0; margin-left: 6px;
  font: inherit; color: var(--lime); text-decoration: underline; cursor: pointer;
}

/* ==========================================================================
   ============ v10 — form busy state · loader progress · empty state =======
   ========================================================================== */

/* Submit feedback — see gt-busy.js. The button stops being a button and starts
   being a status line, so a slow signup reads as working rather than ignored. */
/* pointer-events blocks the second click during the window where the button
   is deliberately NOT yet `disabled` — see gt-busy.js, where disabling it
   synchronously would strip the submitter's name and value from the POST. */
form.is-busy [type="submit"] { opacity: .82; cursor: progress; pointer-events: none; }
form.is-busy [type="submit"]:hover { transform: none; }
.busy-dot {
  display: inline-block; width: 14px; height: 14px; margin-right: 9px;
  vertical-align: -2px; border-radius: 50%;
  border: 2px solid rgba(15,46,34,.25); border-top-color: var(--forest);
  animation: busySpin .7s linear infinite;
}
@keyframes busySpin { to { transform: rotate(360deg); } }

.busy-strip {
  height: 4px; margin-top: 12px; border-radius: 100px; overflow: hidden;
  background: rgba(15,46,34,.10);
}
.busy-strip > i {
  display: block; height: 100%; width: 40%; border-radius: 100px;
  background: linear-gradient(90deg, transparent, var(--lime), var(--forest), transparent);
  animation: busySlide 1.1s var(--ease) infinite;
}
@keyframes busySlide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(320%); }
}
/* This motion is status, not decoration: it is the only signal that a submitted
   form is still running, so it survives the calm-everything block. It is
   slowed rather than stopped, which keeps the meaning without the flicker. */
@media (prefers-reduced-motion: reduce) {
  .busy-dot { animation-duration: 1.6s; }
  .busy-strip > i { animation-duration: 2.4s; }
}

/* ---------- welcome photo: camera capture + quiet secondary button ---------- */

/* Subordinate to abtn-primary by weight, not by size — see .wp-skip. */
.abtn-quiet {
  background: transparent; color: var(--muted);
  border: 1.5px solid rgba(15,46,34,.20);
}
.abtn-quiet:hover { color: var(--forest); border-color: rgba(15,46,34,.42); background: rgba(15,46,34,.03); }

.wp-pickrow { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.wp-cam { cursor: pointer; display: inline-flex; align-items: center; gap: 8px; }
/* A red record dot reads as "live camera" faster than any icon at this size. */
.wp-camdot { width: 9px; height: 9px; border-radius: 50%; background: #e5484d; box-shadow: 0 0 0 3px rgba(229,72,77,.18); }

.wp-cam-stage { margin: 16px 0 4px; text-align: center; }
.wp-cam-stage video {
  width: 100%; max-width: 280px; aspect-ratio: 1; object-fit: cover;
  border-radius: 50%; border: 3px solid var(--lime); background: #0F2E22;
  /* Match the mirrored capture, so the preview is what you get. */
  transform: scaleX(-1);
}
.wp-cam-actions { display: flex; gap: 10px; justify-content: center; margin-top: 14px; }

/* ---------- empty state: "you need a group first" ---------- */

/* Was 34px and sitting bare on the panel, which read as a stray character
   rather than as art. Larger, and set on a tinted disc so it has somewhere to
   live. The explicit emoji font stack matters: the panel inherits a display
   face that has no colour glyphs, so the browser fell back to a flat monochrome
   outline — that is why it did not look "real". */
.ng-panel .ng-emoji {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji",
               "Twemoji Mozilla", "EmojiOne Color", sans-serif;
  font-size: 46px; line-height: 1;
  width: 84px; height: 84px; margin: 0 0 16px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%, rgba(200,241,53,.34), rgba(200,241,53,.10) 70%);
  box-shadow: inset 0 0 0 1px rgba(15,46,34,.10);
  /* Some builds force -webkit-text-fill-color on headings; undo it here or the
     colour glyph is painted as a silhouette. */
  -webkit-text-fill-color: initial;
}

/* ==========================================================================
   ============ v11 — competition builder: rail · card · footer bar =========
   ========================================================================== */

/* Title row + draft chip. Stacks on narrow screens, where the chip sitting
   beside a wrapped h1 would squeeze the title to two words a line. */
.wiz-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin: 0 0 26px;
}
.wiz-top .app-sub { margin-bottom: 0; }

.draft-chip {
  display: inline-flex; align-items: center; gap: 11px; flex: none;
  padding: 12px 18px; border-radius: 14px;
  background: rgba(255,255,255,.05); border: 1px solid rgba(200,241,53,.28);
}
.draft-chip .dc-ic { color: var(--lime); display: grid; place-items: center; }
.draft-chip .dc-ic svg { width: 21px; height: 21px; }
.draft-chip .dc-txt { display: flex; flex-direction: column; line-height: 1.3; }
.draft-chip .dc-txt b { font-size: 14px; font-weight: 700; color: var(--cream); }
.draft-chip .dc-txt i { font-style: normal; font-size: 12.5px; color: rgba(245,241,230,.55); }

/* ---------- progress rail ---------- */
/* Rebuilt as circles joined by rules rather than a row of pills. Pills read as
   four separate buttons; the rail needs to read as one track you are partway
   along, which is what the connectors do. */
.wiz-rail {
  display: flex; gap: 0; align-items: flex-start; justify-content: space-between;
  flex-wrap: nowrap; margin: 0 auto 30px; max-width: 100%;
}
.wiz-step {
  display: flex; align-items: center; gap: 13px; flex: none;
  padding: 0 0 14px; border-radius: 0; border: 0; background: none;
  color: rgba(245,241,230,.55); font-size: 14px; font-weight: 600;
  position: relative;
}
.wiz-step .wz-num {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: rgba(255,255,255,.05); border: 1.5px solid rgba(255,255,255,.14);
  color: rgba(245,241,230,.6); font-size: 16px; font-weight: 800;
}
.wiz-step .wz-num svg { width: 17px; height: 17px; }
.wz-text { display: flex; flex-direction: column; line-height: 1.32; }
.wz-label { font-size: 15px; font-weight: 700; color: rgba(245,241,230,.8); }
.wz-sub { font-size: 12.5px; font-weight: 500; color: rgba(245,241,230,0.52); }

.wiz-step.done .wz-num { background: rgba(200,241,53,.16); border-color: rgba(200,241,53,.5); color: var(--lime); }
.wiz-step.done .wz-label { color: rgba(245,241,230,.9); }

/* Current step: the disc carries the whole highlight. An earlier version also
   drew a lime rule under the step, which read as a second, competing marker and
   boxed the step off from the track it is meant to sit on. One lime object on
   the rail, and it is the circle you are standing on. */
.wiz-step.current .wz-num {
  background: var(--lime); border-color: var(--lime); color: var(--deep);
}
.wiz-step.current .wz-label { color: var(--white); }
.wiz-step.current .wz-sub { color: rgba(245,241,230,.62); }

.wz-line {
  flex: 1 1 auto; height: 1.5px; min-width: 18px; margin: 21px 8px 0;
  background: rgba(255,255,255,.14);
}
.wz-line.done { background: rgba(200,241,53,.4); }

/* Six steps' worth of full labels need roughly 1450-1500px in a row; the
   sheet that holds the rail is capped at 1160px (see .page-sheet), so that
   width is never actually available, at any viewport — a fixed-width box
   trying to hold something permanently wider than it overflows its own
   edge every time, "filling" past the visible rail rather than stopping at
   its last step. Numbers only, with the current step's own label kept (the
   one that says where you are), is what actually fits, at every width. */
.wiz-step .wz-text { display: none; }
.wiz-step.current .wz-text { display: flex; }
/* A step that isn't current still names itself — as a floating tooltip on
   hover/focus rather than inline text, so previewing "what's step 4" from
   step 1 costs nothing in layout width. Positioned off the circle, not the
   whole .wiz-step (which also contains the invisible-but-present .wz-text
   box for every non-current step, and centring on THAT would drift toward
   whichever neighbour last had the wide current label). */
.wiz-step:not(.current) { cursor: pointer; outline: none; }
.wiz-step:not(.current):hover .wz-text,
.wiz-step:not(.current):focus-visible .wz-text {
  display: flex; position: absolute; z-index: 6;
  bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%);
  width: max-content; max-width: 200px;
  background: var(--deep); border: 1px solid rgba(255,255,255,.16);
  border-radius: 10px; padding: 9px 13px;
  box-shadow: 0 14px 30px -10px rgba(0,0,0,.55);
  animation: wzTipIn .14s var(--ease) both;
}
.wiz-step:not(.current):hover .wz-text .wz-label,
.wiz-step:not(.current):focus-visible .wz-text .wz-label { color: var(--white); }
@keyframes wzTipIn {
  from { opacity: 0; transform: translateX(-50%) translateY(3px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@media (max-width: 560px) {
  .wiz-step .wz-num { width: 36px; height: 36px; font-size: 14px; }
  .wz-line { margin: 18px 6px 0; }
  .wiz-top { gap: 14px; }
}

/* ---------- step card ---------- */
.wiz-card { border-radius: 18px; }
.wc-head {
  display: flex; align-items: flex-start; gap: 14px;
  margin: 0 0 24px; padding: 0 0 4px;
}
.wc-ic {
  width: 46px; height: 46px; border-radius: 13px; flex: none;
  display: grid; place-items: center;
  background: rgba(45,122,58,.13); color: var(--forest);
}
.wc-ic svg { width: 23px; height: 23px; }
.wc-head h2 { margin: 0; font-size: 22px; font-weight: 800; color: var(--deep); letter-spacing: -.01em; }
.wc-head p { margin: 3px 0 0; font-size: 14.5px; color: var(--muted); }

/* ---------- fields with a leading icon ---------- */
/* The icon is a sibling rather than a background image so it can carry its own
   colour and stay put when the input is focused or shows an error. */
.fld-ic { position: relative; display: block; }
.fld-ic .fi-mark {
  position: absolute; left: 15px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; display: grid; place-items: center;
  color: var(--muted); pointer-events: none; z-index: 1;
}
.fld-ic .fi-mark svg { width: 100%; height: 100%; }
.fld-ic > input,
.fld-ic > select { padding-left: 46px; }
.fld-ic:focus-within .fi-mark { color: var(--forest); }

/* ---------- footer bar ---------- */
.wiz-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-top: 20px;
  padding: 18px 22px; border-radius: 16px;
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.10);
}
.wf-note { display: flex; align-items: center; gap: 13px; }
.wf-ic {
  width: 42px; height: 42px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  background: rgba(200,241,53,.10); color: var(--lime);
}
.wf-ic svg { width: 21px; height: 21px; }
.wf-txt { display: flex; flex-direction: column; line-height: 1.4; }
.wf-txt b { font-size: 14.5px; font-weight: 700; color: var(--cream); }
.wf-txt i { font-style: normal; font-size: 13px; color: rgba(245,241,230,.55); }
.wiz-foot .wiz-nav { margin: 0; }

@media (max-width: 620px) {
  .wiz-foot { flex-direction: column; align-items: stretch; }
  .wiz-foot .wiz-nav { width: 100%; }
  .wiz-foot .wiz-nav .abtn { flex: 1; justify-content: center; }
}

/* Pre-first-save state: same shape, muted border so it does not compete with
   the real "Draft saved" confirmation it turns into. */
.draft-chip.idle { border-color: rgba(255,255,255,.14); }
.draft-chip.idle .dc-ic { color: rgba(245,241,230,0.52); }

/* ==========================================================================
   ============ v12 — competition builder shell (matches create.png) ========
   ========================================================================== */

.wiz-page .app-main { position: relative; z-index: 1; }

/* The sheet: one dark panel holding the whole builder, floated over the photo.
   Everything inside it is measured from this edge, which is what makes the
   rail, the card and the footer bar read as one object. */
.wiz-sheet {
  position: relative; z-index: 1;
  max-width: 1160px; margin: 0 auto;
  padding: clamp(26px, 3.4vw, 46px) clamp(20px, 3.2vw, 52px) clamp(30px, 3.6vw, 50px);
  border-radius: 22px;
  background: linear-gradient(180deg, rgba(17,52,38,.94), rgba(12,38,27,.96));
  border: 1px solid rgba(255,255,255,.07);
  box-shadow: 0 50px 100px -40px rgba(0,0,0,.75);
}

/* ---------- header inside the sheet ---------- */
.wiz-page .app-eyebrow { color: var(--lime); font-size: 12.5px; letter-spacing: .16em; }
.wiz-page .app-h1 {
  color: #fff; font-size: clamp(32px, 4.2vw, 52px);
  line-height: 1.04; margin: 10px 0 0; letter-spacing: -.02em;
}
.wiz-page .app-sub { color: rgba(232,228,213,.72); font-size: 16px; margin-top: 12px; }
.hl-lime { color: var(--lime); }

/* ---------- the step card: cream, not lime ---------- */
.wiz-form { max-width: 100%; margin: 0; }
.wiz-form > .card,
.wiz-form .wiz-card {
  background: #F6F4EA;
  border: 1px solid rgba(15,46,34,.08);
  border-radius: 18px;
  padding: clamp(22px, 2.4vw, 34px);
  box-shadow: 0 24px 60px -34px rgba(0,0,0,.6);
}
.wiz-form .fld { margin-bottom: 22px; }
.wiz-form .fld:last-child { margin-bottom: 0; }
.wiz-form .fld > label {
  color: var(--deep); font-size: 14.5px; font-weight: 700;
  display: block; margin-bottom: 9px;
}
.wiz-form .hint { color: rgba(15,46,34,0.64); font-size: 13px; margin-top: 8px; }

/* Inputs sit a shade lighter than the card so the field reads as a slot cut
   into it rather than a box drawn on top. */
.wiz-form .fld input[type="text"],
.wiz-form .fld input[type="url"],
.wiz-form .fld input[type="number"],
.wiz-form .fld textarea,
.wiz-form .fld select {
  width: 100%; background: #FFFFFF;
  border: 1px solid rgba(15,46,34,.15); border-radius: 11px;
  padding: 15px 16px; font-size: 15.5px; color: var(--ink);
  font-family: var(--body);
}

/* Icon fields.
   Two previous attempts absorbed the icon by giving the input a big padding-left
   and floating the glyph over it. Both lost: the rule above is
   `.wiz-form .fld input[type="text"]` at (0,3,1) and uses the `padding`
   SHORTHAND, which resets padding-left every time — so any padding-only fix has
   to out-specify it forever, and the next selector added up there silently
   breaks it again. That is why the glyph kept landing on the first character.

   So the icon is no longer overlaid at all. The wrapper becomes the bordered
   control, the glyph is a real flex child occupying its own 46px column, and the
   input inside is stripped bare and simply flexes into what is left. There is no
   overlap to tune because the two never share space. */
.wiz-form .fld-ic {
  display: flex; align-items: stretch; position: relative;
  background: #FFFFFF;
  border: 1px solid rgba(15,46,34,.15); border-radius: 11px;
  overflow: hidden;
}
.wiz-form .fld-ic > .fi-mark {
  position: static; transform: none;
  width: 46px; height: auto; flex: none;
  display: grid; place-items: center;
  border-right: 1px solid rgba(15,46,34,.10);
  background: rgba(15,46,34,.025);
  color: rgba(15,46,34,0.67);
}
.wiz-form .fld-ic > .fi-mark svg { width: 19px; height: 19px; }

/* The input keeps its own type/appearance rules but gives up the frame — the
   wrapper owns it now. !important is here because the shorthand rule above
   out-specifies this by design and there is no reading of the cascade where a
   nested control should redraw its parent's border. */
.wiz-form .fld-ic > input,
.wiz-form .fld-ic > select,
.wiz-form .fld-ic > textarea {
  flex: 1 1 auto; min-width: 0;
  border: 0 !important; border-radius: 0 !important;
  background: transparent !important;
  padding: 15px 16px !important;
  box-shadow: none !important; outline: none;
}
.wiz-form .fld-ic > select { padding-right: 44px !important; }
/* The chevron was painted as the select's own background-image, which the
   transparent background above wipes out. It belongs to the wrapper now. */
.wiz-form .fld-ic:has(> select)::after {
  content: ""; position: absolute; right: 16px; top: 50%; margin-top: -9px;
  width: 18px; height: 18px; pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F2E22' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat center / 18px;
  opacity: .6;
}

/* Focus and error move to the wrapper, since that is what is drawn now. */
.wiz-form .fld-ic:focus-within {
  border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.18);
}
.wiz-form .fld-ic:focus-within > .fi-mark {
  color: var(--forest); background: rgba(45,122,58,.07);
  border-right-color: rgba(45,122,58,.25);
}
.wiz-form .fld:has(.fld-err) .fld-ic {
  border-color: #d93a31; box-shadow: 0 0 0 3px rgba(217,58,49,.12);
}
.wiz-form .fld:has(.fld-err) .fld-ic > .fi-mark {
  color: #C8352D; background: rgba(217,58,49,.07);
  border-right-color: rgba(217,58,49,.25);
}
.wiz-form .fld select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F2E22' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center; background-size: 18px;
  padding-right: 46px;
}
.wiz-form .fld input:focus,
.wiz-form .fld textarea:focus,
.wiz-form .fld select:focus {
  outline: none; border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(45,122,58,.18); background: #fff;
}
.wiz-form .fld input::placeholder { color: rgba(15,46,34,0.64); }

/* ---------- footer bar ---------- */
/* Lime button on the dark bar, dark text on the lime — the one saturated
   element on the page, so the next action is never in question. */
.wiz-page .wiz-foot .abtn-primary {
  background: var(--lime); color: var(--deep); border: 0;
  padding: 16px 30px; font-size: 16px; font-weight: 800; border-radius: 11px;
}
.wiz-page .wiz-foot .abtn-primary:hover { background: var(--lime-dk); filter: none; }
.wiz-page .wiz-foot .abtn-ghost {
  background: transparent; color: rgba(232,228,213,.8);
  border: 1px solid rgba(255,255,255,.18); border-radius: 11px; padding: 16px 24px;
}
.wiz-page .wiz-foot .abtn-ghost:hover { color: #fff; border-color: rgba(255,255,255,.34); }
.wiz-page .wiz-restart {
  background: none; border: 0; padding: 0 0 0 8px; cursor: pointer;
  color: rgba(200,241,53,.8); font-size: 13px; font-weight: 700;
  text-decoration: underline; text-underline-offset: 3px; font-family: var(--body);
}
.wiz-page .wiz-restart:hover { color: var(--lime); }

@media (max-width: 700px) {
  .wiz-sheet { border-radius: 16px; }
}

/* ==========================================================================
   ============ v13 — invalid fields · shared cream-on-green shell ==========
   ========================================================================== */

/* A field that failed validation. :has() lets the wrapper react to the error
   node Django already renders, so no template flag has to be threaded through
   for something the markup already states. */
.wiz-form .fld:has(.fld-err) > label { color: #b3261e; }
.wiz-form .fld:has(.fld-err) input,
.wiz-form .fld:has(.fld-err) select,
.wiz-form .fld:has(.fld-err) textarea,
.wiz-form .fld:has(.fld-err) .fld-ic > input,
.wiz-form .fld:has(.fld-err) .fld-ic > select {
  border-color: #d93a31; background: #FFF7F6;
  box-shadow: 0 0 0 3px rgba(217,58,49,.12);
}
.wiz-form .fld:has(.fld-err) .fi-mark { color: #C8352D; }
.wiz-form .fld-err {
  margin-top: 8px; font-size: 13.5px; font-weight: 700; color: #b3261e;
  display: flex; align-items: center; gap: 7px;
}
/* Django's message is "This field is required." — true, but it does not say
   what to do. The marker adds the instruction without the view having to
   rewrite every validator's wording. */
.wiz-form .fld-err::before {
  content: "!"; flex: none;
  width: 17px; height: 17px; border-radius: 50%;
  background: #d93a31; color: #fff;
  font-size: 12px; font-weight: 900; line-height: 17px; text-align: center;
}

.wiz-save { white-space: nowrap; }

/* --------------------------------------------------------------------------
   The shell: cream panel, green surround.
   Applied per page with .sheet-page on <body> plus a .page-sheet wrapper, so
   page opts in rather than every .app page changing underneath it.
   -------------------------------------------------------------------------- */
/* .page-sheet wraps every app page, but is inert until the page asks for it by
   putting `sheet-page` on the body. An always-on version would have restyled
   every screen in the app as a side effect of one page's redesign. */
.sheet-page .page-sheet {
  position: relative; z-index: 1;
  max-width: 1160px; margin: 0 auto;
  padding: clamp(24px, 3vw, 44px) clamp(18px, 3vw, 48px) clamp(28px, 3.4vw, 48px);
  border-radius: 22px;
  background: linear-gradient(180deg, rgba(17,52,38,.94), rgba(12,38,27,.96));
  border: 1px solid rgba(255,255,255,.07);
  box-shadow: 0 50px 100px -40px rgba(0,0,0,.75);
}
.sheet-page .app-main { position: relative; z-index: 1; }

/* Headings sit on the sheet, so they take the on-dark treatment. */
.sheet-page .page-sheet .app-h1 { color: #fff; }
.sheet-page .page-sheet .app-eyebrow { color: var(--lime); }
.sheet-page .page-sheet .app-sub { color: rgba(232,228,213,.72); }

/* Cards inside a sheet go bright cream — the surface the reading happens on. */
.sheet-page .page-sheet .card {
  background: #F6F4EA;
  border: 1px solid rgba(15,46,34,.08);
  border-radius: 18px;
  box-shadow: 0 24px 60px -34px rgba(0,0,0,.6);
}
/* A card nested inside a card is a note, not a second surface — flatten it or
   the panel gains a shadow every level down. */
.sheet-page .page-sheet .card .card { box-shadow: none; }

@media (max-width: 700px) {
  .gt-sheet { border-radius: 16px; }
}

/* ---------- organisation logo (create wizard, step 1) ----------
   First pass was a grey dashed rectangle with a small square in it — it read as
   a disabled attachment widget, not as somewhere you would put your brand. The
   whole panel is the drop target now: a deep-green tile that shows the logo at
   the size and shape it will actually appear at in search results, so the
   preview is a preview rather than a thumbnail of one.                        */
.org-logo {
  position: relative;
  display: flex; align-items: center; gap: 18px;
  margin-top: 16px; padding: 18px 20px;
  border-radius: 16px;
  background:
    radial-gradient(420px 160px at 12% 0%, rgba(45,122,58,.10), transparent 70%),
    linear-gradient(180deg, #FFFFFF, #FBFAF4);
  border: 1px solid rgba(15,46,34,.13);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
/* Dragging a file anywhere over the panel — the class is set from JS. */
.org-logo.is-drop {
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(45,122,58,.16), inset 0 1px 0 rgba(255,255,255,.9);
}

.ol-preview {
  width: 78px; height: 78px; flex: none; border-radius: 18px; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(160deg, var(--deep), #12402D);
  color: rgba(200,241,53,.75);
  box-shadow: 0 10px 22px -12px rgba(15,46,34,.75);
}
.ol-preview .ol-fallback svg { width: 28px; height: 28px; }
/* A real logo goes on white: brand marks are drawn for light backgrounds and a
   dark tile behind a dark logo hides it. contain, never cover — cropping
   someone's logo is worse than letterboxing it. */
.ol-preview[data-has="1"] { background: #fff; box-shadow: 0 10px 22px -14px rgba(15,46,34,.5); }
.ol-preview img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }

.ol-body { min-width: 0; flex: 1; }
.ol-title {
  display: flex; align-items: center; gap: 9px;
  font-size: 15.5px; font-weight: 800; color: var(--deep); letter-spacing: -.01em;
}
/* "optional" reads as part of the label, not as a warning */
.ol-title span {
  padding: 3px 9px; border-radius: 100px;
  background: rgba(45,122,58,.12); color: var(--forest);
  font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em;
}
.ol-hint { margin: 6px 0 0; font-size: 13.5px; line-height: 1.55; color: rgba(15,46,34,0.64); }
.ol-actions { display: flex; align-items: center; gap: 14px; margin-top: 13px; flex-wrap: wrap; }
.ol-pick {
  cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; font-size: 14px; font-weight: 700; border-radius: 10px;
  background: var(--deep); color: var(--lime); border: 0;
}
.ol-pick:hover { background: var(--deep-2); }
.ol-pick svg { width: 15px; height: 15px; }
.ol-remove {
  background: none; border: 0; padding: 0; cursor: pointer; font-family: var(--body);
  font-size: 13.5px; font-weight: 700; color: #b3261e;
  text-decoration: underline; text-underline-offset: 3px;
}
.ol-remove:hover { color: #8c1d18; }
/* Only shown once there is a file, so it cannot be read as an instruction. */
.ol-drop-note { margin: 9px 0 0; font-size: 12.5px; color: rgba(15,46,34,0.64); }

@media (max-width: 560px) {
  .org-logo { flex-direction: column; align-items: flex-start; text-align: left; }
}

/* ==========================================================================
   ============ v14 — sheet width · dashboard headline · role pills =========
   ========================================================================== */

/* Wider sheet. 1160px was set for the four-step builder, where a narrow measure
   helps; the dashboard carries fixtures, ladders and news side by side and was
   being squeezed into a column with green either side of it. */
/* Widened again so the inner panel sits closer to the page edge — the green
   margin either side was reading as wasted space rather than as a frame. Width
   only; the vertical padding is unchanged. */
.sheet-page .page-sheet {
  max-width: min(1680px, calc(100vw - 96px));
  padding: clamp(26px, 3vw, 44px) clamp(22px, 2.4vw, 40px) clamp(30px, 3.2vw, 48px);
}
@media (max-width: 900px) {
  .sheet-page .page-sheet { max-width: calc(100vw - 28px); }
}
/* The builder keeps the narrower measure — a form reads worse the wider it gets. */
.wiz-page .page-sheet { max-width: 1180px; }

/* --- dashboard headline ---------------------------------------------------
   Every rotating line now carries the greeting's weight, not just slide one.
   The prompts are the point of the banner — "you're 1st on 0 points", "the
   Wall's empty" — and at 14.5px they read as caption under a headline that
   said nothing but the user's own name. */
.dash-prompt { max-width: 900px; }
.dash-prompt .dp-txt,
.dp-greet .dp-txt {
  font-family: var(--display);
  font-size: clamp(21px, 2.5vw, 29px);
  line-height: 1.16; letter-spacing: -.01em;
  color: #fff; opacity: 1;
}
/* The stack is absolutely layered, so its height is the tallest line. Two lines
   of display type need the room or the row below jumps as they cycle. */
.dash-prompt.has-greet { min-height: 82px; }
/* The icon has to grow with the type or it sits at the line's ankle. */
.dp-ic { width: 38px; height: 38px; border-radius: 11px; }
.dp-ic svg { width: 19px; height: 19px; }
.dp-cta { font-size: 12px; padding: 8px 16px; }

@media (max-width: 560px) {
  .dash-prompt.has-greet { min-height: 104px; }
  .dash-prompt .dp-txt, .dp-greet .dp-txt { font-size: 19px; }
}

/* --- role pills on the dark banner ----------------------------------------
   Owner/Manager/Captain were tuned for a light card: mid-green on a pale tint.
   On the deep-green banner that is dark-on-dark and effectively invisible.
   White type on a translucent fill, with the lime kept for Owner so the ranks
   still read apart at a glance. */
.dash-banner .pill-role,
.page-deep .pill-role {
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.22);
  color: #fff;
  font-size: 11.5px; padding: 5px 12px;
}
.dash-banner .pill-role.owner,
.page-deep .pill-role.owner {
  background: var(--lime); border-color: var(--lime); color: var(--deep);
}
.dash-banner .pill-role.manager,
.page-deep .pill-role.manager,
.dash-banner .pill-role.captain,
.page-deep .pill-role.captain {
  background: rgba(255,255,255,.14); color: #fff;
}

/* --- competition name in the picker --------------------------------------- */
.page-deep .dc-lab { color: rgba(232,228,213,.62); }
.page-deep .dc-pick select,
.page-deep .dash-ctl select {
  color: #fff; font-weight: 700; font-size: 15.5px;
}

/* --- competition name in the top nav --------------------------------------
   It sat at 55% opacity as a quiet caption beside the wordmark, which made the
   one piece of context telling you WHICH comp you are looking at the faintest
   thing in the bar. */
.gt-v2 .an-org,
.an-org {
  color: #fff; opacity: .92; font-weight: 700; font-size: 14.5px;
  letter-spacing: -.005em;
}

/* ==========================================================================
   ============ v15 — scroll surfaces on the sheet ==========================
   ========================================================================== */

/* The scrolling data areas sat on the sheet's own dark green, so rows floated
   with nothing behind them. Giving the scroll region a bright cream bed makes
   it read as one surface you are reading down, and the green becomes the frame
   around it rather than the background of the content. */
.sheet-page .gt-listwrap,
.sheet-page .gt-board,
.sheet-page .gt-feed {
  background: #F6F4EA;
  border: 1px solid rgba(15,46,34,.08);
  border-radius: 18px;
  padding: 14px;
  box-shadow: 0 24px 60px -34px rgba(0,0,0,.6);
}
/* One bed, not three — a listwrap already provides it for anything inside. */
.sheet-page .gt-listwrap .gt-board,
.sheet-page .gt-listwrap .gt-feed {
  background: transparent; border: 0; border-radius: 0;
  padding: 0; box-shadow: none;
}
/* Column headings belong to the cream bed now. */
.sheet-page .gt-board .gb-head { color: rgba(15,46,34,0.64); }

/* --- The Wall: posts and replies stay WHITE ------------------------------
   Cream everywhere would have flattened the feed into one sheet of paper. The
   posts are the cards you read; white lifts each one off the cream bed the way
   the tip cards already do, and keeps the thread visually inside its parent. */
.sheet-page .gt-post,
.sheet-page .gt-feed .gt-post {
  background: #FFFFFF;
  border: 1px solid rgba(15,46,34,.10);
  border-radius: 14px;
  box-shadow: 0 10px 26px -20px rgba(15,46,34,.45);
}
.sheet-page .gt-post .gpt-body { color: var(--ink); }
.sheet-page .gt-post .gpt-meta b { color: var(--deep); }
.sheet-page .gt-post .gpt-meta small { color: var(--muted); }
/* Replies sit a step back from the post they hang off, so the nesting reads
   without another border. */
.sheet-page .gt-post .gpt-thread { background: rgba(15,46,34,.03); border-radius: 11px; }
.sheet-page .gt-post .gpt-reply { background: transparent; }
.sheet-page .gt-post .gpt-replybox input,
.sheet-page .gt-post .gpt-replybox textarea { background: #fff; }

/* ==========================================================================
   ============ v16 — confirm dialog · fixture rows · leaderboard head ======
   ========================================================================== */

/* Replaces window.confirm(). See gt-confirm.js for why. */
.gt-confirm { position: fixed; inset: 0; z-index: 10000; display: grid; place-items: center; padding: 20px; }
.gt-confirm[hidden] { display: none; }
body.gtc-open { overflow: hidden; }
.gtc-scrim { position: absolute; inset: 0; background: rgba(9,26,17,.62); backdrop-filter: blur(3px); }
.gtc-panel {
  position: relative; width: 100%; max-width: 440px;
  background: #F6F4EA; border-radius: 20px; padding: 30px 28px 24px;
  border: 1px solid rgba(15,46,34,.10);
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.7);
  text-align: center;
  animation: gtcIn .18s var(--ease);
}
@keyframes gtcIn { from { opacity: 0; transform: translateY(10px) scale(.98); } }
/* The entrance is decoration, not information — the dialog is just as clear
   without it, so reduced motion drops it entirely. */
@media (prefers-reduced-motion: reduce) { .gtc-panel { animation: none; } }

.gtc-ic {
  width: 54px; height: 54px; margin: 0 auto 16px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(45,122,58,.12); color: var(--forest);
}
.gtc-ic svg { width: 26px; height: 26px; }
.gt-confirm.is-danger .gtc-ic { background: rgba(217,58,49,.12); color: #B3261E; }

.gtc-panel h2 { margin: 0; font-size: 21px; font-weight: 800; color: var(--deep); letter-spacing: -.01em; }
.gtc-panel p { margin: 10px 0 0; font-size: 15px; line-height: 1.55; color: rgba(15,46,34,.66); }
.gtc-panel p[hidden] { display: none; }

.gtc-actions { display: flex; gap: 10px; margin-top: 24px; }
.gtc-btn {
  flex: 1; padding: 13px 18px; border-radius: 11px; cursor: pointer;
  font-family: var(--body); font-size: 15px; font-weight: 700;
  border: 1.5px solid transparent; transition: background .15s var(--ease);
}
.gtc-cancel { background: transparent; border-color: rgba(15,46,34,.22); color: var(--deep); }
.gtc-cancel:hover { background: rgba(15,46,34,.05); }
.gtc-ok { background: var(--deep); color: var(--lime); }
.gtc-ok:hover { background: var(--deep-2); }
.gt-confirm.is-danger .gtc-ok { background: #d93a31; color: #fff; }
.gt-confirm.is-danger .gtc-ok:hover { background: #b3261e; }
.gtc-btn:focus-visible { outline: 3px solid rgba(45,122,58,.4); outline-offset: 2px; }

/* --- fixture rows: alternate shading so each match reads as its own row ---
   A flat run of nine identical cards made it hard to tell where one match
   ended and the next began, which matters when you are clicking a team in
   each. Zebra striping is cheaper to read than adding more borders. */
.fixtures .fxp:nth-child(even) { background: rgba(15,46,34,.045); }
.fixtures .fxp:nth-child(odd) { background: rgba(255,255,255,.75); }
.fixtures .fxp { border-radius: 12px; transition: background .15s var(--ease); }
.fixtures .fxp:hover { background: rgba(200,241,53,.16); }
.fixtures .fxp.done { background: rgba(45,122,58,.10); }
.fixtures .fxp.done:nth-child(even) { background: rgba(45,122,58,.14); }

/* --- page headers on the green sheet ---
   These sat at small sizes and muted colours tuned for a light background; on
   the deep sheet they were the least readable text on the page. */
.sheet-page .gt-head .gh-eyebrow,
.sheet-page .gwall-head .gh-eyebrow {
  color: var(--lime); font-size: 14px; font-weight: 700;
  letter-spacing: .06em; opacity: .95;
}
.sheet-page .gt-head h1,
.sheet-page .gt-head .gh-title {
  color: #fff; font-size: clamp(30px, 3.6vw, 44px); letter-spacing: -.02em;
}
.sheet-page .gt-head .gh-sub,
.sheet-page .gt-head p {
  color: rgba(232,228,213,.82); font-size: 16.5px; font-weight: 500;
}
.sheet-page .gt-rounder,
.sheet-page .gt-head .rnd-now,
.sheet-page .gt-head .rnd-when { color: var(--cream); }
.sheet-page .gt-head .rnd-when { font-size: 14px; opacity: .8; }

/* --- the hero post card on the Wall stays white --- */
.sheet-page .gwh-slot .wr-card,
.sheet-page .wr-card {
  background: #FFFFFF;
  border: 1px solid rgba(15,46,34,.10);
  box-shadow: 0 14px 34px -22px rgba(0,0,0,.55);
}
.sheet-page .wr-card .wr-who b { color: var(--deep); }
.sheet-page .wr-card .wr-who small,
.sheet-page .wr-card .wr-when { color: var(--muted); }
.sheet-page .wr-card .wr-body { color: var(--ink); }

/* --- fixture state chip: the four states now read apart ---------------------
   All of them used to share one grey pill, so "Open" and "Tipped" looked
   identical at a glance and the only signal was the word itself. */
.fxp-meta .fxp-state.is-open {
  background: rgba(45,122,58,.10); color: var(--forest); border-color: rgba(45,122,58,.3);
}
.fxp-meta .fxp-state.is-tipped,
.fxp-meta .fxp-state.is-picked {
  background: rgba(200,241,53,.45); color: var(--deep); border-color: rgba(169,209,28,.6);
}
/* Missed is stated plainly rather than alarmingly — the round is gone, and
   colouring it like an error implies something the member can still fix. */
.fxp-meta .fxp-state.is-missed {
  background: rgba(15,46,34,.06); color: var(--muted);
  border-color: var(--app-line); border-style: dashed;
}

/* --- round separator inside the upcoming-fixtures list --------------------
   The list is ordered by kickoff and spans rounds, so this is the only thing
   marking where one round's slate ends. Sticky, because the heading is what
   tells you which round the fixture under your cursor belongs to, and that
   question survives scrolling. */
.fixtures .fx-roundhead {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 18px 0 0; padding: 12px 18px;
  background: var(--deep);
  /* Rounded top only: the round header and the fixtures beneath it are one
     block, so a fully rounded bar floating above them would read as a
     separate thing rather than as this round's heading. */
  border-radius: 12px 12px 0 0;
}
.fixtures .fx-roundhead:first-child { margin-top: 0; }
.fxr-n { font-family: var(--mark); font-size: 16px; font-weight: 800; color: var(--lime); letter-spacing: .02em; }
.fxr-series { font-size: 13.5px; font-weight: 600; color: rgba(232,228,213,.8); }
.fxr-locks { margin-left: auto; font-size: 12.5px; color: rgba(232,228,213,.62); white-space: nowrap; }
/* Cards in a round group sit flush under its header and square off the join. */
.fixtures .fx-roundhead + .fxc { border-radius: 0; border-top: 0; }
.fixtures .fxc { border-left-width: 3px; }
@media (max-width: 520px) { .fxr-locks { margin-left: 0; width: 100%; } }

/* ==========================================================================
   ============ v17 — competition ladder ====================================
   ========================================================================== */

/* Eight columns; the narrow ones are numeric and share a fixed width so the
   figures line up down the table, which is the whole point of a ladder. */
.gt-board.ladder .lad-head,
.gt-board.ladder .lad-row {
  display: grid;
  grid-template-columns: 42px minmax(0,1fr) repeat(6, 46px);
  align-items: center; gap: 6px;
}
.gt-board.ladder .lad-num {
  text-align: right;
  font-family: var(--score); font-variant-numeric: tabular-nums;
  font-size: 14.5px; color: var(--ink);
}
.gt-board.ladder .lad-head .lad-num { font-family: var(--body); font-size: 11.5px; }
.gt-board.ladder .lad-w { color: var(--forest); font-weight: 700; }
.gt-board.ladder .lad-pts { font-weight: 800; color: var(--deep); font-size: 16px; }
.gt-board.ladder .lad-pct { color: var(--muted); }

.lad-crest {
  width: 30px; height: 30px; flex: none; border-radius: 8px; overflow: hidden;
  display: grid; place-items: center;
  background: rgba(15,46,34,.07); color: var(--deep);
  font-size: 11px; font-weight: 800;
}
.lad-crest img { width: 100%; height: 100%; object-fit: contain; }

/* The finals cut. A left edge rather than a row fill: the eighth-place line is
   the one thing everyone looks for, and a band of colour across eight rows
   would compete with the numbers it exists to frame. */
.gt-board.ladder .lad-row.in-finals { box-shadow: inset 3px 0 0 var(--forest); }
.gt-board.ladder .lad-row.is-top { box-shadow: inset 3px 0 0 var(--lime); background: rgba(200,241,53,.10); }
.gt-board.ladder .lad-row.in-finals:nth-child(8) { border-bottom: 2px solid rgba(45,122,58,.45); }

.lad-foot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 14px 2px 0; font-size: 12.5px; color: rgba(232,228,213,.6);
}
.lad-key { display: inline-flex; align-items: center; gap: 7px; }
.lad-key i { width: 3px; height: 15px; border-radius: 2px; background: var(--forest); display: inline-block; }
.lad-when { margin-left: auto; }

/* Draws and percentage are the first to go when space runs out — they are the
   least-read columns, and dropping them keeps W/L/Pts legible on a phone. */
@media (max-width: 640px) {
  .gt-board.ladder .lad-head,
  .gt-board.ladder .lad-row { grid-template-columns: 34px minmax(0,1fr) repeat(4, 40px); }
  .gt-board.ladder .lad-d, .gt-board.ladder .lad-pct { display: none; }
}

/* Action link inside an auto-toast. */
.gtt-cta {
  align-self: flex-start; margin-top: 8px;
  display: inline-block; padding: 6px 13px; border-radius: 100px;
  background: var(--lime); color: var(--deep);
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase; text-decoration: none;
}
.gtt-cta:hover { filter: brightness(1.06); }

/* ==========================================================================
   ============ v18 — nav breathing room · Manage menu ======================
   ========================================================================== */

/* Six visible items plus a Manage control. The bar was running out of room at
   laptop widths, and every item was being squeezed to compensate — so the fix
   is fewer things in the row, not smaller things. */
.gt-v2 .an-links { gap: 2px; margin-left: 14px; }
.gt-v2 .an-links a,
.an-menu-btn { padding: 9px 13px; font-size: 14px; white-space: nowrap; }
/* Below this the row cannot hold six items honestly; the burger takes over. */
@media (max-width: 1180px) {
  .an-links, .an-org { display: none; }
}
@media (min-width: 1181px) and (max-width: 1360px) {
  .gt-v2 .an-links a, .an-menu-btn { padding: 9px 10px; font-size: 13.5px; }
  .gt-v2 .an-org { max-width: 190px; overflow: hidden; text-overflow: ellipsis; }
}

.an-menu { position: relative; display: inline-flex; }
.an-menu-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; cursor: pointer; border-radius: 10px;
  font-family: var(--body); font-weight: 600; color: var(--cream); opacity: .8;
  transition: opacity .15s var(--ease), background .15s var(--ease);
}
.an-menu-btn:hover { opacity: 1; background: rgba(255,255,255,.06); }
.an-menu-btn svg { width: 13px; height: 13px; transition: transform .18s var(--ease); }
.an-menu-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.an-menu-btn.on,
.an-menu-btn[aria-expanded="true"] { color: var(--lime); opacity: 1; background: rgba(200,241,53,.10); }

.an-menu-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 272px; padding: 7px; border-radius: 14px;
  background: linear-gradient(160deg, var(--deep), var(--deep-2));
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 30px 60px -26px rgba(0,0,0,.75);
}
.an-menu-panel[hidden] { display: none; }
.an-menu-panel a {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px; border-radius: 10px; text-decoration: none;
}
.an-menu-panel a:hover { background: rgba(200,241,53,.10); }
/* The description is the point: "Members" alone does not say that invites and
   join requests live there, which is exactly why they were hard to find. */
.an-menu-panel a b { font-size: 14.5px; font-weight: 700; color: var(--cream); }
.an-menu-panel a i { font-style: normal; font-size: 12.5px; color: rgba(232,228,213,.55); }

/* Stat strip now lives inside the round card, between the countdown and the
   fixtures — so it needs its own spacing rather than the old block margin. */
.dash-stats { margin: 18px 0 20px; }

/* ==========================================================================
   ============ v19 — tell-the-boss progress ================================
   ========================================================================== */
.bp-card { margin-bottom: 14px; }
.bp-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.bp-state {
  flex: none; padding: 5px 12px; border-radius: 100px;
  font-size: 11.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
}
.bp-state.is-sent { background: rgba(15,46,34,.08); color: var(--muted); }
.bp-state.is-joined { background: rgba(45,122,58,.14); color: var(--forest); }
.bp-state.is-complete { background: var(--lime); color: var(--deep); }
.bp-note { margin: 12px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--ink); }

.bp-preview { margin-top: 14px; border-top: 1px solid rgba(15,46,34,.10); padding-top: 12px; }
.bp-preview summary {
  cursor: pointer; font-size: 13.5px; font-weight: 700; color: var(--forest);
  list-style: none;
}
.bp-preview summary::-webkit-details-marker { display: none; }
.bp-preview summary::before { content: "▸ "; }
.bp-preview[open] summary::before { content: "▾ "; }
.bp-subject { margin: 12px 0 6px; font-size: 13px; font-weight: 700; color: var(--deep); }
/* The letter is fixed copy, so it is shown exactly as sent rather than
   reflowed — a preview that does not match what arrived is worse than none. */
.bp-preview pre {
  margin: 0; padding: 14px; border-radius: 10px;
  background: rgba(15,46,34,.04); border: 1px solid rgba(15,46,34,.08);
  font-family: var(--body); font-size: 13.5px; line-height: 1.6; color: rgba(15,46,34,.78);
  white-space: pre-wrap; overflow-x: auto;
}

/* ==========================================================================
   ============ v20 — breathing scene zoom ==================================
   ========================================================================== */

/* Every shot animates for its whole dwell (--dwell, set by gt-scenes.js from
   the same PERIOD that drives the swap), so the imagery is never static — the
   previous behaviour animated ~1s of a 3s hold and sat still for the other two.

   Direction alternates per step, and the two keyframes are exact mirrors, so
   each image begins at the scale the last one finished on. That continuity is
   the whole effect: without it every swap would snap back to 1.0 first. */
.shot.zoom-out,
.shot.zoom-in { will-change: transform; }

@media (prefers-reduced-motion: no-preference) {
  .shot.zoom-out { animation: shotZoomOut var(--dwell, 15000ms) linear forwards; }
  .shot.zoom-in  { animation: shotZoomIn  var(--dwell, 15000ms) linear forwards; }

  /* Travel raised from 14% to 26%, and a pan added alongside it.

     The pan is the part that actually buys visibility. A pure centre-scale is
     the least perceptible transform there is — every point moves directly away
     from the middle, so with nothing holding still there is no reference for
     the eye to measure against, and over a 15s dwell 14% read as a still photo.
     Drifting the frame at the same time gives edges that move differently from
     each other, which is what makes it read as a camera rather than a resize.

     At scale 1.26 the image overflows its box by 13% on every side, so a 2.5%
     / 1.5% drift stays far inside the overflow — the frame never runs out of
     picture and no edge gap can appear. The two keyframes stay exact mirrors,
     including the pan, so each image still begins at precisely the position
     and scale the previous one ended on. */
  @keyframes shotZoomOut {
    from { transform: scale(1.26) translate(-2.5%, -1.5%); }
    to   { transform: scale(1.0) translate(0, 0); }
  }
  @keyframes shotZoomIn {
    from { transform: scale(1.0) translate(0, 0); }
    to   { transform: scale(1.26) translate(-2.5%, -1.5%); }
  }

  /* The zoom owns transform now, so the entrance effects that also animated it
     would fight it. Fade keeps working (opacity), and wipe keeps working
     (clip-path) — only the two transform-based ones step aside. */
  .fx-slide .shot.on.zoom-in, .fx-slide .shot.on.zoom-out,
  .fx-zoom  .shot.on.zoom-in, .fx-zoom  .shot.on.zoom-out {
    animation: shotZoomOut var(--dwell, 15000ms) linear forwards;
  }
  .fx-slide .shot.on.zoom-in, .fx-zoom .shot.on.zoom-in {
    animation-name: shotZoomIn;
  }
}

/* Reduced motion gets the same effect at a quarter of the travel rather than
   nothing at all. A 3.5% drift over three seconds is below the threshold that
   causes trouble — it is the 14% version that reads as the camera moving — and
   keeping it means the imagery is alive for everyone instead of the design
   only existing for people who have the setting switched off. */
@media (prefers-reduced-motion: reduce) {
  .shot.zoom-out { animation: shotZoomOutCalm var(--dwell, 15000ms) linear forwards; }
  .shot.zoom-in  { animation: shotZoomInCalm  var(--dwell, 15000ms) linear forwards; }
  /* shotZoomOutCalm / shotZoomInCalm are defined once, further down with the
     rest of the reduced-motion handling. A 3.5% pair used to be declared here
     as well and was shadowed by it — same names, later wins — which made the
     travel look like it was set in two places at once. */
}

/* ==========================================================================
   ============ v21 — public nav ============================================
   ========================================================================== */

/* The 30px gap was set for a shorter menu. With eight links plus two auth
   actions the row ran past the wordmark and squeezed; the fix is tighter,
   evenly-measured spacing with real padding, so the items have room without
   the row growing. */
.nav-links { gap: 4px; }
.nav-links > li { display: flex; align-items: center; }
.nav-links a {
  font-size: 14.5px; padding: 9px 12px; border-radius: 9px;
  opacity: .78; transition: opacity .15s var(--ease), background .15s var(--ease);
}
.nav-links a:hover { opacity: 1; background: rgba(255,255,255,.06); }

/* The underline sat under the padding box and read as detached. Inset it to
   the text instead. */
.nav-links a[aria-current="page"]::after { left: 12px; right: 12px; bottom: 4px; }

/* Auth actions are separated from navigation by a rule, so the eye reads the
   row as "places to go … things to do" rather than ten equal links. */
.nav-sep {
  width: 1px; height: 20px; margin: 0 10px;
  background: rgba(255,255,255,.18);
}
/* Log in is a bordered ghost, Sign up is solid lime — two different weights,
   so they stop looking like the same control twice. Previously Log in was an
   ordinary nav link and disappeared into the eight beside it. */
.nav-links .nav-login {
  border: 1.5px solid rgba(255,255,255,.26);
  padding: 8px 16px; opacity: .95; font-weight: 700;
}
.nav-links .nav-login:hover { border-color: var(--lime); color: var(--lime); background: transparent; }
.btn-nav { font-size: 12.5px; padding: 10px 18px; white-space: nowrap; }

@media (max-width: 1319px) and (min-width: 1121px) {
  .nav-links { gap: 1px; }
  .nav-links a { font-size: 13.5px; padding: 9px 9px; }
  .nav-sep { margin: 0 6px; }
}

/* ---- the four that moved to the footer (Sep 2026) ------------------------
   The Good List, Charities, About Us and Privacy came off the desktop row so
   the bar reads as six destinations rather than a site map, and they now live
   in the footer where a reader goes looking for them.

   They stay in the DRAWER, though. Below 1120px the drawer is not a condensed
   version of the bar, it is the whole site — the footer is a long scroll away
   on a phone, and "Privacy is in the footer" is a desktop answer. So the
   markup carries all ten items and this hides four of them at the widths
   where the footer is genuinely within reach. */
@media (min-width: 1121px) {
  .nav-links .nav-tuck { display: none; }
}
/* In the drawer the group gets a rule above it instead of a vertical divider —
   .nav-sep is a 1px column, which stacked is invisible. */
@media (max-width: 1120px) {
  .nav-links .nav-sep { width: auto; height: 1px; margin: 10px 0 2px; background: rgba(255,255,255,.14); }
}

/* ==========================================================================
   ============ v22 — flow wave · card expand ===============================
   ========================================================================== */

/* FLOW — an ordered sequence. Members rise and drift in from the left in
   order, which is what turns a stagger into a wave: each one is still moving
   as the next begins, so the group reads as one motion travelling down the
   page rather than as items popping in. */
.rv-flow .reveal {
  opacity: 0;
  transform: translate3d(-26px, 20px, 0);
  transition: opacity .62s var(--ease), transform .72s cubic-bezier(.22,.75,.3,1);
}
.rv-flow .reveal.in { opacity: 1; transform: none; }

/* CARDS — a set, not a sequence. They expand into place together: no travel,
   because travel implies a direction to read them in, and there isn't one. */
.rv-cards .reveal {
  opacity: 0;
  transform: scale(.93);
  transition: opacity .5s var(--ease), transform .58s cubic-bezier(.2,.9,.3,1.25);
}
.rv-cards .reveal.in { opacity: 1; transform: none; }

/* Reduced motion keeps the sequence but drops the travel: the items still
   arrive in order, as a fade, so the wave is legible as timing rather than as
   movement. Removing it outright meant the whole flow appeared at once and the
   ordering the content depends on was lost. */
@media (prefers-reduced-motion: reduce) {
  .rv-flow .reveal, .rv-cards .reveal {
    transform: none !important;
    transition: opacity .5s linear !important;
  }
  .rv-flow .reveal.in, .rv-cards .reveal.in { opacity: 1; }
}

/* ==========================================================================
   ============ v23 — public nav fits its row ===============================
   ========================================================================== */

/* The row was squeezed for two reasons, and both are container bugs rather
   than "too many links".

   1. .nav-inner capped itself at --maxw and THEN spent 5vw of gutter inside
      that cap. On a 1900px screen that left 990px of usable row — narrower
      than the 1180px of page content sitting directly beneath it. Adding the
      gutters to the cap restores the full 1180px and makes the wordmark line
      up with the content edge below, which it never did before.
   2. Nothing forbade wrapping, so the leftover pressure landed inside the
      labels: "How It / Works", "Tell the / Boss", "Log / in". Labels are
      atomic — they break the row's rhythm long before they save any space. */
.nav-inner {
  max-width: calc(var(--maxw) + 2 * var(--gutter));
  gap: 20px;
}
.wordmark { flex: 0 0 auto; }
.nav-links { flex: 0 1 auto; flex-wrap: nowrap; gap: 2px; }
.nav-links a { white-space: nowrap; }
.nav-links > li { flex: 0 0 auto; }

/* Slightly tighter than v21 so nine links plus both auth actions clear 1180px
   with room to spare instead of only just. */
.nav-links a { font-size: 14px; padding: 9px 11px; }

/* 1121–1319px: the tightest the row ever gets before the drawer takes over.
   The gutter drops out of the way here — on a laptop, a nav that runs closer
   to the window edge is far better than one that wraps. */
@media (max-width: 1319px) and (min-width: 1121px) {
  .nav-inner { padding-left: 26px; padding-right: 26px; }
  .wordmark { font-size: 23px; }
  .nav-inner { gap: 12px; }
  .nav-links a { font-size: 13px; padding: 8px 8px; }
  .nav-links .nav-login { padding: 7px 13px; }
  .btn-nav { font-size: 11.5px; padding: 9px 14px; }
  .nav-sep { margin: 0 5px; }
}

/* In the drawer the items are stacked, so the desktop compaction has to be
   undone — full-size labels, no nowrap, no bordered ghost squeezed to a
   phone's width. */
@media (max-width: 1120px) {
  .nav-links { flex-wrap: wrap; }
  .nav-links a { font-size: 16px; padding: 14px 0; white-space: normal; }
  .nav-links > li, .nav-links .nav-auth { display: block; width: 100%; }
  .nav-links .nav-login {
    display: block; text-align: center; border-radius: 10px;
    padding: 13px 16px; margin-top: 12px;
    /* restated in full: the drawer's blanket border-bottom would otherwise
       overwrite the ghost button's bottom edge with a hairline rule */
    border: 1.5px solid rgba(255,255,255,.26);
  }
  .nav-links .btn-nav { display: block; font-size: 13px; padding: 14px 18px; }
  .nav-sep { display: none; }              /* the drawer's own rules divide it */
}

/* ==========================================================================
   ============ v24 — nav legibility + mobile drawer ========================
   ========================================================================== */

/* Cream at 78% opacity is a dim grey-green on the deep forest, and 600 weight
   at 14px reads as small print. The nav is the one element on every page, so
   it should be legible at a glance rather than deferential: near-white, a full
   step bolder, a size up. Idle sits at .92 rather than 1 so the current page
   and hover still have somewhere brighter to go. */
.nav-links a {
  color: var(--white);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .005em;
  opacity: .92;
}
.nav-links a:hover,
.nav-links a:focus-visible,
.nav-links a[aria-current="page"] { opacity: 1; color: var(--white); }
.nav-links a:hover { background: rgba(255,255,255,.09); }
.nav-links .nav-login { border-color: rgba(255,255,255,.4); }

/* The bigger, bolder row needs its full 1180px, so the last compaction tier
   now runs to 1319px and full size starts at 1320 — the first width where
   5vw gutters stop eating into the cap. */
.nav-links a { padding: 9px 10px; }

@media (max-width: 1319px) and (min-width: 1121px) {
  .nav-inner { padding-left: 22px; padding-right: 22px; }
  .nav-links a { font-size: 14px; padding: 8px 8px; font-weight: 700; }
}

/* ---------- MOBILE DRAWER ------------------------------------------------
   Stacked, the constraints invert: nothing competes for horizontal room, so
   the row height and the space around each label are all that matter. Rows
   are sized for a thumb (52px+), the hairline rules are lightened so eight of
   them don't read as a table, and the two auth actions are lifted out into
   their own block at the bottom with real air above them — the same "places
   to go / things to do" split the desktop divider makes. */
@media (max-width: 1120px) {
  .nav-links {
    gap: 0;
    padding: 6px clamp(20px, 5vw, 32px) clamp(22px, 5vw, 30px);
    box-shadow: 0 24px 40px -24px rgba(0,0,0,.7);
  }
  .nav-links a {
    font-size: 17px; font-weight: 700; opacity: 1;
    padding: 16px 2px; border-bottom: 1px solid rgba(255,255,255,.09);
    border-radius: 0; background: none;
  }
  .nav-links a:hover { background: none; color: var(--lime); }

  /* Current page is marked by a lime label rather than the desktop underline,
     which floats loose when the row is full-width. */
  .nav-links a[aria-current="page"] { color: var(--lime); }
  .nav-links a[aria-current="page"]::after,
  .nav-links a[aria-current="page"]::before { display: none; }

  /* Last link before the auth block keeps its rule — it is what closes the
     list — but the auth block's own top border would double it up. */
  .nav-auth { border-top: 0; }
  .nav-links .nav-login {
    margin-top: 18px;
    padding: 15px 16px;
    font-size: 16px;
    border-radius: 10px;
  }
  .nav-links .btn-nav {
    margin-top: 10px;
    padding: 16px 18px;
    font-size: 14px;
    border-radius: 10px;
    border: 0;                             /* not one of the list's hairlines */
  }

  /* The drawer slid in from -8px, which on a full-height panel looked like a
     twitch. A longer travel with the panel's own shadow reads as a sheet
     coming down. */
  .nav-links { transform: translateY(-14px); }
  .nav[data-open="true"] .nav-links { transform: none; }

  /* Bigger hamburger target — 8px of padding around a 24px glyph is a 40px
     hit area, under the 44px minimum. */
  .nav-toggle { padding: 10px; margin-right: -6px; }
  .nav-toggle span { width: 26px; }

  /* The bar shrinks to 56px once scrolled, but the drawer was pinned to the
     unscrolled 66px — so opening the menu mid-page left a 10px gap of hero
     showing between the bar and the sheet. */
  .nav.scrolled .nav-links { top: 56px; max-height: calc(100vh - 56px); }

  /* Scrolled, the bar is translucent with a blur behind it. That is right over
     content, but wrong with an opaque sheet hanging off it — the two halves
     stop looking like one panel. Open, it goes solid. */
  .nav[data-open="true"].scrolled {
    background: var(--deep);
    backdrop-filter: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-links { transition: opacity .15s linear !important; transform: none !important; }
}

/* ==========================================================================
   ============ v25 — one zoom everywhere · splits stack on mobile ==========
   ========================================================================== */

/* ---- THE ZOOM ------------------------------------------------------------
   The alternating push/pull (v20) is now the only thing animating a `.shot`.
   It was already written and already correct, but three leftover ken-burns
   rules — `.hero-bg .shot.active`, `.image-band .shot.active`, and a six-
   selector block covering every remaining stack — each set `animation` from a
   three-class selector, which outranks the two-class `.shot.zoom-in`. So on
   every page the site fell back to an unsynced 14–18s infinite loop that had
   no relationship to the moment the picture changed. All three are gone.

   The cycle, per stack: image 1 sits at rest and magnifies over its 15s dwell;
   at the wide end it crossfades to image 2, which is already wide and pulls
   back over its 15s; at rest it crossfades to image 3, which magnifies again.
   No image ever jumps to a scale the previous one was not already holding. */

/* ---- SPLITS ON MOBILE ----------------------------------------------------
   Two image panes side by side is a landscape idea. On a phone each half is
   ~180px wide against a full-height hero — the photos are cropped to slivers
   and neither is readable as a scene. Stacked, both get the full width and
   half the height, which is the same composition turned through 90°. */

/* Home hero collage. flex-grow and the zero basis carry over to the column
   axis unchanged, so the open/close reframe works stacked exactly as it does
   side by side — a half now grows downward instead of sideways. `min-height:
   0` on the pane is what lets the closed one actually collapse on this axis;
   the seam turns from a vertical edge into a horizontal one. */
@media (max-width: 820px) {
  .hero-bg.collage { flex-direction: column; }
  .hero-bg.collage .pane + .pane { border-left: 0; }
  .hero-bg.collage.split .pane + .pane:not(.closed) {
    border-left: 0;
    border-top: 3px solid rgba(200,241,53,.45);
  }
}

/* Auth backdrop. These panes are absolutely positioned rather than flexed, so
   the stack has to be spelled out: left becomes the top half, right the
   bottom. */
@media (max-width: 820px) {
  .auth-bg .ab-pane { width: 100%; height: 50%; }
  .auth-bg .ab-pane.left { top: 0; bottom: auto; left: 0; }
  .auth-bg .ab-pane.right { top: auto; bottom: 0; left: 0; right: 0; }
  .auth-bg .ab-pane.right {
    border-left: 0;
    border-top: 1px solid rgba(200,241,53,.10);
  }
}

/* ---- THE LAST TWO RULES THAT WERE EATING THE ZOOM ------------------------
   Removing the ken-burns leftovers was not quite enough. Two of the four
   entrance styles still set the `animation` SHORTHAND from a three-class
   selector, which replaces the whole property — including the zoom — rather
   than sitting alongside it.

   1. `.fx-wipe .shot.on` (0,3,0) beat `.shot.zoom-in` (0,2,0). The comment
      next to it says wipe is safe because it animates clip-path, and the
      clip-path part is true — but the shorthand still dropped the zoom, so a
      quarter of the site's stacks (fx is assigned round-robin) held still.
      Wipe and zoom animate different properties, so they can simply both run.

   2. Under reduced motion, `.fx-slide/.fx-zoom/.fx-wipe .shot.on
      { animation: none }` (0,3,0) beat the calm zoom (0,2,0) the same way,
      leaving three stacks in four completely static. */

@media (prefers-reduced-motion: no-preference) {
  .fx-wipe .shot.on.zoom-in,
  .fx-wipe .shot.on.zoom-out {
    animation:
      fxWipe 1.05s cubic-bezier(.5,0,.2,1),
      shotZoomOut var(--dwell, 15000ms) linear forwards;
  }
  .fx-wipe .shot.on.zoom-in { animation-name: fxWipe, shotZoomIn; }
}

@media (prefers-reduced-motion: reduce) {
  .fx-fade  .shot.on.zoom-out, .fx-slide .shot.on.zoom-out,
  .fx-zoom  .shot.on.zoom-out, .fx-wipe  .shot.on.zoom-out {
    animation: shotZoomOutCalm var(--dwell, 15000ms) linear forwards;
  }
  .fx-fade  .shot.on.zoom-in, .fx-slide .shot.on.zoom-in,
  .fx-zoom  .shot.on.zoom-in, .fx-wipe  .shot.on.zoom-in {
    animation: shotZoomInCalm var(--dwell, 15000ms) linear forwards;
  }

  /* Now 12% of travel with a matching half-size pan — up from 3.5%, then 6%.
     Still under half of what the full effect does, and spread over a 15s dwell
     it is a drift rather than a move, but the earlier values were so small
     across so long that the picture read as static. An effect nobody can see
     is not a gentler effect, it is an absent one, and the point of keeping a
     calm variant is that reduced-motion users get the design too. */
  @keyframes shotZoomOutCalm {
    from { transform: scale(1.12) translate(-1.2%, -0.7%); }
    to   { transform: scale(1.0) translate(0, 0); }
  }
  @keyframes shotZoomInCalm {
    from { transform: scale(1.0) translate(0, 0); }
    to   { transform: scale(1.12) translate(-1.2%, -0.7%); }
  }
}

/* ==========================================================================
   ============ v26 — the wave =============================================
   ========================================================================== */

/* An ordered sequence arrives as a swell passing along it, and then keeps
   rippling — a flag, not a single flap. Two animations per member, handed over
   one to the next:

     ENTRANCE  one swell as the section arrives, ending at rest
     STANDING  a slow ripple from then on, starting and ending at rest

   It is a handoff rather than both together because two animations on the same
   transform fight, and the winner is whichever the browser resolves last.
   Both loops start and end at translate 0, so the join is seamless.

   Every distance is scaled by --wv. That is the whole reduced-motion story:
   one multiplier, set to a third further down, instead of a second set of
   keyframes to keep in sync with these. */

.rv-wave-x > .reveal,
.rv-wave-y > .reveal { --wv: 1; }

/* ACROSS — the crest travels left to right along the row while each card rises
   and falls in place. That is how a wave works: the water does not travel, the
   crest does. Members do not slide in from the side, because sliding is the
   thing a wave is not. */
@keyframes gtWaveRise {
  0%   { opacity: 0; transform: translate3d(0, calc(34px * var(--wv, 1)), 0); }
  55%  { opacity: 1; transform: translate3d(0, calc(-14px * var(--wv, 1)), 0); }
  78%  { opacity: 1; transform: translate3d(0, calc(5px * var(--wv, 1)), 0); }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* DOWN — the crest travels down the page, so the swell washes in from the left
   across each member in turn. */
@keyframes gtWaveSwell {
  0%   { opacity: 0; transform: translate3d(calc(-44px * var(--wv, 1)), calc(16px * var(--wv, 1)), 0); }
  55%  { opacity: 1; transform: translate3d(calc(11px * var(--wv, 1)), calc(-7px * var(--wv, 1)), 0); }
  78%  { opacity: 1; transform: translate3d(calc(-4px * var(--wv, 1)), calc(2px * var(--wv, 1)), 0); }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* The standing wave. Each member is offset by its index (--rv-i), which is what
   puts the crest at a different point along the row and makes the ripple
   travel, rather than the whole group bobbing together like a raft. */
@keyframes gtFlagBob {
  0%   { transform: translate3d(0, 0, 0); }
  25%  { transform: translate3d(0, calc(-13px * var(--wv, 1)), 0); }
  50%  { transform: translate3d(0, 0, 0); }
  75%  { transform: translate3d(0, calc(9px * var(--wv, 1)), 0); }
  100% { transform: translate3d(0, 0, 0); }
}
@keyframes gtFlagSway {
  0%   { transform: translate3d(0, 0, 0); }
  25%  { transform: translate3d(calc(11px * var(--wv, 1)), calc(-3px * var(--wv, 1)), 0); }
  50%  { transform: translate3d(0, 0, 0); }
  75%  { transform: translate3d(calc(-8px * var(--wv, 1)), calc(3px * var(--wv, 1)), 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* These win over .rv-flow and .reveal.from-left, which set a competing
   transform and transition on the same elements. */
.rv-wave-x > .reveal,
.rv-wave-y > .reveal {
  opacity: 0;
  transform: none;
  transition: none;
  will-change: transform, opacity;
}

/* `both` fill: backwards through the delay so a member waiting its turn stays
   hidden rather than flashing, forwards after so it holds the resting position
   instead of snapping back to the 0% frame. */
.rv-wave-x > .reveal.in { animation: gtWaveRise 1.15s cubic-bezier(.24,.66,.3,1) var(--rv-delay, 0ms) both; }
.rv-wave-y > .reveal.in { animation: gtWaveSwell 1.25s cubic-bezier(.24,.66,.3,1) var(--rv-delay, 0ms) both; }

/* 3.6s a cycle, each member a fifth of a cycle behind the one before it. Long
   enough to read as a swell rather than a jiggle; ease-in-out because water
   accelerates into a crest and slows over the top, where linear reads
   mechanical. These sit after the entrance rules so they win once both classes
   are present, which is the handoff. */
.rv-wave-x > .reveal.wave-live { animation: gtFlagBob 3.6s ease-in-out calc(var(--rv-i, 0) * 0.72s) infinite both; }
.rv-wave-y > .reveal.wave-live { animation: gtFlagSway 4.2s ease-in-out calc(var(--rv-i, 0) * 0.84s) infinite both; }

/* Reading a step that drifts under the cursor is irritating, and the donation
   flow explicitly invites you to "tap any step to hold it and read". */
.rv-wave-x:hover > .reveal.wave-live,
.rv-wave-y:hover > .reveal.wave-live { animation-play-state: paused; }

/* Reduced motion gets a third of the travel, not nothing — the same call this
   file already makes for the scene zoom, and for the same reason: an effect
   nobody can see is not a gentler effect, it is an absent one. At a third,
   the standing ripple is about 4px. */
@media (prefers-reduced-motion: reduce) {
  html:not(.gt-motion) .rv-wave-x > .reveal,
  html:not(.gt-motion) .rv-wave-y > .reveal { --wv: .32; }
}

/* ==========================================================================
   ============ v28 — busy veil ============================================
   ========================================================================== */

/* On a screen where the form IS the page — logging in, verifying a code,
   resetting a password — a changed button is too small a signal for what is
   actually a whole-page wait. The panel goes translucent behind a spinner
   instead: unmissable, and it physically covers the controls, so a second
   submission is not merely discouraged but impossible.

   Translucent rather than opaque on purpose. You can still read what you
   typed, which is what makes it "this is being sent" rather than "this screen
   is gone" — and if the server comes back with an error, the field you need to
   fix was never hidden from you. See gt-busy.js. */

.is-busy-scope { position: relative; }

.busy-veil {
  position: absolute; inset: 0; z-index: 20;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 253, 246, .74);
  backdrop-filter: blur(2.5px) saturate(.92);
  cursor: progress;
  animation: bvFade .16s var(--ease) both;
}
@keyframes bvFade { from { opacity: 0; } to { opacity: 1; } }

.bv-inner {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 4px 20px; text-align: center;
}

.bv-ring {
  width: 44px; height: 44px; border-radius: 50%;
  border: 3px solid rgba(15, 46, 34, .14);
  border-top-color: var(--forest);
  animation: busySpin .8s linear infinite;
}

/* The verify step's own veil treatment (data-busy-kind="lock" on its
   send/resend/verify-code buttons): the shackle works itself open and shut
   in a slow, deliberate try — a lock being tested, not a generic spinner —
   while the keyhole dot pulses along with it. Same shape whether it's
   sending a code or checking one; the label text is what says which. */
.bv-lock { width: 46px; height: 46px; color: var(--forest); position: relative; }
.bv-lock svg { width: 100%; height: 100%; position: relative; z-index: 1; }
.bv-lock .bvl-shackle {
  transform-origin: 8px 11px;
  animation: bvLockShackle 1.5s cubic-bezier(.5,0,.5,1) infinite;
}
.bv-lock .bvl-dot { animation: bvLockDot 1.5s ease-in-out infinite; }
/* Two rings, offset half a cycle apart, expanding outward and fading — a
   sonar ping under the lock rather than the lock sitting on an inert veil.
   Reads as "actively working", the same job the ring spinner does elsewhere,
   just in the vocabulary of this one screen. */
.bv-lock::before, .bv-lock::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid var(--forest);
  animation: bvLockPing 1.8s cubic-bezier(0,.55,.45,1) infinite;
}
.bv-lock::after { animation-delay: .9s; }
@keyframes bvLockShackle {
  0%, 15% { transform: rotate(0deg); }
  50% { transform: rotate(-16deg) translate(-1px, -1px); }
  85%, 100% { transform: rotate(0deg); }
}
@keyframes bvLockDot {
  0%, 40% { opacity: .3; transform: scale(1); }
  55% { opacity: 1; transform: scale(1.35); }
  70%, 100% { opacity: .3; transform: scale(1); }
}
@keyframes bvLockPing {
  0% { transform: scale(.85); opacity: .55; }
  100% { transform: scale(1.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .bv-lock .bvl-shackle, .bv-lock .bvl-dot, .bv-lock::before, .bv-lock::after { animation-duration: 2.6s; }
}

.bv-label {
  font-family: var(--mark); font-weight: 600;
  font-size: 13px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--deep);
}

/* The veil sits inside the panel, which is clipped to a 22px radius by
   .auth-panel — but tell-the-boss veils the card itself, so it has to round
   its own corners or it squares off the card underneath it. */
.bn-send.is-busy-scope > .busy-veil { border-radius: inherit; }

/* Status motion, so it is slowed rather than stopped — see the `.busy-run`
   exemption in the reduced-motion block near the top of this file, which is
   what lets these keep running at all. */
@media (prefers-reduced-motion: reduce) {
  .bv-ring { animation-duration: 1.7s; }
  .busy-veil { animation: none; }
}

/* ==========================================================================
   ============ v29 — footer legibility ====================================
   ========================================================================== */

/* The footer was set as fine print: 13–14px cream at .70–.75 opacity on the
   darkest panel on the site. That is the contrast of a legal disclaimer, and
   it was carrying the site's whole secondary navigation — Platform, Charities,
   Company. Sized and weighted as navigation now: bigger, bolder, and much
   closer to white.

   Opacity carried most of the dimming, so raising it is what does most of the
   work here. Links sit at .92 rather than 1 so hover still has somewhere to
   go, and the lime hover is unchanged. */

.footer-col a {
  font-size: 15.5px;
  font-weight: 600;
  color: var(--white);
  opacity: .92;
}
.footer-col li { margin-bottom: 11px; }

/* Column headings were 13px lime; they are the labels for the groups above, so
   they go up with them but stay a step smaller than the links they head. */
.footer-col h4 {
  font-size: 14px;
  letter-spacing: .1em;
  margin-bottom: 15px;
  opacity: 1;
}

/* "Footy tipping, for good." is the line under the wordmark — the footer's own
   voice, not a link, so it gains size and weight without going full white. */
.footer-brand p {
  font-size: 16px;
  font-weight: 600;
  color: var(--cream-2);
  opacity: .88;
}

/* The bottom row stays deliberately quieter than the navigation above it —
   copyright and a disclaimer are genuinely secondary — but 12px at .6 was
   under the readable floor, so both come up a step. */
.footer-legal { font-size: 13.5px; opacity: .78; }

/* The legal row's own links. Privacy came off the nav bar and landed here, so
   this line has to look like a place to press rather than small print — lime
   on hover, the same tell every other link on the site uses, and a rule under
   it so it does not read as part of the copyright sentence beside it. */
/* The legal row's links, as controls rather than as small print.

   CLIENT, 20 SEP 2026: "those two look small and does not clickable — make
   sure people can see the Terms and Conditions as well as the Privacy Policy."

   They were one word each at the weight of the copyright sentence beside them,
   with a hairline underline doing all the work of saying "press me" — which at
   13px on a dark green ground is not a thing anybody reads as a control. A
   bordered pill is, without needing to be explained, and it is the only shape
   here that cannot be mistaken for a heading. Full names, because "Terms" is
   an abbreviation of a document somebody was asked to agree to and the footer
   is where they come back to read it in full. */
.footer-legal-nav {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 13.5px; font-weight: 700;
}
.footer-legal-nav a {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--cream); text-decoration: none;
  padding: 8px 15px; border-radius: 100px;
  border: 1px solid rgba(232,228,213,.34);
  background: rgba(232,228,213,.05);
  transition: color .15s var(--ease), background .15s var(--ease),
              border-color .15s var(--ease), transform .15s var(--ease);
}
.footer-legal-nav a svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.footer-legal-nav a:hover {
  color: var(--deep); background: var(--lime); border-color: var(--lime);
  transform: translateY(-1px);
}
.footer-legal-nav a:focus-visible {
  outline: 2px solid var(--lime); outline-offset: 2px;
}
.footer-legal-nav span { opacity: .35; }
/* On a phone the row wraps under the copyright; the pills keep their shape
   there rather than stacking into something that reads as another list. */
@media (max-width: 520px) {
  .footer-legal-nav { width: 100%; }
  .footer-legal-nav a { flex: 1 1 auto; justify-content: center; }
}
@media (max-width: 640px) {
  .footer-bottom { flex-direction: column; align-items: flex-start; }
}
.footer-disclaimer { font-size: 13.5px; opacity: .85; font-weight: 600; }

/* ==========================================================================
   ============ v30 — contact form + enquiry inbox ==========================
   ========================================================================== */

/* ---- public: confirmation and errors ---- */
.contact-sent {
  background: rgba(200,241,53,.08);
  border: 1px solid rgba(200,241,53,.34);
  border-radius: 16px; padding: 34px 30px; text-align: center;
}
.contact-sent .cs-tick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 54px; border-radius: 50%; margin-bottom: 16px;
  background: var(--lime); color: var(--deep);
}
.contact-sent .cs-tick svg { width: 26px; height: 26px; fill: currentColor; }
.contact-sent h3 { font-family: var(--display); font-size: 24px; color: var(--white); margin-bottom: 10px; }
.contact-sent p { font-size: 15.5px; line-height: 1.65; color: var(--cream); opacity: .88; }

.contact-err {
  margin: 0 0 16px; padding: 12px 16px; border-radius: 10px;
  background: rgba(214,80,80,.14); border: 1px solid rgba(214,80,80,.4);
  color: #FFD9D9; font-size: 14.5px; font-weight: 600;
}

/* The busy veil on this form sits over a dark panel, so the light veil the
   auth screens use would flash white here. */
.contact-shell .busy-veil { background: rgba(11,35,25,.8); }
.contact-shell .bv-label { color: var(--cream); }
.contact-shell .bv-ring { border-color: rgba(255,255,255,.16); border-top-color: var(--lime); }

/* ---- manage: enquiry inbox ---- */
.enq-tabs { display: flex; gap: 6px; margin: 0 0 18px; flex-wrap: wrap; }
.enq-tabs a {
  padding: 9px 16px; border-radius: 100px; text-decoration: none;
  font-size: 14px; font-weight: 700; color: var(--ink); opacity: .62;
  border: 1px solid rgba(15,46,34,.16);
}
.enq-tabs a:hover { opacity: 1; border-color: rgba(15,46,34,.34); }
.enq-tabs a.on { opacity: 1; background: var(--deep); color: var(--cream); border-color: var(--deep); }
/* On the dark palette a deep-forest pill on a near-black card reads as a
   hole, not as the tab you are on. The accent takes the job over, the same
   way .gts-tab and .gt-chip do. */
html[data-theme="dark"] .enq-tabs a.on { background: var(--lime); color: var(--deep); border-color: var(--lime); }
@media (prefers-color-scheme: dark) {
  html[data-theme="auto"] .enq-tabs a.on, html:not([data-theme]) .enq-tabs a.on {
    background: var(--lime); color: var(--deep); border-color: var(--lime);
  }
}

.enq-snip { display: block; margin-top: 4px; color: var(--muted); font-size: 13px; }
.gt-muted { color: var(--muted); }

.enq-body {
  font-size: 16px; line-height: 1.7; color: var(--ink);
  background: var(--ad-quiet-bg, rgba(15,46,34,.04)); border-left: 3px solid var(--forest);
  border-radius: 0 12px 12px 0; padding: 18px 22px; margin-bottom: 20px;
}
.enq-body.enq-reply { border-left-color: var(--lime); }
.enq-body p:last-child { margin-bottom: 0; }

.enq-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px 24px; }
.enq-meta dt { font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
.enq-meta dd { font-size: 15.5px; font-weight: 600; color: var(--ink); }
.enq-meta a { color: var(--forest); }

.enq-form { margin-top: 4px; }
.enq-label { display: block; font-weight: 700; font-size: 14px; color: var(--ink); margin-bottom: 8px; }
.enq-textarea {
  width: 100%; padding: 16px 18px; border-radius: 12px;
  border: 1.5px solid var(--ad-line-3, rgba(15,46,34,.2)); background: var(--ad-field, #fff);
  font-family: var(--body); font-size: 15.5px; line-height: 1.65; color: var(--ink);
  resize: vertical; outline: none;
}
.enq-textarea:focus { border-color: var(--forest); }
.enq-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 16px; }

.gt-back { margin-bottom: 16px; }
.gt-back a { color: var(--forest); font-weight: 700; text-decoration: none; font-size: 14.5px; }
.gt-back a:hover { text-decoration: underline; }
.gt-empty { padding: 26px 4px; color: var(--muted); font-size: 15.5px; }

/* ==========================================================================
   ============ v31 — contact dropdown ======================================
   ========================================================================== */

/* The "I'm interested in…" field was the last raw native select on the site: an
   OS-drawn popup dropped into the middle of a designed dark panel, with a
   chevron faked from two gradient triangles. gt-ui.js now runs the site's own
   `ddx` dropdown over it, the same component the app and admin already use.

   Two things that needed handling here and nowhere else.

   1. The floating label. `.field label` sits over the control and lifts on
      focus or value, both of which are properties of a native select — and
      after ddx the select is a hidden 1px element with a button beside it, so
      neither ever fires and the label sat across the button's text. gt-ui.js
      marks the field, and the label simply stays lifted: the control below it
      always shows either a choice or a placeholder, so there is nothing for
      the label to get out of the way of.

   2. ddx is styled for the cream app — a white button with forest text. Here
      it sits on the dark contact panel, so it needs the field's own colours or
      it lands like a light-mode widget pasted onto a dark page. */

.field.has-ddx label {
  top: 7px; font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--lime);
}

.contact-shell .ddx-btn {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 25px 16px 10px;          /* room for the lifted label above */
  min-height: 62px;                 /* matches the inputs either side */
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px;
  font-family: var(--body); font-size: 15px; font-weight: 600;
  color: var(--white); text-align: left; cursor: pointer;
  box-shadow: none;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.contact-shell .ddx-btn:hover { border-color: rgba(200,241,53,.5); box-shadow: none; }
.contact-shell .ddx.open .ddx-btn {
  border-color: var(--lime); background: rgba(200,241,53,.05); box-shadow: none;
}
/* Nothing chosen yet: dimmed, because it is a prompt and not an answer. */
.contact-shell .ddx-val.is-placeholder { color: rgba(255,255,255,0.49); font-weight: 500; }
.contact-shell .ddx-chev { color: var(--lime); background: rgba(200,241,53,.12); }
.contact-shell .ddx.open .ddx-chev { background: var(--lime); color: var(--deep); }

.contact-shell .ddx-menu {
  background: #10281D;
  border: 1px solid rgba(200,241,53,.22);
  box-shadow: 0 30px 70px -28px rgba(0,0,0,.85);
}
.contact-shell .ddx-opt { color: var(--cream); }
.contact-shell .ddx-opt:hover { background: rgba(200,241,53,.14); color: var(--lime); }
.contact-shell .ddx-opt.on {
  background: linear-gradient(90deg, rgba(200,241,53,.26), rgba(200,241,53,.08));
  color: var(--white);
}
.contact-shell .ddx-opt.on::after { color: var(--lime); }
.contact-shell .ddx-menu::-webkit-scrollbar-thumb { background: rgba(200,241,53,.3); }

/* The first option carries no text — it is the "nothing picked" state that the
   native select needs, and ddx renders it faithfully as a blank row. A blank
   row in an open menu just looks like a rendering fault, and the lifted label
   is already asking the question, so it is dropped from the list. Interest is
   an optional field; nobody needs a way back to un-answering it. */
.contact-shell .ddx-opt:empty { display: none; }

/* ==========================================================================
   ============ v32 — nav glyphs · account cluster ==========================
   ========================================================================== */

/* ---------- GLYPHS IN BOTH BARS ------------------------------------------
   Every destination now carries an icon beside its label, in both the public
   bar and the member bar. The reason is the same in each: several labels are
   near-neighbours until you have actually read them — News / The Wall / The
   Good List / Charities on the public side, Leaderboard / Ladder on the
   member side — and a glyph separates them at a glance in a way that a bolder
   weight or a wider gap cannot.

   Nothing about the palette moves. The icons take the label's own colour via
   currentColor and sit a step back from it, so the row still reads as one
   line of text with a mark in front of each item rather than as an icon strip
   with captions. Lime stays where it already was: the current page, and hover.

   They are an enhancement to the row, never a change to it. Both bars were
   measured to the pixel — v18 for the member bar, v23/v24 for the public one
   — and a glyph plus its gap is ~22px an item, which is more spare room than
   either has at laptop widths. So below the width where the room exists the
   glyphs drop out and the row is byte-for-byte the layout it was before. The
   drawers keep them, because stacked there is nothing to compete for. */

.nav-links a,
.gt-v2 .an-links a,
.an-menu-btn,
.app-sheet a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.nav-ic,
.an-ic {
  width: 16px; height: 16px;
  flex: none;
  stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  opacity: .62;
  transition: opacity .15s var(--ease), color .15s var(--ease);
}

/* Current page and hover light the glyph lime while the label stays white —
   two signals from one state, and the brighter of the two is the mark rather
   than the word, so the row's typographic colour does not jump around. */
.nav-links a:hover .nav-ic,
.nav-links a:focus-visible .nav-ic,
.nav-links a[aria-current="page"] .nav-ic,
.gt-v2 .an-links a:hover .an-ic,
.gt-v2 .an-links a.on .an-ic,
.an-menu-btn:hover .an-ic,
.an-menu-btn.on .an-ic,
.an-menu-btn[aria-expanded="true"] .an-ic {
  color: var(--lime);
  opacity: 1;
}

/* The two auth actions are buttons, not links, so their glyphs belong to the
   button's own colour at full strength — a 62% mark inside a solid lime pill
   reads as a printing fault. */
.nav-links .nav-login,
.nav-links .btn-nav {
  display: inline-flex; align-items: center; gap: 8px;
}
.nav-links .nav-login .nav-ic { opacity: .9; }
.nav-links .btn-nav .nav-ic { opacity: 1; stroke-width: 2.1; }
.nav-links .nav-login:hover .nav-ic { color: var(--lime); opacity: 1; }

/* The Manage chevron is not one of these: it is a state indicator for the
   panel, sized and rotated by v18, and must keep its own rules. */
.an-menu-btn .an-ic { width: 16px; height: 16px; }

/* ---------- WHERE THE GLYPHS FIT -----------------------------------------
   Public bar: eight links plus two auth actions. Full size starts at 1440px,
   the first width where the 1180px cap plus glyphs clears comfortably. From
   1121–1439px the row is already compacted by v23/v24 and has nothing to
   spare, so the glyphs step out. Below 1121px it is a drawer and they return.
   Member bar: the row itself is gone below 1181px, and 1181–1360px is v18's
   compaction tier, so the same rule applies one tier lower. */
@media (min-width: 1121px) and (max-width: 1439px) {
  .nav-links .nav-ic { display: none; }
}
@media (max-width: 1360px) {
  .gt-v2 .an-links .an-ic,
  .an-menu-btn .an-ic { display: none; }
}

/* Stacked, a row is 17px of label against a whole screen width — the glyph
   can be bigger, and it wants a fixed column so the labels line up down the
   drawer instead of stepping in and out with each icon's width. */
@media (max-width: 1120px) {
  /* The drawer's `li` is a block, so the anchor inside it is the only thing
     that can carry the row. Full width is what makes the hairline rule span
     the panel rather than stopping at the end of each label. */
  .nav-links a { gap: 14px; width: 100%; }
  .nav-links .nav-ic { display: inline-block; width: 20px; height: 20px; opacity: .8; }
  /* v23/v24 make these two `display:block` for centring; flex centres them
     just as well and keeps the glyph on the same line as the label. */
  .nav-links .nav-login,
  .nav-links .btn-nav { display: flex; justify-content: center; }
}

/* ---------- THE MEMBER BAR'S ACCOUNT CLUSTER -----------------------------
   Profile and Log out used to be two unrelated shapes standing side by side:
   a 38px avatar with a lime ring, then a hairline outlined pill. The pill was
   the only outlined control anywhere on that bar, which is what made it look
   unfinished — an outline reads as a placeholder when nothing around it is
   drawn the same way, and next to a filled lime ring it reads as a fainter
   copy of the thing beside it.

   They are one control group now. Both live inside a single raised pill on
   the panel fill the bar already uses for an active item, split by a hairline
   — so the two halves are visibly one thing about you, sitting apart from the
   destinations to their left. No new colour: --gt-deep-2 and lime are both
   already in this bar. */
.gt-v2 .an-right { gap: 12px; }

.gt-v2 .an-account {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: 100px;
  background: var(--gt-deep-2);
  border: 1px solid rgba(255,255,255,.06);
  transition: border-color .15s var(--ease);
}
.gt-v2 .an-account:hover { border-color: rgba(255,255,255,.11); }

.gt-v2 .an-account .an-profile {
  padding: 2px 14px 2px 2px;
  border-radius: 100px;
}
.gt-v2 .an-account .an-profile:hover { background: rgba(255,255,255,.06); }
.gt-v2 .an-account .an-profile .an-avatar { width: 34px; height: 34px; font-size: 14px; }

.gt-v2 .an-acc-sep {
  width: 1px; height: 20px;
  background: rgba(255,255,255,.10);
  flex: none;
}

/* No border of its own — the cluster provides the edge. It is a quiet item at
   rest, because logging out is not something anyone needs help finding, and
   it lights up on hover like every other control on the bar. */
.gt-v2 .an-account .an-logout {
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; background: none;
  border-radius: 100px;
  padding: 9px 15px 9px 13px;
  color: var(--cream); opacity: .72;
  font-size: 13.5px; font-weight: 700; letter-spacing: .005em;
  transition: background .15s var(--ease), color .15s var(--ease), opacity .15s var(--ease);
}
.gt-v2 .an-account .an-logout svg {
  width: 17px; height: 17px; flex: none;
  stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s var(--ease);
}
.gt-v2 .an-account .an-logout:hover {
  background: rgba(200,241,53,.12);
  color: var(--lime);
  opacity: 1;
}
/* The glyph is a door with an arrow leaving through it. On hover the arrow
   takes the step — the whole meaning of the control, in 2px. */
.gt-v2 .an-account .an-logout:hover svg { transform: translateX(-2px); }

/* Narrow: labels go, and both halves become square-ish taps inside the same
   pill. Restated in full because v11's `@media (max-width: 900px)` sets a
   padding on the old bare pill that would otherwise squash this one. */
@media (max-width: 900px) {
  .gt-v2 .an-account { padding: 3px; }
  .gt-v2 .an-account .an-profile { padding: 2px; }
  .gt-v2 .an-account .an-logout { padding: 10px 12px; }
}

/* ---------- THE BURGER'S MISSING BAND ------------------------------------
   .an-links goes at 1180px (v18) but the burger only appeared at 760px, so
   between 761 and 1180 the member bar had no links AND no way to open the
   drawer — every tablet was left with a bar it could not navigate from. The
   burger now appears exactly where the row leaves. */
@media (max-width: 1180px) {
  .an-burger { display: inline-grid; }
}

/* ---------- THE DRAWER ---------------------------------------------------
   Same glyph column as the public drawer, plus a section label for the admin
   block — three destinations that belong together and are not peers of
   Dashboard, exactly as the Manage menu groups them on the bar. */
.app-sheet .sheet-panel { gap: 4px; overflow-y: auto; }
.app-sheet a { gap: 13px; }
.app-sheet .an-ic { width: 19px; height: 19px; opacity: .7; }
.app-sheet a:hover .an-ic { color: var(--lime); opacity: 1; }

.app-sheet .sheet-label {
  font-family: var(--mark);
  font-size: 11px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(232,228,213,0.56);
  padding: 16px 14px 6px;
}

/* Log out is the one item that undoes you, so it is pushed to the foot of the
   panel with a rule above it rather than sitting as the ninth link in a list. */
.app-sheet .sheet-logout {
  margin-top: auto;
  border-top: 1px solid rgba(255,255,255,.09);
  border-radius: 0;
  padding-top: 18px;
}
.app-sheet .sheet-logout:hover { background: rgba(200,241,53,.10); }

/* ==========================================================================
   ============ v33 — the menus on a phone ==================================
   ========================================================================== */

/* Both menus were desktop layouts with a stacking rule bolted on. Neither was
   a panel: the public one was a list of hairline rules hanging off the bar
   with the page still scrolling behind it, and the member one was a 300px
   strip with the links in it and no indication of whose account you were in.

   Below, both become the same thing — a panel that covers what it covers, is
   dismissed the way a panel is dismissed, and puts its glyph column, its
   groups and its exit in the same places the desktop bars put them. The
   palette does not move; these are the same greens and the same lime. */

/* ---------- SCROLL LOCK --------------------------------------------------
   The page behind an open panel holds still. Paired with overscroll-behavior
   on the panels themselves, which is the half that matters on a phone: it
   stops a flick that reaches the end of the panel's own list from handing the
   scroll on to whatever is underneath. The width pin keeps the desktop
   scrollbar's width reserved, so the page does not jump sideways as the lock
   goes on and off. */
body.nav-open,
body.sheet-open {
  overflow: hidden;
  width: 100%;
}

/* ---------- PUBLIC DRAWER ------------------------------------------------ */
@media (max-width: 1120px) {
  /* A scrim, so the drawer sits ON the page rather than being an extension of
     the bar. It is painted by the nav itself — there is no element for it, and
     inventing one would mean the drawer could not be opened without it. */
  /* This can be position:fixed inside a sticky bar only because v24 already
     turns the scrolled bar's own backdrop-filter off while it is open — a
     filtered ancestor would become the containing block and the scrim would
     be pinned to the 56px bar instead of to the viewport. The two rules go
     together; neither is safe to drop on its own. */
  .nav[data-open="true"]::after {
    content: "";
    position: fixed; left: 0; right: 0; bottom: 0; top: 66px;
    background: rgba(6,20,13,.62);
    backdrop-filter: blur(3px);
    z-index: 1;
  }
  .nav.scrolled[data-open="true"]::after { top: 56px; }
  .nav-links { z-index: 2; }

  /* The panel: rounded off at the bottom so it reads as a sheet that has come
     down and stopped, not as a list that has been cut off by the viewport. */
  .nav-links {
    border-bottom: 0;
    border-radius: 0 0 20px 20px;
    padding: 10px clamp(16px, 4vw, 26px) clamp(18px, 4vw, 26px);
    max-height: calc(100vh - 66px);
    max-height: calc(100dvh - 66px);       /* phone chrome moves; dvh follows it */
    overflow-y: auto;
    overscroll-behavior: contain;          /* a flick at the end stays in here */
  }
  /* Restated, because the unconditional max-height above lands after v24's
     scrolled variant and would otherwise leave the panel 10px too tall once
     the bar has shrunk. */
  .nav.scrolled .nav-links { max-height: calc(100dvh - 56px); }

  /* Rows are taps, not text. A row is the full width of the panel, the glyph
     holds a fixed column so the labels line up, and the pressed state is a
     filled pill rather than a colour change — on a phone the finger is on top
     of the label at the moment the feedback is needed. */
  .nav-links a {
    padding: 15px 12px;
    margin: 1px 0;
    border-bottom: 0;
    border-radius: 12px;
    font-size: 16.5px;
    min-height: 52px;
  }
  .nav-links a:active { background: rgba(200,241,53,.14); }
  .nav-links a[aria-current="page"] {
    background: rgba(200,241,53,.10);
    color: var(--lime);
  }
  .nav-links a[aria-current="page"] .nav-ic { color: var(--lime); opacity: 1; }

  /* The hairline between every pair is gone. Eight rules down a panel read as
     a table of data; one rule above the auth block reads as the division that
     is actually there — places to go, then things to do. */
  /* The `> li` selector that makes the drawer's rows full width also catches
     the separator, which is an li too — and 100% plus horizontal margins is
     wider than the panel's content box, which put a horizontal scrollbar
     across the bottom of the menu. It gets its width back explicitly. */
  .nav-sep {
    display: block; height: 1px;
    margin: 12px 6px 6px;
    background: rgba(255,255,255,.10);
  }
  .nav-links > li.nav-sep { width: auto; }
  .nav-auth { margin-top: 0; }
  .nav-links .nav-login { margin-top: 8px; min-height: 52px; }

  /* Sign up free was invisible in this drawer: v24 kills the background on
     every `.nav-links a` so the stacked rows are flat, and that beats
     .btn-nav's own lime by one specificity point — leaving --deep text on the
     --deep panel. It has been unreadable on a phone since v24; the drawer just
     had nothing else in it bold enough to make the gap obvious. Restated here
     rather than in v24 so the flat-row rule stays a single simple statement. */
  .nav-links .btn-nav {
    margin-top: 8px; min-height: 52px;
    background: var(--lime);
    color: var(--deep) !important;
  }
  .nav-links .btn-nav:active { background: #b9e02c; }

  /* Rows arrive in sequence behind the panel's own drop — a short one, since
     this is a menu and not a hero. Reduced motion drops it entirely below. */
  .nav[data-open="true"] .nav-links > li {
    animation: navRowIn .26s var(--ease) backwards;
    animation-delay: calc(var(--r, 0) * 22ms);
  }
  .nav-links > li:nth-child(1) { --r: 1; }
  .nav-links > li:nth-child(2) { --r: 2; }
  .nav-links > li:nth-child(3) { --r: 3; }
  .nav-links > li:nth-child(4) { --r: 4; }
  .nav-links > li:nth-child(5) { --r: 5; }
  .nav-links > li:nth-child(6) { --r: 6; }
  .nav-links > li:nth-child(7) { --r: 7; }
  .nav-links > li:nth-child(8) { --r: 8; }
  .nav-links > li:nth-child(9) { --r: 9; }
  .nav-links > li:nth-child(n+10) { --r: 10; }

  /* The wordmark is the biggest thing on a phone bar and does not need to be.
     A step down gives the row back to the toggle, which is the only control
     up there that anyone is aiming at. */
  .nav-inner { height: 66px; }
  .wordmark { font-size: 24px; }
}

@keyframes navRowIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- MEMBER BAR ON A PHONE ---------------------------------------- */

/* Below 760px the bar is brand, notifications, menu — and nothing else. The
   account cluster comes off it because at that width both its halves lose
   their labels and become two unexplained taps next to a third; all of it is
   in the sheet, under the user's own name, where it can be read. Above 760 the
   row is gone but the width is still a tablet's, so the cluster stays. */
@media (max-width: 760px) {
  .gt-v2 .an-account { display: none; }
  .gt-v2 .app-nav .an-inner { height: 60px; gap: 10px; padding: 0 16px; }
  .gt-v2 .an-brand { font-size: 21px; }
  .gt-v2 .an-right { gap: 4px; margin-left: auto; }
  .an-burger { padding: 9px; border-radius: 10px; }
  .an-burger:active { background: rgba(255,255,255,.08); }
}

/* The charity strip is the one thing on these pages that is pure status. On a
   phone it was a four-part row squeezed into 320px; the label and the bar are
   what matter, so the season/sport tail goes and the rest is given the width. */
@media (max-width: 560px) {
  .gt-v2 .charity-strip .cs-inner { flex-wrap: wrap; gap: 6px 10px; padding: 9px 16px; }
  .gt-v2 .charity-strip .cs-right { display: none; }
  .gt-v2 .charity-strip .cs-bar { flex: 1 0 100%; }
  .gt-v2 .charity-strip .cs-label { font-size: 12px; }
}

/* ---------- MEMBER SHEET ------------------------------------------------- */

.app-sheet { backdrop-filter: blur(3px); }
.app-sheet .sheet-panel {
  width: min(340px, 88vw);
  padding: 0 14px 14px;
  box-shadow: -30px 0 70px -30px rgba(0,0,0,.8);
  overscroll-behavior: contain;
}

/* Header: who you are, which org, and the way out of the panel. This is the
   member bar's account cluster, unpacked — on a phone there is room to say
   the name instead of standing for it with a single letter. */
.app-sheet .sheet-head {
  display: flex; align-items: center; gap: 10px;
  position: sticky; top: 0; z-index: 2;
  margin: 0 -14px 6px;
  padding: 14px;
  background: var(--deep);
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.app-sheet .sheet-me {
  display: flex; align-items: center; gap: 11px;
  flex: 1 1 auto; min-width: 0;
  padding: 4px; border-radius: 100px;
}
.app-sheet .sheet-me .an-avatar { width: 40px; height: 40px; font-size: 16px; flex: none; }
.app-sheet .sheet-me-txt { min-width: 0; line-height: 1.2; }
.app-sheet .sheet-me-txt b {
  display: block; font-size: 15px; font-weight: 700; color: var(--white);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-sheet .sheet-me-txt i {
  display: block; font-style: normal; font-size: 12.5px;
  color: rgba(232,228,213,.55);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-sheet .sheet-x {
  flex: none; width: 40px; height: 40px;
  display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%;
  color: var(--cream); opacity: .7; cursor: pointer;
  transition: background .15s var(--ease), opacity .15s var(--ease);
}
.app-sheet .sheet-x:hover, .app-sheet .sheet-x:active { background: rgba(255,255,255,.09); opacity: 1; }
.app-sheet .sheet-x svg {
  width: 20px; height: 20px;
  stroke: currentColor; fill: none;
  stroke-width: 2; stroke-linecap: round;
}

/* Rows sized for a thumb, same 52px floor as the public drawer. */
.app-sheet a { min-height: 52px; padding: 14px 12px; border-radius: 12px; }
.app-sheet a:active { background: rgba(200,241,53,.14); }

/* Log out keeps its rule and its place at the foot, but not the square corners
   the border gave it — it is still a row in this list, just the last one. */
.app-sheet .sheet-logout {
  border-radius: 12px;
  margin-top: auto;
  padding-top: 14px;
  border-top: 0;
  position: relative;
}
.app-sheet .sheet-logout::before {
  content: "";
  position: absolute; left: 6px; right: 6px; top: -7px;
  height: 1px; background: rgba(255,255,255,.10);
}

@media (prefers-reduced-motion: reduce) {
  .nav[data-open="true"] .nav-links > li { animation: none; }
}

/* ==========================================================================
   ============ v34 — the member bar decides what gives ====================
   ========================================================================== */

/* Adding glyphs to the member row made it wide enough to need a millimetre
   from somewhere, and flex took it from the wrong place: every child was
   shrinkable, so the account cluster was squeezed until "Log out" wrapped onto
   two lines and the pill grew taller than the 68px bar it sits in.

   Only one thing in this row can lose width without losing meaning, and that
   is the org name — it truncates to an ellipsis and stays readable, and the
   full name is on the dashboard, in the sheet header, and in its own title
   attribute. Everything else is a fixed control or a set of labels that must
   not wrap, so everything else is now immovable.

   The arithmetic this guarantees: brand 74 + links 781 (seven items, glyphs
   on, the widest this row ever gets) + right cluster 259 + gaps 66 = 1180
   against a 1300 cap, so the org always has at least 120px and the row can
   never overflow no matter how the org is named. */
.gt-v2 .an-brand,
.gt-v2 .an-links,
.gt-v2 .an-right,
.gt-v2 .an-account { flex: none; }

.gt-v2 .an-org {
  flex: 0 1 auto;
  min-width: 0;                     /* without this a flex item will not shrink
                                       below its content, ellipsis or not */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gt-v2 .an-account .an-profile,
.gt-v2 .an-account .an-logout,
.gt-v2 .an-plabel,
.gt-v2 .an-llabel { white-space: nowrap; }

/* Room, measured rather than hoped for. With glyphs on and a staff account
   showing all seven items, the row needs 1271px of content and the 1300px cap
   leaves 1212px once its 44px gutters are taken out — a 59px deficit, which
   under v34's rules lands entirely on the org name and truncates it to
   nothing. Two things pay it back, and both are things the row is better off
   without:

   The word "Profile" beside the avatar. A ringed avatar in the corner of an
   app bar is already the most legible profile control there is, it carries a
   title, and the phone sheet spells it out in full under the user's own name.
   That is ~51px for a label nobody reads. Log out keeps its word, because a
   lone door glyph is genuinely ambiguous and it is the control that was
   called out as looking unfinished in the first place.

   And a millimetre off each glyph — 15px on a 7px gap inside 11px of padding
   rather than 16/8/13. That is ~42px across seven items and is invisible at
   a glance; the truncated org name was not. Together: ~93px against a 59px
   deficit, so the org keeps its full width with room to spare. */
@media (min-width: 1361px) {
  .gt-v2 .an-plabel { display: none; }
  .gt-v2 .an-account .an-profile { padding: 2px; }
  .gt-v2 .an-links .an-ic,
  .an-menu-btn .an-ic { width: 15px; height: 15px; }
  .gt-v2 .an-links a,
  .an-menu-btn { gap: 7px; padding: 9px 11px; }
}

/* ==========================================================================
   ============ v35 — My Tips: the fixture card gets its room ==============
   ========================================================================== */

/* The card was laid out when it held three things: two clubs, a pick, and a
   result badge. It now holds seven — clubs, pick buttons, "you tipped X", a
   state badge, a timestamp, the kickoff/venue row and, mid-game, a clock and
   score. Every one of those was added into the same 14px of padding and the
   same 6px row gap, so the card reads as one dense block with no grouping:
   the timestamp sits as close to the badge above it as the badge does to the
   line above that, and nothing tells you which lines belong together.

   Nothing here is a rewrite. It is the same three columns and the same state
   row; they are given the space the content now needs, and the gaps are made
   to mean something — tight inside a group, wider between groups. */

.gt-fixtures { gap: 14px; }

.gt-fix {
  padding: 18px 20px;
  /* Column gap up from 14: the state column is right-aligned text running
     back toward the pick buttons, and 14px between two hard edges was not
     enough to read as a gutter. Row gap up from 6 so the match-state row
     below is separated from the pick row by more than a hairline. */
  gap: 14px 22px;
  /* The state column carried three stacked lines in 0.9fr while the pick
     column had 1.15fr for two buttons. "Tipped 5 Aug, 7:39 PM" wrapped in it,
     which is what made the card look broken rather than merely tight. */
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr);
}

/* One rhythm down the state column instead of a 4px gap plus a stray 3px
   margin on the last line. The badge gets a touch more air than the text
   either side of it, because it is the one thing here that is a control-shaped
   object rather than a sentence. */
.gt-fix .gfx-state { gap: 7px; }
.gt-fix .gfx-state .res { margin: 1px 0; }
.gt-fix .gfx-stamp { margin-top: 0; }
.gt-fix .gfx-picked { line-height: 1.45; }

/* The kickoff/venue row is about the match, not the pick, so the rule above it
   is a real division and is spaced like one — even air above and below rather
   than 4px up and 10px down. */
.gt-fix .ms {
  margin-top: 2px;
  padding-top: 14px;
  gap: 8px 12px;
}

/* The clubs row: 12px between a crest-and-name pair and the "vs" disc read as
   the same distance as the gap inside the pair itself. */
.gt-fix .gfx-teams { gap: 16px; }

/* ---------- STACKED ------------------------------------------------------
   Below 900 the three columns become three blocks down the card, and the
   column gap no longer applies — so the row gap is the only thing separating
   a set of pick buttons from the sentence about them. It has to be bigger
   than the gaps inside each block, or the card is one undifferentiated run of
   text, which is exactly how it read. */
@media (max-width: 900px) {
  .gt-fix { gap: 16px; padding: 16px 16px 18px; }
  .gt-fix .gfx-state {
    gap: 6px;
    padding-top: 14px;
    border-top: 1px solid var(--gt-hair);
  }
  /* The state block is now fenced by its own rule, so the match-state row
     underneath does not need a second one directly below it. */
  .gt-fix .ms { border-top: 0; padding-top: 0; margin-top: -2px; }
}

/* Under 560 the two clubs stack and the "vs" disc is hidden, which left two
   club names in a column with nothing to say they are opponents rather than a
   list. A hairline between them says it without costing a row. */
@media (max-width: 560px) {
  .gt-fix .gfx-teams { gap: 0; }
  .gt-fix .gfx-teams > .team-cell:first-child {
    padding-bottom: 11px;
    margin-bottom: 11px;
    border-bottom: 1px solid var(--gt-hair);
  }
}

/* ---------- THE STAT STRIP AND FILTER TABS -------------------------------
   Same complaint one level up: the three stat tiles and the filter row sat
   14px from the list with no more separation than the cards have from each
   other, so the page had no top-level grouping either. */
.gt-strip { gap: 14px; margin-bottom: 22px; }
.gt-filters { margin-bottom: 18px; gap: 8px; }

/* ---------- THE OVERLAP --------------------------------------------------
   The card is three columns wide, but it does not live at page width — it
   sits in the left half of .gt-shell beside the sidebar, so on a 1920px
   screen the card is about 610px and each of its three columns gets ~200px.
   Two club cells and a "vs" disc do not fit in 200px, and the club name is a
   bare text node inside a flex row: an anonymous flex item, which cannot
   shrink below its own min-content and so does not wrap or ellipsis — it
   simply runs out of the cell. That is the overlap. "Essendon" ran into the
   vs disc and "Richmond" ran under the NO TIP pill, on every card.

   Widening the columns cannot fix it, because there is no width to give. The
   fixture is two facts, not three: WHO is playing, and WHAT you did about it.
   So the clubs take a full row of their own — at ~570px they are never short
   — and the pick and its state share the row underneath, which is also the
   order you read the card in. Same DOM, same three blocks; only the grid
   changes. */
.gt-fix {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
}
.gt-fix .gfx-teams { grid-column: 1 / -1; }
.gt-fix .gfx-pick  { grid-column: 1; align-self: center; }
.gt-fix .gfx-state { grid-column: 2; align-self: center; }
.gt-fix .ms        { grid-column: 1 / -1; }

/* Belt and braces for the anonymous-flex-item problem above: if a club name
   ever is longer than the row it now has, it wraps inside its own cell
   instead of leaving it. A name is worth two lines; it is not worth landing
   on the control next to it. */
.gt-fix .team-cell { overflow: hidden; }
/* Guarded: unscoped, this restated the three-up clubs row on top of the ≤560
   rule that stacks them, and phone-width names went back to breaking mid-word
   ("Adelai / Crows"). It only ever meant to hold at the widths where the two
   clubs sit side by side. */
@media (min-width: 561px) {
  .gt-fix .gfx-teams { grid-template-columns: minmax(0,1fr) 34px minmax(0,1fr); }
}

/* The clubs row is now the card's headline, so it gets the size to match and
   a hairline under it — the same division the match-state row already has. */
.gt-fix .team-cell { font-size: 15.5px; line-height: 1.35; }
.gt-fix .gfx-teams {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--gt-hair);
}

/* Stacked, the pick and state go under each other, and .gfx-state's own rule
   from the block above would then double up with the clubs rule. */
@media (max-width: 900px) {
  .gt-fix { grid-template-columns: 1fr; }
  .gt-fix .gfx-pick, .gt-fix .gfx-state { grid-column: 1; }
}
@media (max-width: 560px) {
  /* the two clubs stack, so the pair rule and the per-club rule would stack too */
  .gt-fix .gfx-teams > .team-cell:first-child { border-bottom-color: var(--gt-hair); }
}

/* ==========================================================================
   ============ v36 — the bar's corner is one set of icon buttons ==========
   ========================================================================== */

/* Notifications, profile and log out are now three controls on one rhythm:
   same 40px round target, same hover, same gap. Previously they were a round
   bell, then a raised pill containing a ringed avatar and the word "Profile",
   then a second pill with a door and the words "Log out" — three different
   shapes for three peers, and ~90px of labels in a row that was already
   short of room (which is what was pushing the org name to an ellipsis).

   The words are gone, not lost: both carry title and aria-label, the phone
   sheet still spells them out in full under the user's own name, and an
   avatar and a door at 40px are not glyphs anyone has to decode. */

.gt-v2 .an-right { gap: 8px; align-items: center; }

.gt-v2 .an-iconbtn {
  width: 40px; height: 40px;
  flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: transparent;
  color: var(--cream);
  text-decoration: none;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.gt-v2 .an-iconbtn:hover { background: rgba(255,255,255,.09); }
.gt-v2 .an-iconbtn:focus-visible {
  outline: 2px solid var(--lime); outline-offset: 2px; background: rgba(255,255,255,.09);
}

/* The avatar is the button's face, so it fills it rather than sitting inside
   a second shape. The lime ring is what marks it as yours. */
.gt-v2 .an-profile .an-avatar {
  width: 36px; height: 36px;
  border: 2px solid rgba(200,241,53,.55);
  background: var(--gt-deep-3); color: var(--lime);
  font-size: 14px; overflow: hidden;
  transition: border-color .15s var(--ease);
}
.gt-v2 .an-profile:hover { background: transparent; }
.gt-v2 .an-profile:hover .an-avatar { border-color: var(--lime); }

/* Log out is the only destructive-ish control up here, so it is the only one
   that changes colour rather than just lifting — lime on hover, and the arrow
   steps out of the door. */
.gt-v2 .an-logout {
  border: 0; padding: 0; opacity: .78;
  background: transparent;
}
.gt-v2 .an-logout svg {
  width: 19px; height: 19px;
  stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s var(--ease);
}
.gt-v2 .an-logout:hover {
  background: rgba(200,241,53,.14);
  color: var(--lime);
  opacity: 1;
}
.gt-v2 .an-logout:hover svg { transform: translateX(-2px); }

/* The bell joins the set: it was a 6px-padded square with an 8px radius. */
.gt-v2 .bell-btn {
  width: 40px; height: 40px; padding: 0;
  border-radius: 50%; opacity: .78;
}
.gt-v2 .bell-btn:hover { background: rgba(255,255,255,.09); opacity: 1; }
.gt-v2 .bell-btn svg { width: 19px; height: 19px; }
.gt-v2 .bell-badge { top: 2px; right: 2px; }

/* With the labels gone the row has ~90px back, so the glyph metrics that v34
   shaved to buy that room are no longer needed — the links go back to their
   full size and the org name keeps its width. */
@media (min-width: 1361px) {
  .gt-v2 .an-links .an-ic,
  .an-menu-btn .an-ic { width: 16px; height: 16px; }
  .gt-v2 .an-links a,
  .an-menu-btn { gap: 8px; padding: 9px 13px; }
}

/* ==========================================================================
   ============ v37 — the wizard's dropdowns are clickable =================
   ========================================================================== */

/* Organisation type and State/territory did nothing when you clicked them, and
   the reason is a two-part miss rather than a broken control.

   The wizard field is a flex row — a 46px icon cell, then the control — and
   the rule that makes the control fill the rest names `> input, > select,
   > textarea`. It was written when the select was still a native select. But
   gt-ui.js progressively replaces every select in .app-main with a .ddx
   widget and moves the real select inside it, so the flex child is a .ddx,
   which matches none of those three, gets no flex, and shrink-wraps to its
   own content: a 302px button sitting in a 968px field.

   The other 666px — 68% of what looks like the control — is the wrapper. A
   click there lands on .fld-ic and nothing happens, which is exactly the
   report. The menu itself was fine all along; it opened correctly every time
   it was actually reached.

   So .ddx joins the three element names as a thing that fills the field, and
   drops its own frame while it is in there — the wrapper draws the border,
   the background and the focus ring, and a bordered button inside a bordered
   field was a box in a box. */

.wiz-form .fld-ic > .ddx { flex: 1 1 auto; min-width: 0; }

.wiz-form .fld-ic > .ddx .ddx-btn {
  width: 100%;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  /* matches the padding the inputs beside it get, so a text field and a
     dropdown in the same column have their text on the same line */
  padding: 15px 16px;
  font-size: 15px;
}
.wiz-form .fld-ic > .ddx .ddx-btn:hover { background: rgba(15,46,34,.02); box-shadow: none; }
.wiz-form .fld-ic > .ddx.open .ddx-btn { box-shadow: none; }

/* Focus and open state belong to the wrapper, the same way :focus-within
   already handles the text inputs. */
.wiz-form .fld-ic:has(> .ddx.open) {
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(45,122,58,.18);
}
.wiz-form .fld-ic:has(> .ddx.open) > .fi-mark { color: var(--forest); }

/* The menu is positioned against .ddx, which now spans the field, so it drops
   the full width of the control instead of a third of it. It also has to
   escape the wrapper: .wiz-form .fld-ic sets overflow:hidden to clip the icon
   cell against the rounded corner, and that was clipping the open menu too. */
.wiz-form .fld-ic { overflow: visible; }
.wiz-form .fld-ic > .fi-mark { border-radius: 10px 0 0 10px; }
.wiz-form .fld-ic > .ddx .ddx-menu { top: calc(100% + 10px); }

/* Nothing in the wrapper should swallow a click meant for the control. */
.wiz-form .fld-ic > .fi-mark,
.wiz-form .fld-ic::after { pointer-events: none; }

/* ==========================================================================
   ============ v38 — the department directory ============================
   ========================================================================== */

/* A directory, not a table. Someone arriving here is scanning for one name
   they already know ("where's IT?"), so the layout is a grid of cards sized to
   be swept rather than read row by row, and the name is the biggest thing in
   each one. */

.dept-search { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

.dept-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 14px;
}

.dept-card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 18px;
  background: var(--gt-card);
  border: 1px solid var(--gt-hair);
  border-left: 4px solid var(--gt-hair);
  border-radius: var(--gt-r-sm);
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.dept-card:hover { border-left-color: var(--forest); box-shadow: 0 16px 34px -26px rgba(15,46,34,.5); }

/* A pending department is visibly not one of the real ones yet: it is in the
   list only for the people who can act on it, and it should not read as a
   destination while it waits. */
.dept-card.is-pending {
  border-left-color: #C79A2B;
  background: linear-gradient(90deg, rgba(199,154,43,.09), var(--gt-card) 60%);
}

.dept-card .dc-top { display: flex; align-items: center; gap: 12px; min-width: 0; }
/* The group's mark. This rule was written for `.dc-mark`, which no template
   has ever rendered — groups.html writes `.dc-ic`. So it matched nothing, the
   bare <svg> inside had no width or height to inherit, and it grew to fill the
   flex row: a 184px invisible block that squeezed the group's name and member
   count into an 81px gutter. That, and not the styling, is why the card looked
   broken. Named for the class that actually exists now.

   flex:none and an explicit size on BOTH the tile and the svg, because an
   unsized inline <svg> in a flex row is exactly how this happened. */
.dept-card .dc-ic {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: 12px;
  /* Tinted from the group's own tone (see .tone-* above), falling back to the
     house green for a group whose kind matched no glyph. */
  background: color-mix(in srgb, var(--tone-a, #2D7A3A) 15%, transparent);
  color: var(--tone-a, var(--forest));
}
.dept-card .dc-ic svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.dept-card .dc-name { min-width: 0; line-height: 1.25; }
.dept-card .dc-name b {
  display: block; font-size: 16px; font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dept-card .dc-name i {
  display: block; font-style: normal; font-size: 12.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.dept-card .dc-meta {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  /* A shade darker than --muted (#6B7A6E), which lands at 4.11:1 on this
     card's cream and misses the 4.5:1 floor for text this size. Same sage
     family, just enough of it to be read rather than guessed at. */
  font-size: 12.5px; color: #566356;
}
.dept-card .dc-meta > span { display: inline-flex; align-items: center; gap: 6px; }
.dept-card .dc-meta svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 1.9; }
.dept-card .dc-flag {
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 100px;
  background: rgba(199,154,43,.18); color: #7a5c10;
}

/* The action sits on its own line at the foot so every card's button lands on
   the same baseline regardless of how long the name above it ran. */
.dept-card .dc-actions { margin-top: auto; padding-top: 2px; }
.dept-card .dc-note { font-size: 12.5px; color: var(--muted); font-style: italic; }

.dept-form { max-width: 560px; }
.dept-form .fld { margin-bottom: 14px; }

@media (max-width: 560px) {
  .dept-grid { grid-template-columns: 1fr; }
  .dept-search .abtn { flex: 1; justify-content: center; }
}

/* ==========================================================================
   ============ v39 — the work-email check =================================
   ========================================================================== */

/* The code field. Six digits read back off a phone, so they get the treatment
   the email gives them: monospaced, large enough that a transposed pair is
   obvious before it is submitted, and — six digits, six boxes — each one its
   own slot rather than a single field holding all of them at once. */
.otp-row { display: flex; gap: 10px; justify-content: center; }
/* `.wiz-form .fld input[type="text"]` (0,3,1) sets width:100% on every text
   input in the wizard, and a plain `.otp-box` class (0,2,0) loses that fight
   — six boxes each computing to 100% width, which is the same trap the
   icon-field comment above documents. `.wiz-form .fld .otp-row .otp-box`
   (0,4,0) out-specifies it instead of trying to win on `!important`. */
.wiz-form .fld .otp-row .otp-box {
  flex: none;
  width: 46px;
  height: 56px;
  font-family: var(--score), 'Courier New', monospace;
  font-size: 24px !important;
  font-weight: 800;
  text-align: center;
  font-variant-numeric: tabular-nums;
  padding: 0 !important;
  background: rgba(45,122,58,.04);
  border-width: 1.5px !important;
  border-radius: 10px;
}
.wiz-form .fld .otp-row .otp-box:focus { background: #fff; border-color: var(--lime); box-shadow: 0 0 0 3px rgba(200,241,53,.22); }

.verify-sent p { margin: 0 0 16px; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.verify-sent b { color: var(--ink); }

.verify-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
/* A disabled resend is a countdown, not a dead control: it says when it will
   work, so it must not look like something that is broken. */
.verify-actions .abtn[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }

.verify-done {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; border-radius: var(--gt-r-sm);
  background: rgba(45,122,58,.09);
  border: 1px solid rgba(45,122,58,.28);
  animation: vdArrive .5s var(--ease) both;
}
/* The payoff after the lock veil: a correct code lands here on the next
   page. One entrance, not a loop — this is a moment, not a status. */
@keyframes vdArrive {
  from { opacity: 0; transform: translateY(7px) scale(.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.verify-done .vd-tick {
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--forest); color: #fff;
  animation: vdTickRing .9s var(--ease) .2s both;
}
.verify-done .vd-tick svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2.6; }
@keyframes vdTickRing {
  0% { box-shadow: 0 0 0 0 rgba(45,122,58,.5); }
  65% { box-shadow: 0 0 0 9px rgba(45,122,58,0); }
  100% { box-shadow: 0 0 0 0 rgba(45,122,58,0); }
}
.verify-done b { display: block; font-size: 16px; color: var(--ink); }
.verify-done i { display: block; font-style: normal; font-size: 13px; color: var(--muted); margin-top: 2px; }

@media (max-width: 560px) {
  .verify-actions .abtn { flex: 1 1 100%; justify-content: center; }
}

/* ==========================================================================
   ============ v40 — ghost buttons on the dark sheet ======================
   ========================================================================== */

/* .abtn-ghost is drawn for a cream card: dark ink, hairline border. The
   dashboard and every other sheet-page paints a dark green gradient behind
   it, so those buttons were dark-on-dark with a border nobody could see.
   "Departments" and "Find a group" were invisible; the lime primary beside
   them was fine, which is exactly why it read as two missing buttons rather
   than a broken row.
   The wizard footer already solved this for itself (v-wiz), which left the
   same fix missing everywhere else the sheet is used. This generalises it. */

.sheet-page .page-sheet .abtn-ghost {
  color: var(--cream);
  border-color: rgba(255,255,255,.28);
  background: rgba(255,255,255,.04);
}
.sheet-page .page-sheet .abtn-ghost:hover {
  color: #fff;
  border-color: var(--lime);
  background: rgba(200,241,53,.12);
}
.sheet-page .page-sheet .abtn-ghost svg { stroke: currentColor; }

/* The same problem for the small ghost buttons inside cards on the sheet
   (Remove admin, Make admin, Decline). Those sit on .card, which IS cream,
   so they must keep the dark treatment — scoped to direct sheet children
   rather than everything inside it. */
.sheet-page .page-sheet .card .abtn-ghost {
  color: var(--ink);
  border-color: var(--app-line);
  background: transparent;
}
.sheet-page .page-sheet .card .abtn-ghost:hover {
  color: var(--forest);
  border-color: var(--forest);
  background: transparent;
}

/* Labels and plain text directly on the sheet need the same consideration:
   .dc-lab is --muted, tuned for cream. */
.sheet-page .page-sheet .dash-ctl .dc-lab { color: rgba(232,228,213,.6); }

/* The field that caused a verification error is marked, not just annotated:
   the message underneath is easy to miss on a form of three near-identical
   boxes, and the red edge is what draws the eye to the right one. */
.wiz-form .fld.has-err .fld-ic {
  border-color: #C0392B;
  box-shadow: 0 0 0 3px rgba(192,57,43,.14);
}
.wiz-form .fld.has-err .fld-ic > .fi-mark { color: #C0392B; }

/* ==========================================================================
   ============ v41 — icons with some life in them =========================
   ========================================================================== */

/* The glyph set is a single-weight line drawing, which is fine at 16px in a
   nav row and thin and lifeless anywhere it has to carry a block of content.
   Two changes, no new artwork:

   1. Weight. 1.8–1.9 stroke on a 20px glyph reads as a wireframe. Content
      glyphs go to 2.0–2.2, which is the difference between a diagram and an
      icon.
   2. A ground to stand on. A glyph floating in text has nothing holding it;
      the same glyph in a tinted rounded tile reads as an object. That is the
      treatment in the department mockup and it is what gives these presence
      without commissioning a filled set. */

/* ---- the tile ---- */
.gt-ic-tile {
  width: 42px; height: 42px; flex: none;
  display: grid; place-items: center;
  border-radius: 13px;
  background: linear-gradient(150deg, rgba(200,241,53,.22), rgba(45,122,58,.14));
  border: 1px solid rgba(45,122,58,.20);
  color: var(--forest);
  transition: background .18s var(--ease), border-color .18s var(--ease), transform .18s var(--ease);
}
.gt-ic-tile svg {
  width: 21px; height: 21px;
  stroke: currentColor; fill: none;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.gt-ic-tile.sm { width: 34px; height: 34px; border-radius: 11px; }
.gt-ic-tile.sm svg { width: 17px; height: 17px; }
.gt-ic-tile.lg { width: 52px; height: 52px; border-radius: 16px; }
.gt-ic-tile.lg svg { width: 26px; height: 26px; }

/* On the dark sheet the cream-card tint disappears, so it gets its own. */
.sheet-page .page-sheet > .gt-ic-tile,
.page-deep .gt-ic-tile.on-dark {
  background: rgba(200,241,53,.14);
  border-color: rgba(200,241,53,.30);
  color: var(--lime);
}

/* A tile inside something clickable lifts with it. */
a:hover > .gt-ic-tile,
.dept-card:hover .gt-ic-tile,
.gt-scard:hover .gt-ic-tile {
  background: linear-gradient(150deg, rgba(200,241,53,.34), rgba(45,122,58,.20));
  border-color: rgba(45,122,58,.34);
}

/* ---- existing glyphs, given weight ----------------------------------------
   Scoped to content areas. The nav is deliberately excluded: at 15–16px a
   heavier stroke closes up the counters and the row starts to look shouty. */
.page-sheet .card svg:not(.gt-ic-tile svg):not(.ddx-chev),
.gt-scard svg,
.gt-empty .ge-ic svg,
.wc-ic svg {
  stroke-width: 2;
}

/* Section heads inside cards: the glyph was the same size and weight as the
   body copy beside it, so it read as punctuation rather than a marker. */
.gt-scard .gsc-head svg { width: 18px; height: 18px; stroke-width: 2.2; color: var(--forest); }
.gt-empty .ge-ic {
  width: 54px; height: 54px; border-radius: 16px;
  background: linear-gradient(150deg, rgba(200,241,53,.20), rgba(45,122,58,.12));
  border: 1px solid rgba(45,122,58,.18);
  color: var(--forest);
  display: grid; place-items: center;
}
.gt-empty .ge-ic svg { width: 26px; height: 26px; stroke-width: 2; }

/* ==========================================================================
   ============ v42 — departments to the mockup · Manage menu ==============
   ========================================================================== */

/* ---- the directory grid ---- */
/* Four across on a wide sheet, per the design. minmax with auto-fit rather
   than a fixed four, so it degrades to 3/2/1 on the way down instead of
   forcing a horizontal scroll. */
.dept-grid { grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 16px; }

.dept-card {
  gap: 14px;
  padding: 18px 18px 20px;
  background: #fff;
  border: 1px solid var(--gt-hair);
  border-left: 1px solid var(--gt-hair);   /* the mockup has no accent stripe */
  border-radius: 16px;
  box-shadow: 0 2px 4px -2px rgba(15,46,34,.06);
}
.dept-card:hover {
  border-color: rgba(45,122,58,.4);
  box-shadow: 0 18px 38px -26px rgba(15,46,34,.45);
  transform: translateY(-2px);
}
.dept-card .dc-top { gap: 13px; align-items: flex-start; }
.dept-card .dc-name b { font-size: 16.5px; white-space: normal; line-height: 1.25; }
.dept-card .dc-meta { font-size: 12.5px; }

/* The wall button is the point of the card, so it is the solid lime one and
   it spans the card rather than hugging its label. */
.dept-card .dc-actions .abtn { width: 100%; justify-content: center; }
.dept-card .dc-actions .abtn-primary { font-size: 13.5px; padding: 11px 14px; }

/* A pending department keeps its amber edge — it is genuinely a different
   state and the mockup has no equivalent to borrow from. */
.dept-card.is-pending { border-left: 4px solid #C79A2B; }

/* ---- search ---- */
/* One rounded field with the button beside it, on its own card, per the
   design. The input carries the magnifier inside it rather than outside. */
.dept-search .fld-ic {
  background: #F7F6F0;
  border: 1px solid var(--gt-hair);
  border-radius: 12px;
  display: flex; align-items: stretch; overflow: hidden;
}
.dept-search .fld-ic > .fi-mark {
  width: 44px; flex: none; display: grid; place-items: center;
  color: var(--muted); background: transparent; border: 0;
}
.dept-search .fld-ic > .fi-mark svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.dept-search input[type="search"] {
  flex: 1 1 auto; min-width: 0;
  border: 0 !important; background: transparent !important;
  padding: 14px 16px 14px 0 !important; font-size: 15px;
  box-shadow: none !important; outline: none;
}
.dept-search .fld-ic:focus-within { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.16); }

/* ---- create form ---- */
.dept-form { max-width: none; }
.dept-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 6px 24px;
  margin-bottom: 18px;
}
.fld-opt {
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-left: 6px;
  padding: 2px 7px; border-radius: 100px; background: rgba(15,46,34,.06);
}

/* The departments page is a sheet page, so its cards are cream on dark. The
   create panel is the one place a form sits directly on a card here, and its
   inputs need the same frame the wizard gives them. */
.dept-form .fld-ic {
  display: flex; align-items: stretch; position: relative;
  background: #fff; border: 1px solid rgba(15,46,34,.15); border-radius: 11px;
  overflow: visible;
}
.dept-form .fld-ic > .fi-mark {
  position: static; transform: none;
  width: 44px; height: auto; flex: none;
  display: grid; place-items: center;
  border-right: 1px solid rgba(15,46,34,.10);
  background: rgba(15,46,34,.025);
  color: rgba(15,46,34,0.67);
  border-radius: 10px 0 0 10px;
  pointer-events: none;
}
.dept-form .fld-ic > .fi-mark svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.dept-form .fld-ic > input,
.dept-form .fld-ic > select {
  flex: 1 1 auto; min-width: 0;
  border: 0 !important; border-radius: 0 !important;
  background: transparent !important;
  padding: 14px 16px !important;
  box-shadow: none !important; outline: none;
}
/* Same fix the wizard needed: the ddx widget replaces the select and must
   fill the field, or two thirds of the control does nothing when clicked. */
.dept-form .fld-ic > .ddx { flex: 1 1 auto; min-width: 0; }
.dept-form .fld-ic > .ddx .ddx-btn {
  width: 100%; border: 0; border-radius: 0; background: transparent;
  box-shadow: none; padding: 14px 16px; font-size: 15px;
}
.dept-form .fld-ic:has(> .ddx.open),
.dept-form .fld-ic:focus-within {
  border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.16);
}
.dept-form .hint { margin-top: 6px; }

/* ---- the Manage dropdown ------------------------------------------------
   It was a flat dark panel with two lines of text per row. Given the same
   glyph tiles as everything else, the rows become scannable and the panel
   stops looking like a context menu from another product. */
.an-menu-panel {
  min-width: 330px; padding: 8px;
  border-radius: 16px;
  background: linear-gradient(165deg, #14392655, var(--gt-deep-2));
  border: 1px solid rgba(200,241,53,.16);
  box-shadow: 0 34px 70px -28px rgba(0,0,0,.8);
  backdrop-filter: blur(10px);
}
.an-menu-panel a {
  flex-direction: row; align-items: center; gap: 12px;
  padding: 11px 12px; border-radius: 11px;
}
.an-menu-panel a .mi-ic {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center; border-radius: 10px;
  background: rgba(200,241,53,.12);
  border: 1px solid rgba(200,241,53,.2);
  color: var(--lime);
}
.an-menu-panel a .mi-ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; }
.an-menu-panel a .mi-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.an-menu-panel a:hover { background: rgba(200,241,53,.14); }
.an-menu-panel a:hover .mi-ic { background: rgba(200,241,53,.24); border-color: rgba(200,241,53,.45); }
.an-menu-panel a b { font-size: 14.5px; color: #fff; }
.an-menu-panel a i { font-size: 12px; line-height: 1.35; }

/* The base .fld-ic .fi-mark is position:absolute, so making the wrapper a
   flex row is not enough — the icon stayed pinned over the input and landed
   on top of the placeholder ("Sear(o)h departments"). It has to be returned
   to the flow to become a flex item. Same reset the wizard field needed. */
.dept-search .fld-ic > .fi-mark,
.dept-form .fld-ic > .fi-mark {
  position: static;
  transform: none;
  height: auto;
}

/* ==========================================================================
   ============ v43 — photography in the app ===============================
   ========================================================================== */

/* The client's match-day shots were doing one job: side strips behind the
   page sheet, at the very edge of vision. That is decoration. These give them
   somewhere to actually be seen, on the screens where a photograph says
   something the copy cannot — the moment an organisation goes live, the
   screen where you invite your team, and the empty states that are otherwise
   a sentence in a box. */

.photo-banner {
  position: relative;
  min-height: 208px;
  border-radius: 20px;
  overflow: hidden;
  display: flex; align-items: flex-end;
  margin-bottom: 22px;
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 30px 60px -34px rgba(0,0,0,.7);
}
.photo-banner.is-tall { min-height: 300px; }

.photo-banner .pb-img {
  position: absolute; inset: 0;
  background-size: cover;
  /* Framed low: these are phone shots where the subject sits below centre,
     and a plain `center` crop cuts the heads off at banner ratios. */
  background-position: center 32%;
  transform: scale(1.02);      /* hides any edge softness from the resize */
}
.photo-banner .pb-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(7,22,15,.18) 0%, rgba(7,22,15,.62) 58%, rgba(7,22,15,.92) 100%),
    linear-gradient(90deg, rgba(7,22,15,.55) 0%, rgba(7,22,15,0) 62%);
}
.photo-banner .pb-body {
  position: relative; z-index: 1;
  padding: clamp(20px, 3vw, 32px);
  max-width: 640px;
}
.photo-banner.is-centred { align-items: center; justify-content: center; text-align: center; }
.photo-banner.is-centred .pb-body { max-width: 620px; }

.photo-banner .pb-eyebrow {
  font-family: var(--mark); font-size: 11.5px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--lime); margin-bottom: 8px;
}
.photo-banner .pb-head {
  margin: 0; color: #fff;
  font-family: var(--display, var(--mark)); font-weight: 800;
  font-size: clamp(24px, 3.4vw, 34px); line-height: 1.1; letter-spacing: -.01em;
}
.photo-banner .pb-lead {
  margin: 10px 0 0; color: rgba(255,255,255,.82);
  font-size: 15.5px; line-height: 1.55;
}
.photo-banner .pb-slot { margin-top: 18px; display: flex; gap: 10px; flex-wrap: wrap; }
.photo-banner.is-centred .pb-slot { justify-content: center; }

@media (max-width: 560px) {
  .photo-banner { min-height: 180px; border-radius: 16px; }
  .photo-banner.is-tall { min-height: 230px; }
  /* On a narrow screen the horizontal half of the scrim does nothing but
     darken the whole frame, so it goes and the vertical one carries it. */
  .photo-banner .pb-scrim {
    background: linear-gradient(180deg, rgba(7,22,15,.25) 0%, rgba(7,22,15,.7) 55%, rgba(7,22,15,.94) 100%);
  }
}

/* ---- empty states get a photograph too ----------------------------------
   A department directory with nothing in it, or a wall with no posts, was a
   sentence in a bordered box. Same component, shorter, with the message on
   it — so the first thing anyone sees on a brand new org still looks like
   the product rather than an error. */
.gt-empty.has-photo {
  position: relative; overflow: hidden;
  border: 0; border-radius: 18px;
  min-height: 200px; padding: 0;
  display: flex; align-items: flex-end;
}
.gt-empty.has-photo .ge-photo {
  position: absolute; inset: 0;
  background-size: cover; background-position: center 38%;
}
.gt-empty.has-photo .ge-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(7,22,15,.2), rgba(7,22,15,.88));
}
.gt-empty.has-photo > div { position: relative; z-index: 1; padding: 24px; }
.gt-empty.has-photo h3 { color: #fff; }
.gt-empty.has-photo p { color: rgba(255,255,255,.78); }
.gt-empty.has-photo a { color: var(--lime); }

/* ==========================================================================
   ============ v44 — filled icons, with colour ============================
   ========================================================================== */

/* The stroke set is a line drawing. Correct at 16px in a nav row, and lifeless
   as the subject of a card — which is the note: the icons should look like
   icons, the way an app icon does, not like a wireframe.
   The filled set (ic-f-*) is solid silhouettes with no stroke, knocked out in
   white on a coloured tile. Colour is per department type, so the directory is
   scannable before a word of it is read. */

.gt-ic-tile.filled {
  border: 0;
  background: linear-gradient(150deg, var(--tone-a), var(--tone-b));
  color: #fff;
  box-shadow: 0 6px 14px -6px var(--tone-shadow, rgba(15,46,34,.5));
}
.gt-ic-tile.filled svg {
  /* the whole point: painted, not outlined */
  fill: currentColor;
  stroke: none;
  width: 22px; height: 22px;
}
.gt-ic-tile.filled.sm svg { width: 18px; height: 18px; }
.gt-ic-tile.filled.lg svg { width: 27px; height: 27px; }

/* Tones. Each is a two-stop gradient rather than a flat fill, because a flat
   square of colour reads as a swatch and a gradient reads as an object. Kept
   deep enough that white knocks out cleanly on every one. */
.tone-green  { --tone-a:#2D7A3A; --tone-b:#1C5628; --tone-shadow:rgba(45,122,58,.45); }
.tone-blue   { --tone-a:#2F6FB5; --tone-b:#1E4C81; --tone-shadow:rgba(47,111,181,.45); }
.tone-teal   { --tone-a:#1F8A8A; --tone-b:#14625F; --tone-shadow:rgba(31,138,138,.45); }
.tone-gold   { --tone-a:#C79A2B; --tone-b:#94701A; --tone-shadow:rgba(199,154,43,.45); }
.tone-violet { --tone-a:#6B4EA8; --tone-b:#4A3579; --tone-shadow:rgba(107,78,168,.45); }
.tone-pink   { --tone-a:#B84A72; --tone-b:#8A3252; --tone-shadow:rgba(184,74,114,.45); }
.tone-slate  { --tone-a:#4A5A63; --tone-b:#313E45; --tone-shadow:rgba(74,90,99,.45); }
.tone-amber  { --tone-a:#C2782B; --tone-b:#8F551A; --tone-shadow:rgba(194,120,43,.45); }
.tone-navy   { --tone-a:#3B5480; --tone-b:#27385A; --tone-shadow:rgba(59,84,128,.45); }
.tone-sky    { --tone-a:#2E7FA8; --tone-b:#1D5A7A; --tone-shadow:rgba(46,127,168,.45); }

/* The Manage menu rows get the same treatment: solid glyph on lime, which is
   the one place the brand colour belongs at this size. */
.an-menu-panel a .mi-ic {
  background: linear-gradient(150deg, var(--lime), #A8CE22);
  border: 0;
  color: var(--deep);
}
.an-menu-panel a .mi-ic svg { fill: currentColor; stroke: none; width: 18px; height: 18px; }
.an-menu-panel a:hover .mi-ic { background: linear-gradient(150deg, #D8FF4A, var(--lime)); }

/* Empty-state mark, same idea. */
.gt-empty .ge-ic { background: linear-gradient(150deg, var(--lime), #A8CE22); border: 0; color: var(--deep); }
.gt-empty .ge-ic svg { fill: currentColor; stroke: none; }

/* ==========================================================================
   ============ v45 — video backgrounds ====================================
   ========================================================================== */

/* The client's clips are silent match footage, so they are backgrounds, not
   things anyone presses play on. That decides everything here: muted, looping,
   inline, no controls, and a poster frame that is what a visitor actually sees
   until the file has buffered.

   The poster is not a nicety. It is the whole of what gets shown to anyone on
   reduced motion, on a data saver, or on a browser that refuses autoplay —
   which is most mobile browsers unless the video is muted. So the poster is
   set on the element AND painted as a CSS background behind it, and the video
   simply fades in on top once it can play. Nothing ever renders empty. */

.photo-banner .pb-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 32%;
  opacity: 0;
  transition: opacity .6s var(--ease);
}
.photo-banner .pb-video.ready { opacity: 1; }

/* Reduced motion: the video never plays and never loads. The poster is the
   design, not a fallback — a looping background is exactly the kind of
   unrequested movement the setting exists to stop. */
@media (prefers-reduced-motion: reduce) {
  .photo-banner .pb-video { display: none !important; }
}

/* ==========================================================================
   ============ v46 — nav glyphs, filled ===================================
   ========================================================================== */

/* Both bars move to the filled set. The earlier note was that a heavier
   stroke at 15px closes up the counters — true, and the answer turned out not
   to be a heavier stroke but no stroke at all. A solid silhouette holds its
   shape at small sizes far better than an outline does, which is exactly why
   every OS tab bar is drawn this way.
   Colour stays where it was: the glyph carries the label's own colour and
   lifts to lime on hover and on the current page. Nothing about the palette
   moves; the shapes just stop looking like wireframes. */
.nav-ic, .an-ic {
  fill: currentColor;
  stroke: none;
  opacity: .72;
}
.nav-links a:hover .nav-ic,
.nav-links a:focus-visible .nav-ic,
.nav-links a[aria-current="page"] .nav-ic,
.gt-v2 .an-links a:hover .an-ic,
.gt-v2 .an-links a.on .an-ic,
.an-menu-btn:hover .an-ic,
.an-menu-btn.on .an-ic,
.an-menu-btn[aria-expanded="true"] .an-ic { opacity: 1; color: var(--lime); }

/* The lime sign-up pill: a filled glyph on lime needs the deep green, and at
   full opacity or it muddies. */
.nav-links .btn-nav .nav-ic { opacity: 1; }
.nav-links .nav-login .nav-ic { opacity: .9; }

/* ==========================================================================
   ============ v47 — the create-department modal ==========================
   ========================================================================== */

/* Two steps in one panel. The steps do not fade in and out on top of each
   other — they slide, so the panel reads as one surface moving rather than
   two screens swapping. Height animates with it, because a panel that snaps
   from tall to short is the thing that makes a modal feel cheap. */

.dept-cta {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  margin-top: 22px; padding: 22px 24px;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: 18px;
}
.dept-cta .abtn svg { width: 18px; height: 18px; fill: currentColor; stroke: none; }

.dept-modal { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 20px; }
.dept-modal[hidden] { display: none; }
.dept-modal .dm-scrim {
  position: absolute; inset: 0;
  background: rgba(6,20,13,.72); backdrop-filter: blur(4px);
  opacity: 0; transition: opacity .22s var(--ease);
}
.dept-modal.is-open .dm-scrim { opacity: 1; }

.dept-modal .dm-panel {
  position: relative; z-index: 1;
  width: min(560px, 100%);
  max-height: min(88vh, 760px);
  overflow: hidden auto;
  background: var(--gt-card);
  border-radius: 22px;
  border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 50px 100px -34px rgba(0,0,0,.75);
  padding: 30px 30px 26px;
  transform: translateY(14px) scale(.985);
  opacity: 0;
  transition: transform .26s cubic-bezier(.2,.9,.3,1.05), opacity .2s var(--ease);
}
.dept-modal.is-open .dm-panel { transform: none; opacity: 1; }

body.dept-modal-open { overflow: hidden; }

.dm-x {
  position: absolute; top: 16px; right: 16px;
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: rgba(15,46,34,.06);
  color: var(--muted); cursor: pointer; transition: .15s var(--ease);
}
.dm-x:hover { background: rgba(15,46,34,.12); color: var(--ink); }
.dm-x svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; }

/* Progress: two dots and a line that fills. Deliberately not a bar with a
   percentage — two steps is not a journey and a 50% bar overstates it. */
.dm-rail { display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
.dm-dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(15,46,34,.18); transition: background .25s var(--ease); }
.dm-dot.on { background: var(--forest); }
.dm-line { flex: 1; height: 2px; background: rgba(15,46,34,.12); border-radius: 2px; max-width: 46px; }

/* The slide. Steps stack in the same grid cell so the panel keeps one height
   context, and the inactive one is taken out of the tab order. */
.dm-form { display: grid; }
.dm-step {
  grid-area: 1 / 1;
  opacity: 0; transform: translateX(26px);
  transition: opacity .24s var(--ease), transform .3s cubic-bezier(.2,.85,.3,1);
  pointer-events: none; visibility: hidden;
}
.dm-step.is-active { opacity: 1; transform: none; pointer-events: auto; visibility: visible; }
.dm-panel[data-step="1"] .dm-step[data-step="2"] { transform: translateX(26px); }
.dm-panel[data-step="2"] .dm-step[data-step="1"] { transform: translateX(-26px); }

.dm-eyebrow {
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--forest);
}
.dm-title { margin: 6px 0 4px; font-size: 24px; font-weight: 800; color: var(--ink); letter-spacing: -.01em; }
.dm-lead { margin: 0 0 20px; font-size: 14.5px; color: var(--muted); line-height: 1.55; }

/* Organisation rows */
.dm-org {
  display: flex; align-items: center; gap: 13px;
  padding: 13px 14px; margin-bottom: 8px;
  border: 1.5px solid var(--gt-hair); border-radius: 14px;
  cursor: pointer; background: #fff;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.dm-org:hover { border-color: rgba(45,122,58,.45); }
.dm-org.is-picked { border-color: var(--forest); background: rgba(45,122,58,.06); }
.dm-org-txt { flex: 1; min-width: 0; line-height: 1.25; }
.dm-org-txt b { display: block; font-size: 15.5px; color: var(--ink); }
.dm-org-txt i { display: block; font-style: normal; font-size: 12.5px; color: var(--muted); }
.dm-tick { width: 22px; height: 22px; flex: none; display: grid; place-items: center; color: var(--forest); opacity: 0; transition: opacity .15s var(--ease); }
.dm-org.is-picked .dm-tick { opacity: 1; }
.dm-tick svg { width: 20px; height: 20px; fill: currentColor; stroke: none; }

.dm-switch {
  background: none; border: 0; padding: 6px 0; margin-bottom: 10px;
  font-family: var(--body); font-size: 13.5px; font-weight: 700;
  color: var(--forest); cursor: pointer; text-decoration: underline;
}
.dm-orglist[hidden] { display: none; }

/* Type chips */
.dm-types { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.dm-type {
  padding: 9px 15px; border-radius: 100px;
  border: 1.5px solid var(--gt-hair); background: #fff;
  font-family: var(--body); font-size: 13.5px; font-weight: 600; color: var(--ink);
  cursor: pointer; transition: .15s var(--ease);
}
.dm-type:hover { border-color: rgba(45,122,58,.5); }
.dm-type.on { background: var(--forest); border-color: var(--forest); color: #fff; }

.dm-own, .dm-name { margin-bottom: 16px; }
.dm-own label, .dm-name label { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.dm-own .fld-ic, .dm-name .fld-ic {
  display: flex; align-items: stretch; background: #fff;
  border: 1px solid rgba(15,46,34,.15); border-radius: 11px; overflow: hidden;
}
.dm-own .fld-ic > .fi-mark, .dm-name .fld-ic > .fi-mark {
  position: static; transform: none; width: 44px; height: auto; flex: none;
  display: grid; place-items: center; pointer-events: none;
  border-right: 1px solid rgba(15,46,34,.1); background: rgba(15,46,34,.025); color: rgba(15,46,34,0.67);
}
.dm-own .fld-ic > .fi-mark svg, .dm-name .fld-ic > .fi-mark svg { width: 18px; height: 18px; fill: currentColor; stroke: none; }
.dm-own input, .dm-name input {
  flex: 1 1 auto; min-width: 0; border: 0 !important; background: transparent !important;
  padding: 13px 15px !important; box-shadow: none !important; outline: none; font-size: 15px;
}
.dm-own .fld-ic:focus-within, .dm-name .fld-ic:focus-within {
  border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.16);
}

.dm-actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; margin-top: 22px; }

@media (prefers-reduced-motion: reduce) {
  .dept-modal .dm-panel, .dm-step { transition: opacity .15s linear !important; transform: none !important; }
}
@media (max-width: 560px) {
  .dept-modal { padding: 0; align-items: flex-end; }
  .dept-modal .dm-panel { width: 100%; max-height: 92vh; border-radius: 22px 22px 0 0; padding: 26px 20px 22px; }
  .dm-actions .abtn { flex: 1; justify-content: center; }
}


/* ==========================================================================
   THE TERMS AGREEMENT — the summary, and the box you tick
   --------------------------------------------------------------------------
   Used twice from one source: beside the checkbox on the last step of the org
   wizard, and as the short version at the top of /terms/. Both print the same
   three paragraphs from goodtip/legal.py.

   The summary is styled to be READ, which is the entire point of it — a
   numbered list with real line height and a bold lead sentence per item, not
   the 11px grey small print everybody has been trained to skip. If it looks
   like small print it will be treated as small print, and then the checkbox
   under it is a formality rather than an agreement.
   ========================================================================== */
.terms-three { margin: 4px 0 0; padding: 0; list-style: none; counter-reset: t3; }
.terms-three li {
  counter-increment: t3;
  position: relative; padding-left: 38px; margin-bottom: 16px;
}
.terms-three li::before {
  content: counter(t3);
  position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--mark); font-size: 12px; font-weight: 900;
  background: var(--lime); color: var(--deep);
}
.terms-three li b {
  display: block; margin-bottom: 4px;
  font-size: 15.5px; line-height: 1.4; color: var(--deep);
}
.terms-three li span {
  display: block; font-size: 14.5px; line-height: 1.62; color: var(--ink);
}
/* On the deep member sheet the wizard card sits on, the palette flips. */
.page-deep .terms-three li b { color: #fff; }
.page-deep .terms-three li span { color: rgba(232,228,213,.78); }

/* The tick itself. Given its own bordered block rather than being a checkbox
   loose under a paragraph, because it is the control that gates the Create
   button and it has to look like a decision. */
.terms-tick {
  display: flex; align-items: flex-start; gap: 12px;
  margin-top: 6px; padding: 14px 16px;
  border: 1px solid rgba(15,46,34,.16); border-radius: 12px;
  background: rgba(255,255,255,.35);
  font-size: 14.5px; line-height: 1.55; cursor: pointer;
}
.terms-tick input {
  flex: none; width: 20px; height: 20px; margin: 0; accent-color: var(--forest);
  cursor: pointer;
}
.terms-tick.has-error { border-color: #B3261E; background: rgba(179,38,30,.06); }
.page-deep .terms-tick {
  border-color: rgba(200,241,53,.22); background: rgba(255,255,255,.04);
  color: rgba(232,228,213,.88);
}
.page-deep .terms-tick input { accent-color: var(--lime); }

/* ==========================================================================
   LOCKED FEATURES — the disabled control, and the prompt beside it
   --------------------------------------------------------------------------
   Groups are sold from the Workplace plan up (billing/pricing.py). The client's
   instruction was that a customer below that line must SEE the feature, not
   have it quietly removed: "a visible but disabled/greyed group-creation entry
   point, with a click triggering the upgrade prompt ... don't just hide the
   option, they should see what they're missing".

   .is-locked is the greyed control. It is applied to real <button disabled>
   elements, never to a div pretending to be one — a disabled button announces
   itself to a screen reader, refuses the click without JavaScript, and cannot
   be tabbed into and "pressed" with the keyboard the way a styled div can.

   .lock-prompt is the panel that explains it. It sits on the deep member sheet,
   so it is drawn light-on-dark; the small padlock chip carries the lime the
   rest of the page uses for anything actionable, because the upgrade IS the
   action here.
   ========================================================================== */
.is-locked,
.abtn.is-locked {
  opacity: .45;
  cursor: not-allowed;
  /* Enough of the original look survives that it still reads as the button it
     would be, which is the whole point of showing it. */
  filter: grayscale(.85);
  box-shadow: none;
}
.is-locked:hover { filter: grayscale(.85); transform: none; }
/* The padlock that marks a control as gated rather than merely unavailable. */
.is-locked .lk-pad { width: 15px; height: 15px; fill: currentColor; stroke: none; flex: none; }

.lock-prompt {
  margin-top: 16px; max-width: 620px;
  padding: 18px 20px 20px;
  border: 1px solid rgba(200,241,53,.26); border-radius: 16px;
  background: linear-gradient(180deg, rgba(200,241,53,.07), rgba(255,255,255,.02));
}
.lock-prompt .lk-head { display: flex; align-items: flex-start; gap: 12px; }
.lock-prompt .lk-ic {
  flex: none; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(200,241,53,.14); color: var(--lime);
}
.lock-prompt .lk-ic svg { width: 17px; height: 17px; }
.lock-prompt .lk-title {
  display: block; font-family: var(--mark); font-weight: 900;
  font-size: 16px; color: #fff; letter-spacing: -.01em;
}
.lock-prompt .lk-plan {
  display: block; margin-top: 2px;
  font-size: 12.5px; color: var(--lime); opacity: .85; font-weight: 700;
}
.lock-prompt .lk-body {
  margin: 12px 0 0; font-size: 14.5px; line-height: 1.62;
  color: rgba(232,228,213,.78);
}
.lock-prompt .lk-gets { margin: 12px 0 0; padding: 0; list-style: none; }
.lock-prompt .lk-gets li {
  position: relative; padding-left: 20px; margin-bottom: 5px;
  font-size: 13.5px; line-height: 1.5; color: rgba(232,228,213,.72);
}
.lock-prompt .lk-gets li::before {
  content: ""; position: absolute; left: 4px; top: 8px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--lime); opacity: .75;
}
.lock-prompt .lk-acts { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }

/* On a LIGHT surface the panel has to flip. That is three places and they are
   not obviously the same family: the marketing pages' cream sections, the org
   wizard's card, and .card — which is the member app's own card and is
   #FCFBF5 in both themes, so the "it's a member page, therefore it's dark"
   shortcut is wrong. The groups screen draws this panel inside one of those
   cards on its switched-off state, which is exactly where it would have been
   light text on light paper. */
.on-cream .lock-prompt,
.card .lock-prompt,
.wiz-card .lock-prompt {
  border-color: rgba(15,46,34,.16);
  background: linear-gradient(180deg, rgba(45,122,58,.06), rgba(15,46,34,.02));
}
.on-cream .lock-prompt .lk-title,
.card .lock-prompt .lk-title,
.wiz-card .lock-prompt .lk-title { color: var(--deep); }
.on-cream .lock-prompt .lk-ic,
.card .lock-prompt .lk-ic,
.wiz-card .lock-prompt .lk-ic { background: rgba(45,122,58,.12); color: var(--forest); }
.on-cream .lock-prompt .lk-plan,
.card .lock-prompt .lk-plan,
.wiz-card .lock-prompt .lk-plan { color: var(--forest); opacity: 1; }
.on-cream .lock-prompt .lk-body,
.card .lock-prompt .lk-body,
.wiz-card .lock-prompt .lk-body { color: var(--ink); }
.on-cream .lock-prompt .lk-gets li,
.card .lock-prompt .lk-gets li,
.wiz-card .lock-prompt .lk-gets li { color: var(--muted); }

/* ==========================================================================
   ============ v48 — departments, redesigned ==============================
   ========================================================================== */

/* ---- hero ---- */
.dept-hero {
  display: grid; grid-template-columns: minmax(0,1fr) auto;
  gap: 28px; align-items: center;
  margin-bottom: 26px;
}
.dept-hero .dh-copy h1 {
  margin: 6px 0 10px; color: #fff;
  font-family: var(--display, var(--mark)); font-weight: 800;
  font-size: clamp(30px, 4vw, 44px); line-height: 1.02; letter-spacing: -.02em;
}
.dept-hero .dh-lead {
  margin: 0; max-width: 46ch;
  font-size: 15.5px; line-height: 1.62; color: rgba(232,228,213,.76);
}
.dept-hero .dh-side {
  display: flex; align-items: center; gap: 26px; flex: none;
}
.dept-hero .dh-cta svg { width: 17px; height: 17px; fill: currentColor; stroke: none; }

/* The orbit. Two rings, a trophy, and real member faces sitting on the ring
   at fixed points — no animation, because a spinning illustration next to a
   search field is a distraction from the one thing this page is for. */
.dh-orbit { position: relative; width: 190px; height: 170px; flex: none; }
.dho-ring, .dho-ring.two {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  border-radius: 50%; border: 1px solid rgba(200,241,53,.16);
}
.dho-ring { width: 168px; height: 168px; }
.dho-ring.two { width: 124px; height: 124px; border-color: rgba(200,241,53,.10); }
.dho-trophy {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 96px; height: 96px; display: grid; place-items: center;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #2C6E42, #0E2A1C 72%);
  box-shadow: 0 20px 44px -18px rgba(0,0,0,.75), inset 0 1px 0 rgba(200,241,53,.22);
}
.dho-trophy svg { width: 50px; height: 50px; fill: var(--lime); stroke: none; filter: drop-shadow(0 3px 10px rgba(200,241,53,.4)); }
.dho-face {
  position: absolute; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; overflow: hidden;
  background: var(--gt-deep-3); color: var(--lime);
  font-family: var(--mark); font-weight: 800; font-size: 12px;
  border: 2px solid rgba(200,241,53,.5);
  box-shadow: 0 8px 18px -8px rgba(0,0,0,.7);
}
.dho-face img { width: 100%; height: 100%; object-fit: cover; }
.dho-face.f1 { top: 6px; right: 20px; }
.dho-face.f2 { bottom: 14px; left: 6px; }
.dho-face.f3 { bottom: 28px; right: 2px; }

/* ---- search ---- */
.dept-searchbar {
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 18px; padding: 14px;
  margin-bottom: 18px;
}
.dept-search { display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap; }
.dept-search .ds-field {
  flex: 1 1 260px; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  background: #fff; border-radius: 12px; padding: 0 16px;
  border: 1px solid transparent;
}
.dept-search .ds-field:focus-within { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.2); }
.dept-search .ds-ic { flex: none; display: grid; place-items: center; color: var(--muted); }
.dept-search .ds-ic svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.dept-search input[type="search"] {
  flex: 1; min-width: 0; border: 0 !important; background: transparent !important;
  padding: 15px 0 !important; font-size: 15px; box-shadow: none !important; outline: none;
}
.dept-search .ds-go { flex: none; padding: 0 26px; border-radius: 12px; }

/* ---- cards ---- */
.dept-grid { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; }
.dept-card {
  padding: 0; overflow: hidden; gap: 0;
  background: #fff; border-radius: 18px; border: 1px solid var(--gt-hair);
  border-left: 1px solid var(--gt-hair);
}
.dept-card .dc-body { padding: 20px 20px 18px; flex: 1; }
.dept-card .dc-top { align-items: flex-start; gap: 14px; }
.dept-card .dc-name b { font-size: 17px; }
.dept-card .dc-name .dc-meta {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 6px;
  font-size: 12.5px; color: var(--muted);
}
.dept-card .dc-meta svg { width: 14px; height: 14px; fill: currentColor; stroke: none; }
.dept-card .dc-count {
  flex: none; margin-left: auto;
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  background: rgba(15,46,34,.06); padding: 6px 11px; border-radius: 8px;
}
/* The action gets its own bar with a rule above it, so every card's button
   lands on the same line no matter how long the name ran. */
.dept-card .dc-foot {
  border-top: 1px solid var(--gt-hair);
  padding: 15px 20px; text-align: center; margin-top: auto;
}
.dept-card .dc-link {
  display: inline-flex; align-items: center; gap: 8px; justify-content: center;
  font-family: var(--body); font-size: 14.5px; font-weight: 700;
  color: var(--ink); text-decoration: none; background: none; border: 0; cursor: pointer;
}
.dept-card .dc-link:hover { color: var(--forest); }
.dept-card .dc-plus { font-size: 17px; line-height: 1; }
.dept-card .dc-decide { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }

/* The prompt card: same shape, cream, so it reads as part of the set rather
   than an advert bolted to the end. */
.dept-card.is-prompt { background: #F6F4EA; }
.dept-card.is-prompt .dc-body { display: flex; flex-direction: column; align-items: flex-start; }
.dc-prompt-ic {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; margin-bottom: 14px;
  background: rgba(15,46,34,.07); color: var(--forest);
}
.dc-prompt-ic svg { width: 21px; height: 21px; fill: currentColor; stroke: none; }
.dc-prompt-h { font-size: 17px; color: var(--ink); }
.dc-prompt-p { margin: 6px 0 0; font-size: 14px; line-height: 1.55; color: var(--muted); }

/* ---- the three facts ---- */
.dept-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0; margin-top: 30px; padding-top: 26px;
  border-top: 1px solid rgba(255,255,255,.1);
}
.df-item { display: flex; gap: 14px; align-items: flex-start; padding: 4px 26px; }
.df-item + .df-item { border-left: 1px solid rgba(255,255,255,.09); }
.df-item:first-child { padding-left: 0; }
.df-ic { width: 34px; height: 34px; flex: none; display: grid; place-items: center; color: var(--lime); }
.df-ic.round { border-radius: 50%; border: 1px solid rgba(200,241,53,.3); }
.df-ic svg { width: 21px; height: 21px; fill: currentColor; stroke: none; }
.df-item b { display: block; font-size: 14.5px; color: #fff; margin-bottom: 4px; }
.df-item p { margin: 0; font-size: 13.5px; line-height: 1.5; color: rgba(232,228,213,.66); }

@media (max-width: 900px) {
  .dept-hero { grid-template-columns: 1fr; }
  .dept-hero .dh-side { flex-direction: row-reverse; justify-content: space-between; }
  .dh-orbit { width: 150px; height: 140px; }
  .dho-ring { width: 132px; height: 132px; } .dho-ring.two { width: 96px; height: 96px; }
  .dho-trophy { width: 76px; height: 76px; } .dho-trophy svg { width: 40px; height: 40px; }
  .df-item { padding: 4px 0; } .df-item + .df-item { border-left: 0; padding-top: 18px; }
}
@media (max-width: 560px) {
  .dept-hero .dh-side { flex-direction: column; align-items: stretch; gap: 18px; }
  .dept-hero .dh-cta { justify-content: center; }
  .dh-orbit { margin: 0 auto; }
  .dept-search .ds-go { flex: 1; }
}

/* The eyebrow inherits .gh-eyebrow, which is tuned for a cream card and lands
   dark-green-on-dark-green here — invisible. The hero is on the sheet, so it
   takes the on-dark treatment the rest of the sheet headings already use. */
.dept-hero .gh-eyebrow {
  color: var(--lime);
  font-family: var(--mark); font-size: 12px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
}

/* Cards in a row stretch to the tallest, which left the shorter one with a
   pool of empty white between its last line and its footer. The body no
   longer grows; the gap goes under it instead, so both cards keep their
   footers aligned without either looking half-empty. */
.dept-grid { align-items: stretch; }
.dept-card .dc-body { flex: 0 0 auto; }
.dept-card { justify-content: flex-start; }
.dept-card .dc-foot { margin-top: auto; }

/* ==========================================================================
   ============ v49 — MatchReader on the tipping screen ====================
   ========================================================================== */

/* The read sits between the pick and the match-state row: after you have seen
   who is playing and what you did about it, before the clock and the ground.
   That is the order somebody actually asks the questions in.

   It is quiet by design. A tipping screen belongs to the tipper's own opinion,
   and an insight that shouted would start reading as instruction. */
.gt-fix .gfx-reader {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-top: 2px; padding: 11px 13px;
  border-radius: 12px;
  background: rgba(45,122,58,.06);
  border: 1px solid rgba(45,122,58,.16);
}
.gt-fix .gr-badge {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  font-family: var(--mark); font-size: 11.5px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--forest);
}
.gt-fix .gr-badge svg { width: 14px; height: 14px; fill: currentColor; stroke: none; }
.gt-fix .gr-why { font-size: 12.5px; line-height: 1.5; color: var(--muted); }

/* Tier carries a weight, so a Dominant Position does not look identical to an
   Even Contest. Colour is the same green throughout: this is confidence, not
   a warning, and a red/amber scale would read as a tip to avoid the game. */
.gt-fix .gfx-reader.tier-even-contest { background: rgba(15,46,34,.04); border-color: rgba(15,46,34,.1); }
.gt-fix .gfx-reader.tier-even-contest .gr-badge { color: var(--muted); }
.gt-fix .gfx-reader.tier-strong-position,
.gt-fix .gfx-reader.tier-dominant-position {
  background: rgba(45,122,58,.11); border-color: rgba(45,122,58,.3);
}

@media (max-width: 560px) {
  .gt-fix .gfx-reader { flex-direction: column; gap: 6px; }
}

/* MatchReader on the dashboard fixture rows. Same content as the My Tips
   version, tighter, because these rows are a list you scan rather than cards
   you study. It sits above the picker: a hint read after the choice is made
   is not a hint.

   Two bands: the verdict, then the evidence. The verdict band is the only
   part that has to survive being skimmed, so the tier and the team it favours
   carry the weight and everything under them is deliberately quieter. */
.fxp-reader {
  margin: 8px 0 2px; padding: 9px 11px 9px;
  border-radius: 10px;
  background: rgba(45,122,58,.07);
  border: 1px solid rgba(45,122,58,.15);
}
.fxr-head { display: flex; align-items: center; gap: 7px; }
.fxr-spark { width: 13px; height: 13px; flex: none; fill: var(--forest); stroke: none; }
.fxr-tier {
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--forest);
  flex: none;
}
/* The team is the actionable half — a tier without a name is a mood. */
.fxr-lead {
  font-size: 12.5px; font-weight: 700; color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fxr-lead::before { content: "·"; color: var(--muted); font-weight: 400; margin-right: 6px; opacity: .6; }

/* Four steps, one per tier above Even Contest, so the difference between a
   Slight Edge and a Dominant Position is visible without reading either. */
.fxr-meter { margin-left: auto; flex: none; display: inline-flex; align-items: flex-end; gap: 3px; }
.fxr-meter i { width: 4px; border-radius: 2px; background: rgba(15,46,34,.13); }
.fxr-meter i:nth-child(1) { height: 6px; }
.fxr-meter i:nth-child(2) { height: 8px; }
.fxr-meter i:nth-child(3) { height: 10px; }
.fxr-meter i:nth-child(4) { height: 12px; }
.fxr-meter[data-lvl="1"] i:nth-child(-n+1),
.fxr-meter[data-lvl="2"] i:nth-child(-n+2),
.fxr-meter[data-lvl="3"] i:nth-child(-n+3),
.fxr-meter[data-lvl="4"] i:nth-child(-n+4) { background: var(--forest); }

/* The evidence: each side's last five, oldest to newest. */
.fxr-form { display: grid; gap: 4px; margin-top: 8px; }
.fxr-row { display: flex; align-items: center; gap: 9px; font-size: 11.5px; color: var(--muted); }
.fxr-team { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fxr-row.is-lead .fxr-team { color: var(--ink); font-weight: 700; }
.fxr-pips { flex: none; display: inline-flex; gap: 3px; }
.fxr-pips i { width: 6px; height: 6px; border-radius: 50%; background: rgba(15,46,34,.15); }
.fxr-pips i.w { background: var(--forest); }
.fxr-row.is-lead .fxr-pips i.w { box-shadow: 0 0 0 2px rgba(45,122,58,.14); }
.fxr-rec {
  flex: none; min-width: 46px; text-align: right;
  font-family: var(--score); font-weight: 700; font-size: 11.5px; color: var(--muted);
}

/* Tier carries a weight. Same green throughout — this is confidence, not a
   warning, and a red/amber scale would read as "avoid this game". */
.fxp-reader.tier-even-contest { background: rgba(15,46,34,.05); border-color: rgba(15,46,34,.1); }
.fxp-reader.tier-even-contest .fxr-tier,
.fxp-reader.tier-even-contest .fxr-spark { color: var(--muted); fill: var(--muted); }
.fxp-reader.tier-strong-position,
.fxp-reader.tier-dominant-position { background: rgba(45,122,58,.12); border-color: rgba(45,122,58,.28); }

@media (max-width: 560px) {
  .fxr-tier { font-size: 10.5px; }
  .fxr-rec { min-width: 42px; }
}

/* ==========================================================================
   ============ v50 — the round recap card: board + starters ================
   ==========================================================================
   Sits under the recap's written summary inside .gt-post.is-recap. Two jobs:
   show the group where it stood at that round, and give somebody an easy way
   into the thread.

   The board is a three-column grid rather than a table so the name column can
   take the pressure on a phone: rank, name, movement and a "sat out" note all
   compete for one line, and the two number columns are fixed at their widest
   plausible value so the rows line up down the card. Name gets whatever is
   left and ellipses rather than wrapping into a second row, which would break
   the vertical rhythm every reader is using to scan the ladder.
   -------------------------------------------------------------------------- */

.gpt-recap { margin: 14px 0 4px; display: grid; gap: 14px; }

.grc-board {
  border: 1px solid rgba(15,46,34,.10); border-radius: 14px;
  background: rgba(255,255,255,.55); overflow: hidden;
}
.grc-bhead, .grc-brow {
  display: grid; grid-template-columns: minmax(0,1fr) 58px 62px;
  align-items: center; gap: 8px; padding: 9px 14px;
}
.grc-bhead {
  font-family: var(--mark); font-weight: 700; font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  background: rgba(15,46,34,.04);
}
.grc-bhead span:not(:first-child), .grc-brow .grc-pts, .grc-brow .grc-tot { text-align: right; }
.grc-brow + .grc-brow { border-top: 1px solid rgba(15,46,34,.07); }
.grc-brow.lead { background: rgba(200,241,53,.16); }

.grc-who { display: flex; align-items: center; gap: 8px; min-width: 0; }
.grc-rank {
  font-family: var(--mark); font-style: normal; font-weight: 700; font-size: 11px;
  width: 20px; height: 20px; flex: none; border-radius: 6px;
  display: grid; place-items: center;
  background: rgba(15,46,34,.07); color: var(--forest);
}
.grc-brow.lead .grc-rank { background: var(--lime); color: var(--deep); }
.grc-who b {
  font-size: 14px; color: var(--ink); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.grc-mv { font-size: 10.5px; font-weight: 700; flex: none; }
.grc-mv.up { color: #2d7a3a; }
.grc-mv.dn { color: #9a2b2b; }
.grc-out {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); border: 1px solid rgba(15,46,34,.12);
  border-radius: 100px; padding: 1px 7px; flex: none;
}
.grc-pts { font-size: 13px; color: var(--forest); font-weight: 600; }
.grc-tot { font-family: var(--display); font-size: 15px; color: var(--ink); }

/* Starters. Deliberately styled as suggestions rather than as send buttons:
   tapping one only fills the reply box below, so they must not read as the
   primary action of the card. */
.grc-tlab {
  display: block; font-family: var(--mark); font-weight: 700; font-size: 10.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  margin-bottom: 8px;
}
.grc-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.grc-chip {
  font: inherit; font-size: 13px; text-align: left; cursor: pointer;
  padding: 8px 14px; border-radius: 100px; color: var(--forest);
  background: rgba(255,255,255,.7); border: 1px solid rgba(15,46,34,.14);
  transition: background .14s var(--ease), border-color .14s var(--ease), transform .14s var(--ease);
}
.grc-chip:hover { background: var(--lime); border-color: var(--lime); color: var(--deep); }
.grc-chip:active { transform: translateY(1px); }
.grc-chip:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }

@media (max-width: 560px) {
  /* The season total is the number people came for; the round column is the
     one that goes when there is no room for both. */
  .grc-bhead, .grc-brow { grid-template-columns: minmax(0,1fr) 52px; padding: 9px 12px; }
  .grc-bhead span:nth-child(2), .grc-brow .grc-pts { display: none; }
  .grc-chip { font-size: 12.5px; padding: 7px 12px; }
}

/* ==========================================================================
   ============ v52 — the fixture card, side by side ========================
   ==========================================================================
   Replaces v51's stacked rows. A fixture is two sides FACING each other, and
   the layout has to say that: stacked, they read as a list of two unrelated
   clubs and the eye has to travel down and back to compare form. Three
   columns keeps the comparison horizontal, which is the shape of the question
   being asked, and puts the versus where it belongs.

   The two side columns are 1fr each so they are exactly equal whatever the
   club names are — an away side that got less room than the home side would
   quietly imply something about the fixture.
   -------------------------------------------------------------------------- */

.fxc {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-left: 3px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 14px 16px 16px; display: grid; gap: 12px;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.fxc:hover { box-shadow: 0 14px 30px -24px rgba(15,46,34,.5); }
.fxc.is-picked { border-left-color: var(--lime); }
.fxc.phase-live { border-left-color: #c0392b; }

.fxc-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted); }
.fxc-when { font-weight: 600; color: var(--forest); }
.fxc-venue { display: inline-flex; align-items: center; gap: 4px; }
.fxc-venue svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.fxc-state {
  margin-left: auto; font-family: var(--mark); font-weight: 700; font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; padding: 3px 9px; border-radius: 100px;
}
.fxc-state.is-open   { background: rgba(15,46,34,.07); color: var(--forest); }
.fxc-state.is-tipped { background: var(--lime); color: var(--deep); }
.fxc-state.is-missed { background: rgba(154,43,43,.10); color: #9a2b2b; }
.fxc-state.is-shut   { background: rgba(15,46,34,.10); color: #5B695E; }

/* ---- the matchup ------------------------------------------------------- */
.fxc-teams {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: start;
  gap: 12px; padding: 2px 0;
}
.fxc-v {
  align-self: start; margin-top: 30px; font-family: var(--mark); font-weight: 700;
  font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); padding: 6px 4px; white-space: nowrap;
  position: relative;
}
/* Faint rules either side so the versus reads as a divider between two
   columns rather than as a third item in a row of three. */
.fxc-v::before, .fxc-v::after {
  content: ""; position: absolute; left: 50%; width: 1px; height: 12px;
  background: rgba(15,46,34,.16);
}
.fxc-v::before { top: -14px; }
.fxc-v::after  { bottom: -14px; }

.fxc-side { display: grid; gap: 10px; justify-items: stretch; min-width: 0; }

/* The club, and the act of picking it.
   Crest left, name right, on one wide box — the shape in the mock. The name
   is allowed to wrap to a second line rather than shrink, because
   "Canterbury-Bankstown Bulldogs" ellipsed at this width says nothing. */
.fxc-team {
  display: grid; grid-template-columns: 44px minmax(0,1fr); align-items: center;
  gap: 14px; position: relative; width: 100%; font: inherit; cursor: pointer;
  text-align: left; padding: 14px 16px; border-radius: 14px; min-width: 0;
  background: rgba(255,255,255,.75); border: 1.5px solid rgba(15,46,34,.13);
  transition: background .14s var(--ease), border-color .14s var(--ease), transform .12s var(--ease);
}
.fxc-team:hover { border-color: rgba(15,46,34,.32); background: #fff; }
.fxc-team:active { transform: translateY(1px); }
.fxc-team:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
.fxc-team.sel { background: var(--lime); border-color: var(--lime); }
.fxc-team.is-dim { opacity: .55; cursor: default; }
.fxc-team.is-dim:hover { background: rgba(255,255,255,.75); border-color: rgba(15,46,34,.13); }
div.fxc-team { cursor: default; }

.fxc-crest { display: block; }
.fxc-crest .crest { width: 44px; height: 44px; font-size: 13px; }
.fxc-name {
  font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.28;
  /* Two lines then ellipsis. A club name is two or three words and the box is
     built to hold them; past that, truncating beats overflowing the card. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; word-break: break-word;
}
/* Over the corner rather than in the flow, so choosing a side cannot change
   one column's height and knock the two out of line. */
.fxc-tick {
  position: absolute; top: 8px; right: 8px; width: 20px; height: 20px;
  border-radius: 50%; background: var(--deep); display: grid; place-items: center;
  opacity: 0; transform: scale(.6); transition: opacity .14s var(--ease), transform .14s var(--ease);
}
.fxc-team.sel .fxc-tick { opacity: 1; transform: none; }
.fxc-tick svg { width: 12px; height: 12px; fill: none; stroke: var(--lime); stroke-width: 3.5; stroke-linecap: round; stroke-linejoin: round; }

/* ---- form strip: the traffic lights ------------------------------------ */
.fxc-form {
  position: relative; display: flex; align-items: center; justify-content: center;
  gap: 9px; font: inherit; cursor: pointer; padding: 7px 10px; border-radius: 100px;
  background: none; border: 1px solid transparent; width: 100%;
  transition: background .14s var(--ease), border-color .14s var(--ease);
}
.fxc-form:hover, .fxc-form:focus-visible { background: rgba(15,46,34,.05); border-color: rgba(15,46,34,.14); }
.fxc-form:focus-visible { outline: 2px solid var(--forest); outline-offset: 1px; }
.fxc-dots { display: inline-flex; gap: 5px; }
/* Bigger than v51's 9px. These are the fastest read on the card — five shapes
   you compare without reading — and at 9px the colour was doing all the work
   on a phone. */
.fxc-dots i { width: 13px; height: 13px; border-radius: 50%; display: block; box-shadow: inset 0 -1px 0 rgba(0,0,0,.14); }
.fxc-dots i.w { background: #2d7a3a; }
.fxc-dots i.d { background: #e0a51f; }
.fxc-dots i.l { background: #c0392b; }
.fxc-rec { font-size: 12px; font-weight: 700; color: var(--muted); white-space: nowrap; }

/* Hover hint. Larger and structured rather than one small grey line — it has
   to explain three colours to somebody who has never seen them. */
.fxc-hint {
  position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translate(-50%, 4px);
  z-index: 8; background: var(--deep); color: #fff; text-align: left;
  font-size: 13px; line-height: 1.5; padding: 11px 14px; border-radius: 10px;
  width: max-content; max-width: 240px; white-space: normal; pointer-events: none;
  opacity: 0; visibility: hidden;
  box-shadow: 0 18px 34px -18px rgba(9,26,19,.7);
  transition: opacity .14s var(--ease), transform .14s var(--ease), visibility .14s;
}
.fxc-hint b { display: block; font-size: 13.5px; color: var(--lime); margin-bottom: 3px; }
.fxc-hint i { display: block; font-style: normal; margin-top: 6px; font-size: 12.5px; color: var(--lime); }
.fxc-hint::after {
  content: ""; position: absolute; top: 100%; left: 50%; margin-left: -6px;
  border: 6px solid transparent; border-top-color: var(--deep);
}
.fxc-form:hover .fxc-hint, .fxc-form:focus-visible .fxc-hint { opacity: 1; visibility: visible; transform: translate(-50%, 0); }

/* ---- MatchReader ------------------------------------------------------- */
.fxc-readwrap { border-top: 1px solid rgba(15,46,34,.10); padding-top: 12px; }
.fxc-why {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  font: inherit; cursor: pointer; padding: 11px 14px; border-radius: 12px;
  color: var(--ink); background: rgba(200,241,53,.16); border: 1px solid rgba(15,46,34,.10);
  transition: background .14s var(--ease), border-color .14s var(--ease);
}
.fxc-why:hover { background: rgba(200,241,53,.3); border-color: rgba(15,46,34,.2); }
.fxc-why:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
.fxc-why-badge {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  font-family: var(--mark); font-weight: 700; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--deep);
  background: var(--lime); padding: 5px 10px; border-radius: 100px;
}
.fxc-why-badge svg { width: 13px; height: 13px; }
.fxc-why-text { display: grid; min-width: 0; }
.fxc-why-text b { font-size: 14.5px; font-weight: 700; line-height: 1.3; }
.fxc-why-text small { font-size: 12px; color: var(--muted); }
.fxc-why .fxc-chev {
  width: 14px; height: 14px; margin-left: auto; flex: none;
  transform: rotate(-90deg); transition: transform .16s var(--ease);
  stroke: var(--forest); fill: none; stroke-width: 2.4;
}
.fxc-readwrap.is-open .fxc-chev { transform: rotate(90deg); }
.fxc-why-shut { display: none; }
.fxc-readwrap.is-open .fxc-why-open { display: none; }
.fxc-readwrap.is-open .fxc-why-shut { display: block; }

.fxc-read {
  position: relative; margin-top: 10px; padding: 16px 38px 16px 16px;
  border-radius: 12px; background: rgba(200,241,53,.12);
  border: 1px solid rgba(15,46,34,.10);
  animation: fxcRead .18s var(--ease) both;
}
@keyframes fxcRead { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.fxc-read p { font-size: 14.5px; line-height: 1.6; color: var(--ink); margin: 0 0 8px; }
.fxc-read p:last-child { margin-bottom: 0; }
.fxc-read-foot { font-size: 12px !important; color: var(--muted) !important; font-style: italic; }
.fxc-read-tier { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.fxc-tier-name { font-family: var(--mark); font-weight: 700; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--deep); }
.fxc-meter { display: inline-flex; gap: 3px; }
.fxc-meter i { width: 18px; height: 5px; border-radius: 3px; background: rgba(15,46,34,.15); }
.fxc-meter[data-lvl="1"] i:nth-child(-n+1),
.fxc-meter[data-lvl="2"] i:nth-child(-n+2),
.fxc-meter[data-lvl="3"] i:nth-child(-n+3),
.fxc-meter[data-lvl="4"] i:nth-child(-n+4) { background: var(--forest); }
.fxc-read-x {
  position: absolute; top: 8px; right: 10px; background: none; border: 0; cursor: pointer;
  font-size: 21px; line-height: 1; color: var(--muted); padding: 2px 7px; border-radius: 6px;
}
.fxc-read-x:hover { color: var(--deep); background: rgba(15,46,34,.08); }

/* ---- locked round ------------------------------------------------------ */
.fxc-shut {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-radius: 10px;
  background: rgba(15,46,34,.05); border: 1px dashed rgba(15,46,34,.16);
  font-size: 12.5px; color: #5d6b60;
}
.fxc-shut svg { width: 14px; height: 14px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; }

.gt-lockbar {
  display: flex; align-items: center; gap: 12px; margin: 0 0 14px;
  padding: 13px 16px; border-radius: 12px;
  background: rgba(15,46,34,.05); border: 1px solid rgba(15,46,34,.12);
}
.gt-lockbar svg { width: 20px; height: 20px; flex: none; stroke: #5d6b60; fill: none; stroke-width: 1.8; }
.gt-lockbar b { display: block; font-size: 14px; color: var(--ink); }
.gt-lockbar span { font-size: 13px; color: var(--muted); }

/* ---- My Tips keeps a graded outcome under the card --------------------- */
.gt-fixwrap { display: grid; gap: 0; }
.gt-fixwrap .gfx-state {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; font-size: 12.5px;
  border: 1px solid var(--gt-hair); border-top: 0;
  border-radius: 0 0 var(--gt-r-sm) var(--gt-r-sm); background: rgba(15,46,34,.03);
}
.gt-fixwrap .fxc { border-radius: var(--gt-r-sm) var(--gt-r-sm) 0 0; }
.gt-fixwrap.correct .fxc { border-left-color: #2d7a3a; }
.gt-fixwrap.missed  .fxc { border-left-color: #c0392b; }

/* ---- last five, with the scores ---------------------------------------- */
body.fxh-open { overflow: hidden; }
.fxh-modal { position: fixed; inset: 0; z-index: 120; display: grid; place-items: center; padding: 20px; }
.fxh-back { position: absolute; inset: 0; background: rgba(9,26,19,.55); backdrop-filter: blur(2px); animation: fxhFade .16s var(--ease) both; }
.fxh-panel {
  position: relative; width: min(440px, 100%); max-height: 80vh; overflow: auto;
  background: var(--gt-card); border-radius: 18px; padding: 18px 20px 20px;
  box-shadow: 0 40px 80px -30px rgba(9,26,19,.6);
  animation: fxhPop .2s var(--ease) both;
}
@keyframes fxhFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fxhPop { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
.fxh-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.fxh-head h3 { font-family: var(--display); font-size: 18px; color: var(--ink); margin: 0; }
.fxh-x { margin-left: auto; background: none; border: 0; cursor: pointer; font-size: 26px; line-height: 1; color: var(--muted); padding: 2px 10px; border-radius: 8px; }
.fxh-x:hover { color: var(--deep); background: rgba(15,46,34,.07); }
.fxh-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.fxh-row { display: grid; grid-template-columns: 30px minmax(0,1fr); gap: 10px 12px; align-items: center; padding: 11px 13px; border-radius: 10px; background: rgba(15,46,34,.035); }
.fxh-badge { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-family: var(--mark); font-weight: 700; font-size: 12px; color: #fff; }
.fxh-row.w .fxh-badge { background: #2d7a3a; }
.fxh-row.d .fxh-badge { background: #e0a51f; }
.fxh-row.l .fxh-badge { background: #c0392b; }
.fxh-what { font-size: 14px; color: var(--ink); }
.fxh-where { grid-column: 2; font-size: 12px; color: var(--muted); }
.fxh-sum { margin: 14px 0 0; font-size: 13px; color: var(--muted); text-align: center; }

@media (max-width: 560px) {
  /* Stays side by side. The whole point of the layout is the comparison, and
     a phone is exactly where stacking would force the scroll-and-remember
     that the columns exist to avoid. Everything shrinks instead. */
  .fxc { padding: 12px 12px 14px; }
  .fxc-teams { gap: 6px; }
  .fxc-team { padding: 11px 6px 10px; border-radius: 12px; }
  .fxc-crest .crest { width: 34px; height: 34px; font-size: 11px; }
  .fxc-name { font-size: 12.5px; }
  .fxc-v { font-size: 10.5px; padding: 6px 2px; }
  .fxc-dots i { width: 11px; height: 11px; }
  .fxc-rec { font-size: 11px; }
  .fxc-form { gap: 6px; padding: 6px 4px; }
  .fxc-hint { max-width: 200px; font-size: 12.5px; }
  .fxc-why { gap: 8px; padding: 10px 11px; flex-wrap: wrap; }
  .fxc-why-text b { font-size: 13.5px; }
}


/* ---- v53: competition filter, buttons only ------------------------------
   Was a panel with a toggle and a checkbox list for picking a combination.
   It went because a filter that needs opening, ticking and applying is three
   actions to answer what one chip answers, and it sat above the fixtures as
   a form to fill in before you were allowed to tip. */
.sfilter {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r-sm); padding: 16px 20px 18px; margin: 0 0 16px;
}
.sfilter-lead {
  margin: 0 0 12px; text-align: center;
  font-family: var(--mark); font-weight: 700; font-size: 12px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.sfilter-chips { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.sfilter-chip {
  font-family: var(--body); font-weight: 600; font-size: 14px;
  padding: 10px 20px; border-radius: 100px; text-decoration: none;
  color: var(--forest); background: rgba(255,255,255,.7);
  border: 1.5px solid rgba(15,46,34,.14); white-space: nowrap;
  transition: background .14s var(--ease), border-color .14s var(--ease), color .14s var(--ease);
}
.sfilter-chip:hover { border-color: var(--forest); background: #fff; }
.sfilter-chip:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
.sfilter-chip.on { background: var(--lime); border-color: var(--lime); color: var(--deep); font-weight: 700; }

@media (max-width: 560px) {
  .sfilter { padding: 13px 14px 15px; }
  .sfilter-chip { font-size: 13px; padding: 9px 15px; }
}

/* A round beyond the tipping window still lists, so people can see what is
   coming, but it must not look pressable. */
.fx-roundhead.is-shut { opacity: .72; }
.fx-roundhead .fxr-locks.shut { display: inline-flex; align-items: center; gap: 5px; font-style: italic; }
.fx-roundhead .fxr-locks.shut svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 1.8; }





/* ---- My Tips keeps a graded outcome under the card --------------------- */
.gt-fixwrap { display: grid; gap: 0; }
.gt-fixwrap .gfx-state {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; font-size: 12.5px;
  border: 1px solid var(--gt-hair); border-top: 0;
  border-radius: 0 0 var(--gt-r-sm) var(--gt-r-sm); background: rgba(15,46,34,.03);
}
.gt-fixwrap .fxc { border-radius: var(--gt-r-sm) var(--gt-r-sm) 0 0; }
.gt-fixwrap.correct .fxc { border-left-color: #2d7a3a; }
.gt-fixwrap.missed  .fxc { border-left-color: #c0392b; }

/* ---- last five, with the scores ---------------------------------------- */
body.fxh-open { overflow: hidden; }
.fxh-modal { position: fixed; inset: 0; z-index: 120; display: grid; place-items: center; padding: 20px; }
.fxh-back { position: absolute; inset: 0; background: rgba(9,26,19,.55); backdrop-filter: blur(2px); animation: fxhFade .16s var(--ease) both; }
.fxh-panel {
  position: relative; width: min(440px, 100%); max-height: 80vh; overflow: auto;
  background: var(--gt-card); border-radius: 18px; padding: 18px 20px 20px;
  box-shadow: 0 40px 80px -30px rgba(9,26,19,.6);
  animation: fxhPop .2s var(--ease) both;
}
@keyframes fxhFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fxhPop { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
.fxh-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.fxh-head h3 { font-family: var(--display); font-size: 18px; color: var(--ink); margin: 0; }
.fxh-x { margin-left: auto; background: none; border: 0; cursor: pointer; font-size: 26px; line-height: 1; color: var(--muted); padding: 2px 10px; border-radius: 8px; }
.fxh-x:hover { color: var(--deep); background: rgba(15,46,34,.07); }
.fxh-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.fxh-row { display: grid; grid-template-columns: 30px minmax(0,1fr); gap: 10px 12px; align-items: center; padding: 11px 13px; border-radius: 10px; background: rgba(15,46,34,.035); }
.fxh-badge { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-family: var(--mark); font-weight: 700; font-size: 12px; color: #fff; }
.fxh-row.w .fxh-badge { background: #2d7a3a; }
.fxh-row.d .fxh-badge { background: #e0a51f; }
.fxh-row.l .fxh-badge { background: #c0392b; }
.fxh-what { font-size: 14px; color: var(--ink); }
.fxh-where { grid-column: 2; font-size: 12px; color: var(--muted); }
.fxh-sum { margin: 14px 0 0; font-size: 13px; color: var(--muted); text-align: center; }

@media (max-width: 560px) {
  /* Stays side by side. The whole point of the layout is the comparison, and
     a phone is exactly where stacking would force the scroll-and-remember
     that the columns exist to avoid. Everything shrinks instead. */
  .fxc { padding: 12px 12px 14px; }
  .fxc-teams { gap: 6px; }
  .fxc-team { padding: 11px 6px 10px; border-radius: 12px; }
  .fxc-crest .crest { width: 34px; height: 34px; font-size: 11px; }
  .fxc-name { font-size: 12.5px; }
  .fxc-v { font-size: 10.5px; padding: 6px 2px; }
  .fxc-dots i { width: 11px; height: 11px; }
  .fxc-rec { font-size: 11px; }
  .fxc-form { gap: 6px; padding: 6px 4px; }
  .fxc-hint { max-width: 200px; font-size: 12.5px; }
  .fxc-why { gap: 8px; padding: 10px 11px; flex-wrap: wrap; }
  .fxc-why-text b { font-size: 13.5px; }
}



/* ==========================================================================
   ============ v31 — scoped loading veils ==================================
   ========================================================================== */

/* The panel-level counterpart to the splash. See gt-veil.js for which region
   gets veiled and why polling is exempt.
   
   Deliberately reuses .busy-veil from the form path rather than introducing a
   second look: an app with one loading state reads as one app. What is added
   here is the sport marker and the cases a form never hit — veiling a table,
   a filter bar, or a card grid. */

/* A ball with a track running round it. The ball alone said "sport" but not
   "working" — it bounced in place with nothing to measure it against. The ring
   sweeping around it is the part that reads as progress, and the two together
   are unmistakably this app rather than a generic spinner.

   Built as a rotating conic gradient masked to a ring, so it is one element
   with no SVG and no extra markup: the ball sits inside it, and the whole
   thing scales by changing one width. */
.bv-ball {
  width: 54px; height: 54px; position: relative;
  display: grid; place-items: center;
  animation: none;                    /* the ring spins; the ball bounces */
}
/* The track: a full circle at low opacity, so the sweep has something to run
   against and the control never looks half-drawn. */
.bv-ball::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 3px solid rgba(15, 46, 34, .13);
}
/* The sweep. */
.bv-ball::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 3px solid transparent;
  border-top-color: var(--forest);
  border-right-color: var(--forest);
  animation: bvSpin .9s linear infinite;
}
@keyframes bvSpin { to { transform: rotate(360deg); } }

/* The ball itself, now a child element so the ring can own the box. */
.bv-ball i {
  width: 20px; height: 27px; display: block; position: relative;
  background: radial-gradient(120% 120% at 35% 30%, #F2EFE6, #cfcab6);
  border-radius: 50% / 42%;
  border: 1.4px solid rgba(15,46,34,.30);
  animation: bvBounce 1.05s cubic-bezier(.3,.5,.4,1) infinite;
  filter: drop-shadow(0 3px 4px rgba(15,46,34,.28));
}
/* The lacing, so it reads as an oval ball and not a lozenge. */
.bv-ball i::before {
  content: ""; position: absolute; left: 50%; top: 16%; bottom: 16%;
  width: 1.6px; margin-left: -.8px; background: var(--forest);
}
.bv-ball i::after {
  content: ""; position: absolute; left: 50%; top: 28%;
  width: 7px; height: 1.2px; margin-left: -3.5px;
  box-shadow: 0 3px var(--forest), 0 6px var(--forest), 0 9px var(--forest);
}
@keyframes bvBounce {
  0%, 100% { transform: translateY(-3px) rotate(-11deg); }
  50%      { transform: translateY(3px) rotate(11deg); }
}

/* Scoped veils sit on panels that are often shorter than the auth panels the
   original was built for, so the padding is trimmed and the label sits closer. */
.bv-scoped { border-radius: inherit; }
.bv-scoped .bv-inner { gap: 11px; padding: 4px 14px; }
.bv-scoped .bv-label { font-size: 11.5px; letter-spacing: .14em; }

/* Short regions — a filter bar, a single row — cannot fit a stacked ball and
   caption without the veil growing taller than what it covers. Below that
   height it goes side-by-side and drops the ball to match the text. */
@media (min-height: 0px) {
  .bv-scoped[data-veil-compact] .bv-inner { flex-direction: row; gap: 10px; }
  .bv-scoped[data-veil-compact] .bv-ball { width: 32px; height: 32px; }
  .bv-scoped[data-veil-compact] .bv-ball i { width: 12px; height: 16px; }
}

/* On the deep-green sheet pages the cream veil is right, but on the dark board
   headers and the deep panels it would glare. Inverted there. */
.gt-dark .bv-scoped,
.page-deep .gt-prov .bv-scoped {
  background: rgba(11,35,25,.78);
}
.gt-dark .bv-scoped .bv-label { color: var(--cream); }

@media (prefers-reduced-motion: reduce) {
  .bv-ball::after { animation-duration: 2.4s; }
  .bv-ball i { animation-duration: 2.1s; }
}

/* ==========================================================================
   ============ v32 — ladder legibility ====================================
   ========================================================================== */

/* THE HEADER WAS INVISIBLE.
   
   .gt-board .gb-head paints dark-green text on the deep-green bar, which is
   correct on its own. Then the sheet-page rule recoloured the TEXT to
   rgba(15,46,34,.55) — near-black — for a cream bed, without also removing the
   dark background it was sitting on. Dark on dark: "Club P W L D % Pts" simply
   did not render for anyone using the ladder or leaderboard.
   
   Fixed by finishing what that rule started: on a sheet page the head is a
   plain caption row on the cream bed, so the dark plate goes too. */
.sheet-page .gt-board .gb-head {
  background: transparent;
  padding: 6px 20px 10px;
  border-bottom: 1px solid rgba(15,46,34,.13);
  border-radius: 0;
  color: rgba(15,46,34,.72);
}

/* Column striping. A ladder is read ACROSS — one club's row against eight
   columns — and an unbroken cream field gives the eye nothing to track along.
   A tint on alternating numeric columns, running the full height of the table,
   does that without the heavy banding of full row stripes.
   
   Deliberately on the columns rather than the rows: rows already carry meaning
   here (the finals cut, the leader), and striping them too would put three
   competing signals on the same surface. */
.gt-board.ladder .lad-col { position: relative; }
.gt-board.ladder .lad-head .lad-col::before,
.gt-board.ladder .lad-row .lad-col::before {
  content: ""; position: absolute; inset: -13px -3px; z-index: 0;
  background: rgba(15, 46, 34, .045);
  pointer-events: none;
}
/* Alternate, so adjacent columns are distinguishable rather than one solid
   block. nth-of-type counts the spans in the row: 3=P 4=W 5=L 6=D 7=% 8=Pts. */
.gt-board.ladder .lad-col:nth-of-type(even)::before { background: transparent; }
.gt-board.ladder .lad-head .lad-col::before { inset: -6px -3px -10px; }
/* The figure has to sit above its own tint. */
.gt-board.ladder .lad-num { position: relative; z-index: 1; }

/* Points is the column the table exists to report, so it gets the strongest
   treatment rather than sharing the alternating tint. */
.gt-board.ladder .lad-pts::before {
  background: rgba(45, 122, 58, .10) !important;
}
.gt-board.ladder .lad-row .lad-pts { color: var(--deep); font-weight: 800; }

/* Club crests in the ladder. The shared .crest sizing is tuned for match
   cards, which are roomier than a table row. */
.gt-board.ladder .gb-who .crest {
  width: 28px; height: 28px; flex: none; font-size: 10px;
}

/* The first column of figures needs a little air from the club name, or the
   longest names run straight into the P column. */
.gt-board.ladder .gb-who { padding-right: 10px; }

/* ==========================================================================
   ============ v33 — dashboard round navigator ============================
   ========================================================================== */

/* Move between rounds without leaving the dashboard. See the comment in
   dashboard_slate.html for why the flat cross-round list had to go.

   HUGS ITS CONTENT. The first cut stretched the pill to the full width of the
   panel, which put the three controls in the left third and left the other
   two thirds as an empty bordered box — the bar read as something that had
   failed to load rather than as a control. A navigator is a control cluster,
   so it is sized like one. */
.rnav {
  /* END TO END, directly under the competition chips.
     The chips and the navigator are one control group in use — pick a code,
     then move through its rounds — so they read as a pair when they share a
     width and sit together. The earlier inline-flex version hugged its content
     and floated at the left edge of a full-width filter above it, which made
     two related controls look unrelated. */
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: 100px; padding: 5px 8px; margin: -4px 0 14px;
  width: 100%; position: relative;
}
/* The picker takes the slack so the arrows stay pinned to the ends, which is
   what makes a full-width bar read as a navigator rather than as a stretched
   pill with controls rattling around inside it.

   IT HAS TO BE THE .ddx THAT GROWS, not the <select>. gt-ui.js progressively
   replaces every native select with a custom dropdown and hides the original
   at 1px — so sizing `select` styles something nobody can see, which is why
   the control stayed 197px wide in an 832px slot with a canyon of empty pill
   between it and the right arrow. */
.rnav .rnav-pick { flex: 1 1 auto; min-width: 0; }
.rnav .ddx { flex: 1 1 auto; width: 100%; }
.rnav .rnav-pick select { width: 100%; max-width: none; }

/* Bigger than a form field, because this is the page's primary control rather
   than one question among ten on a form. The round you are looking at is the
   thing the whole panel below it answers to. */
.rnav .ddx-btn {
  width: 100%;
  padding: 11px 16px;
  font-size: 16px;
  font-weight: 700;
  border-radius: 100px;
  background: var(--gt-bg);
}
.rnav .ddx-menu { border-radius: 16px; }
.rnav .ddx-opt { padding: 11px 16px; font-size: 14.5px; }

.rnav-arrow {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center; text-decoration: none;
  background: rgba(15,46,34,.06); color: var(--deep);
  transition: background .15s var(--ease), color .15s var(--ease);
}
.rnav-arrow svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.2; }
.rnav-arrow:hover { background: var(--deep); color: var(--lime); }
/* The end of the season is a real edge, so the control stays in place and goes
   quiet rather than vanishing — a disappearing arrow makes the row jump and
   leaves you wondering whether you mis-clicked. */
.rnav-arrow.is-off { opacity: .22; pointer-events: none; }

.rnav-pick { display: flex; align-items: center; min-width: 0; }
/* Borderless: the pill around it is already the border, and a second one
   inside it read as a box within a box. */
.rnav-pick select {
  border: 0; background: transparent; color: var(--ink);
  padding: 7px 10px; font-size: 15px; font-weight: 700;
  font-family: var(--body); cursor: pointer;
  min-width: 0; max-width: 230px;
}
.rnav-pick select:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; border-radius: 8px; }

/* How the round went, divided off rather than floated to a far edge — with the
   bar hugging its content there is no far edge to float to, and the numbers
   belong to the round named beside them. */
.rnav-score {
  display: flex; align-items: baseline; gap: 6px;
  padding-left: 12px; margin-left: 4px;
  border-left: 1px solid var(--gt-hair);
}
.rns-hit {
  font-family: var(--score); font-weight: 900; font-size: 18px; color: var(--deep);
  font-variant-numeric: tabular-nums;
}
.rns-hit span { font-size: 12.5px; font-weight: 700; color: var(--muted); }
.rns-lab {
  font-family: var(--mark); font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.rns-pts {
  font-family: var(--score); font-weight: 900; font-size: 18px; color: var(--forest);
  font-variant-numeric: tabular-nums; margin-left: 3px;
}
.rns-pts span { font-size: 10.5px; margin-left: 2px; }

.rnav-now {
  text-decoration: none; margin-left: 4px;
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase;
  background: var(--lime); color: var(--deep);
  padding: 8px 14px; border-radius: 100px; white-space: nowrap;
}
.rnav-now:hover { filter: brightness(1.06); }
.rnav-now.is-quiet { background: rgba(15,46,34,.06); color: var(--deep); }
.rnav-now.is-quiet:hover { background: rgba(15,46,34,.12); filter: none; }

@media (max-width: 560px) {
  .rnav { display: flex; width: 100%; border-radius: 18px; padding: 8px; }
  .rnav-pick { flex: 1 1 auto; }
  .rnav-pick select { max-width: none; width: 100%; }
  .rnav-score { width: 100%; border-left: 0; padding-left: 0; margin-left: 0; justify-content: center; }
  .rnav-now { width: 100%; text-align: center; margin-left: 0; }
}

/* Graded outcomes on a fixture card. Correct is the lime the app already uses
   for a live pick; missed is muted rather than alarming — you lost a tip, the
   card should not shout at you about it a week later. */
.fxc-state.is-hit  { background: var(--lime); color: var(--deep); font-weight: 800; }
.fxc-state.is-miss { background: rgba(15,46,34,.09); color: #7a6a6a; }

/* When the score strip is already carrying the emphasis, the way-home link
   steps down to a quiet secondary so the round's result stays the loudest
   thing in the bar. */
.rnav-now.is-quiet {
  background: rgba(15,46,34,.06); color: var(--deep);
  margin-left: 10px;
}
.rnav-now.is-quiet:hover { background: rgba(15,46,34,.11); filter: none; }
/* With a score strip present the auto margin belongs to the score, not to the
   link that follows it. */
.rnav-score + .rnav-now { margin-left: 10px; }

/* ==========================================================================
   ============ v34 — sync panel: automatic first ===========================
   ========================================================================== */

/* The page led with a manual form, so the reasonable reading was "syncing is
   something you do". It is not, and has not been for a while. This block says
   what runs and how often, above the fallback control. */
.gt-auto {
  background: var(--gt-deep); color: var(--cream);
  border-radius: 18px; padding: 22px 24px; margin-bottom: 22px;
}
.gta-head { display: flex; align-items: flex-start; gap: 13px; margin-bottom: 18px; }
.gta-head h3 {
  font-family: var(--mark); font-size: 13px; font-weight: 800;
  letter-spacing: .13em; text-transform: uppercase; color: var(--lime); margin: 0 0 5px;
}
.gta-head p { font-size: 13.5px; color: rgba(232,228,213,.75); margin: 0; max-width: 62ch; }

/* A live pulse, because "running" is the whole claim of this panel. */
.gta-dot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--lime);
  flex: none; margin-top: 4px; position: relative;
  box-shadow: 0 0 0 0 rgba(200,241,53,.6);
  animation: gtaPulse 2.2s ease-out infinite;
}
@keyframes gtaPulse {
  0%   { box-shadow: 0 0 0 0 rgba(200,241,53,.55); }
  70%  { box-shadow: 0 0 0 11px rgba(200,241,53,0); }
  100% { box-shadow: 0 0 0 0 rgba(200,241,53,0); }
}

.gta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.gta-item {
  background: rgba(255,255,255,.055); border: 1px solid rgba(232,228,213,.13);
  border-radius: 13px; padding: 13px 15px;
}
.gta-item.wide { grid-column: 1 / -1; }
.gta-item b {
  display: block; font-size: 14.5px; color: var(--white); font-weight: 700; margin-bottom: 2px;
}
.gta-item > span {
  display: inline-block; font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--lime); margin-bottom: 6px;
}
.gta-item small { display: block; font-size: 12.5px; color: rgba(232,228,213,.7); line-height: 1.5; }

.gt-subhead {
  font-family: var(--mark); font-size: 12px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 12px;
}

.gd-note {
  font-size: 12.5px; color: rgba(232,228,213,.72);
  margin: -2px 0 16px; line-height: 1.55;
}

@media (prefers-reduced-motion: reduce) { .gta-dot { animation: none; } }

/* ==========================================================================
   ============ v35 — admin news list =======================================
   ========================================================================== */

/* Cards, not a table. See the comment in manage/news.html: four columns on a
   640px minimum inside a half-width panel scrolled sideways at every real
   width, and the column that got pushed off was the actions. */
.np-list { display: grid; gap: 12px; }

.np-row {
  display: grid; grid-template-columns: 76px minmax(0,1fr); gap: 15px;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: 15px; padding: 14px;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.np-row:hover { border-color: rgba(45,122,58,.32); box-shadow: 0 14px 30px -26px rgba(15,46,34,.5); }
/* A draft is not live to anyone, so it reads back a step without being greyed
   into illegibility — you still have to be able to work on it. */
.np-row.is-draft { background: repeating-linear-gradient(135deg, var(--gt-card) 0 12px, rgba(15,46,34,.022) 12px 24px); }

.np-thumb {
  width: 76px; height: 76px; border-radius: 11px; overflow: hidden;
  background: rgba(15,46,34,.06); display: grid; place-items: center; color: var(--muted);
}
.np-thumb img { width: 100%; height: 100%; object-fit: cover; }
.np-thumb svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 1.6; }

.np-body { min-width: 0; }
.np-top { display: flex; align-items: flex-start; gap: 12px; }
/* The headline is the field that matters and now looks like it. */
.np-title {
  font-family: var(--display); font-size: 16.5px; line-height: 1.35;
  color: var(--ink); margin: 0; flex: 1 1 auto; min-width: 0;
}
.np-top .gt-chip { flex: none; margin-top: 1px; }

.np-meta {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--muted); margin: 5px 0 11px;
}
.np-tag { display: inline-flex; align-items: center; gap: 5px; }
.np-tag svg, .np-sent svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 1.9; }
.np-sep { opacity: .45; }
.np-sent { display: inline-flex; align-items: center; gap: 4px; color: var(--forest); font-weight: 600; }

.np-acts { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.np-acts form { margin: 0; }
.np-btn {
  display: inline-block; border: 1px solid var(--gt-hair); background: var(--gt-bg);
  color: var(--ink); border-radius: 100px; padding: 6px 14px;
  font-size: 12.5px; font-weight: 600; text-decoration: none; cursor: pointer;
  transition: background .14s var(--ease), border-color .14s var(--ease), color .14s var(--ease);
}
.np-btn:hover { background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.22); }
.np-btn-go { background: var(--lime); border-color: transparent; color: var(--deep); font-weight: 700; }
.np-btn-go:hover { filter: brightness(1.05); background: var(--lime); }

/* Delete is pushed to the far end AND coloured, because in the old row it was
   the fourth of four identical links — one slip from Email members. */
.np-del { margin-left: auto; }
.np-btn-danger { color: var(--danger, #9a2b2b); border-color: rgba(154,43,43,.28); background: rgba(154,43,43,.05); }
.np-btn-danger:hover { background: #9a2b2b; border-color: #9a2b2b; color: #fff; }

@media (max-width: 560px) {
  .np-row { grid-template-columns: 54px minmax(0,1fr); }
  .np-thumb { width: 54px; height: 54px; }
  .np-del { margin-left: 0; }
}

/* ---- Story editor: full-page create/edit, replacing the sidebar form ----
   The client's exact complaint was that the box to write a story in was too
   small. This is a wide, standalone page: a styled headline, a full-height
   body canvas with its own formatting toolbar, and everything else (tag,
   teaser, featured image, sources, publish) as calm supporting fields around
   it rather than crowding the writing surface. */
.ned-shell { max-width: 900px; margin: 0 auto; }
.ned-topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 20px;
}
.ned-topbar .ned-back { font-size: 13px; font-weight: 700; color: var(--forest); text-decoration: none; }
.ned-topbar .ned-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.ned-meta-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 22px; }
.ned-meta-row .fld { margin-bottom: 0; }
.ned-meta-row select { width: auto; min-width: 140px; }
.ned-url { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted); }
.ned-url-k { font-weight: 600; }
.ned-url-v {
  font-family: var(--mark); font-weight: 600; color: var(--ink);
  background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 8px;
  padding: 5px 9px; max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ned-url-btn {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer;
  background: none; border: 1px solid var(--gt-hair); border-radius: 8px; padding: 6px 10px;
  font-family: var(--body); font-size: 12.5px; font-weight: 700; color: var(--forest);
}
.ned-url-btn:hover { border-color: var(--forest); background: rgba(45,122,58,.07); }
.ned-url-btn svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; }
.ned-url-btn.copied { background: var(--lime); border-color: var(--lime); color: var(--deep); }

.ned-field-label { font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 8px; display: block; }

.ned-editor { margin-bottom: 26px; }
.ned-toolbar {
  display: flex; align-items: center; gap: 3px; flex-wrap: wrap;
  background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 12px 12px 0 0;
  padding: 7px;
}
.ned-toolbar button, .ned-toolbar select {
  border: 1px solid transparent; background: transparent; color: var(--ink);
  border-radius: 8px; padding: 6px 9px; font-size: 13px; font-weight: 700; cursor: pointer;
  font-family: var(--body); line-height: 1;
}
.ned-toolbar button:hover, .ned-toolbar select:hover { background: rgba(15,46,34,.07); }
/* A 118px cap used to cut the longer option labels — "Small subtitle",
   "Condensed" — off mid-word, so the control showed a name you could not read
   and gave no clue what it was set to. Each picker now sizes to its own widest
   option and holds that width (flex-shrink off, or a tight toolbar would claw
   the pixels straight back); when the page is narrow the toolbar wraps to a
   second line instead, which costs a few pixels of height and nothing else. */
.ned-toolbar select.ned-pick {
  flex: 0 0 auto; width: auto; max-width: none;
  padding: 6px 8px; font-weight: 600;
  border-color: var(--gt-hair); background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink));
}
.ned-toolbar select.ned-pick:hover { border-color: var(--forest); background: var(--ned-surf, #fff); }

/* ---- Toolbar drop-downs: font and size ----
   Both are a control with a list hanging under it rather than a <select>,
   because a select truncates its own labels at a width the browser picks and
   cannot draw an option in the font that option names. Note `overflow:
   visible` on the shells below — the rounded corners are cut on the children
   instead, since clipping the shell would clip the list right off. */
.ned-menu {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  min-width: 100%; width: max-content; max-width: 320px;
  max-height: 268px; overflow-y: auto; overscroll-behavior: contain;
  padding: 6px; background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink));
  border: 1px solid var(--app-line); border-radius: 11px;
  box-shadow: 0 14px 34px rgba(15, 46, 34, .17);
}
.ned-menu.open { display: block; }
.ned-menu-head {
  margin: 0; padding: 7px 9px 4px; font-family: var(--body);
  font-size: 10px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted);
}
.ned-menu-head + .ned-menu-item { margin-top: 0; }
/* Sits inside .ned-toolbar, so it has to out-specify the plain button rule
   above — everything except font-family, which each item sets inline to show
   itself off in its own typeface. */
.ned-toolbar .ned-menu-item {
  display: flex; align-items: baseline; gap: 9px; width: 100%;
  padding: 7px 10px; border: 0; border-radius: 7px; background: none;
  color: var(--ink); font-size: 15px; font-weight: 500; line-height: 1.3;
  text-align: left; white-space: nowrap; cursor: pointer;
}
.ned-toolbar .ned-menu-item em {
  font-family: var(--body); font-style: normal; font-size: 11px;
  font-weight: 600; color: var(--muted);
}
.ned-toolbar .ned-menu-item:hover { background: rgba(45, 122, 58, .10); }
/* Where you already are, so opening the list answers that before you pick. */
.ned-toolbar .ned-menu-item.is-on { background: var(--lime); color: var(--deep); }
.ned-toolbar .ned-menu-item.is-on em { color: var(--deep); opacity: .7; }

.ned-font { position: relative; display: inline-flex; flex: 0 0 auto; }
.ned-toolbar .ned-font-btn {
  display: inline-flex; align-items: center; gap: 7px; height: 30px;
  padding: 0 9px; border: 1px solid var(--gt-hair); border-radius: 8px;
  background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink)); font-weight: 600; font-size: 13px;
  /* The name is never shortened — the toolbar wraps to a second line first.
     A control that names a font you cannot read is the bug being fixed. */
  white-space: nowrap; max-width: none;
}
.ned-toolbar .ned-font-btn:hover { border-color: var(--forest); background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink)); }
.ned-caret { width: 9px; height: 6px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; opacity: .7; }

/* ---- Font size: a number you nudge, or pick off a list ---- */
.ned-size {
  position: relative;
  display: inline-flex; align-items: center; flex: 0 0 auto;
  border: 1px solid var(--gt-hair); border-radius: 8px; background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink));
}
.ned-toolbar .ned-size-step {
  width: 26px; height: 30px; padding: 0; border: 0; background: none;
  color: var(--forest); font-size: 15px; font-weight: 800; line-height: 1; cursor: pointer;
}
.ned-size-step[data-size-step="-1"] { border-radius: 7px 0 0 7px; }
.ned-size-step[data-size-step="1"] { border-radius: 0 7px 7px 0; }
.ned-toolbar .ned-size-step:hover { background: rgba(45,122,58,.12); }
.ned-size-num {
  width: 42px; padding: 6px 0; text-align: center; background: none; outline: none;
  border: 0; border-left: 1px solid var(--gt-hair); border-right: 1px solid var(--gt-hair);
  font-family: var(--body); font-size: 13px; font-weight: 700; color: var(--ink);
  cursor: pointer; -moz-appearance: textfield;
}
.ned-size-num:focus { background: rgba(200,241,53,.25); }
/* The native spinners are tiny, and the − / + buttons beside them are the
   whole point of this control. */
.ned-size-num::-webkit-outer-spin-button,
.ned-size-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Numbers read better in a narrow column, centred, than as a ragged list. */
.ned-menu.is-sizes { min-width: 76px; padding: 5px; }
.ned-toolbar .ned-menu.is-sizes .ned-menu-item {
  justify-content: center; padding: 6px 8px;
  font-family: var(--body); font-size: 13px; font-weight: 700;
}
/* ---- Undo / redo, and the pressed state on the format buttons ----
   A toolbar that never shows what is switched on is write-only: you can turn
   bold on but the button looks the same either way, so the only way to check
   is to squint at the letters. */
.ned-toolbar button.is-on {
  background: var(--lime);
  color: var(--deep);
  border-color: transparent;
}
/* The ink restated, not inherited: on the dark palette the inherited ink is
   near-white, and near-white on lime is 1.1:1. */
.ned-toolbar button.is-on:hover { background: var(--lime); color: var(--deep); }
/* Greyed out is the only signal that there is nothing left to undo — and the
   only sign the editor is tracking the work at all. */
.ned-toolbar button[data-history]:disabled {
  opacity: .32;
  cursor: default;
}
.ned-toolbar button[data-history]:disabled:hover { background: transparent; }
.ned-toolbar button[data-history],
.ned-toolbar button.ned-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 30px; padding: 0;
}

/* ---- Text colour and highlight ----
   A swatch grid instead of the native colour input, which opens the operating
   system's own dialog: a full modal, three clicks deep, that has never heard
   of the house palette. */
.ned-swatch { position: relative; display: inline-flex; flex: 0 0 auto; }
.ned-toolbar .ned-swatch-btn {
  display: inline-flex; align-items: center; gap: 5px; height: 30px;
  padding: 0 7px; border: 1px solid var(--gt-hair); border-radius: 8px;
  background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink));
}
.ned-toolbar .ned-swatch-btn:hover { border-color: var(--forest); background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink)); }
.ned-sw-ic { width: 16px; height: 16px; flex: none; }
/* The bar under the icon is the whole point of the button: it shows the colour
   about to be applied, and for a highlight it shows nothing when there is
   none — a swatch painted the colour of the page would read as white. */
.ned-sw-bar {
  width: 15px; height: 5px; border-radius: 2px; flex: none;
  background: var(--deep); border: 1px solid rgba(15,46,34,.16);
}
.ned-sw-bar.is-none {
  background: repeating-linear-gradient(
    45deg, transparent 0 3px, rgba(192,57,43,.55) 3px 4px
  );
}
.ned-menu.is-swatches { min-width: 208px; padding: 7px; }
.ned-sw-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; padding: 4px 3px 7px;
}
.ned-toolbar .ned-sw {
  width: 100%; aspect-ratio: 1; padding: 0; border-radius: 7px;
  background: var(--sw); border: 1px solid rgba(15,46,34,.18); cursor: pointer;
}
.ned-toolbar .ned-sw:hover {
  background: var(--sw);
  box-shadow: 0 0 0 2px var(--deep);
}
.ned-toolbar .ned-sw.is-on { box-shadow: 0 0 0 2px var(--deep); }
.ned-sw-nib {
  width: 14px; height: 14px; border-radius: 4px; flex: none;
  border: 1px solid rgba(15,46,34,.2);
}
.ned-sw-nib.is-none {
  background: repeating-linear-gradient(
    45deg, transparent 0 3px, rgba(192,57,43,.55) 3px 4px
  );
}
.ned-toolbar .ned-menu-item.ned-sw-none {
  font-family: var(--body); font-size: 13px; font-weight: 600; align-items: center;
}
.ned-sw-custom {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 9px; margin-top: 3px; border-top: 1px solid var(--gt-hair);
  font-family: var(--body); font-size: 12px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}

.ned-toolbar input[type="color"] {
  width: 30px; height: 30px; padding: 2px; border: 1px solid var(--gt-hair);
  border-radius: 8px; background: none; cursor: pointer;
}
.ned-toolbar .ned-sep { width: 1px; align-self: stretch; background: var(--gt-hair); margin: 3px 5px; }
.ned-tool-wrap { position: relative; }

.ned-pop {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 5;
  background: var(--ned-surf, #fff); color: var(--ned-surf-ink, var(--ink)); border: 1px solid var(--gt-hair); border-radius: 10px; padding: 10px;
  box-shadow: 0 18px 40px -20px rgba(15,46,34,.5); gap: 8px; align-items: center; min-width: 260px;
}
.ned-pop.open { display: flex; }
.ned-pop input { flex: 1; padding: 8px 10px; border: 1px solid var(--app-line); border-radius: 8px; font-size: 13px; }
.ned-pop button {
  flex: none; background: var(--lime); border: none; color: var(--deep);
  border-radius: 8px; padding: 8px 12px; font-weight: 700; font-size: 13px; cursor: pointer;
}

.ned-surface {
  border: 1px solid var(--gt-hair); border-top: none; border-radius: 0 0 12px 12px;
  background: var(--ned-surf, #fff); padding: 20px 22px; outline: none; color: var(--ned-surf-ink, var(--ink));
}
.ned-surface:empty:before { content: attr(data-placeholder); color: var(--muted); }
.ned-surface p { margin: 0 0 14px; }
.ned-surface h2 { font-family: var(--display); font-size: 22px; margin: 22px 0 10px; color: var(--ink); }
.ned-surface h3 { font-family: var(--display); font-size: 18px; margin: 18px 0 8px; color: var(--ink); }
.ned-surface blockquote {
  border-left: 3px solid var(--lime); margin: 14px 0; padding: 4px 0 4px 16px;
  color: var(--muted); font-style: italic;
}
.ned-surface img { max-width: 100%; border-radius: 10px; margin: 10px 0; display: block; }
.ned-surface ul, .ned-surface ol { margin: 0 0 14px 22px; }
.ned-surface a { color: var(--forest); }

.ned-editor.is-headline .ned-surface {
  font-family: var(--display); font-size: clamp(24px, 3.6vw, 34px); line-height: 1.2;
  padding: 18px 22px; min-height: 1.3em;
}
.ned-editor.is-teaser .ned-surface {
  min-height: 3.6em; font-size: 17px; line-height: 1.6; padding: 14px 18px; color: var(--ink);
}
.ned-editor.is-body .ned-surface { min-height: 46vh; font-size: 16px; line-height: 1.75; }

.ned-hint { color: var(--muted); font-weight: 400; font-size: 12px; }

/* The browser's own file input cannot be styled, shows a truncated filename,
   previews nothing and gives no way to take an image back off a post. It is
   kept off-screen and driven from the button, so this row is a real drop zone:
   thumbnail, filename and weight, replace, remove. */
.ned-drop {
  position: relative;
  border: 1.5px dashed var(--gt-hair); border-radius: 14px; padding: 20px;
  display: flex; align-items: center; gap: 16px; background: var(--gt-card);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.ned-drop.has-image { border-style: solid; }
.ned-drop.is-dragging { border-color: var(--forest); background: rgba(45,122,58,.07); }
.ned-drop .ned-drop-prev {
  width: 92px; height: 92px; border-radius: 10px; overflow: hidden; flex: none;
  background: rgba(15,46,34,.06); display: grid; place-items: center; color: var(--muted);
}
.ned-drop .ned-drop-prev img { width: 100%; height: 100%; object-fit: cover; }
.ned-drop .ned-drop-ph { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 1.6; }
.ned-drop .ned-drop-txt { flex: 1; min-width: 0; }
.ned-drop .ned-drop-txt b {
  display: block; font-size: 14.5px; color: var(--ink); margin-bottom: 3px;
  /* break-word, not anywhere. Both wrap a long filename with no spaces in it,
     but `anywhere` also lets the box report a min-content width of one
     character, so anything competing for room in this flex row can squeeze the
     label into a vertical column of letters — which is exactly what a leaked
     template comment did here. `break-word` keeps the longest word as the
     floor, so the collapse cannot happen again. */
  overflow-wrap: break-word;
}
.ned-drop .ned-drop-txt span { font-size: 12.5px; color: var(--muted); }
.ned-drop-acts { flex: none; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ned-drop-btn {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  background: var(--lime); border: 1px solid transparent; color: var(--deep);
  border-radius: 10px; padding: 10px 16px;
  font-family: var(--body); font-size: 13.5px; font-weight: 700; white-space: nowrap;
}
.ned-drop-btn:hover { filter: brightness(1.05); }
.ned-drop-btn svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.ned-drop-clear {
  cursor: pointer; background: none; border: 1px solid var(--gt-hair); color: var(--ned-danger, #9a2b2b);
  border-radius: 10px; padding: 10px 14px;
  font-family: var(--body); font-size: 13px; font-weight: 700; white-space: nowrap;
}
.ned-drop-clear:hover { background: #9a2b2b; color: #fff; border-color: #9a2b2b; }
.ned-drop-clear[hidden] { display: none; }
/* Off-screen rather than display:none — a hidden input still has to be
   focusable enough for .click() and for the form to post its file. */
.ned-drop-file { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.ned-source-row { display: flex; gap: 10px; margin-bottom: 10px; align-items: center; }
.ned-source-row input { flex: 1; padding: 10px 12px; border: 1px solid var(--app-line); border-radius: 9px; font-size: 14px; }
.ned-source-row input.ned-src-url { flex: 1.3; }
.ned-source-remove {
  flex: none; width: 32px; height: 32px; border-radius: 9px; border: 1px solid var(--gt-hair);
  background: var(--gt-bg); color: var(--ned-danger, #9a2b2b); cursor: pointer; display: grid; place-items: center;
}
.ned-source-remove:hover { background: #9a2b2b; color: #fff; border-color: #9a2b2b; }
.ned-source-remove svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; }
.ned-source-add {
  display: inline-flex; align-items: center; gap: 6px; background: none; border: 1px dashed var(--gt-hair);
  color: var(--forest); border-radius: 9px; padding: 9px 14px; font-weight: 700; font-size: 13px; cursor: pointer;
}
.ned-source-add:hover { border-color: var(--forest); background: rgba(45,122,58,.06); }
.ned-source-add svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2.2; }

.ned-publish-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 14px; padding: 16px 18px;
  margin-top: 6px;
}
.ned-publish-row label { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 14px; font-weight: 600; color: var(--ink); }
.ned-publish-row input[type="checkbox"] { width: 18px; height: 18px; }

@media (max-width: 640px) {
  .ned-toolbar { position: static; }
  .ned-editor.is-body .ned-surface { min-height: 34vh; }
  .ned-drop { flex-wrap: wrap; }
  .ned-drop-acts { width: 100%; }
  .ned-drop-btn { flex: 1; justify-content: center; }
  .ned-url-v { max-width: 100%; }
}

/* ---- Article share bar + optional sources, on the published story page ---- */
.art-share, .pn-share {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--app-line);
}
.art-share .as-label, .pn-share .as-label {
  font-size: 12.5px; font-weight: 700; color: var(--muted); text-transform: uppercase;
  letter-spacing: .06em; margin-right: 2px;
}
.as-btn {
  width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--app-line); color: var(--ink); text-decoration: none;
  transition: transform .15s var(--ease), background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
  background: #fff; cursor: pointer;
}
.as-btn svg { width: 17px; height: 17px; }
.as-btn.as-facebook svg, .as-btn.as-linkedin svg { fill: currentColor; }
.as-btn.as-copy svg { fill: none; stroke: currentColor; stroke-width: 2; }
.as-btn:hover { transform: translateY(-2px); }
.as-btn.as-facebook:hover { background: #1877F2; border-color: #1877F2; color: #fff; }
.as-btn.as-linkedin:hover { background: #0A66C2; border-color: #0A66C2; color: #fff; }
.as-btn.as-copy {
  width: auto; border-radius: 100px; padding: 0 16px; gap: 7px; font-size: 12.5px; font-weight: 700;
}
.as-btn.as-copy.copied { background: var(--lime); border-color: var(--lime); color: var(--deep); }

.art-sources, .pn-sources {
  margin-top: 22px; padding: 16px 18px; background: var(--gt-card);
  border: 1px solid var(--app-line); border-radius: 12px;
}
.art-sources h4, .pn-sources h4 { font-size: 12.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 8px; }
.art-sources ol, .pn-sources ol { margin: 0; padding-left: 18px; }
.art-sources li, .pn-sources li { font-size: 13.5px; margin-bottom: 5px; }
.art-sources a, .pn-sources a { color: var(--forest); word-break: break-word; }

/* ---- Footer brand-column socials ----
   The footer's "Follow us" list reuses .social-btn (see CONTACT v2 +
   SOCIALS above) rather than its own icon-only treatment: one labelled
   set of social links, styled one way, instead of two different-looking
   social rows on the same page. */

/* ==========================================================================
   ============ v36 — leaderboard column tint ==============================
   ========================================================================== */

/* Same treatment as the ladder, for the same reason: the table is read across
   — one person's row against their record and their points — and an unbroken
   field gives the eye nothing to follow. Only the two numeric columns are
   tinted; Rank and Tipper are the anchors and stay clear.
   
   Applied by position rather than by a class, because this table's cells are
   named per column (.gb-correct, .gb-pts) rather than sharing one. */
.gt-board .gb-head > span:nth-child(3),
.gt-board .gb-head > span:nth-child(4),
.gt-board .gb-row > .gb-correct,
.gt-board .gb-row > .gb-pts { position: relative; }

.gt-board .gb-row > .gb-correct::before,
.gt-board .gb-row > .gb-pts::before,
.gt-board .gb-head > span:nth-child(3)::before,
.gt-board .gb-head > span:nth-child(4)::before {
  content: ""; position: absolute; inset: -13px -7px; z-index: 0;
  pointer-events: none;
}
.gt-board .gb-row > .gb-correct::before,
.gt-board .gb-head > span:nth-child(3)::before { background: rgba(15,46,34,.04); }
/* Points is what the board ranks on, so it carries the stronger tint — the
   same weighting the ladder gives its own Pts column. */
.gt-board .gb-row > .gb-pts::before,
.gt-board .gb-head > span:nth-child(4)::before { background: rgba(45,122,58,.09); }
.gt-board .gb-head > span:nth-child(3)::before,
.gt-board .gb-head > span:nth-child(4)::before { inset: -6px -7px -10px; }

/* Figures above their own tint. */
.gt-board .gb-correct, .gt-board .gb-pts,
.gt-board .gb-head > span { z-index: 1; }

/* The ladder defines its own striping on .lad-col; this must not double up on
   it, or the Pts column gets two overlapping tints. */
.gt-board.ladder .gb-head > span:nth-child(3)::before,
.gt-board.ladder .gb-head > span:nth-child(4)::before { display: none; }

/* ==========================================================================
   ============ v37 — non-destructive busy button ==========================
   ========================================================================== */

/* For buttons picked up by the blanket pass, where the label was NOT authored.
   See gt-busy.js: swapping contents for the word "Working" wrecks any button
   not built to hold words — the Wall's "×" delete becomes a wide rectangle,
   icon buttons lose their icon, short buttons jump width mid-click.
   
   This changes nothing about the box. The button keeps its own size, its own
   content and its own colours; the label dims and a spinner sits on top. */
.is-btn-busy {
  position: relative;
  cursor: progress;
  pointer-events: none;
}
/* Dim the content in place rather than removing it, so the width is held by
   the same text that was always there. */
.is-btn-busy > * { opacity: .18; }
.is-btn-busy {
  color: transparent !important;
  text-shadow: none !important;
}

.is-btn-busy::after {
  content: "";
  position: absolute; top: 50%; left: 50%;
  width: 15px; height: 15px; margin: -7.5px 0 0 -7.5px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  /* currentColor is transparent above, so the ring needs its own colour. Two
     variants because these buttons sit on both light and dark grounds. */
  border-color: rgba(15,46,34,.28);
  border-top-color: var(--forest);
  animation: busySpin .75s linear infinite;
}
/* The ring has to contrast with the button it sits on, and the buttons split
   two ways rather than one.

   DARK grounds — a white ring. */
.abtn-dark.is-btn-busy::after,
.tier-cta.is-btn-busy::after {
  border-color: rgba(255,255,255,.3);
  border-top-color: #fff;
}
/* LIME grounds — a dark ring. White on lime is nearly invisible, which is what
   the first pass shipped: the button dimmed its label and appeared to show
   nothing at all in its place. */
.abtn-primary.is-btn-busy::after,
.gt-go-btn.is-btn-busy::after {
  border-color: rgba(15,46,34,.28);
  border-top-color: var(--deep);
}

@media (prefers-reduced-motion: reduce) {
  .is-btn-busy::after { animation-duration: 1.8s; }
}

/* A finished round's head reports what happened, not a lock time that passed
   months ago. Muted, because it is context for games you can no longer act on. */
.fxr-locks.done { color: rgba(15,46,34,0.64); font-weight: 600; }

/* ==========================================================================
   ============ v38 — sticky nav sits ON the page ===========================
   ========================================================================== */

/* The app bar is dark green, full-bleed, and floats over a photographic
   background while the content sheet below it is a narrower column. With only
   a 1px hairline under it there was nothing to say the bar was in front —
   content slid beneath it with no shadow to cross, so at a glance the bar read
   as a separate slab rather than as chrome the page passes under.
   
   A soft drop shadow is the whole fix: it gives the content something to
   disappear behind, which is what makes a sticky header feel attached to the
   viewport instead of stranded over the artwork. */
.app-nav {
  box-shadow: 0 10px 28px -18px rgba(0, 0, 0, .75);
}

/* An auto-assigned pick, on My Tips. Amber rather than the neutral grey of a
   real timestamp: it is not an error, but it IS the one line on the card that
   somebody might want to argue with, so it should not read as ordinary chrome. */
.gfx-stamp.is-auto {
  color: #84691E;
  background: rgba(240, 190, 60, .14);
  border-radius: 100px;
  padding: 2px 10px;
  font-weight: 600;
}

/* ==========================================================================
   ============ v39 — the in-play game you didn't tip =======================
   ========================================================================== */

/* My Tips shows your picks, with one exception: a match being played right now
   stays whether you tipped it or not, because "this is on and you are not in
   it" is worth knowing while the round is live. See tipping.views.my_tips_view.

   It must not look like one of your own rows. Dashed rather than solid, and
   muted rather than coloured — present, clearly not yours, and not shouting
   about a thing you can no longer do anything about. */
.gt-fixwrap.no-tip .fxc {
  border-left-color: rgba(15, 46, 34, .22);
  border-left-style: dashed;
  background: rgba(15, 46, 34, .015);
}
.gt-fixwrap.no-tip .gfx-state { background: rgba(15, 46, 34, .04); }
.gt-fixwrap.no-tip .res.none {
  color: #6b6b6b;
  font-weight: 600;
}
/* The clubs are not your pick, so they do not carry the picked-team emphasis
   the rest of the page uses — that colouring means "this is the side I chose". */
.gt-fixwrap.no-tip .fxc-team { opacity: .82; }

/* ==========================================================================
   ============ the create-a-group sheet ===================================
   ========================================================================== */

/* Replaces the permanent form that used to sit under the group list. It asked
   for a name, a "kind" and a free-text description as three separate
   optional-looking fields, which is three decisions ahead of the only one that
   matters. This asks that one: which team is this — pick the kind your
   organisation actually uses, or name something the list has never heard of. */
.gmodal { position: fixed; inset: 0; z-index: 120; display: grid; place-items: center; padding: 20px; }
.gmodal[hidden] { display: none; }
.gmodal .gm-backdrop { position: absolute; inset: 0; background: rgba(6,20,14,.7); backdrop-filter: blur(3px); }
.gmodal .gm-panel {
  position: relative; width: min(620px, 100%); max-height: min(86vh, 760px);
  display: flex; flex-direction: column;
  background: #F6F4EA; color: var(--ink);
  border-radius: 20px; overflow: hidden;
  box-shadow: 0 50px 100px -30px rgba(0,0,0,.75);
}
.gmodal .gm-panel > form { display: flex; flex-direction: column; min-height: 0; }
@media (prefers-reduced-motion: no-preference) {
  .gmodal .gm-panel { animation: gm-rise .26s var(--ease) both; }
}
@keyframes gm-rise { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }

.gmodal .gm-head {
  display: flex; align-items: flex-start; gap: 16px;
  padding: 24px 26px 16px; border-bottom: 1px solid rgba(15,46,34,.09);
}
.gmodal .gm-head h2 { font-family: var(--display); font-size: 23px; color: var(--ink); margin: 0; letter-spacing: -.01em; }
.gmodal .gm-head p { margin: 5px 0 0; font-size: 13.5px; color: #566356; }
.gmodal .gm-x {
  margin-left: auto; flex: none; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center; cursor: pointer;
  background: rgba(15,46,34,.06); border: 0; color: var(--ink);
}
.gmodal .gm-x:hover { background: rgba(15,46,34,.12); }
.gmodal .gm-x svg { width: 16px; height: 16px; }

.gmodal .gm-body { padding: 20px 26px 4px; overflow-y: auto; min-height: 0; }
.gmodal .gm-label {
  font-size: 11px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: #566356; margin-bottom: 11px;
}
.gmodal .gm-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.gmodal .gm-chip {
  padding: 9px 15px; border-radius: 100px; cursor: pointer;
  background: #fff; border: 1.5px solid rgba(15,46,34,.14);
  font-family: var(--body); font-size: 13.5px; font-weight: 600; color: var(--ink);
  transition: border-color .14s var(--ease), background .14s var(--ease), color .14s var(--ease);
}
.gmodal .gm-chip:hover { border-color: var(--forest); color: var(--forest); }
.gmodal .gm-chip.on {
  background: var(--lime); border-color: var(--lime); color: var(--deep); font-weight: 800;
}
.gmodal .gm-or {
  display: flex; align-items: center; gap: 12px; margin: 20px 0 16px;
  font-size: 12px; color: #566356;
}
.gmodal .gm-or::before, .gmodal .gm-or::after {
  content: ""; flex: 1; height: 1px; background: rgba(15,46,34,.12);
}
.gmodal .gm-body .fld input[type="text"] {
  width: 100%; padding: 13px 15px; font-size: 15px; border-radius: 11px;
  background: #fff; border: 1.5px solid rgba(15,46,34,.15); color: var(--ink);
  font-family: var(--body);
}
.gmodal .gm-body .fld input[type="text"]:focus {
  outline: none; border-color: var(--lime); box-shadow: 0 0 0 3px rgba(200,241,53,.25);
}

.gmodal .gm-foot {
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 18px 26px 22px; margin-top: auto;
  border-top: 1px solid rgba(15,46,34,.09); background: rgba(15,46,34,.02);
}
@media (max-width: 560px) {
  .gmodal .gm-foot { flex-direction: column-reverse; }
  .gmodal .gm-foot .abtn { width: 100%; justify-content: center; }
}

/* The sheet's panel is CREAM while the page it sits on is .page-deep, so the
   page-level .abtn-ghost — cream text, pale border, meant for the dark sheet —
   landed cream-on-cream at 1.16:1 and the Cancel button was invisible. Same
   trap as .room-bar and the group card's meta line: the page being dark says
   nothing about the surface a control is drawn on. */
.gmodal .gm-foot .abtn-ghost,
.sheet-page .gmodal .gm-foot .abtn-ghost {
  background: #fff;
  border-color: rgba(15,46,34,.18);
  color: var(--ink);
}
.gmodal .gm-foot .abtn-ghost:hover,
.sheet-page .gmodal .gm-foot .abtn-ghost:hover {
  border-color: var(--forest); color: var(--forest); background: #fff;
}

/* ==========================================================================
   ============ nav: brand to the edge, org name legible ===================
   ========================================================================== */

/* The nav row stays centred on the same max-width as the page content, so the
   brand lines up with the content beneath it rather than sitting out on the
   window edge. (A full-bleed version was tried and pulled: it put the brand
   and the account controls in different optical columns to everything else on
   the page.) Only the type sizing below is kept from that attempt. */

/* The organisation is the thing this control exists to name — and at 14px,
   sharing a row with eight nav links, it read as a caption rather than as
   where you are. */
.an-ctx-btn { font-size: 15px; }
.an-ctx-org { font-weight: 700; font-size: 15.5px; opacity: .95; }
.an-ctx-group { font-size: 15.5px; }
.an-ctx-sep { font-size: 15px; }

/* ==========================================================================
   ============ room switcher — one control, both surfaces =================
   ========================================================================== */

/* Replaces .room-bar and .room-strip (and the nav dropdown's group list) with
   a single segmented control. The old pattern hid the rooms behind a dropdown
   or listed them as "IT →" buttons that never marked the one you were in;
   a segmented control states position and options in the same glance.

   Two skins, chosen by the caller, never inferred: .on-light for cream cards,
   .on-dark for the sheet. */
.room-switch { margin-bottom: 20px; }

.room-switch .rw-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.room-switch .rw-eyebrow {
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  flex: none;
}
.room-switch .rw-note { margin: 0; font-size: 13px; line-height: 1.45; }
.room-switch .rw-note b { font-weight: 800; }

/* The track. Scrolls rather than wraps: an org with a dozen groups should push
   sideways, not grow a second row that shifts the page every time you switch. */
.room-switch .rw-seg {
  display: flex; align-items: stretch; gap: 4px;
  padding: 5px; border-radius: 15px;
  overflow-x: auto; scrollbar-width: none;
}
.room-switch .rw-seg::-webkit-scrollbar { display: none; }
.room-switch .rw-seg form { margin: 0; display: flex; }

.room-switch .rw-opt {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  padding: 9px 16px; border-radius: 11px; cursor: pointer;
  border: 0; background: transparent; font-family: var(--body);
  white-space: nowrap;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.room-switch .rw-opt b { font-size: 14px; font-weight: 700; line-height: 1.2; }
.room-switch .rw-opt i {
  font-style: normal; font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; opacity: .62;
}

.room-switch .rw-more {
  display: inline-flex; align-items: center; gap: 5px; align-self: center;
  margin-left: auto; padding: 0 14px; white-space: nowrap;
  font-size: 12.5px; font-weight: 800; text-decoration: none;
}
.room-switch .rw-more svg { width: 13px; height: 13px; }
.room-switch .rw-more:hover { text-decoration: underline; }

/* ---------- on a cream card (dashboard) ---------- */
.room-switch.on-light .rw-eyebrow { color: #566356; }
.room-switch.on-light .rw-note { color: #566356; }
.room-switch.on-light .rw-note b { color: var(--ink); }
.room-switch.on-light .rw-seg { background: rgba(45,122,58,.08); border: 1px solid rgba(15,46,34,.1); }
.room-switch.on-light .rw-opt { color: var(--ink); }
.room-switch.on-light .rw-opt:hover { background: rgba(255,255,255,.7); }
.room-switch.on-light .rw-opt.on { background: var(--lime); color: var(--deep); box-shadow: 0 4px 12px -4px rgba(140,180,30,.6); }
.room-switch.on-light .rw-opt.on i { opacity: .72; }
.room-switch.on-light .rw-more { color: var(--forest); }

/* ---------- on the dark sheet (tips, leaderboard, ladder) ---------- */
.room-switch.on-dark .rw-eyebrow { color: rgba(232,228,213,.55); }
.room-switch.on-dark .rw-note { color: rgba(232,228,213,.72); }
.room-switch.on-dark .rw-note b { color: #fff; }
.room-switch.on-dark .rw-seg { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); }
.room-switch.on-dark .rw-opt { color: var(--cream); }
.room-switch.on-dark .rw-opt:hover { background: rgba(255,255,255,.09); }
.room-switch.on-dark .rw-opt.on { background: var(--lime); color: var(--deep); box-shadow: 0 4px 14px -4px rgba(200,241,53,.5); }
.room-switch.on-dark .rw-opt.on i { opacity: .72; }
.room-switch.on-dark .rw-more { color: var(--lime); }

@media (max-width: 640px) {
  .room-switch .rw-more { margin-left: 0; }
}

/* ---------- CHARITY CARD ----------
   The logo tile, the name and the site, used identically by the wizard's
   picker, the election ballot, the tie-break and the result. See
   components/_charity_card.html for why this is one component rather than
   four hand-rolled variations. */
.ch-card { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }
.ch-tile {
  width: 44px; height: 44px; flex: none; border-radius: 10px;
  object-fit: contain; background: #fff; border: 1px solid var(--app-line);
  /* Charity logos are drawn to sit on white. Letting one bleed to the tile
     edge puts a coloured mark hard against a coloured card, so it is inset. */
  padding: 4px;
}
.ch-tile-initials {
  display: grid; place-items: center; padding: 0;
  font-family: var(--mark); font-weight: 800; font-size: 15px; letter-spacing: .02em;
  color: hsl(var(--ch-hue) 55% 28%);
  background: hsl(var(--ch-hue) 62% 92%);
  border-color: hsl(var(--ch-hue) 45% 82%);
}
.ch-body { display: flex; flex-direction: column; min-width: 0; }
.ch-name { font-weight: 700; color: var(--ink); font-size: 15px; line-height: 1.3; }
.ch-url {
  font-size: 12.5px; color: var(--muted); margin-top: 2px;
  /* A long URL must not push the ballot count off the card. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ch-note { font-size: 12px; color: var(--muted); margin-top: 3px; }
.ch-count { margin-left: auto; font-family: var(--score); font-weight: 800; font-size: 18px; color: var(--ink); }
.opt-card:has(input:checked) .ch-tile { border-color: rgba(200,241,53,.55); }
@media (max-width: 560px) { .ch-tile { width: 38px; height: 38px; } }
/* the small logo beside a name in the closed-vote bar chart */
.vz-lab .vz-logo { width: 20px; height: 20px; border-radius: 5px; object-fit: contain; background: #fff; vertical-align: -4px; margin-right: 6px; }
/* review step: charity cards sit inside the read-back row */
.wsum-r .ch-card { flex: 0 1 auto; }
.wsum-r .ch-tile { width: 34px; height: 34px; }
.wsum-chlist { display: flex; flex-direction: column; gap: 8px; }

/* ---------- CARRY-ACROSS REVIEW ---------- */
.carry-room { padding-top: 16px; }
.carry-head { display: flex; gap: 12px; align-items: flex-start; cursor: pointer; position: relative; }
.carry-head input { position: absolute; opacity: 0; width: 0; height: 0; }
.carry-head .oc-box { width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--app-line); flex: none; display: grid; place-items: center; margin-top: 1px; }
.carry-head .oc-box svg { width: 14px; height: 14px; stroke: #fff; fill: none; stroke-width: 3; opacity: 0; }
.carry-head:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.carry-head:has(input:checked) .oc-box svg { opacity: 1; }
.carry-head-body { display: flex; flex-direction: column; min-width: 0; }
/* The conflict list is indented under its room so it reads as belonging to it,
   and dimmed when the room itself is unticked — an override on a room you are
   not carrying into would do nothing, and must not look like it would. */
.carry-conflicts { margin: 14px 0 0 34px; padding-top: 12px; border-top: 1px solid var(--app-line); }
.carry-room:has(.carry-head input:not(:checked)) .carry-conflicts { opacity: .4; pointer-events: none; }
.carry-warn { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; color: var(--muted); margin: 0 0 10px; }
.carry-warn svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.8; flex: none; margin-top: 2px; }
.carry-conflict { display: flex; gap: 10px; align-items: center; padding: 9px 0; cursor: pointer; position: relative; }
.carry-conflict input { position: absolute; opacity: 0; width: 0; height: 0; }
.carry-conflict .oc-box { width: 19px; height: 19px; border-radius: 5px; border: 2px solid var(--app-line); flex: none; display: grid; place-items: center; }
.carry-conflict .oc-box svg { width: 12px; height: 12px; stroke: #fff; fill: none; stroke-width: 3; opacity: 0; }
.carry-conflict:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.carry-conflict:has(input:checked) .oc-box svg { opacity: 1; }
.cc-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cc-teams { font-size: 13px; color: var(--muted); }
.cc-swap { display: flex; align-items: center; gap: 7px; font-size: 14px; font-weight: 600; }
.cc-swap svg { width: 14px; height: 14px; stroke: var(--muted); fill: none; stroke-width: 2; flex: none; }
.cc-was { text-decoration: line-through; color: var(--muted); font-weight: 500; }
.cc-now { color: var(--ink); }
.carry-note { font-size: 13px; color: var(--muted); margin: 12px 0 0 34px; }

/* ---- First-visit walkthrough (templates/partials/onboarding.html) --------
   A ring around the thing being named and a card beside it. Both are placed
   by script from the target's own rect, so nothing here positions anything —
   these rules are only what the two look like. */
.coach { position: fixed; inset: 0; z-index: 200; }
.coach[hidden] { display: none; }
.coach-veil { position: absolute; inset: 0; background: rgba(8,21,14,.55); }
/* The ring is drawn with a giant spread shadow rather than a cut-out mask:
   one box, no clip-path, and the target stays fully visible and legible
   underneath rather than being dimmed with everything else. */
.coach-ring {
  position: fixed; border-radius: 12px; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(8,21,14,.55), 0 0 0 2px var(--lime, #c8f04a);
  transition: top .18s ease, left .18s ease, width .18s ease, height .18s ease;
}
/* The veil is the click-to-skip surface; the ring sits on top of it and must
   not swallow that, hence pointer-events:none above. */
.coach-pop {
  position: fixed; z-index: 2; width: min(340px, calc(100vw - 24px));
  background: var(--paper, #fff); color: var(--ink);
  border-radius: 14px; padding: 16px 16px 12px;
  box-shadow: 0 18px 44px rgba(8,21,14,.34);
  transition: top .18s ease, left .18s ease;
}
.coach-step { font-size: 11px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.coach-title { margin: 6px 0 4px; font-size: 18px; line-height: 1.25; }
.coach-body { margin: 0; font-size: 14px; line-height: 1.5; color: var(--muted); }
.coach-foot { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.coach-skip { background: none; border: 0; padding: 4px 2px; font: inherit; font-size: 13px; color: var(--muted); cursor: pointer; text-decoration: underline; }
.coach-dots { display: flex; gap: 5px; margin-left: auto; }
.coach-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(8,21,14,.2); }
.coach-dots i.on { background: var(--ink); }
@media (max-width: 640px) {
  .coach-pop { left: 12px !important; right: 12px; width: auto; }
}

/* ---------- Manage → Charities, and a group's own cause ----------
   Added Aug 2026 with group-level charities. Four small pieces, all leaning
   on the existing app tokens rather than introducing a palette: a section
   heading between stacked lists, the "you are backing this" panel, a
   non-interactive variant of .opt-card (the same card without the hover and
   pointer affordances, because these ones are being READ, not chosen), and
   the group→charity rows. */
.sec-h { font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
         color: var(--muted); margin: 26px 0 10px; }

.ch-current { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.ch-current-label { font-size: 11px; font-weight: 800; letter-spacing: .07em;
                    text-transform: uppercase; color: var(--muted); }

/* A card nobody can click. Without this the read-only lists rendered with a
   hover lift and a pointer cursor, which promises an interaction that isn't
   there — people clicked them and nothing happened. */
.opt-card.is-static { cursor: default; }
.opt-card.is-static:hover { transform: none; box-shadow: none; border-color: var(--app-line); }
.opt-card.is-winner { border-color: var(--forest); box-shadow: 0 0 0 1px var(--forest) inset; }
.opt-card.is-static .vz-track { margin-top: 10px; }

.gcr { display: grid; grid-template-columns: minmax(120px, 1fr) minmax(140px, 1.4fr) auto;
       align-items: center; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--app-line); }
.gcr:last-child { border-bottom: 0; padding-bottom: 0; }
.gcr:first-child { padding-top: 0; }
.gcr-name { font-weight: 700; color: var(--ink); }
.gcr-ch { color: var(--ink); font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
@media (max-width: 560px) {
  .gcr { grid-template-columns: 1fr auto; }
  .gcr-ch { grid-column: 1 / -1; }
}

/* ============================================================================
   CONFIRM SHEET, STEP ONE: WHERE THESE TIPS GO
   ----------------------------------------------------------------------------
   Added Aug 2026 when carrying moved in front of the save. The client's note
   on the old full-page version was that it looked like a form from another
   product — and it did: bare checkboxes, a card per room, a radio group headed
   "While we're here". The rebuild treats a room as a THING you are sending
   tips to rather than as a row in a settings page, which is the mental model
   people already have when they get here.

   Everything below is scoped to .tipsheet so the standalone /tip/carry/ page
   (the no-JS path) keeps its own existing treatment untouched. */

/* Both panes scroll independently; the panel itself never does. */
.tipsheet .ts-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 12px 22px; }
.tipsheet .ts-body[hidden] { display: none; }
.tipsheet .ts-list { padding: 0; }

/* Step chips. Small, quiet, and only shown when there IS a step one. */
.tipsheet .ts-steps { display: flex; gap: 6px; padding: 14px 22px 0; }
.tipsheet .ts-steps[hidden] { display: none; }
.tipsheet .ts-step {
  font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); background: rgba(15,46,34,.06);
  padding: 5px 10px; border-radius: 100px; transition: background .18s, color .18s;
}
.tipsheet .ts-step.is-on { background: var(--forest); color: var(--white); }

/* The opening line: a big number and a sentence, not a paragraph. */
.cs-lede { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.cs-lede p { font-size: 14px; color: var(--ink); line-height: 1.45; margin: 0; }
.cs-badge {
  flex: none; display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 14px;
  background: var(--lime); color: var(--deep);
  font-family: var(--score); font-weight: 900; font-size: 20px;
}

/* One room. The whole block is the target, and it reads as selected — a
   checkbox alone left people unsure which rooms were actually ticked. */
.cs-room {
  border: 1.5px solid var(--gt-hair); border-radius: 14px;
  padding: 13px 15px; margin-bottom: 10px;
  transition: border-color .16s, background .16s;
}
.cs-room:has(input[name="room"]:checked) { border-color: var(--forest); background: rgba(45,122,58,.045); }
.cs-room.has-conflict:has(input[name="room"]:checked) { border-color: #C8901F; background: rgba(200,144,31,.05); }
.cs-head { display: flex; align-items: flex-start; gap: 12px; cursor: pointer; }
.cs-head input { position: absolute; opacity: 0; pointer-events: none; }
.cs-head-body { min-width: 0; }
.cs-room-name { display: block; font-size: 15px; font-weight: 700; color: var(--ink); }
.cs-room-meta { display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.cs-room-meta b { color: var(--forest); font-weight: 800; }

/* Conflicts. Amber, not red: nothing has gone wrong, there is just a choice
   here that we refuse to make on the member's behalf. */
.cs-conflicts { margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--gt-hair); }
.cs-warn { display: flex; gap: 8px; font-size: 12.5px; color: #8A6314; line-height: 1.45; margin-bottom: 10px; }
.cs-warn svg { width: 15px; height: 15px; flex: none; margin-top: 2px; stroke: currentColor; fill: none; stroke-width: 2; }
.cs-conflict {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 8px 0; border-bottom: 1px solid var(--gt-hair);
}
.cs-conflict:last-child { border-bottom: 0; padding-bottom: 0; }
.cs-conflict input { position: absolute; opacity: 0; pointer-events: none; }
.cc-body { min-width: 0; }
.cc-teams { display: block; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.cc-swap { display: flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 12px; }
.cc-swap svg { width: 12px; height: 12px; stroke: var(--muted); fill: none; stroke-width: 2.4; flex: none; }
.cc-was { color: var(--muted); text-decoration: line-through; }
.cc-now { color: var(--forest); font-weight: 700; }
.cs-note { font-size: 12px; color: var(--muted); margin: 10px 0 0; }

/* "Stop asking me" folds away. It is a real preference, but it is not the
   question on this screen, and three radios sitting open under every room
   made the step look like a settings page. */
.cs-remember {
  margin-top: 4px;
  border: 1px solid var(--gt-hair); border-radius: 14px;
  background: var(--white);
}
.cs-remember summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 12px;
  padding: 15px 16px;
  font-size: 14.5px; font-weight: 600; color: var(--ink);
}
.cs-remember summary::-webkit-details-marker { display: none; }
.cs-remember summary span { flex: 1 1 auto; }
.cs-remember .cs-gear { width: 18px; height: 18px; flex: none; stroke: var(--muted); fill: none; stroke-width: 1.8; }
.cs-remember .cs-chev { width: 16px; height: 16px; flex: none; stroke: var(--muted); fill: none; stroke-width: 2.2; transition: transform .16s var(--ease); }
.cs-remember[open] .cs-chev { transform: rotate(180deg); }
.cs-remember[open] summary { border-bottom: 1px solid var(--gt-hair); }
.cs-remember > .cs-opt:first-of-type { margin-top: 6px; }
.cs-remember > .cs-opt, .cs-remember > .cs-fine { padding-left: 16px; padding-right: 16px; }
.cs-remember > .cs-fine { padding-bottom: 14px; }
.cs-opt { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; padding: 9px 0; }
.cs-opt input { position: absolute; opacity: 0; pointer-events: none; }
.cs-opt b { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink); }
.cs-opt i { display: block; font-style: normal; font-size: 12px; color: var(--muted); margin-top: 1px; line-height: 1.4; }
.cs-fine { font-size: 11.5px; color: var(--muted); margin: 6px 0 0; }

/* THE TICK BOXES, AND WHY THEY NEEDED WRITING OUT.

   This block used to say the sheet "reuses .oc-box from the option cards".
   It does not — every dimension of .oc-box is scoped to `.opt-card .oc-box`,
   `.carry-head .oc-box` and `.carry-conflict .oc-box`, which are the OLD carry
   screen's class names. Step one was rebuilt on `cs-` classes, so the only
   rule reaching these boxes was `flex: none`.

   An unsized <span> around an unsized <svg> does not vanish quietly: the icon
   took its intrinsic size, filled the card, and painted the check path in
   default black. Every room read as a giant black arrow. Sizes belong here. */
.cs-head .oc-box {
  flex: none;
  width: 34px; height: 34px;
  border-radius: 9px;
  border: 2px solid var(--gt-hair);
  background: var(--white);
  display: grid; place-items: center;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cs-head .oc-box svg { width: 17px; height: 17px; stroke: #fff; fill: none; stroke-width: 3.2; opacity: 0; }
.cs-head:has(input:checked) .oc-box { background: var(--deep); border-color: var(--deep); }
.cs-head:has(input:checked) .oc-box svg { opacity: 1; }

.cs-conflict .oc-box {
  flex: none;
  width: 22px; height: 22px;
  border-radius: 6px;
  border: 2px solid var(--gt-hair);
  background: var(--white);
  display: grid; place-items: center;
}
.cs-conflict .oc-box svg { width: 13px; height: 13px; stroke: #fff; fill: none; stroke-width: 3.2; opacity: 0; }
.cs-conflict:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.cs-conflict:has(input:checked) .oc-box svg { opacity: 1; }

/* The remember radios carry no icon in the markup, so the dot is drawn here. */
.cs-opt .oc-box {
  flex: none;
  width: 20px; height: 20px; margin-top: 1px;
  border-radius: 50%;
  border: 2px solid var(--gt-hair);
  background: var(--white);
  display: grid; place-items: center;
}
.cs-opt .oc-box::after {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--forest); transform: scale(0); transition: transform .14s var(--ease);
}
.cs-opt:has(input:checked) .oc-box { border-color: var(--forest); }
.cs-opt:has(input:checked) .oc-box::after { transform: scale(1); }

/* "Needs your decision". Named on the head rather than left to be discovered
   further down the card: a room that is about to overwrite a deliberate pick
   should say so where the room is named. Amber for the same reason the card
   is — a choice to make, not a mistake to fix. */
.cs-flag {
  margin-left: auto; align-self: flex-start; flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
  color: #8A6314; background: rgba(200,144,31,.14);
  border: 1px solid rgba(200,144,31,.32);
  padding: 4px 10px; border-radius: 100px;
}
.cs-flag svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2.2; flex: none; }

/* ---------------------------------------------------------------------------
   STEP TWO: THE PREVIEW, MADE WORTH LOOKING AT
   Was a flat list of "Team v Team" with a pill on the right, which read as a
   receipt. The pick is the thing being confirmed, so the pick is what gets the
   weight: a tick, the chosen team at full size, the losing side struck back. */
.tipsheet .ts-item {
  align-items: center; gap: 12px; padding: 12px 0;
}
.tipsheet .ts-item .tsi-match b { font-size: 14px; font-weight: 600; color: var(--muted); }
.tipsheet .ts-item .tsi-yes {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 800;
}
.tipsheet .ts-item .tsi-yes::before {
  content: "✓"; display: grid; place-items: center;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--forest); color: var(--white); font-size: 11px; font-weight: 900;
}
.tipsheet .ts-item.none { opacity: .72; }

/* ---------- Manage → Public pages ----------
   The copy editor. Wide, quiet fields: the client is reading sentences and
   comparing them against what the site said before, so the text is the only
   thing on screen with any weight. */
.pe-field { padding: 12px 0; border-bottom: 1px solid var(--app-line); }
.pe-field:last-child { border-bottom: 0; padding-bottom: 0; }
.pe-field label {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 7px;
}
.pe-field input[type=text], .pe-field textarea {
  width: 100%; box-sizing: border-box;
  border: 1px solid var(--app-line); border-radius: 9px;
  padding: 10px 12px; font-family: var(--body); font-size: 14.5px;
  color: var(--ink); background: var(--white); line-height: 1.5;
}
.pe-field textarea { resize: vertical; }
.pe-field input:focus, .pe-field textarea:focus {
  outline: 2px solid var(--forest); outline-offset: 1px; border-color: var(--forest);
}
/* What it said before. Small, and only shown once there is something to
   compare against — on an untouched field it would just be the same sentence
   printed twice. */
.pe-orig { margin: 6px 0 0; font-size: 12px; color: var(--muted); font-style: italic; }

.pe-save { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 6px 0 30px; }

.pe-media { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 14px; }
.pe-m {
  background: var(--app-card); border: 1px solid var(--app-line);
  border-radius: 12px; overflow: hidden; display: flex; flex-direction: column;
}
.pe-m img, .pe-m video { width: 100%; height: 132px; object-fit: cover; display: block; background: rgba(15,46,34,.06); }
/* A hidden picture still has to be findable, so it is dimmed rather than
   removed from the grid — otherwise "hide" and "delete" look identical here. */
.pe-m.is-hidden img, .pe-m.is-hidden video { opacity: .35; filter: grayscale(1); }
.pe-m-body { padding: 10px 12px 2px; }
.pe-m-body b { display: block; font-size: 13.5px; color: var(--ink); }
.pe-m-body small { display: block; font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.pe-m-acts { display: flex; gap: 6px; padding: 8px 12px 12px; margin-top: auto; }
.pe-m-acts form { margin: 0; }

/* ==========================================================================
   MESSAGES (templates/orgs/member_messages.html)

   The page is the conversations; writing one is a sheet. A compose form left
   permanently open put the rare action above the common one and pushed the
   replies — the thing people come back for — below the fold.
   ========================================================================== */
.msg-hero {
  display: flex; align-items: flex-start; gap: 18px; flex-wrap: wrap;
  /* NOT --gt-card. That token is the light cream card used on the sheet, and
     this panel sits on `page-deep` carrying white text — borrowing it painted
     white on white and the whole heading vanished. A translucent white over
     the dark page is what the other panels on these screens use. */
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 20px;
  padding: 26px 28px;
  margin-bottom: 18px;
}
.msg-hero-ic {
  flex: none; width: 66px; height: 66px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(200,241,53,.10); border: 1px solid rgba(200,241,53,.30);
}
.msg-hero-ic svg { width: 28px; height: 28px; stroke: var(--lime); fill: none; stroke-width: 2; }
.msg-hero-body { flex: 1 1 340px; min-width: 0; }
.msg-hero-body h1 {
  font-family: var(--display); font-size: clamp(26px, 3.4vw, 38px);
  color: #fff; margin: 0 0 6px; letter-spacing: -.01em;
}
.msg-hero-body p { margin: 0; color: rgba(255,255,255,.66); font-size: 15px; max-width: 62ch; }

.msg-private { display: flex; align-items: flex-start; gap: 10px; flex: none; }
.msg-private svg { width: 20px; height: 20px; stroke: var(--lime); fill: none; stroke-width: 1.9; margin-top: 2px; flex: none; }
.msg-private b { display: block; color: #fff; font-size: 14px; font-weight: 700; line-height: 1.3; }
.msg-private small { display: block; color: rgba(255,255,255,.55); font-size: 12.5px; }

.msg-actions { margin-bottom: 20px; }
.msg-actions .abtn svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; }

/* `.msg-list .gt-row` and `.msg-row-ic` used to sit here. Both were written
   against `.gt-row`, a component that only ever existed inside gt-shell.css's
   `body.gt-shell` block — which the member's inbox is not — so the row they
   were tuning had no styling to tune. The inbox row is `.msg-item` now,
   shared with the organisation admin's list, and it carries its own icon. */

/* The empty state is a designed state, not a gap — most members will never
   have written to their admins, and that is not a failure. */
.msg-empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 46px 20px;
  border: 1.5px dashed rgba(255,255,255,.18);
  border-radius: 18px;
  text-align: center;
}
.msg-empty svg { width: 26px; height: 26px; stroke: var(--lime); fill: none; stroke-width: 1.8; margin-bottom: 4px; }
.msg-empty b { color: #fff; font-size: 16px; font-weight: 700; }
.msg-empty span { color: rgba(255,255,255,.55); font-size: 14px; }

/* ---- The composer -------------------------------------------------------- */
.msg-sheet .ts-panel { width: min(660px, 100%); }
.msg-form { background: var(--cream); padding: 20px 22px 22px; }
.msg-fld { margin-bottom: 16px; }
.msg-fld:last-child { margin-bottom: 0; }
.msg-fld > label {
  display: flex; align-items: center; gap: 8px;
  font-size: 14.5px; font-weight: 700; color: var(--deep);
  margin-bottom: 8px;
}
.msg-fld > label svg { width: 16px; height: 16px; stroke: var(--forest); fill: none; stroke-width: 2; flex: none; }
.msg-fld select, .msg-fld input[type="text"], .msg-fld textarea {
  width: 100%; font: inherit; font-size: 15px; color: var(--ink);
  background: #fff; border: 1px solid var(--gt-hair); border-radius: 11px;
  padding: 13px 14px;
}
.msg-fld select:focus, .msg-fld input:focus, .msg-fld textarea:focus {
  outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.14);
}
.msg-fld textarea { resize: vertical; min-height: 132px; line-height: 1.55; }
.msg-fld[hidden] { display: none; }

.msg-area { position: relative; }
.msg-count {
  position: absolute; right: 12px; bottom: 10px;
  font-size: 11.5px; color: var(--muted); pointer-events: none;
  font-variant-numeric: tabular-nums;
}

.msg-foot { background: var(--cream); border-top: 1px solid var(--gt-hair); }
.msg-foot .abtn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }

@media (max-width: 620px) {
  .msg-hero { padding: 20px; }
  .msg-private { order: 3; width: 100%; }
}

/* ==========================================================================
   CHARITY TILES (templates/components/_charity_tile.html)

   On the charity screens the charities ARE the content, and the question is
   "which of these do I recognise?" — which a 44px mark inside a text row does
   not answer. So the logo is carried at a size worth looking at and the grid
   is even, so a page of causes reads as a set rather than as a list.
   ========================================================================== */
.ch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: 14px;
  margin-bottom: 26px;
}
.ch-item {
  position: relative;
  display: flex; flex-direction: column;
  padding: 18px 18px 16px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 16px;
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.ch-item:hover { border-color: rgba(200,241,53,.34); background: rgba(255,255,255,.07); }
.ch-item-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 13px; }

/* White plate behind the mark. Charity logos are drawn for white and a good
   half of them ship as dark PNGs with no background of their own; dropped
   straight onto the deep green they disappear. */
.ch-item-logo {
  width: 56px; height: 56px; border-radius: 14px; flex: none;
  object-fit: contain; background: #fff; padding: 7px;
}
.ch-item-initials {
  display: grid; place-items: center; padding: 0;
  background: hsl(var(--ch-hue, 140) 46% 90%);
  color: hsl(var(--ch-hue, 140) 52% 26%);
  font-family: var(--display); font-size: 19px; letter-spacing: .01em;
}
.ch-item-chip {
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 100px; white-space: nowrap;
  background: rgba(200,241,53,.14); color: var(--lime);
  border: 1px solid rgba(200,241,53,.28);
}
.ch-item-name { font-size: 15px; font-weight: 700; color: #fff; line-height: 1.3; }
.ch-item-url { font-size: 12.5px; color: rgba(255,255,255,.50); margin-top: 3px; word-break: break-word; }
.ch-item-note { font-size: 12px; color: rgba(255,255,255,0.49); margin-top: 6px; }

.ch-add-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  padding: 20px 22px;
  border: 1.5px dashed rgba(255,255,255,.18);
  border-radius: 18px;
  margin-top: 6px;
}
.ch-add-row .abtn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }

.ch-sheet .ts-panel { width: min(560px, 100%); }
.ch-form { background: var(--cream); padding: 18px 22px 22px; }
.ch-form-hint { font-size: 13px; color: var(--muted); margin: 0 0 16px; line-height: 1.5; }
.ch-form .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.ch-form input[type="text"], .ch-form input[type="url"] {
  width: 100%; font: inherit; font-size: 15px; color: var(--ink);
  background: #fff; border: 1px solid var(--gt-hair); border-radius: 11px; padding: 13px 14px;
}
.ch-form input:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.14); }
.ch-form .fld-err { font-size: 12.5px; color: #A62121; margin-top: 6px; }
.ch-foot { background: var(--cream); border-top: 1px solid var(--gt-hair); }

/* ==========================================================================
   GROUPS — search and card head (templates/orgs/groups.html)

   The search was an input with a Search button and a Clear link beside it: a
   round trip to the server to answer "which of these is the Finance one?",
   which the browser could already answer because every group is on the page.
   It reads as one field now, and it filters as you type.
   ========================================================================== */
.grp-search { margin-top: 4px; }
.grp-search-form {
  display: flex; align-items: center; gap: 10px;
  max-width: 560px;
  padding: 0 6px 0 15px;
  height: 50px;
  background: var(--gt-card);
  border: 1px solid var(--gt-hair);
  border-radius: 100px;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.grp-search-form:focus-within { border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.14); }
.grp-search-ic { width: 18px; height: 18px; flex: none; stroke: var(--muted); fill: none; stroke-width: 2; }
.grp-search-form input {
  flex: 1 1 auto; min-width: 0;
  border: 0; background: none; font: inherit; font-size: 15px;
  color: var(--ink); padding: 0;
}
.grp-search-form input:focus { outline: none; box-shadow: none; }
.grp-search-form input::-webkit-search-cancel-button { display: none; }

.grp-search-x {
  flex: none; width: 26px; height: 26px; border: 0; border-radius: 50%;
  background: rgba(15,46,34,.08); color: var(--muted);
  font-size: 18px; line-height: 1; cursor: pointer;
}
.grp-search-x:hover { background: rgba(15,46,34,.15); color: var(--ink); }
.grp-search-x[hidden] { display: none; }

/* The submit is what makes this a real form for anyone without scripting; with
   the live filter running it is redundant, so it stays quiet rather than
   competing with the field it sits inside. */
.grp-search-go {
  flex: none; border: 0; border-radius: 100px; cursor: pointer;
  padding: 9px 18px; font: inherit; font-size: 14px; font-weight: 700;
  background: var(--deep); color: #fff;
}
.grp-search-go:hover { background: var(--forest); }

.grp-search-count {
  margin: 9px 0 0; padding-left: 4px;
  font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.grp-search-count[hidden] { display: none; }
.grp-none { margin: 16px 0 0; color: var(--muted); font-size: 15px; }
.grp-none[hidden] { display: none; }
.dept-card[hidden] { display: none; }

/* The card head, out of inline styles and into names — the member count and
   the cause now sit on their own line instead of being run together. */
.dept-card .dc-head { min-width: 0; }
.dept-card .dc-name {
  font-family: var(--display); font-size: 17px; color: var(--ink);
  line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ===========================================================================
   DELEGATED ADMINISTRATION
   The screens for running a team: who the administrators are, what each may
   do, the queue of work waiting on approval, and the record of all of it.

   Everything here is written against tokens rather than literal colours, so
   the HQ theme switch can repaint the whole area by redefining eight
   variables instead of this file re-stating every rule in a dark block.
   The tokens themselves live in gt-shell.css beside the shell's own.
   =========================================================================== */

/* ---- The page the wizard sits on ----------------------------------------
   NOT `.gt-shell`. That class is a two-column grid (content + a 320px
   sidebar) and these pages have no sidebar, so using it parked the whole
   wizard in the narrow column with a dead strip beside it — the single
   biggest reason the screens read as unfinished. This is the same measure
   without the second track. */
.ad-page { max-width: 1080px; }
.ad-page.is-wide { max-width: none; }

/* ---- Back to the list ---- */
.ad-crumb { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; flex-wrap: wrap; }
.ad-crumb a {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600; color: var(--ad-quiet);
  text-decoration: none; padding: 6px 12px 6px 9px;
  border: 1px solid var(--ad-line); border-radius: 999px;
  background: var(--ad-card); transition: border-color .15s, color .15s;
}
.ad-crumb a:hover { color: var(--forest); border-color: var(--forest); text-decoration: none; }
.ad-crumb svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2.1; }

/* ---- A step card ---------------------------------------------------------
   The wizard is three of these stacked. They are the same object as the
   product's `.gt-panel` but with more air, because the middle one is a
   nineteen-row consent screen and a panel built for a table does not give it
   room to be read. */
.ad-card {
  background: var(--ad-card);
  border: 1px solid var(--ad-line);
  border-radius: 18px;
  padding: 28px clamp(18px, 3vw, 34px) 30px;
  box-shadow: 0 1px 2px rgba(15,46,34,.04), 0 8px 24px -18px rgba(15,46,34,.28);
}
.ad-card + .ad-card { margin-top: 20px; }

.ad-cardhead { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.ad-cardhead h2 {
  font-family: var(--display); font-size: 22px; line-height: 1.15;
  color: var(--ad-ink); margin: 0;
}
.ad-cardhead p { margin: 6px 0 0; font-size: 14px; line-height: 1.55; color: var(--ad-quiet); max-width: 62ch; }
.ad-cardhead .ad-headright { margin-left: auto; flex: none; }

/* The step number. Deep forest disc, lime numeral — the brand's one loud
   pairing, used once per card and nowhere else on the page. */
.ad-stepnum {
  display: grid; place-items: center; flex: none;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--deep); color: var(--lime);
  font-family: var(--display); font-size: 17px; line-height: 1;
}

/* ---- Step 1: the address belongs to somebody already -------------------
   Shown when the email typed in turns out to be a member's. Tinted rather
   than red: nothing has gone wrong, and the panel is the answer to the
   question the form just asked, not a complaint about it. */
.ad-known {
  display: flex; align-items: flex-start; gap: 14px;
  margin: 0 0 22px; padding: 16px 18px;
  border: 1px solid var(--ad-line-2); border-left: 3px solid var(--forest);
  border-radius: 14px; background: var(--ad-tint-2);
}
.ad-known-ic { flex: none; width: 22px; height: 22px; margin-top: 1px; }
.ad-known-ic svg {
  width: 22px; height: 22px; fill: none; stroke: var(--forest);
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.ad-known-body { min-width: 0; }
.ad-known-body b { display: block; font-family: var(--display); font-size: 15.5px; color: var(--ad-ink); line-height: 1.25; }
.ad-known-body p { margin: 6px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--ad-quiet); max-width: 62ch; }

/* Their name comes from their account and cannot be typed over here. */
.ad-fields input[readonly] { background: var(--ad-quiet-bg); color: var(--ad-quiet); cursor: default; }
.ad-fields input[readonly]:focus { border-color: var(--ad-line-2); box-shadow: none; }

/* ---- Step 1: the fields ---- */
.ad-fields { display: grid; gap: 18px; }
@media (min-width: 760px) {
  .ad-fields { grid-template-columns: 1fr 1fr; }
  .ad-fields .fld-full { grid-column: 1 / -1; }
}
.ad-fields .fld { margin-bottom: 0; }
.ad-fields .fld > label { font-size: 13px; font-weight: 700; color: var(--ad-ink); margin-bottom: 8px; }
.ad-fields input, .ad-fields select, .ad-fields textarea {
  background: var(--ad-field); border: 1px solid var(--ad-line-2);
  color: var(--ad-ink); border-radius: 12px; padding: 13px 15px; font-size: 14.5px;
}
.ad-fields input::placeholder, .ad-fields textarea::placeholder { color: var(--ad-faint); }
.ad-fields input:focus, .ad-fields select:focus, .ad-fields textarea:focus {
  border-color: var(--forest); box-shadow: 0 0 0 3px var(--ad-ring);
}

/* ---- Step 2: full access, or pick ---------------------------------------
   Two cards rather than a select, because the difference between them is a
   paragraph and not a word. The illustration on the right is decorative and
   carries no state the border and radio do not already carry. */
.ad-level { display: grid; gap: 14px; margin-bottom: 26px; }
@media (min-width: 820px) { .ad-level { grid-template-columns: 1fr 1fr; } }

.ad-levelopt {
  position: relative; display: flex; align-items: flex-start; gap: 14px;
  margin: 0; cursor: pointer; padding: 20px 22px;
  border: 1.5px solid var(--ad-line-2); border-radius: 16px;
  background: var(--ad-card); transition: border-color .16s, background .16s, box-shadow .16s;
}
.ad-levelopt:hover { border-color: var(--ad-line-3); }
.ad-levelopt .ad-optbody { flex: 1; min-width: 0; }
.ad-levelopt b { display: block; font-family: var(--display); font-size: 16px; color: var(--ad-ink); line-height: 1.2; }
.ad-levelopt small { display: block; font-size: 13px; line-height: 1.55; color: var(--ad-quiet); margin-top: 6px; }

/* The radio, drawn rather than native, so the two cards agree across
   browsers about how big "selected" looks. */
.ad-levelopt input[type="radio"],
.ad-check input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.ad-radio {
  flex: none; width: 20px; height: 20px; margin-top: 2px; border-radius: 50%;
  border: 2px solid var(--ad-line-3); background: var(--ad-card);
  display: grid; place-items: center; transition: border-color .16s, background .16s;
}
.ad-radio::after {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: transparent; transition: background .16s;
}
.ad-levelopt:has(input:checked) { border-color: var(--forest); background: var(--ad-tint); box-shadow: 0 0 0 1px var(--forest) inset; }
.ad-levelopt:has(input:checked) .ad-radio { border-color: var(--forest); }
.ad-levelopt:has(input:checked) .ad-radio::after { background: var(--forest); }
.ad-levelopt:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--ad-ring); }

/* Full access reads gold, not red. It is a legitimate choice the owner's
   partner is expected to hold — the page should make it weighty, not alarming. */
.ad-levelopt.is-full:has(input:checked) { border-color: var(--gold-dk); background: var(--ad-tint-gold); box-shadow: 0 0 0 1px var(--gold-dk) inset; }
.ad-levelopt.is-full:has(input:checked) .ad-radio { border-color: var(--gold-dk); }
.ad-levelopt.is-full:has(input:checked) .ad-radio::after { background: var(--gold-dk); }

.ad-optart { flex: none; width: 46px; height: 46px; align-self: center; opacity: .9; }
.ad-optart svg { width: 100%; height: 100%; display: block; }

/* ---- Step 2: the capability groups --------------------------------------
   One collapsible card per area. Open on load: the point of the screen is
   that somebody reads the nineteen things before consenting to them, and a
   stack of shut drawers invites ticking the headers instead. */
.ad-caps-pick { display: grid; gap: 14px; }

.ad-capgroup {
  border: 1px solid var(--ad-line); border-radius: 16px;
  background: var(--ad-card); overflow: hidden;
}
.ad-capgroup[open] { border-color: var(--ad-line-2); }

.ad-capgroup > summary {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 20px; cursor: pointer; list-style: none;
  background: var(--ad-head); transition: background .15s;
}
.ad-capgroup > summary::-webkit-details-marker { display: none; }
.ad-capgroup > summary:hover { background: var(--ad-head-h); }

/* The area's icon, in a soft disc. Green for the everyday areas, gold for
   the two that reach members' own records. */
.ad-capic {
  flex: none; display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 12px;
  background: var(--ad-ic-bg); color: var(--ad-ic-fg);
}
.ad-capic svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.ad-capgroup.is-care .ad-capic { background: var(--ad-ic-bg-gold); color: var(--ad-ic-fg-gold); }

.ad-capgroup-t { flex: 1; min-width: 0; }
.ad-capgroup-t b { display: block; font-size: 15px; font-weight: 700; color: var(--ad-ink); line-height: 1.25; }
.ad-capgroup-t small { display: block; font-size: 12.5px; color: var(--ad-quiet); margin-top: 2px; }

/* How many of this group's rows are on, so a collapsed card still says
   something. Driven by gt-team.js. */
.ad-capcount {
  flex: none; font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 4px 10px; border-radius: 999px;
  background: var(--ad-head-h); color: var(--ad-quiet);
}
.ad-capcount.is-on { background: var(--ad-tint-2); color: var(--forest); }

.ad-capchev { flex: none; width: 16px; height: 16px; stroke: var(--ad-faint); fill: none; stroke-width: 2.2; transition: transform .18s; }
.ad-capgroup[open] .ad-capchev { transform: rotate(180deg); }

/* "Tick all of these" lives inside the open card, not in the summary bar:
   a checkbox in a row that also toggles the drawer is a coin flip on what a
   click does. */
.ad-capall {
  display: flex; align-items: center; gap: 11px; margin: 0;
  padding: 12px 20px; border-top: 1px solid var(--ad-line);
  font-size: 12.5px; font-weight: 600; color: var(--ad-quiet); cursor: pointer;
}
.ad-capall:hover { color: var(--forest); }

.ad-caplist { padding: 0; }

/* One row per capability, carrying both decisions: hold it, and optionally
   hold it under review. Two separate lists would be two things to keep
   mentally aligned. */
.ad-caprow {
  display: flex; align-items: center; gap: 18px;
  padding: 14px 20px; border-top: 1px solid var(--ad-line);
  transition: background .13s;
}
.ad-caprow:hover { background: var(--ad-row-h); }
.ad-caprow:has(> .ad-capmain input:checked) { background: var(--ad-tint); }

.ad-capmain { display: flex; align-items: flex-start; gap: 13px; flex: 1; cursor: pointer; margin: 0; min-width: 0; }
.ad-capmain b { display: block; font-size: 14px; font-weight: 700; color: var(--ad-ink); line-height: 1.35; }
.ad-capmain small { display: block; font-size: 12.5px; line-height: 1.55; color: var(--ad-quiet); margin-top: 3px; max-width: 58ch; }

/* The drawn checkbox, matching the drawn radio above. */
.ad-check { position: relative; flex: none; margin-top: 1px; line-height: 0; }
.ad-box {
  display: grid; place-items: center; width: 20px; height: 20px;
  border: 2px solid var(--ad-line-3); border-radius: 6px;
  background: var(--ad-card); transition: background .15s, border-color .15s;
}
.ad-box svg { width: 12px; height: 12px; stroke: #fff; fill: none; stroke-width: 3; opacity: 0; transition: opacity .13s; }
.ad-check input:checked + .ad-box { background: var(--forest); border-color: var(--forest); }
.ad-check input:checked + .ad-box svg { opacity: 1; }
.ad-check input:indeterminate + .ad-box { background: var(--forest); border-color: var(--forest); }
.ad-check input:indeterminate + .ad-box::after {
  content: ""; position: absolute; width: 10px; height: 2px; border-radius: 2px; background: #fff;
}
.ad-check input:indeterminate + .ad-box svg { opacity: 0; }
.ad-check input:focus-visible + .ad-box { box-shadow: 0 0 0 3px var(--ad-ring); }
.ad-box { position: relative; }

/* "care" — a different amount of thought before ticking, not a different check. */
.ad-warn {
  font-style: normal; font-size: 10px; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ad-gold-fg); background: var(--ad-tint-gold-2);
  padding: 2px 7px; border-radius: 999px; margin-left: 7px; vertical-align: 1px;
}

/* The approval control, on the right of its own row. */
.ad-capreview {
  display: inline-flex; align-items: center; gap: 8px; flex: none; margin: 0;
  font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap;
  padding: 6px 12px 6px 9px; border-radius: 999px;
  border: 1px solid var(--ad-line-2); background: var(--ad-card); color: var(--ad-quiet);
  transition: background .15s, border-color .15s, color .15s, opacity .15s;
}
.ad-capreview .ad-box { width: 16px; height: 16px; border-width: 1.5px; border-radius: 5px; }
.ad-capreview .ad-box svg { width: 10px; }
.ad-capreview:has(input:checked) {
  background: var(--ad-tint-gold-2); border-color: var(--gold-dk); color: var(--ad-gold-fg);
}
.ad-capreview:has(input:checked) .ad-box { background: var(--gold-dk); border-color: var(--gold-dk); }
/* Greyed rather than hidden when the capability is not held: a control that
   vanishes and reappears makes the row jump as you tick down the list. */
.ad-capreview.is-off { opacity: .32; cursor: default; }
/* Nothing to hold back — this either happens or it doesn't. */
.ad-capreview.is-na {
  border-color: transparent; background: transparent;
  color: var(--ad-faint); font-weight: 600; cursor: default; padding-right: 12px;
}

/* ---- Step 3: the summary, built from the boxes above --------------------- */
.ad-preview {
  border: 1px solid var(--ad-line); border-radius: 16px;
  padding: 22px 24px; background: var(--ad-head);
}
.ad-preview.is-empty { border-style: dashed; text-align: center; padding: 34px 24px; }
.ad-pv-empty svg { width: 34px; height: 34px; stroke: var(--ad-faint); fill: none; stroke-width: 1.5; margin-bottom: 10px; }
.ad-pv-empty p { margin: 0; font-size: 13.5px; color: var(--ad-quiet); }

.ad-pv-head {
  display: flex; align-items: center; gap: 8px;
  margin: 22px 0 10px; font-size: 11px; font-weight: 800;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ad-quiet);
}
.ad-pv-head:first-child { margin-top: 0; }
.ad-pv-head::after { content: ""; flex: 1; height: 1px; background: var(--ad-line); }
.ad-pv-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.ad-pv-list li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 13.5px; line-height: 1.5; color: var(--ad-ink);
}
.ad-pv-list li::before {
  content: ""; flex: none; width: 6px; height: 6px; margin-top: 6px;
  border-radius: 50%; background: var(--forest);
}
.ad-pv-list.is-wait li { color: var(--ad-gold-fg); }
.ad-pv-list.is-wait li::before { background: var(--gold-dk); }
.ad-pv-note { margin: 12px 0 0; font-size: 12.5px; color: var(--ad-quiet); }

/* ---- The action bar ------------------------------------------------------
   Sticky at the foot of the page, not fixed to the viewport: fixed would sit
   over the last capability row on a laptop, and the one row you cannot read
   is the one you are about to consent to. Sticky bottom leaves the document
   its full height and the bar rides the end of it. */
.ad-bar {
  position: sticky; bottom: 0; z-index: 20;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 20px; padding: 16px clamp(18px, 3vw, 26px);
  background: var(--ad-bar); border: 1px solid var(--ad-line);
  border-radius: 16px;
  box-shadow: 0 -2px 20px -8px rgba(15,46,34,.20);
  backdrop-filter: blur(8px);
}
.ad-bar .ad-barnote { font-size: 12.5px; color: var(--ad-quiet); margin: 0; }
.ad-bar .ad-barright { margin-left: auto; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.ad-btn {
  display: inline-flex; align-items: center; gap: 9px;
  font: inherit; font-size: 14px; font-weight: 700; line-height: 1;
  padding: 13px 22px; border-radius: 999px; cursor: pointer;
  text-decoration: none; border: 1.5px solid transparent;
  transition: filter .15s, border-color .15s, color .15s, background .15s;
}
.ad-btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
.ad-btn.is-go { background: var(--forest); border-color: var(--forest); color: #fff; }
.ad-btn.is-go:hover { filter: brightness(1.08); text-decoration: none; color: #fff; }
.ad-btn.is-go:active { transform: translateY(1px); }
.ad-btn.is-quiet { background: transparent; border-color: var(--ad-line-3); color: var(--ad-quiet); }
.ad-btn.is-quiet:hover { border-color: var(--forest); color: var(--forest); text-decoration: none; }
.ad-btn.is-danger { background: transparent; border-color: rgba(192,57,43,.4); color: #C0392B; }
.ad-btn.is-danger:hover { background: rgba(192,57,43,.08); text-decoration: none; }

@media (max-width: 620px) {
  .ad-bar { flex-direction: column; align-items: stretch; }
  .ad-bar .ad-barright { margin-left: 0; }
  .ad-bar .ad-btn { justify-content: center; width: 100%; }
  .ad-caprow { flex-wrap: wrap; gap: 10px; }
  .ad-capreview { margin-left: 33px; }
}

/* ---- What one administrator holds, on the team list ---- */
.ad-caps { display: flex; flex-wrap: wrap; gap: 6px; }
.ad-cap {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px;
  border-radius: 999px; background: var(--ad-tint-2); color: var(--forest);
  font-size: 11.5px; font-weight: 600;
}
.ad-cap.is-reviewed { background: var(--ad-tint-gold-2); color: var(--ad-gold-fg); }
.ad-cap em { font-style: normal; font-weight: 800; font-size: 9.5px; text-transform: uppercase; letter-spacing: .06em; opacity: .8; }
tr.is-off { opacity: .55; }

/* ---- "What you can do", on a person's own page ---- */
.ad-mine { display: grid; gap: 12px; }
.ad-mine-row {
  display: flex; align-items: flex-start; gap: 13px;
  padding: 14px 16px; border: 1px solid var(--ad-line);
  border-radius: 14px; background: var(--ad-card);
}
.ad-mine-ic { display: grid; place-items: center; width: 30px; height: 30px; flex: none; border-radius: 10px; }
.ad-mine-ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.2; }
.ad-mine-ic.is-go { background: var(--ad-tint-2); color: var(--forest); }
.ad-mine-ic.is-wait { background: var(--ad-tint-gold-2); color: var(--ad-gold-fg); }
.ad-mine-row b { display: block; font-size: 14px; color: var(--ad-ink); }
.ad-mine-row small { display: block; font-size: 12.5px; color: var(--ad-quiet); margin-top: 2px; }

/* ---- Tasks ---- */
.ad-task-form { display: grid; gap: 14px; max-width: 620px; }
.ad-task-form .fld { margin-bottom: 0; }
.ad-tasks { display: grid; gap: 12px; }
.ad-task {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 16px 18px; border: 1px solid var(--ad-line); border-radius: 14px; background: var(--ad-card);
}
.ad-task b { font-size: 14.5px; color: var(--ad-ink); }
.ad-task p { margin: 6px 0 4px; font-size: 13px; line-height: 1.55; color: var(--ad-quiet); }

/* ---- What came back from review ---- */
.ad-decisions { display: grid; gap: 12px; }
.ad-decision { padding: 16px 18px; border: 1px solid var(--ad-line); border-radius: 14px; background: var(--ad-card); }
.ad-dec-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.ad-dec-note {
  margin: 10px 0 0; padding-left: 13px; border-left: 3px solid var(--lime);
  font-size: 13.5px; line-height: 1.6; color: var(--ad-ink);
}
.ad-dec-changed { margin: 8px 0 0; font-size: 12.5px; }

/* ---- Reviewing one submission ---- */
.rv-field { margin-bottom: 18px; }
.rv-box { width: 100%; font-size: 13.5px; line-height: 1.6; }
.rv-box.is-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
.rv-static {
  padding: 12px 14px; border: 1px solid var(--ad-line); border-radius: 10px;
  background: var(--ad-head); font-size: 13px; line-height: 1.6;
  color: var(--ad-quiet); white-space: pre-wrap; word-break: break-word;
}
/* The markup fields are the ones worth seeing rendered — judging a blog post
   by reading its HTML is not reviewing it. */
.rv-preview { margin-top: 8px; }
.rv-preview summary { font-size: 12.5px; font-weight: 700; color: var(--forest); cursor: pointer; }
.rv-render { margin-top: 10px; padding: 18px 20px; border: 1px solid var(--ad-line); border-radius: 12px; background: var(--ad-card); }
.rv-render img { max-width: 100%; height: auto; }
.rv-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 18px; }

/* ---- The activity filter ---- */
.ad-filter { display: flex; gap: 8px; flex-wrap: wrap; }
.ad-filter select {
  padding: 8px 12px; border: 1px solid var(--ad-line-2); border-radius: 999px;
  background: var(--ad-card); color: var(--ad-ink); font-family: var(--body); font-size: 13px;
}

/* ---- What an invitation says you are being trusted with ----
   On the dark invitation page, which is its own screen and not themed. */
.inv-grants { margin: 0 0 22px; padding: 16px 18px; border-radius: 12px; background: rgba(232, 228, 213, .08); border: 1px solid rgba(232, 228, 213, .16); }
.inv-grant-head { margin: 0 0 6px; font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--lime); }
.inv-grants ul { margin: 0 0 12px; padding-left: 20px; font-size: 14px; line-height: 1.7; }
.inv-grants ul.is-reviewed { color: #E8D48A; }
.inv-grants ul:last-child { margin-bottom: 0; }
.inv-note { font-size: 12.5px; opacity: .75; margin: 0; }

/* ==========================================================================
   ABOUT US + PRIVACY POLICY
   ==========================================================================
   Both pages shipped with markup that named classes this stylesheet did not
   have — `.prose`, `.cards-3`, `.card-lift`, `.wrap-narrow`. Nothing was
   broken in the sense of throwing; the pages simply rendered as unstyled
   paragraphs and square divs, which is what "the About page doesn't look
   finished" meant. Everything below is the missing half.

   The two pages share a vocabulary on purpose. A privacy policy that looks
   like it was bolted on by a different team is the exact thing that makes a
   reader distrust it, so it gets the same rails, the same card, the same
   heading rhythm as the page that sells the product — only quieter.
   ========================================================================== */

/* ---- Prose ---------------------------------------------------------------
   Measure first. These are the only two pages on the site where somebody
   reads more than three sentences in a row, so the column is capped at 68
   characters and the leading is opened up; everything else here follows from
   that. */
.prose { max-width: 68ch; font-size: 17px; line-height: 1.78; color: var(--ink); }
.prose > * + * { margin-top: 18px; }
.prose p { margin: 0; }
.prose strong { font-weight: 700; color: var(--deep); }
.prose a { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }
.prose a:hover { color: var(--deep); }
.prose h2 {
  font-family: var(--display); font-size: 22px; line-height: 1.25;
  color: var(--deep); margin-top: 34px;
}
.prose h3 { font-family: var(--display); font-size: 18px; color: var(--deep); margin-top: 26px; }
.prose ul, .prose ol { margin: 0; padding-left: 22px; }
.prose li + li { margin-top: 8px; }
.prose-wide { max-width: 78ch; }

.wrap-narrow { max-width: 860px; margin-left: auto; margin-right: auto; }

/* ---- Rotating side rails, for public pages with a narrow column ----------
   The app has `.page-scenes` for this and it is the right shape, but it is
   fixed to the viewport edges — which works there because the app content
   sits on a centred sheet. A public section is full-bleed, so the same markup
   would simply be painted over. `.pg-rails` is the public variant: it hangs
   off the section rather than the viewport, so it fills exactly the margin
   the content is not using and disappears the moment that margin is gone. */
.pg-rails { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.pg-rails .rail { position: absolute; top: 0; bottom: 0; width: clamp(120px, 13vw, 230px); overflow: hidden; }
.pg-rails .rail.left { left: 0; }
.pg-rails .rail.right { right: 0; }
.pg-rails .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  opacity: 0; transition: opacity 2s var(--ease);
}
.pg-rails .shot.on { opacity: 1; }
/* A wash towards the content, so the column never has to compete with a
   photograph for the reader's eye at the edge of the measure. */
.pg-rails .rail::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(244,242,234,.25), rgba(244,242,234,.86) 62%, var(--cream-2));
}
.pg-rails .rail.right::after {
  background: linear-gradient(270deg, rgba(244,242,234,.25), rgba(244,242,234,.86) 62%, var(--cream-2));
}
.has-rails { position: relative; }
.has-rails > .wrap, .has-rails > .wrap-narrow { position: relative; z-index: 1; }
@media (max-width: 1240px) { .pg-rails { display: none; } }

/* ---- The card that lifts -------------------------------------------------
   Used by both pages and by the values grid. Three things happen on hover and
   they are one gesture, not three: the card rises, its top rule fills from
   the left in lime, and the icon tile warms. Pulling any one of them out
   leaves a hover that reads as a mistake rather than as a response. */
.cards-3 { display: grid; gap: 18px; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: stretch; }
.cards-2 { display: grid; gap: 18px; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
@media (max-width: 900px) { .cards-3, .cards-2 { grid-template-columns: 1fr; } }

.card-lift {
  position: relative; display: flex; flex-direction: column; gap: 10px;
  background: var(--white);
  border: 1px solid rgba(15,46,34,.10);
  border-radius: 16px;
  padding: 26px 24px 24px;
  overflow: hidden;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.card-lift::before {
  content: ""; position: absolute; left: 0; top: 0; height: 3px; width: 100%;
  background: linear-gradient(90deg, var(--lime), var(--forest));
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.card-lift:hover, .card-lift:focus-within {
  transform: translateY(-6px);
  border-color: rgba(45,122,58,.28);
  box-shadow: 0 18px 40px -22px rgba(15,46,34,.45);
}
.card-lift:hover::before, .card-lift:focus-within::before { transform: scaleX(1); }
.card-lift h3 { font-family: var(--display); font-size: 19px; line-height: 1.25; color: var(--deep); margin: 0; }
.card-lift p { margin: 0; font-size: 15px; line-height: 1.65; color: #4A5A4F; }

/* The icon tile. Its lime ring is drawn as a second box so it can scale
   independently of the glyph — scaling the tile itself would drag the icon
   with it and read as a zoom rather than as a pulse. */
.card-lift .cl-ic {
  position: relative; width: 46px; height: 46px; border-radius: 13px;
  display: grid; place-items: center; margin-bottom: 4px;
  background: rgba(45,122,58,.10); color: var(--forest);
  transition: background .3s var(--ease), color .3s var(--ease);
}
.card-lift .cl-ic svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.card-lift .cl-ic::after {
  content: ""; position: absolute; inset: -5px; border-radius: 17px;
  border: 1px solid rgba(200,241,53,0);
  transition: border-color .35s var(--ease), inset .35s var(--ease);
}
.card-lift:hover .cl-ic { background: var(--deep); color: var(--lime); }
.card-lift:hover .cl-ic::after { border-color: rgba(200,241,53,.55); inset: -8px; }

/* A watermark numeral, bottom right. Decorative and deliberately nearly
   invisible until the card is hovered — it gives the set an order without
   spending a line of the card on one. */
/* TOP RIGHT, AND OUT OF THE TEXT'S WAY (client, 22 Sep 2026: "have those
   numbers be at the top right and make sure they do not overlap with that of
   the text").

   It sat bottom-right at 62px, behind the paragraph. On the three cards whose
   copy runs long — "The money is traceable", "You get the receipt" — the last
   line of the paragraph ran straight through the numeral, and at 5% opacity
   that reads as the text having a printing fault rather than as a watermark.

   Two changes, and the second is the one that actually fixes it. Moving it to
   the top right gets it away from the copy's longest edge. Reserving space for
   it in the card's own padding means it cannot be overlapped at any length:
   .card-lift now has room set aside on the right of its first line, so the
   heading wraps before it reaches the numeral instead of running under it. */
.card-lift .cl-n {
  position: absolute; right: 18px; top: 14px;
  font-family: var(--display); font-size: 46px; line-height: 1;
  color: rgba(15,46,34,.07); pointer-events: none;
  transition: color .35s var(--ease), transform .35s var(--ease);
}
.card-lift:hover .cl-n { color: rgba(45,122,58,.14); transform: translateY(2px); }
/* The reserved corner. The icon and heading stop short of it; the paragraph,
   which starts below the numeral's baseline, uses the full width. */
.card-lift:has(.cl-n) .cl-ic,
.card-lift:has(.cl-n) > h3 { padding-right: 58px; }

/* On dark sections the same card flips its ground rather than getting a
   second class — one component, two grounds. */
.on-dark .card-lift { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.12); }
.on-dark .card-lift h3 { color: var(--white); }
.on-dark .card-lift p { color: rgba(232,228,213,.78); }
.on-dark .card-lift .cl-ic { background: rgba(200,241,53,.14); color: var(--lime); }
.on-dark .card-lift .cl-n { color: rgba(255,255,255,.05); }
.on-dark .card-lift:hover { border-color: rgba(200,241,53,.4); }
.on-dark .card-lift:hover .cl-ic { background: var(--lime); color: var(--deep); }

/* ---- About: the story split ---------------------------------------------
   Heading sticks while the prose scrolls past it. The two are one thought and
   the heading is the half you need to still have in view at the bottom of
   the column. Collapses to stacked below 960px, where sticky would mean the
   heading eating a third of a phone screen for the whole section. */
.story-split { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: clamp(28px, 5vw, 72px); align-items: start; }
.story-split .ss-aside { position: sticky; top: 108px; }
.story-split .ss-aside .eyebrow { margin-bottom: 10px; }
.story-split .ss-aside h2 { font-family: var(--display); font-size: clamp(26px, 3vw, 38px); line-height: 1.14; color: var(--deep); margin: 0; }
.story-split .ss-aside .ss-rule { width: 62px; height: 3px; margin-top: 18px; background: linear-gradient(90deg, var(--lime), var(--forest)); border-radius: 2px; }
@media (max-width: 960px) {
  .story-split { grid-template-columns: 1fr; }
  .story-split .ss-aside { position: static; }
}

/* ---- About: the figure row ----------------------------------------------
   Three facts, equal weight, no ranking. Sits half over the section boundary
   so it reads as belonging to both the hero above and the story below. */
.fact-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; background: rgba(15,46,34,.10); border: 1px solid rgba(15,46,34,.10); border-radius: 16px; overflow: hidden; }
.fact-row .fact { background: var(--white); padding: 26px 22px; text-align: center; transition: background .3s var(--ease); }
.fact-row .fact:hover { background: var(--cream-2); }
.fact-row .fact b { display: block; font-family: var(--display); font-size: clamp(28px, 3.4vw, 40px); line-height: 1; color: var(--deep); }
.fact-row .fact span { display: block; margin-top: 8px; font-size: 13px; letter-spacing: .04em; color: var(--muted); }
@media (max-width: 760px) { .fact-row { grid-template-columns: 1fr; } }

/* ---- Privacy: the layout -------------------------------------------------
   A contents rail beside the clauses, because a privacy policy is a reference
   document and nobody reads one end to end — they arrive looking for the one
   paragraph about who can see their email address. The rail is sticky and
   marks where you are; the clauses are cards so a section has an edge you can
   see, which is what makes "this bit here" possible to point at. */
.legal-layout { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: clamp(24px, 4vw, 56px); align-items: start; }
.legal-toc { position: sticky; top: 104px; }
.legal-toc .lt-head {
  font-family: var(--mark); font-size: 11.5px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  padding: 0 12px 10px;
}
.legal-toc a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: 9px; margin-bottom: 2px;
  font-size: 13.5px; line-height: 1.3; color: #4A5A4F; text-decoration: none;
  border-left: 2px solid transparent;
  transition: background .18s, color .18s, border-color .18s;
}
.legal-toc a:hover { background: rgba(45,122,58,.07); color: var(--deep); text-decoration: none; }
.legal-toc a.on { background: rgba(45,122,58,.10); border-left-color: var(--forest); color: var(--deep); font-weight: 600; }
.legal-toc a i { font-style: normal; font-family: var(--score); font-size: 12px; color: var(--muted); min-width: 16px; }
.legal-toc a.on i { color: var(--forest); }
@media (max-width: 960px) {
  .legal-layout { grid-template-columns: 1fr; }
  /* Horizontal chip row on a phone: a 12-item vertical list above the content
     is a wall the reader has to scroll past to reach the thing it indexes. */
  .legal-toc { position: static; display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px; -webkit-overflow-scrolling: touch; }
  .legal-toc .lt-head { display: none; }
  .legal-toc a { flex: none; border-left: 0; border: 1px solid rgba(15,46,34,.12); border-radius: 999px; white-space: nowrap; }
  .legal-toc a.on { border-color: var(--forest); }
}

.legal-card {
  position: relative;
  background: var(--white);
  border: 1px solid rgba(15,46,34,.10);
  border-radius: 16px;
  padding: 26px clamp(20px, 3vw, 32px) 28px;
  margin-bottom: 16px;
  /* Clears the sticky nav when jumped to from the contents rail. */
  scroll-margin-top: 96px;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.legal-card:hover { border-color: rgba(45,122,58,.24); box-shadow: 0 14px 34px -24px rgba(15,46,34,.5); }
.legal-card > h2 {
  display: flex; align-items: baseline; gap: 12px;
  font-family: var(--display); font-size: 20px; line-height: 1.25; color: var(--deep);
  margin: 0 0 14px;
}
.legal-card > h2 .lc-n {
  font-family: var(--score); font-size: 13px; font-weight: 600; letter-spacing: .06em;
  color: var(--forest); background: rgba(45,122,58,.10);
  border-radius: 7px; padding: 3px 8px; flex: none;
}
/* Inside a card the prose has already been given its measure by the card. */
.legal-card .prose { max-width: none; font-size: 15.5px; line-height: 1.72; }
.legal-card .prose h2 { font-size: 17px; margin-top: 24px; }

/* The Terms page opens with the same three paragraphs the sign-up checkbox
   shows, above the clause layout rather than inside it — somebody arriving
   from that checkbox meets what they just agreed to before the long version
   starts. Borrows .legal-note's card treatment at a size that suits three
   paragraphs rather than one. */
.legal-summary {
  background: var(--cream-2); border: 1px solid rgba(15,46,34,.10);
  border-left: 4px solid var(--lime); border-radius: 0 16px 16px 0;
  padding: clamp(20px, 3vw, 28px) clamp(20px, 3vw, 30px);
}
.legal-summary .eyebrow { margin-bottom: 4px; }
.legal-summary .section-heading { font-size: clamp(22px, 2.6vw, 28px); }

.legal-note {
  display: flex; gap: 14px; align-items: flex-start;
  background: rgba(45,122,58,.06);
  border: 1px solid rgba(45,122,58,.18);
  border-radius: 14px; padding: 18px 20px; margin-bottom: 20px;
}
.legal-note svg { width: 20px; height: 20px; flex: none; margin-top: 2px; fill: none; stroke: var(--forest); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.legal-note p { margin: 0; font-size: 14px; line-height: 1.6; color: #3C4B41; }
.legal-note b { color: var(--deep); }

/* ---- A compact hero, for pages that are mostly reading ------------------- */
.hero-lean { position: relative; overflow: hidden; background: var(--deep); color: var(--cream); padding: clamp(96px, 12vw, 132px) 0 clamp(44px, 6vw, 64px); }
.hero-lean .hl-bg { position: absolute; inset: 0; }
.hero-lean .hl-bg .shot { position: absolute; inset: 0; background-size: cover; background-position: center; opacity: 0; transition: opacity 2s var(--ease); }
.hero-lean .hl-bg .shot.on { opacity: .34; }
.hero-lean .hl-veil { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(12,36,23,.72), rgba(12,36,23,.92)); }
.hero-lean .wrap { position: relative; z-index: 1; }
.hero-lean h1 { font-family: var(--display); font-size: clamp(34px, 5.2vw, 58px); line-height: 1.06; margin: 14px 0 0; color: var(--white); }
.hero-lean .hl-sub { margin: 16px 0 0; max-width: 62ch; font-size: 17px; line-height: 1.7; color: rgba(232,228,213,.86); }
.hero-lean .hl-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.hero-lean .hl-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 999px;
  border: 1px solid rgba(200,241,53,.35); background: rgba(200,241,53,.08);
  font-size: 12.5px; font-weight: 600; letter-spacing: .04em; color: var(--lime);
}
.hero-lean .hl-chip svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }

/* ==========================================================================
   TAKING A TIP BACK
   ==========================================================================
   The round page's two sides became <button>s when picking had to become a
   toggle (templates/tip_round.html). They were labels, and a <button> arrives
   with a user-agent font, its own text alignment and a shrink-to-fit width, so
   the three properties a label got for free are restated here. Everything else
   about how a side looks is unchanged — same .pick .team rules as before.
   ========================================================================== */
.pick button.team {
  font: inherit; width: 100%; text-align: left;
  appearance: none; -webkit-appearance: none;
}
.pick button.team.away { text-align: right; }
.pick button.team:disabled { cursor: default; opacity: .75; }
.pick button.team:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }

/* The tick beside a fixture has two things to say now. "Saved" is the good
   news in forest; a take-back is not bad news and must not be red — it is the
   member telling us something, and it reads quietly. */
.saved-tick.undo { color: var(--muted); }
.saved-tick.undo svg { opacity: .8; }

/* ==========================================================================
   THE CHAT
   ==========================================================================
   One conversation component, used by both ends of a message thread and by
   the Wall's reply threads. Before this the three were three different
   treatments of the same object, and the message thread in particular was a
   stack of <p> with a name above each — legible, and not something anyone
   would call a conversation.

   TOKENS FIRST, so the component can sit on the member app's cream sheet and
   on the manage area's white cards without a second copy of every rule. Only
   these five change between grounds; everything below paints from them.
   ========================================================================== */
.chat {
  --ch-mine:      #123A25;                    /* your bubble  — deep forest  */
  --ch-mine-fg:   #F1F5EC;
  --ch-theirs:    #FFFFFF;                    /* their bubble — white on cream */
  --ch-theirs-fg: #17342A;
  --ch-line:      rgba(15,46,34,.12);
  --ch-quiet:     #6B7A6E;
  --ch-accent:    var(--forest);
}
/* On a dark ground the two sides swap roles rather than both going dark: the
   point of the arrangement is that the sides are TOLD APART at a glance, and
   two dark bubbles on dark is one colour with a gap in it. */
.page-deep .chat, .gt-dark .chat {
  --ch-mine:      #C8F135;
  --ch-mine-fg:   #0C2417;
  --ch-theirs:    rgba(255,255,255,.07);
  --ch-theirs-fg: #E8E4D5;
  --ch-line:      rgba(232,228,213,.16);
  --ch-quiet:     #9BAE9F;
  --ch-accent:    var(--lime);
}

.chat-list { list-style: none; margin: 0; padding: 4px 2px 8px; display: flex; flex-direction: column; gap: 4px; }

.chat-row {
  position: relative;
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 30px;
  align-items: end; gap: 9px;
  /* Set by the swipe gesture (gt-chat.js). Declared here so the transform
     below has something to read on a row that has never been touched. */
  --swipe: 0px;
  transform: translateX(var(--swipe));
  transition: transform .22s var(--ease);
}
/* While a finger is on it the bubble must track the finger exactly, so the
   easing that makes the spring-back feel good has to come off. */
.chat-row.swiping { transition: none; }
.chat-row.mine { grid-template-columns: 30px minmax(0, 1fr) 34px; }
.chat-row + .chat-row { margin-top: 2px; }
/* A run from one sender closes up; a change of speaker gets air. */
.chat-row:not(.tail) { margin-top: 14px; }
.chat-row:first-child { margin-top: 0; }

.chat-av {
  grid-column: 1; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; overflow: hidden; flex: none;
  background: var(--forest); color: #fff;
  font-family: var(--display); font-size: 13px; line-height: 1;
  align-self: flex-end;
}
.chat-av img { width: 100%; height: 100%; object-fit: cover; }
/* Kept in the layout, hidden from view: removing it would let the second
   bubble of a run slide under the first. */
.chat-row.tail .chat-av { visibility: hidden; }
.chat-row.mine .chat-av { grid-column: 3; background: var(--deep); color: var(--lime); }
.page-deep .chat-row.mine .chat-av { background: var(--lime); color: var(--deep); }

.chat-col { grid-column: 2; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }
.chat-row.mine .chat-col { align-items: flex-end; }

.chat-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 4px; padding: 0 4px; }
.chat-who {
  font: inherit; font-size: 13px; font-weight: 700; line-height: 1.2;
  color: var(--ch-theirs-fg); background: none; border: 0; padding: 0;
  cursor: pointer; border-bottom: 1px dotted transparent;
}
.chat-who:hover { border-bottom-color: currentColor; }
.chat-when { font-size: 11.5px; color: var(--ch-quiet); }

/* The profile card. Sits under the name it belongs to rather than floating at
   the pointer — a popover that follows the cursor has to be dismissed before
   you can read anything behind it, and this is a detail you glance at. */
.chat-peek {
  display: inline-flex; align-items: center; gap: 10px;
  margin: 0 0 6px; padding: 8px 12px 8px 8px;
  background: var(--ch-theirs); color: var(--ch-theirs-fg);
  border: 1px solid var(--ch-line); border-radius: 12px;
  box-shadow: 0 8px 22px -14px rgba(15,46,34,.5);
}
.chat-peek .cp-av {
  width: 36px; height: 36px; border-radius: 50%; overflow: hidden; flex: none;
  display: grid; place-items: center; background: var(--forest); color: #fff;
  font-family: var(--display); font-size: 14px;
}
.chat-peek .cp-av img { width: 100%; height: 100%; object-fit: cover; }
.chat-peek .cp-body b { display: block; font-size: 13.5px; line-height: 1.25; }
.chat-peek .cp-body small { display: block; font-size: 11.5px; color: var(--ch-quiet); }

.chat-bubble {
  position: relative;
  max-width: min(78%, 560px);
  padding: 10px 13px 8px;
  border-radius: 16px;
  background: var(--ch-theirs); color: var(--ch-theirs-fg);
  border: 1px solid var(--ch-line);
  box-shadow: 0 1px 2px rgba(15,46,34,.05);
  /* The tail. One square corner on the sender's side, which is the whole of
     the "speech bubble" idea and costs no pseudo-element. */
  border-bottom-left-radius: 5px;
}
.chat-row.mine .chat-bubble {
  background: var(--ch-mine); color: var(--ch-mine-fg);
  border-color: transparent;
  border-bottom-left-radius: 16px; border-bottom-right-radius: 5px;
}
/* Only the LAST bubble of a run wears the tail; the ones above it are the
   same person still talking. A row is mid-run when the row after it is a
   tail — which is a look-ahead, so :has() rather than :not(:last-child). That
   earlier form asked a different question ("is this the last row on the
   page") and got the answer wrong for every run but the final one. */
.chat-row:has(+ .chat-row.tail) .chat-bubble { border-bottom-left-radius: 16px; }
.chat-row.mine:has(+ .chat-row.tail) .chat-bubble { border-bottom-right-radius: 16px; }

.chat-text { margin: 0; font-size: 14.5px; line-height: 1.55; overflow-wrap: anywhere; }
.chat-stamp { display: block; margin-top: 3px; font-size: 10.5px; text-align: right; opacity: .6; }
/* The stamp holds the receipt now, so it stops being a plain block of text
   and becomes a row: time, then ticks, then the word. */
.chat-row.mine .chat-stamp {
  display: flex; align-items: center; justify-content: flex-end; gap: 7px;
  opacity: 1; color: inherit;
}
.chat-row.mine .chat-stamp > :first-child { opacity: .6; }

/* ---- Sent / read ----------------------------------------------------------
   ORANGE FOR SENT, GREEN FOR READ, and neither of them is this product's
   lime. Lime is the approval colour everywhere else in the app — a saved tip,
   a correct pick, the current nav item — so a lime tick on a message nobody
   has opened would read as "done" at the exact moment it means "waiting".
   Orange is not spent on anything else in the chat, and the green is the
   forest already used for status rather than the accent used for actions.

   The tick is a stroke, not a glyph: two paths at the same weight as the rest
   of the interface, so one tick and two are plainly the same mark repeated.
   The word beside it is what makes the pair legible to anyone who cannot
   separate the two hues, and to a screen reader reading the line straight
   through — which is why it is real text rather than a title attribute. */
.chat-tick { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; line-height: 1; }
.chat-tick svg {
  width: 15px; height: 11px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.1;
  stroke-linecap: round; stroke-linejoin: round;
}
.chat-tick i { font-style: normal; font-weight: 700; letter-spacing: .02em; }
/* Blue for read, not green. The first brief said green; the client's latest
   is "a single tick that is yellow that reads sent, if seen turns two ticks
   that are blue and means read" — which is also the convention people arrive
   already knowing, and it stops "read" sharing a hue with the brand's own
   green, where a receipt reads as decoration rather than as state. The
   yellow moves off orange at the same time. 5.9:1 and 6.4:1 on the bubble
   under them. */
.chat-tick.sent { color: var(--tick-sent, #E8A317); }
.chat-tick.read { color: var(--tick-read, #4FC3F7); }

/* THE TWO COLOURS ARE TOKENS BECAUSE THE BUBBLE UNDER THEM IS NOT ONE COLOUR.
   The defaults above are drawn for a DARK sent-bubble, which is what the
   light theme gives you (--ch-mine: #14382A) and what a first pass assumed
   was true everywhere. It is not: on the green theme the sides deliberately
   swap so they stay told apart on a dark ground, which makes your own bubble
   full-strength LIME — and a mid-green tick on lime is a shape you can find
   only if you already know it is there.

   So the same two states get a second pair of values, mixed for lime rather
   than for forest: a burnt amber and a deep blue, both dark enough to read
   against #C8F135 at the weight these strokes are drawn. Same meanings, same
   words beside them; only the ink changes with the paper. 5.2:1 and 6.7:1. */
.page-deep .chat-row.mine, .gt-dark .chat-row.mine {
  --tick-sent: #8A4B00;
  --tick-read: #0B4A8F;
}

/* The quote of the message being answered. A left bar and a tint, the shape
   every chat app uses for this — recognised before it is read. */
.chat-quote {
  display: block; margin: -2px 0 7px; padding: 6px 10px;
  border-left: 3px solid var(--ch-accent); border-radius: 7px;
  background: rgba(15,46,34,.06); text-decoration: none; color: inherit;
}
.chat-row.mine .chat-quote { background: rgba(255,255,255,.12); }
.page-deep .chat-quote { background: rgba(255,255,255,.09); }
.page-deep .chat-row.mine .chat-quote { background: rgba(12,36,23,.14); }
.chat-quote:hover { text-decoration: none; filter: brightness(.97); }
.chat-quote b { display: block; font-size: 11.5px; letter-spacing: .01em; }
.chat-quote span { display: block; font-size: 12.5px; opacity: .78; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Landing on a quoted message. Two beats, so it is visible on a screen the
   reader is still scrolling. */
@keyframes chat-flash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(200,241,53,0); }
  20%, 60% { box-shadow: 0 0 0 4px rgba(200,241,53,.55); }
}
.chat-row.chat-lit .chat-bubble { animation: chat-flash 1.5s var(--ease); border-radius: 16px; }

.chat-files { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 2px; }
.chat-shot {
  display: block; width: 148px; height: 108px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--ch-line); background: rgba(15,46,34,.06);
}
.chat-shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .3s var(--ease); }
.chat-shot:hover img { transform: scale(1.05); }
.chat-file {
  display: inline-flex; align-items: center; gap: 9px; max-width: 100%;
  padding: 8px 12px 8px 9px; border-radius: 10px; text-decoration: none;
  background: rgba(15,46,34,.06); border: 1px solid var(--ch-line); color: inherit;
}
.chat-row.mine .chat-file { background: rgba(255,255,255,.12); }
.chat-file:hover { text-decoration: none; border-color: var(--ch-accent); }
.chat-file .cf-ic { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: rgba(255,255,255,.18); flex: none; }
.chat-file .cf-ic svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; }
.chat-file .cf-txt { min-width: 0; }
.chat-file .cf-txt b { display: block; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-file .cf-txt small { display: block; font-size: 11px; opacity: .7; }

/* The reply arrow. Present but nearly invisible until the row is hovered, so
   a thread is bubbles rather than bubbles-and-a-column-of-buttons. It stays
   fully visible on touch, where there is no hover to reveal it with. */
.chat-replybtn {
  grid-column: 3; align-self: center; justify-self: center;
  width: 28px; height: 28px; border-radius: 50%; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  background: transparent; border: 1px solid transparent; color: var(--ch-quiet);
  opacity: 0; transition: opacity .18s, background .18s, color .18s;
}
.chat-row.mine .chat-replybtn { grid-column: 1; }
.chat-replybtn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chat-row:hover .chat-replybtn, .chat-replybtn:focus-visible { opacity: 1; }
.chat-replybtn:hover { background: rgba(45,122,58,.12); color: var(--ch-accent); border-color: var(--ch-line); }
@media (hover: none) { .chat-replybtn { opacity: .5; } }

/* The swipe target, revealed from under the bubble as it travels. Positioned
   from the row's own box rather than placed in the grid — an auto-placed
   pseudo-element would take a grid cell and push the avatar out of one. */
.chat-row::before {
  content: ""; position: absolute; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--ch-accent); opacity: 0;
  transition: opacity .15s;
  pointer-events: none;
}
.chat-row.theirs::before { left: 2px; }
.chat-row.mine::before { right: 2px; }
.chat-row.will-reply::before { opacity: .9; }

.chat-none { text-align: center; padding: 26px 0; color: var(--ch-quiet); font-size: 14px; }

/* ---- The composer -------------------------------------------------------- */
.chat-composer {
  margin-top: 14px; padding: 10px;
  background: var(--ch-theirs); border: 1px solid var(--ch-line);
  border-radius: 16px;
}
.page-deep .chat-composer { background: rgba(255,255,255,.05); }

.cc-quote {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px 7px 0; margin: 0 2px 8px;
  border-radius: 9px; background: rgba(45,122,58,.08);
}
.cc-quote .ccq-bar { width: 3px; align-self: stretch; border-radius: 3px; background: var(--ch-accent); margin-left: 8px; flex: none; }
.cc-quote .ccq-txt { min-width: 0; flex: 1 1 auto; }
.cc-quote .ccq-txt b { display: block; font-size: 12px; color: var(--ch-accent); }
.cc-quote .ccq-txt small { display: block; font-size: 12.5px; color: var(--ch-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-quote .ccq-x { font: inherit; font-size: 19px; line-height: 1; background: none; border: 0; color: var(--ch-quiet); cursor: pointer; padding: 0 6px; }
.cc-quote .ccq-x:hover { color: var(--ch-theirs-fg); }

.cc-files { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 2px 8px; }
.cc-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 6px 5px 10px; border-radius: 999px;
  background: rgba(45,122,58,.10); border: 1px solid var(--ch-line);
  font-size: 12px; max-width: 100%;
}
.cc-chip b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 180px; }
.cc-chip small { color: var(--ch-quiet); }
.cc-chip-x { font: inherit; font-size: 15px; line-height: 1; background: none; border: 0; cursor: pointer; color: var(--ch-quiet); padding: 0 2px; }
.cc-chip-x:hover { color: #B3261E; }

.cc-row { display: flex; align-items: flex-end; gap: 8px; }
.cc-clip {
  display: grid; place-items: center; width: 38px; height: 38px; flex: none;
  border-radius: 50%; cursor: pointer; color: var(--ch-quiet);
  transition: background .15s, color .15s;
}
.cc-clip:hover { background: rgba(45,122,58,.12); color: var(--ch-accent); }
.cc-clip svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.cc-clip input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.chat-composer textarea {
  flex: 1 1 auto; min-width: 0; resize: none; overflow-y: auto;
  min-height: 38px; max-height: 190px; padding: 9px 13px;
  font: inherit; font-size: 14.5px; line-height: 1.45;
  color: var(--ch-theirs-fg);
  background: rgba(15,46,34,.04);
  border: 1px solid var(--ch-line); border-radius: 19px;
}
.page-deep .chat-composer textarea { background: rgba(255,255,255,.06); }
.chat-composer textarea:focus { outline: none; border-color: var(--ch-accent); background: transparent; }
.chat-composer textarea::placeholder { color: var(--ch-quiet); }

.cc-send {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  height: 38px; padding: 0 16px; border-radius: 19px; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 700;
  background: var(--forest); color: #fff; border: 0;
  transition: background .15s, transform .1s;
}
.page-deep .cc-send { background: var(--lime); color: var(--deep); }
.cc-send:hover { background: var(--deep); }
.page-deep .cc-send:hover { background: #DBFF57; }
.cc-send:active { transform: translateY(1px); }
.cc-send svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
@media (max-width: 560px) { .cc-send span { display: none; } .cc-send { padding: 0 13px; } }

.cc-hint { margin: 8px 4px 0; font-size: 11.5px; color: var(--ch-quiet); }
@media (max-width: 700px) { .cc-hint { display: none; } }

.chat-shut {
  display: flex; align-items: center; gap: 10px;
  margin-top: 14px; padding: 12px 16px; border-radius: 12px;
  background: rgba(15,46,34,.05); border: 1px dashed var(--ch-line);
  font-size: 13px; color: var(--ch-quiet);
}
.chat-shut svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.9; }

@media (max-width: 620px) {
  .chat-bubble { max-width: 84%; }
  .chat-shot { width: 118px; height: 88px; }
}

/* ==========================================================================
   THE MESSAGE INBOX ROW
   ==========================================================================
   Shared by the member's inbox and the organisation admin's. It replaces
   `.gt-row`, which was styled inside gt-shell.css under `body.gt-shell` — a
   class the /manage/ pages do not set. So the admin's message list arrived
   with NO styling at all: bare anchors, black on cream, subject and status
   running together on one line. That is what "some text are not visible" was.

   One component instead of a second copy, because both lists answer the same
   question with the same parts: what it is about, whose room it is in, when it
   last moved, and whether it needs you.
   ========================================================================== */
.msg-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }

.msg-item {
  display: flex; align-items: center; gap: 15px;
  padding: 15px 18px;
  background: var(--gt-card, #fff);
  border: 1px solid var(--gt-hair, rgba(15,46,34,.10));
  border-radius: 14px;
  text-decoration: none; color: inherit;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease), transform .16s var(--ease);
}
.msg-item:hover {
  text-decoration: none;
  border-color: rgba(45,122,58,.35);
  box-shadow: 0 10px 26px -18px rgba(15,46,34,.55);
  transform: translateY(-1px);
}
/* Something unread is the only thing on the row worth an accent, so it gets
   the only one: a lime edge down the left. A whole row tinted for "new" makes
   the list read as a wall of alerts by the third one. */
.msg-item.is-new { border-left: 3px solid var(--lime); padding-left: 16px; }

.msg-item .mi-ic {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: 12px;
  background: rgba(45,122,58,.10); color: var(--forest);
}
.msg-item .mi-ic.notice { background: rgba(212,160,23,.16); color: #8A6508; }
.msg-item .mi-ic svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.msg-item .mi-main { flex: 1 1 auto; min-width: 0; }
.msg-item .mi-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.msg-item .mi-top b { font-size: 15px; font-weight: 700; color: var(--ink); }
/* The organisation. On the member's inbox this is the row's most important
   secondary fact — it is the thing that was missing when the page showed one
   organisation's threads and said nothing about which. */
.msg-item .mi-org {
  font-size: 11.5px; font-weight: 600; letter-spacing: .03em;
  padding: 2px 9px; border-radius: 999px;
  background: rgba(15,46,34,.07); color: var(--muted);
  white-space: nowrap;
}
.msg-item .mi-main small { display: block; margin-top: 4px; font-size: 12.5px; color: var(--muted); }
.msg-item .mi-end { flex: none; display: flex; align-items: center; gap: 10px; }
.msg-item .mi-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--lime); box-shadow: 0 0 0 3px rgba(200,241,53,.25); }

/* Inside a page sheet the reading surface is the warm cream every .card on
   one of these screens uses, not pure white — a single white row among cream
   cards reads as a different component rather than the same list. */
.sheet-page .page-sheet .msg-item { background: #F6F4EA; }

.msg-tally { font-size: 13px; color: var(--muted); }

@media (max-width: 620px) {
  .msg-item { gap: 12px; padding: 13px 14px; }
  .msg-item .mi-ic { width: 34px; height: 34px; border-radius: 10px; }
  .msg-item .mi-top b { font-size: 14px; }
}

/* ---- The page a thread sits on ------------------------------------------ */
.chat-shell { max-width: 860px; }
.chat-page { max-width: 900px; }
.chat-top { display: flex; align-items: center; gap: 14px; margin: -6px 0 16px; }
.chat-top-status { margin-left: auto; }
.chat-card { padding: 18px 20px 20px; }
.chat-close { margin-top: 16px; }

.ch-back {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--forest); text-decoration: none;
}
.ch-back:hover { color: var(--deep); text-decoration: underline; }
.page-deep .ch-back { color: var(--lime); }
.page-deep .ch-back:hover { color: #DBFF57; }

/* The mobile sheet's own copy of the nav count. `.an-count` is scoped to
   `.an-links a`, and the sheet is a different list — so without this the
   badge simply vanished at the width where the bar becomes a burger, which is
   the width where a member is most likely to be looking. */
.sheet-panel a .an-count {
  display: inline-block; min-width: 18px; padding: 0 5px; margin-left: 6px;
  border-radius: 100px; background: var(--lime); color: var(--deep);
  font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
  vertical-align: middle;
}

/* The manage inbox's tab row. Was an inline flex with a spacer span pushing
   the action to the right; at tablet widths the tabs and the button collided
   because nothing was allowed to wrap. */
.msg-bar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.msg-tabs { display: flex; gap: 6px; flex-wrap: wrap; flex: 1 1 auto; }
/* The open count, inside the tab rather than in the label. A parenthesised
   number in the text moves the tab's width every time a member writes in. */
.gts-tab i {
  font-style: normal; margin-left: 7px; padding: 1px 7px; border-radius: 999px;
  background: rgba(45,122,58,.14); color: var(--forest); font-size: 11.5px; font-weight: 800;
}
.gts-tab.on i { background: rgba(255,255,255,.22); color: #fff; }

/* ==========================================================================
   THE WALL'S THREADS, REPLYING TO ONE REPLY
   ==========================================================================
   The Wall already had avatars, names and a thread under each post. What it
   had no way to say was WHICH reply a reply was answering, so under a post
   with a dozen of them a one-line joke was floating free of the thing it was
   about. It now uses the same controls as a message thread — the arrow, the
   swipe, the quote block — because the client asked for one behaviour in both
   places and a second implementation is a second thing to keep in step.
   ========================================================================== */
.gpt-reply { position: relative; --swipe: 0px; transform: translateX(var(--swipe)); transition: transform .22s var(--ease); }
.gpt-reply.swiping { transition: none; }
/* The swipe target under the reply, same as a chat row's. */
.gpt-reply::before {
  content: ""; position: absolute; top: 50%; left: 0; transform: translateY(-50%);
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--forest); opacity: 0; transition: opacity .15s; pointer-events: none;
}
.gpt-reply.mine::before { left: auto; right: 0; background: var(--lime); }
.gpt-reply.will-reply::before { opacity: .9; }

/* The avatar became a button so the profile card has something to hang off.
   A <button> arrives with a user-agent font and border, and this one has to
   keep looking exactly like the <span> it replaces. */
button.gpr-av { font: inherit; border: 0; padding: 0; cursor: pointer; font-weight: 700; font-size: 12px; }
button.gpr-av:hover { filter: brightness(1.08); }

.gpr-tools { display: flex; align-items: flex-start; gap: 2px; flex: none; }
/* On the Wall the arrow lives in a row of controls rather than in its own grid
   column, so it drops the chat's absolute placement and keeps only the look. */
.gpt-reply .chat-replybtn { position: static; }
.gpt-reply:hover .chat-replybtn { opacity: 1; }

/* The quote inside a Wall reply. The chat's own rule paints from --ch- tokens
   that only exist inside .chat, so the Wall states the two it needs. */
.gpr-body .chat-quote {
  margin: 5px 0 4px; background: rgba(15,46,34,.06);
  border-left-color: var(--forest); max-width: 460px;
}
.gpr-body .chat-quote b { color: var(--forest); }
.gpr-body .chat-quote span { color: var(--ink); }
.gpr-body .chat-peek { margin-top: 8px; }
.gpr-body .chat-peek .cp-av { font-size: 13px; }
.gpt-reply.chat-lit { animation: chat-flash 1.5s var(--ease); border-radius: 10px; }

/* The reply box gained a banner above it, so it stops being one flex row and
   becomes a column with a row inside. */
.gpt-replybox { display: block; }
.gpr-row { display: flex; align-items: flex-end; gap: 10px; }
.gpt-replybox .cc-quote { max-width: 520px; }
@media (max-width: 640px) {
  .gpr-row { flex-wrap: wrap; }
  .gpr-row textarea { flex: 1 1 100%; order: 1; }
  .gpr-row button[type="submit"] { order: 2; margin-left: auto; }
}

/* ==========================================================================
   CHARITIES YOU CAN FIX
   ==========================================================================
   A charity with no logo was a dead end: the initials tile is a designed
   fallback, but nothing on the screen said it could be replaced, and nothing
   let anybody replace it. Every tile an admin may change is now a link to the
   edit form, and the ones missing a mark say so.
   ========================================================================== */
a.ch-item { text-decoration: none; color: inherit; }
a.ch-item:hover { text-decoration: none; transform: translateY(-2px); }
a.ch-item { transition: border-color .16s var(--ease), background .16s var(--ease), transform .16s var(--ease); }

/* The pencil, top-right, revealed on hover. Always visible it is a column of
   identical glyphs down a grid of fifty; on hover it is an affordance that
   turns up exactly where the pointer already is. */
.ch-item-edit {
  position: absolute; top: 10px; right: 10px;
  display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 8px; background: rgba(200,241,53,.16); color: var(--lime);
  opacity: 0; transition: opacity .16s var(--ease);
}
.ch-item-edit svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.9; }
a.ch-item:hover .ch-item-edit, a.ch-item:focus-visible .ch-item-edit { opacity: 1; }

.ch-item-fix {
  margin-top: 10px; font-size: 12px; font-weight: 700; color: var(--lime); opacity: .85;
}

/* ---- The edit form ------------------------------------------------------- */
.ch-edit { max-width: 640px; }
.che-now {
  display: flex; align-items: center; gap: 16px;
  padding: 16px; margin-bottom: 20px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: 14px;
}
.che-mark { flex: none; }
.che-mark img, .che-initials {
  width: 72px; height: 72px; border-radius: 16px;
  object-fit: contain; background: #fff; padding: 8px;
}
.che-initials {
  display: grid; place-items: center; padding: 0;
  background: hsl(var(--ch-hue, 140) 46% 90%);
  color: hsl(var(--ch-hue, 140) 52% 26%);
  font-family: var(--display); font-size: 24px;
}
.che-now-txt b { display: block; font-size: 14.5px; color: #fff; }
.che-now-txt small { display: block; margin-top: 4px; font-size: 12.5px; line-height: 1.5; color: rgba(255,255,255,.6); max-width: 46ch; }

.che-refetch {
  display: flex; align-items: flex-start; gap: 11px; cursor: pointer;
  padding: 14px 16px; margin-top: 4px;
  background: rgba(200,241,53,.07);
  border: 1px solid rgba(200,241,53,.22);
  border-radius: 12px;
}
.che-refetch input { margin-top: 3px; width: 16px; height: 16px; flex: none; accent-color: var(--lime); }
.che-refetch b { display: block; font-size: 14px; color: #fff; font-weight: 600; }
.che-refetch small { display: block; margin-top: 3px; font-size: 12.5px; color: rgba(255,255,255,.6); }

.che-foot { display: flex; gap: 10px; justify-content: flex-end; margin-top: 22px; }

/* ==========================================================================
   A STORY PAGE THAT LOOKS LIKE THE REST OF THE APP
   ==========================================================================
   News detail was the one member screen with no side imagery, no stage, and
   no card — a bare column of text with a photograph on top of it, on whatever
   background it happened to inherit. It now stands on the same deep-green
   ground as the dashboard, with the rotating strips down the edges, and the
   article is a cream card on it.

   The head moved above the picture at the same time. On a phone the old order
   spent the first two-thirds of the screen on a stock photograph and made you
   scroll to find out what the story was.
   ========================================================================== */
.art-page, .art-more {
  background: var(--gt-sheet, #F4F2EA);
  border: 1px solid var(--gt-hair, rgba(15,46,34,.10));
  border-radius: 20px;
  padding: clamp(22px, 3.4vw, 40px);
  box-shadow: 0 24px 60px -34px rgba(0,0,0,.55);
}
.art-more { margin-top: 28px; }
.art-page .art-back { color: var(--forest); }

.art-head { margin-bottom: 22px; }
.art-kicker {
  display: inline-block; margin-bottom: 12px;
  font-family: var(--mark); font-size: 11.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 11px; border-radius: 999px;
  background: rgba(45,122,58,.12); color: var(--forest);
}
.art-page .art-title { margin-bottom: 12px; }
.art-page .art-lead {
  font-size: 18.5px; line-height: 1.6; color: #34433A; font-weight: 500;
  margin: 0 0 18px; max-width: 62ch;
  /* A rule down the left, so the standfirst is visibly not the first
     paragraph of the story. Without it the two run together and the lead
     reads as an oddly bold opening. */
  border-left: 3px solid var(--lime); padding-left: 16px;
}

.art-byline { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding-top: 4px; border-top: 1px solid var(--gt-hair, rgba(15,46,34,.10)); }
.art-byline .ab-who { display: inline-flex; align-items: center; gap: 9px; margin-top: 12px; }
.art-byline .ab-av {
  width: 30px; height: 30px; border-radius: 50%; overflow: hidden; flex: none;
  display: grid; place-items: center; background: var(--deep); color: var(--lime);
  font-family: var(--display); font-size: 12px;
}
.art-byline .ab-av img { width: 100%; height: 100%; object-fit: cover; }
.art-byline .ab-who b { font-size: 13.5px; color: var(--ink); }
.art-byline .ab-when { margin-top: 12px; font-size: 12.5px; color: var(--muted); }

/* The rotating slot for a post with no picture of its own. Was a flat green
   panel with the wordmark stamped on it, which said "this post has no image"
   rather more clearly than it needed to. */
.art-hero-shots { position: relative; }
.art-hero-shots .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center 35%;
  opacity: 0; transition: opacity 2s var(--ease);
}
.art-hero-shots .shot.on { opacity: 1; }
.art-hero-shots .sport-tag { z-index: 1; }

/* Reading measure, and the rhythm inside it. The body is authored in the news
   editor and arrives as arbitrary HTML, so these have to cover headings and
   quotes the template never sees. */
.art-page .art-body { max-width: 68ch; }
.art-page .art-body h2 { font-family: var(--display); font-size: 22px; line-height: 1.25; color: var(--deep); margin: 30px 0 12px; }
.art-page .art-body h3 { font-family: var(--display); font-size: 18px; color: var(--deep); margin: 24px 0 10px; }
.art-page .art-body ul, .art-page .art-body ol { margin: 0 0 16px; padding-left: 22px; }
.art-page .art-body li { margin-bottom: 7px; }
.art-page .art-body img { border-radius: 12px; margin: 18px 0; }
.art-page .art-body blockquote {
  margin: 20px 0; padding: 4px 0 4px 18px;
  border-left: 3px solid var(--forest);
  font-size: 17px; line-height: 1.6; color: #34433A;
}
.art-page .art-body a { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }
.art-page .art-body a:hover { color: var(--deep); }

/* ==========================================================================
   THE MEMBER APP'S LIGHT THEME
   ==========================================================================
   The app's designed look is the deep green — that IS its dark theme, and it
   stays the default. This is the same screens translated into warm daylight,
   offered through the switch in the nav (partials/_app_theme_switch.html).

   WHY IT KEYS ON [data-theme="light"] AND NOTHING ELSE.
   /admin/ and the HQ shell are light by design and go dark on `auto` when the
   device asks. Here it is the other way round: the green is the product, so
   `auto` and an unset key both keep it. Letting prefers-color-scheme flip the
   member app would hand every visitor whose phone is on dark mode a look
   nobody has signed off — a redesign by side effect rather than a choice.

   WHAT IT IS, AS A DESIGN.
   The first pass translated the frame and left the rest: a dark page turned
   pale, with the cards still cream and the accents still tuned for green
   behind them. It read plain and dry, which is the note the client gave. This
   pass gives light its own palette rather than a de-saturated copy of the
   dark one, on four layers that never collide:

       warm cream page  →  white cards  →  warm-beige insets  →  lime accents

   White dominates, cream carries the warmth, deep forest carries the
   contrast, and the lime is spent deliberately — active nav, the primary
   button, the selected room, one number on one tile — rather than sprayed
   about. It is a yellow-GREEN, never a bright yellow: #C4F000 as a fill under
   deep-green text, and #4E7A0B when the accent has to BE the text, because
   lime on white is a colour you cannot read.

   HOW IT REACHES EVERY OTHER PAGE.
   Every private screen wears `page-deep sheet-page` (see the body_class block
   in dashboard.html, ladder.html, wall.html, and the rest), and everything on
   them is drawn from the same handful of tokens. So the palette below is set
   once, on <html> and again on <body>, and the dashboard, My Tips, the
   Leaderboard, the Ladder, the Wall, Messages, Groups, Members and the
   organisation wizard all inherit the same light theme without a page of
   their own. The rules after the token block are the places that had a colour
   welded in because they knew what was behind them.

   WHY THE PALETTE IS RESTATED ON `body`.
   `.gt-v2` is on the body of every one of these pages and it sets --gt-card,
   --gt-sheet and --gt-hair itself. That is inheritance, not specificity: a
   property set on the body replaces the inherited one for the whole subtree
   however strong the selector on <html> was. Same trap as the admin shell —
   see the note on gt-shell.css — so the same fix, on the same element.
   ========================================================================== */
html[data-theme="light"] body.gt-app,
html[data-theme="light"] body.manage-app,
html[data-theme="light"] body.gt-shell {
  /* ---- The four layers ----
     Cream is gone, and so is every translucent overlay that went with it. The
     client's note was that the cream read as haze rather than as a surface —
     "that misty vibe", and on the room switcher "that cream, I'm not feeling
     it, not been able to see it". It was doing two jobs badly: separating
     surfaces AND carrying warmth, and at the contrast a warm off-white can
     manage against white it did neither.

     So the page is white, the cards are white, and what separates them is a
     border and a shadow — a decision, not a tint. Where a surface genuinely
     has to read as INSIDE another one, it takes a green tint rather than a
     warm one, because green is the brand and it survives being pale. Every
     layer below is opaque; nothing on these screens is a translucent white
     over something else any more. */
  --lt-page:      #FFFFFF;   /* white — the page itself */
  --lt-surface:   #FFFFFF;   /* white — the cards you read on */
  --lt-surface-2: #F2F7EA;   /* soft green — a box inside a box */
  --lt-tint:      #F7FBF1;   /* the faintest green — hover, quiet ground */
  --lt-sel:       #EEF8C8;   /* pale lime — kept only for row highlights */

  /* ---- Ink ---- */
  --lt-ink:    #0F2E22;      /* deep forest — headings and body */
  --lt-forest: #176B45;      /* rich green — secondary action, status */
  /* Supporting copy. 5.2:1 on plain white, which is where the note that used
     to sit here measured it — but it is as often written on a pale chip or a
     tinted row, and on those it fell to 4.4:1. Five points darker holds 4.5:1
     everywhere it is actually used, and 5.6:1 on the white it was tuned for.
     See scripts/check_contrast.py. */
  --lt-quiet:  #636A62;      /* warm grey — supporting copy */

  /* ---- The accent, in its two jobs ----
     A fill wants the bright end; text wants the dark end. Treating them as
     one colour is how a lime accent becomes an unreadable yellow caption. */
  --lt-accent:     #C4F000;  /* fills: buttons, the active pill */
  --lt-accent-2:   #B8E600;  /* the same fill, pressed */
  --lt-accent-ink: #4E7A0B;  /* the accent AS type, on white — 4.6:1 */

  /* ---- Hairlines and lift ---- */
  --lt-line:   rgba(15,46,34,.13);
  --lt-line-2: rgba(15,46,34,.08);
  --lt-line-3: rgba(15,46,34,.20);   /* controls, which need a visible edge */
  /* White on white means the edge IS the separation, so it is drawn a little
     harder than it would be over cream. */
  --lt-line-g: rgba(23,107,69,.22);  /* the green edge, for green surfaces */
  --lt-lift:   0 1px 2px rgba(15,46,34,.04), 0 12px 28px -22px rgba(15,46,34,.30);
  --lt-lift-2: 0 2px 4px rgba(15,46,34,.05), 0 26px 54px -34px rgba(15,46,34,.38);

  /* ---- The existing app tokens, re-pointed ----
     Everything downstream paints from these, which is why one block reaches
     screens this file has no rule for. */
  --app-bg:     var(--lt-page);
  --app-card:   var(--lt-surface);
  --app-card-2: var(--lt-surface-2);
  --app-line:   var(--lt-line);
  --app-line-2: var(--lt-line-2);
  --gt-sheet:   var(--lt-surface);
  --gt-card:    var(--lt-surface);
  --gt-hair:    var(--lt-line);
  --card-1:     var(--lt-surface-2);
  --line:       var(--lt-line);

  /* --muted and --forest are the two brand tokens tuned for cream-on-green.
     On white the grey is a shade too cool and the green a shade too light, so
     both move — once, here, rather than in the forty rules that read them. */
  --muted:  var(--lt-quiet);
  --forest: var(--lt-forest);
}

/* ==========================================================================
   THE STAGE
   ========================================================================== */
html[data-theme="light"] .gt-app { background: var(--lt-page); color: var(--lt-ink); }

/* Flat white, and deliberately flat. This carried two big radial washes to
   keep the cream from reading as a dead fill; on white the same washes are
   the haze the client asked to be rid of. A light theme does not need the
   page to be doing anything. */
html[data-theme="light"] .gt-app.page-deep { background: var(--lt-page); }
html[data-theme="light"] .gt-app.page-deep .app-head .app-h1 { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.page-deep .app-head .app-eyebrow { color: var(--lt-accent-ink); }
html[data-theme="light"] .gt-app.page-deep .app-head .app-sub { color: var(--lt-quiet); opacity: 1; }

/* The photo strips down both edges stay — they are the frame this product is
   recognised by. What changes is only what they fade INTO: their wash is a
   gradient towards the page, so it has to be the page's colour, or the strips
   end in a dark smear against cream. */
/* THE STRIPS KEEP THEIR PHOTOGRAPHS, UNVEILED.
   A cream wash was tried here and taken straight back out: it is the "misty"
   the client objected to, and it was solving a problem the cream page created
   rather than one the photographs have. The decision above — "clear photos,
   just a hairline where they meet the content, no mist" — turns out to hold
   in both themes. All light needs is its own hairline. */
html[data-theme="light"] .gt-app.page-deep .page-scenes .scene.left { border-right-color: var(--lt-line); }
html[data-theme="light"] .gt-app.page-deep .page-scenes .scene.right { border-left-color: var(--lt-line); }

/* ==========================================================================
   THE NAV
   ==========================================================================
   White, with a warm hairline and the faintest shadow so it separates from
   the cream as it sticks. Not a green bar: the header is furniture, and a
   solid brand colour across the top is the one thing that would stop this
   reading as a light theme at all. */
/* NOT A FLAT WHITE BAR. The client's words: "instead of just pure white let it
   have a blend on that white and that green, kind of a fusion, with a nice
   pattern to it."

   END TO END, which the first attempt was not: it held flat white across the
   left two thirds and only turned green under the account controls, so the
   "fusion" was really a green corner on a white bar. The wash now starts
   green at the wordmark, opens to white through the middle where the links
   are and the labels need the most contrast, and closes green again at the
   right — a blend across the whole width rather than at one end of it.

   The diagonal hatch over the top is what stops a seven-stop gradient reading
   as a smudge: it gives the surface a texture to be, at a strength you only
   notice when you look for it. Both layers sit on nothing translucent. */
html[data-theme="light"] .app .app-nav {
  background:
    repeating-linear-gradient(135deg,
      rgba(23,107,69,.04) 0 1.5px, rgba(255,255,255,0) 1.5px 8px),
    linear-gradient(100deg,
      #E7F3CE 0%, #F2F9E3 18%, #FBFDF5 38%, #FFFFFF 52%, #F7FBEA 70%, #EDF6DA 86%, #E4F2C6 100%);
  border-bottom: 1px solid rgba(23,107,69,.18);
  box-shadow: 0 1px 0 rgba(255,255,255,.7) inset, 0 10px 22px -20px rgba(15,46,34,.35);
}
html[data-theme="light"] .app .an-brand { color: var(--lt-ink); }
html[data-theme="light"] .app .an-brand span { color: var(--lt-accent-ink); }
html[data-theme="light"] .app .an-links a { color: #46554B; opacity: 1; }
html[data-theme="light"] .app .an-links a:hover { background: var(--lt-tint); color: var(--lt-ink); }

/* THE ACTIVE PAGE: A SOLID FILL, NOT A TINT.
   Pale lime was tried and rejected — "that green infill on the menu, it looks
   like it's drawn with pencil, I don't feel it, make it have a nice colour
   infill." Correct: a 12%-ish wash of an accent under dark type is a hint that
   something might be selected, not a statement that it is.

   The answer was already in the product. /manage/ fills its current rail item
   and its current tab with solid deep forest, white type, lime icon
   (gt-shell.css `.gts-rail a.on`, `.gts-tab.on`) — the client singled those
   screens out as handling colour well. So the member app says "you are here"
   the same way, and the two areas stop disagreeing about what selected looks
   like. */
html[data-theme="light"] .app .an-links a.on {
  background: var(--lt-ink); color: #FFFFFF; font-weight: 600;
  box-shadow: 0 6px 14px -8px rgba(15,46,34,.55);
}
html[data-theme="light"] .app .an-links a.on svg { stroke: var(--lime); color: var(--lime); }
html[data-theme="light"] .app .an-links a.on:hover { background: #17402F; color: #FFFFFF; }

/* THE GLYPHS TRAVEL FROM LIGHT GREEN TO DARK GREEN.
   On the green theme the marks sit at 62% cream and light up lime on hover —
   dim to bright. On white that reads backwards, because the brighter a green
   gets the LESS of it there is against the page. So the resting state is a
   light, friendly green and the hover state is the deep forest: the glyph
   gets heavier as you approach it, not lighter, which is the same direction
   the label travels and the same direction the fill travels when the item
   becomes current. The current item keeps its lime, because by then the tile
   under it is deep green and lime is the only bright thing left. */
html[data-theme="light"] .app .an-links a .an-ic,
html[data-theme="light"] .app .an-menu-btn .an-ic { color: #8DC63F; opacity: 1; }
html[data-theme="light"] .app .an-links a:hover .an-ic,
html[data-theme="light"] .app .an-links a:focus-visible .an-ic,
html[data-theme="light"] .app .an-menu-btn:hover .an-ic,
html[data-theme="light"] .app .an-menu-btn[aria-expanded="true"] .an-ic { color: var(--lt-ink); opacity: 1; }
html[data-theme="light"] .app .an-links a.on .an-ic { color: var(--lime); opacity: 1; }
html[data-theme="light"] .app .an-menu-btn { color: #46554B; }
html[data-theme="light"] .app .an-menu-btn:hover { color: var(--lt-ink); }

/* The context chip is a control, so it gets a control's edge — a clean white
   card, not a tinted wash. */
html[data-theme="light"] .app .an-ctx-btn {
  color: var(--lt-ink); background: var(--lt-surface);
  border-color: var(--lt-line-3);
}
html[data-theme="light"] .app .an-ctx-btn:hover {
  border-color: rgba(23,107,69,.45); background: var(--lt-tint);
}
html[data-theme="light"] .app .an-ctx-org { opacity: .78; }
/* The group half decides which ladder a tip lands on, so it stays the
   emphasised one — in a green that can actually be read on white. */
html[data-theme="light"] .app .an-ctx-group { color: var(--lt-forest); }
html[data-theme="light"] .app .an-iconbtn { color: #46554B; }
html[data-theme="light"] .app .an-iconbtn:hover { background: var(--lt-tint); color: var(--lt-ink); }
html[data-theme="light"] .app .an-burger { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .bell-btn { color: #46554B; }
html[data-theme="light"] .app .an-chip .cn b { color: var(--lt-ink); }
html[data-theme="light"] .app .an-org { color: var(--lt-ink); opacity: .78; }

/* The drop-down panels hang off a white bar now, so they need their own edge
   rather than the contrast the dark bar was giving them for free. */
/* THE DROP-DOWNS, WHICH WERE UNREADABLE.
   "In the menu dropdowns words are not visible." They were not: the panels are
   a deep-green gradient in the green theme and every label inside them is
   written for it — `.an-menu-panel a b { color: #fff }`, `.an-ctx-row .mi-txt
   b { color: #fff }`, the sub-labels at 55% cream, the tick and the current
   organisation in lime. The first pass turned the PANEL white and left all of
   that alone, so the organisation list, the group list and the Manage and
   Admin menus were white on white — the one place in the app where a member
   picks which organisation they are tipping for.

   Every one of them is restated here. It is a long block on purpose: each of
   those colours was chosen against green, and there is no single token that
   flips them. */
html[data-theme="light"] .app .an-menu-panel,
html[data-theme="light"] .app .an-ctx-panel {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line);
  box-shadow: var(--lt-lift-2);
}
html[data-theme="light"] .app .an-menu-panel a,
html[data-theme="light"] .app .an-ctx-row { color: var(--lt-ink); }
html[data-theme="light"] .app .an-menu-panel a b,
html[data-theme="light"] .app .an-ctx-row .mi-txt b { color: var(--lt-ink); }
html[data-theme="light"] .app .an-menu-panel a i,
html[data-theme="light"] .app .an-ctx-row .mi-txt i { color: var(--lt-quiet); }
html[data-theme="light"] .app .an-menu-panel a:hover { background: var(--lt-tint); }
html[data-theme="light"] .app .an-menu-panel a .mi-ic {
  background: var(--lt-surface-2);
  border-color: var(--lt-line-g);
  color: var(--lt-forest);
}
html[data-theme="light"] .app .an-menu-panel a:hover .mi-ic {
  background: var(--lt-sel); border-color: rgba(23,107,69,.38); color: var(--lt-ink);
}

/* Section headings inside the panel — "Your organisation", "Groups". */
html[data-theme="light"] .gt-app .an-ctx-head {
  color: var(--lt-forest);
  text-transform: uppercase; letter-spacing: .1em;
}
html[data-theme="light"] .gt-app .an-ctx-col-groups { border-left-color: var(--lt-line); }
/* the same divider, once it has become a top border on narrow screens */
html[data-theme="light"] .gt-app .an-ctx-col-groups { border-top-color: var(--lt-line); }
html[data-theme="light"] .gt-app .an-ctx-row:hover,
html[data-theme="light"] .gt-app .an-ctx-row-static:hover { background: var(--lt-tint); }

/* The organisation you are in gets the same solid fill as the current nav
   item and /manage/'s current rail row, for the same reason: this is the row
   the whole panel exists to point at. */
html[data-theme="light"] .gt-app .an-ctx-row.on,
html[data-theme="light"] .gt-app .an-ctx-row-static:hover,
html[data-theme="light"] .gt-app .an-ctx-row.on:hover {
  background: var(--lt-ink);
}
html[data-theme="light"] .gt-app .an-ctx-row.on .mi-txt b { color: #FFFFFF; }
html[data-theme="light"] .gt-app .an-ctx-row.on .mi-txt i { color: rgba(255,255,255,.66); }
html[data-theme="light"] .gt-app .an-ctx-row.on::after { color: var(--lime); }
html[data-theme="light"] .gt-app .an-ctx-more { color: var(--lt-forest); }
html[data-theme="light"] .gt-app .an-ctx-filter {
  background: var(--lt-surface); border-color: var(--lt-line-3); color: var(--lt-ink);
}
html[data-theme="light"] .gt-app .an-ctx-filter::placeholder { color: var(--lt-quiet); }
/* THE ORGANISATION LIST, GREENER. Asked for directly — "make that dropdown
   scroll still more green for visibility." White rows on a white panel had
   nothing to say where the list started or ended; on a green bed each row is
   an object, the scroll region reads as a region, and the current
   organisation's solid green fill still wins because it is darker than the
   bed rather than merely different from it. */
html[data-theme="light"] .gt-app .an-ctx-scroll {
  background: var(--lt-surface-2);
  border: 1px solid var(--lt-line-g);
  border-radius: 11px;
  scrollbar-color: rgba(23,107,69,.42) transparent;
}
html[data-theme="light"] .gt-app .an-ctx-scroll .an-ctx-row:hover { background: #FFFFFF; }
html[data-theme="light"] .gt-app .an-ctx-scroll::-webkit-scrollbar-thumb {
  background-color: rgba(23,107,69,.42);
}
/* The fade at the foot of a scrolling list is a gradient towards the surface
   under it, and that surface was `--deep-2`. Left alone it painted a dark
   smear across the bottom two rows of a white panel. */
html[data-theme="light"] .gt-app .an-ctx-listwrap::after {
  background: linear-gradient(180deg, rgba(242,247,234,0), var(--lt-surface-2));
}

/* "JUPITER doesn't use groups" — the one line in the panel that appears only
   when an organisation has none, at 55% cream, on white. Invisible exactly
   when it is the only thing the column has to say. */
html[data-theme="light"] .gt-app .an-ctx-empty { color: var(--lt-quiet); }

/* ==========================================================================
   THE CHARITY STRIP
   ==========================================================================
   A quiet beige band under the nav — present, not competing with it. */
html[data-theme="light"] .gt-app .charity-strip {
  background: var(--lt-tint); color: #33453A;
  border-bottom: 1px solid var(--lt-line-2);
}
html[data-theme="light"] .gt-app .charity-strip .cs-amt { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .charity-strip svg { fill: var(--lt-forest); }
html[data-theme="light"] .gt-app .charity-strip .cs-bar { background: rgba(15,46,34,.09); }
html[data-theme="light"] .gt-app .charity-strip .cs-bar > i {
  background: linear-gradient(90deg, var(--lt-forest), var(--lt-accent-2));
}
html[data-theme="light"] .gt-app .charity-strip.nopledge .cs-amt { color: var(--lt-forest); }

/* ==========================================================================
   THE SHEET, AND WHAT SITS ON IT
   ==========================================================================
   In the dark theme the sheet is a deep green panel and the cards on it are
   cream: the sheet is the ground and the cards are the paper. Inverting that
   literally stacks three near-identical warm tones and is exactly the flat,
   dry look the client described.

   So in light the sheet steps out of the way entirely. It keeps its width and
   its padding, which is all the layout needs from it, and the page becomes
   the ground. What the eye reads instead is a two-step: a white card with a
   real edge, and inside it a soft green box for the round. The client
   described exactly this nesting — "an outer box, then inside it a box that
   houses this content" — and asked for the outer one to be white rather than
   cream. It is, and so is the page behind it; the green box is what makes the
   nesting legible without a third colour. */
html[data-theme="light"] .gt-app.sheet-page .page-sheet {
  background: none;
  border: 0;
  box-shadow: none;
}
@media (max-width: 900px) {
  html[data-theme="light"] .gt-app.sheet-page .page-sheet { max-width: 100%; }
}
html[data-theme="light"] .gt-app.sheet-page .page-sheet .app-h1 { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.sheet-page .page-sheet .app-eyebrow { color: var(--lt-accent-ink); }
html[data-theme="light"] .gt-app.sheet-page .page-sheet .app-sub { color: var(--lt-quiet); }

/* The white card on a white page. The edge is doing ALL of the separating
   now, so it is drawn at a strength you can actually see, with the shadow
   under it rather than instead of it. */
html[data-theme="light"] .gt-app .card,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .card {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line);
  border-radius: 18px;
  box-shadow: var(--lt-lift);
}
/* The box inside the box — the round, its countdown, its tiles and the tip
   slip. Soft green with a green edge, so it reads as a compartment of the
   card rather than as another card. This is the third layer, and the only
   one; anything deeper flattens back to white. */
html[data-theme="light"] .gt-app .card .card,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .card .card {
  background: var(--lt-surface-2);
  border-color: var(--lt-line-g);
  box-shadow: none;
}
/* Tiles and controls sitting inside that green box go back to white, or they
   dissolve into it. */
html[data-theme="light"] .gt-app .card .card .stat-card,
html[data-theme="light"] .gt-app .card .card .cd-cell { background: #FFFFFF; }
html[data-theme="light"] .gt-app .card-h { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .card-lead { color: var(--lt-quiet); }

/* The reading beds — the ladder, the leaderboard, the Wall's feed, the
   fixture list. Beige rather than white, so the white rows inside them lift. */
html[data-theme="light"] .gt-app.sheet-page .gt-listwrap,
html[data-theme="light"] .gt-app.sheet-page .gt-board,
html[data-theme="light"] .gt-app.sheet-page .gt-feed {
  background: var(--lt-surface-2);
  border: 1px solid var(--lt-line-g);
  box-shadow: var(--lt-lift);
}
html[data-theme="light"] .gt-app.sheet-page .gt-board .gb-head {
  color: var(--lt-quiet);
  border-bottom-color: var(--lt-line-3);
}
html[data-theme="light"] .gt-app.sheet-page .gt-post,
html[data-theme="light"] .gt-app.sheet-page .gt-feed .gt-post,
html[data-theme="light"] .gt-app.sheet-page .gwh-slot .wr-card,
html[data-theme="light"] .gt-app.sheet-page .wr-card,
/* ==========================================================================
   MESSAGES
   ==========================================================================
   "On the messages I cannot see anything." Nothing on this screen had been
   touched, and every part of it above the inbox list is written for the deep
   green: the hero is `rgba(255,255,255,.05)` — a translucent white over a
   dark page — carrying a white h1, a 66%-white paragraph, and a "Private &
   secure" block in white and lime. On a white page that is white on white,
   top to bottom, which is precisely what the client saw.

   The panel stops being translucent and becomes a real card, and everything
   printed on it takes ink. */
html[data-theme="light"] .gt-app .msg-hero {
  background: var(--lt-surface-2);
  border: 1px solid var(--lt-line-g);
}
html[data-theme="light"] .gt-app .msg-hero-ic {
  background: #FFFFFF; border-color: var(--lt-line-g);
}
html[data-theme="light"] .gt-app .msg-hero-ic svg { stroke: var(--lt-forest); }
html[data-theme="light"] .gt-app .msg-hero-body h1 { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .msg-hero-body p { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .msg-private svg { stroke: var(--lt-forest); }
html[data-theme="light"] .gt-app .msg-private b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .msg-private small { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .msg-empty { border-color: var(--lt-line-g); }
html[data-theme="light"] .gt-app .msg-empty svg { stroke: var(--lt-forest); }
html[data-theme="light"] .gt-app .msg-empty b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .msg-empty span { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .msg-tally { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .msg-item .mi-ic {
  background: var(--lt-surface-2); color: var(--lt-forest);
}
html[data-theme="light"] .gt-app .msg-item .mi-top b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .msg-item .mi-main small { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .msg-item .mi-org {
  background: var(--lt-surface-2); color: var(--lt-forest); border: 1px solid var(--lt-line-g);
}
html[data-theme="light"] .gt-app .msg-item.is-new { border-left-color: var(--lt-forest); }
html[data-theme="light"] .gt-app .msg-item .mi-dot {
  background: var(--lt-forest); box-shadow: 0 0 0 3px rgba(23,107,69,.18);
}
html[data-theme="light"] .gt-app .msg-form,
html[data-theme="light"] .gt-app .msg-foot { background: #FFFFFF; }

html[data-theme="light"] .gt-app .msg-item,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .msg-item {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line);
  box-shadow: var(--lt-lift);
}
html[data-theme="light"] .gt-app.sheet-page .gt-post .gpt-thread { background: var(--lt-surface-2); }
html[data-theme="light"] .gt-app .art-page,
html[data-theme="light"] .gt-app .art-more {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line);
  box-shadow: var(--lt-lift);
}

/* PAGE HEADERS ON THE SHEET. These were written white-on-green for the Wall,
   the Leaderboard and the Ladder, and there is no green left under them. */
html[data-theme="light"] .gt-app.sheet-page .gt-head .gh-eyebrow,
html[data-theme="light"] .gt-app.sheet-page .gwall-head .gh-eyebrow { color: var(--lt-accent-ink); }
html[data-theme="light"] .gt-app.sheet-page .gt-head h1,
html[data-theme="light"] .gt-app.sheet-page .gt-head .gh-title { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.sheet-page .gt-head .gh-sub,
html[data-theme="light"] .gt-app.sheet-page .gt-head p { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app.sheet-page .gt-rounder,
html[data-theme="light"] .gt-app.sheet-page .gt-head .rnd-now,
html[data-theme="light"] .gt-app.sheet-page .gt-head .rnd-when { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app.sheet-page .gt-head .rnd-now { color: var(--lt-ink); }

/* The icon tile got a lime-on-green treatment for the dark sheet; on white
   the same alpha is a pale yellow blur. */
html[data-theme="light"] .gt-app.sheet-page .page-sheet > .gt-ic-tile,
html[data-theme="light"] .gt-app.page-deep .gt-ic-tile.on-dark {
  background: var(--lt-sel);
  border-color: rgba(23,107,69,.24);
  color: var(--lt-forest);
}
html[data-theme="light"] .gt-app .gt-empty .ge-ic {
  background: linear-gradient(150deg, var(--lt-sel), var(--lt-tint));
  color: var(--lt-forest);
}

/* ==========================================================================
   BUTTONS
   ==========================================================================
   Ghost buttons on the sheet were given cream type and a white-alpha border
   because the sheet was dark green. On a cream page that is white on white —
   the single worst thing this theme could leave behind, since "Find an
   organisation" and "Groups" are ghost buttons. They go back to being what
   they are drawn as: a white control with a hairline. */
html[data-theme="light"] .gt-app.sheet-page .page-sheet .abtn-ghost,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .card .abtn-ghost,
html[data-theme="light"] .gt-app .abtn-ghost {
  color: var(--lt-ink);
  border-color: var(--lt-line-3);
  background: var(--lt-surface);
}
html[data-theme="light"] .gt-app.sheet-page .page-sheet .abtn-ghost:hover,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .card .abtn-ghost:hover,
html[data-theme="light"] .gt-app .abtn-ghost:hover {
  color: var(--lt-ink);
  border-color: rgba(23,107,69,.5);
  background: var(--lt-tint);
}

/* The primary: the lime fill with deep-green type on it. This is the one
   place the accent is allowed to be the largest thing in its row. */
html[data-theme="light"] .gt-app .abtn-primary {
  background: var(--lt-accent); color: var(--lt-ink);
  box-shadow: 0 1px 2px rgba(15,46,34,.06), 0 14px 26px -18px rgba(150,190,0,.75);
}
html[data-theme="light"] .gt-app .abtn-primary:hover {
  background: var(--lt-accent-2); color: var(--lt-ink);
  box-shadow: 0 2px 4px rgba(15,46,34,.08), 0 18px 32px -18px rgba(150,190,0,.85);
}
html[data-theme="light"] .gt-app .abtn-primary:active { background: #A8D400; color: var(--lt-ink); }
/* The dark button keeps its deep-green fill — it is the contrast anchor in a
   row of white controls — but its label goes cream rather than lime, which at
   button size was reading as a warning. */
html[data-theme="light"] .gt-app .abtn-dark { background: var(--lt-ink); color: #F2F7E4; }
html[data-theme="light"] .gt-app .abtn-dark:hover { background: #17402F; filter: none; }
html[data-theme="light"] .gt-app .dp-cta { background: var(--lt-accent); color: var(--lt-ink); }

/* ==========================================================================
   THE HERO
   ==========================================================================
   THE PICTURE IS THE PANEL. Two goes at this were wrong in the same way. The
   first faded white across the whole thing and turned the stadium into a grey
   ghost behind the words; the second made the left half solid white, which
   read cleanly but cost half the photograph — "let it fully show the images
   or video, that split where we have the white background, let it be fully
   images can be seen, and make the text will be seen."

   So there is no white half any more. The photograph runs the full width and
   the type sits ON it, which means the type stops being deep green: nothing
   dark is readable over a lit stadium at any veil strength that still lets
   you see the stadium. It goes white, over a green scrim that is heavy enough
   at the left edge to hold contrast and gone by two thirds across.

   THIS IS THE ONE PLACE THE LIGHT THEME GOES DARK, and that is the right
   trade rather than an inconsistency. Everywhere else on the page a surface
   exists to be read against; here the surface is a photograph, and a
   photograph you have veiled into legibility is no longer worth showing. The
   scrim is lighter than the green theme's — .88 to .06 against .94 to .35 —
   so more of the picture survives here than there.

   The lime rule under the headline stays. It is the one accent that reads as
   well on the photograph as it does on white. */
html[data-theme="light"] .gt-app .dash-banner {
  color: var(--cream);
  border: 1px solid var(--lt-line);
  border-radius: 22px;
  box-shadow: var(--lt-lift-2);
}
html[data-theme="light"] .gt-app .dash-banner .veil {
  background: linear-gradient(100deg,
    rgba(9,28,19,.82) 0%,
    rgba(9,28,19,.68) 28%,
    rgba(9,28,19,.44) 50%,
    rgba(9,28,19,.20) 72%,
    rgba(9,28,19,.05) 100%);
}
/* THE SHADOW IS WHY THE SCRIM CAN STAY LIGHT.
   The headline is a rotating message — it can be four words or two lines —
   so it runs as far right as it needs to, well past where the scrim has given
   the picture back. Carrying the whole burden on the scrim would mean veiling
   two thirds of the photograph to protect a sentence that is usually short.
   A shadow costs nothing where the type isn't, so the scrim holds the left
   and the shadow covers wherever the words actually land. */
html[data-theme="light"] .gt-app .dash-banner .db-eyebrow,
html[data-theme="light"] .gt-app .dash-banner .db-h1,
html[data-theme="light"] .gt-app .dash-banner .db-rank,
html[data-theme="light"] .gt-app .dash-banner .dp-txt,
html[data-theme="light"] .gt-app .dash-banner .dp-greet .dp-txt {
  text-shadow: 0 1px 2px rgba(6,20,13,.8), 0 3px 18px rgba(6,20,13,.75);
}
html[data-theme="light"] .gt-app .dash-banner .db-eyebrow { color: var(--lime); opacity: .9; }
html[data-theme="light"] .gt-app .dash-banner .db-h1,
html[data-theme="light"] .gt-app .dash-banner .dp-greet .dp-txt { color: #FFFFFF; }
html[data-theme="light"] .gt-app .dash-banner .db-rank { color: var(--cream); opacity: .82; }
html[data-theme="light"] .gt-app .dash-banner .dp-txt { color: var(--cream); opacity: .95; }
html[data-theme="light"] .gt-app .dash-banner .dp-ic {
  background: rgba(200,241,53,.16); border-color: rgba(200,241,53,.30);
}
html[data-theme="light"] .gt-app .dash-banner .dp-ic svg { stroke: var(--lime); }
html[data-theme="light"] .gt-app .dash-banner .dp-dot { background: rgba(255,253,244,.32); }
html[data-theme="light"] .gt-app .dash-banner .dp-dot.on { background: var(--lime); }

/* The rule under the headline. Sized to the type block rather than the panel,
   and only where the greeting stack actually is. */
html[data-theme="light"] .gt-app .dash-banner .dash-prompt::after,
html[data-theme="light"] .gt-app .dash-banner .db-h1::after {
  content: ""; display: block;
  width: 58px; height: 4px; border-radius: 100px;
  margin-top: 12px;
  background: linear-gradient(90deg, var(--lime), rgba(200,241,53,.25));
}

/* The role chips are on the photograph too, so they keep the on-dark
   treatment the green theme already gives them: OWNER carries the accent, the
   other two stay quiet. The chips that appear ELSEWHERE on a light page — on
   a white card, away from the hero — take ink instead, which is the pair of
   rules below. */
html[data-theme="light"] .gt-app.page-deep .pill-role {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line-3);
  color: #3E4E44;
}
html[data-theme="light"] .gt-app.page-deep .pill-role.owner {
  background: var(--lt-accent); border-color: var(--lt-accent-2); color: var(--lt-ink);
}
html[data-theme="light"] .gt-app .dash-banner .pill-role {
  background: rgba(255,255,255,.16);
  border: 1px solid rgba(255,255,255,.26);
  color: #fff;
}
html[data-theme="light"] .gt-app .dash-banner .pill-role.owner {
  background: var(--lime); border-color: var(--lime); color: var(--lt-ink);
}

/* ==========================================================================
   THE ORGANISATION CONTROL ROW
   ==========================================================================
   YOUR ORGANISATION · [ masterclass ▾ ] · Groups · Find an organisation ·
   + Create an organisation. One row of white controls with one lime primary
   at the end of it. */
html[data-theme="light"] .gt-app.page-deep .dc-lab,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .dash-ctl .dc-lab {
  color: var(--lt-quiet);
}
html[data-theme="light"] .gt-app.page-deep .dc-pick select,
html[data-theme="light"] .gt-app.page-deep .dash-ctl select,
html[data-theme="light"] .gt-app .dash-ctl .dc-pick > select {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line-3);
  color: var(--lt-ink);
  box-shadow: var(--lt-lift);
}
html[data-theme="light"] .gt-app .dash-ctl .dc-pick > select:hover { border-color: rgba(23,107,69,.45); }

/* ==========================================================================
   WHICH ROOM YOU ARE TIPPING IN
   ==========================================================================
   This is the control the client could not read: "AquaFlow Water Co /
   Everyone, Customer Service, Finance, Fleet & Logistics, Operations — make it
   have a nice infill; that cream, I'm not feeling it, not been able to see
   it." The tray was warm beige on a white card, which is a 2% difference, and
   the chosen room was a pale lime wash.

   Now the tray is a soft green with a green edge — it reads as a tray — and
   the chosen room is the solid deep fill used by the nav and by /manage/. The
   room decides which ladder your tips land on, so of everything on the
   dashboard this is the one that has to be unmistakable. */
html[data-theme="light"] .gt-app .room-switch.on-light .rw-eyebrow,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-eyebrow { color: var(--lt-accent-ink); }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-note,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-note { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-note b,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-note b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-seg,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-seg {
  background: var(--lt-surface-2);
  border: 1px solid var(--lt-line-g);
}
html[data-theme="light"] .gt-app .room-switch.on-light .rw-opt,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-opt { color: #33453A; }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-opt i,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-opt i { color: var(--lt-quiet); opacity: 1; }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-opt:hover,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-opt:hover { background: #FFFFFF; color: var(--lt-ink); }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-opt.on,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-opt.on {
  background: var(--lt-ink); color: #FFFFFF;
  box-shadow: 0 6px 16px -8px rgba(15,46,34,.6);
}
html[data-theme="light"] .gt-app .room-switch.on-light .rw-opt.on i,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-opt.on i { color: var(--lime); opacity: 1; }
html[data-theme="light"] .gt-app .room-switch.on-light .rw-more,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-more { color: var(--lt-forest); }

/* ==========================================================================
   THE ROUND, AND THE COUNTDOWN
   ==========================================================================
   LIVE is green because it is a status, not an accent. The countdown cells
   were deep-green plates with lime numerals, which is right on a dark card
   and far too heavy on a white one — three black bricks in the corner of the
   page. White cards, hairline, deep-green numerals. */
html[data-theme="light"] .gt-app .badge-live { color: var(--lt-forest); }
html[data-theme="light"] .gt-app .badge-live::before { background: var(--lt-forest); }
html[data-theme="light"] .gt-app .cd-cell {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line-3);
  color: var(--lt-ink);
  box-shadow: 0 1px 2px rgba(15,46,34,.04);
}
html[data-theme="light"] .gt-app .cd-cell b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .cd-cell small { color: var(--lt-quiet); opacity: 1; }
html[data-theme="light"] .gt-app .cd-locked { color: var(--lt-quiet); }

/* ==========================================================================
   THE THREE TILES
   ==========================================================================
   Season points keeps the deep-green fill: with everything else on the page
   white, one dark object is what stops the row reading as three empty boxes,
   and the lime numeral on it is the single brightest thing on the dashboard.
   Rank and Tips this round stay white with a hairline. */
html[data-theme="light"] .gt-app .stat-card {
  background: var(--lt-surface);
  border: 1px solid var(--lt-line);
  box-shadow: var(--lt-lift);
}
html[data-theme="light"] .gt-app .stat-card .s-lab { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .stat-card .s-num { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .stat-card .s-sub { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .stat-card .s-ic { stroke: var(--lt-forest); opacity: .14; }
html[data-theme="light"] .gt-app .stat-card.accent {
  background: linear-gradient(160deg, #14382A, var(--lt-ink));
  border-color: var(--lt-ink);
  box-shadow: 0 2px 4px rgba(15,46,34,.10), 0 26px 50px -30px rgba(15,46,34,.55);
}
html[data-theme="light"] .gt-app .stat-card.accent .s-lab { color: #D9E4D6; opacity: .78; }
html[data-theme="light"] .gt-app .stat-card.accent .s-num { color: #CDF23F; }
html[data-theme="light"] .gt-app .stat-card.accent .s-sub { color: #D9E4D6; opacity: .72; }
html[data-theme="light"] .gt-app .stat-card.accent .s-ic { stroke: #CDF23F; opacity: .18; }

/* ==========================================================================
   FIXTURES, TABLES AND PICKS
   ==========================================================================
   These were already drawn for a light surface. What they need is the tint
   strengths adjusted: an alpha tuned to sit on cream disappears on white. */
html[data-theme="light"] .gt-app .fixtures .fxp:hover { background: var(--lt-sel); }
html[data-theme="light"] .gt-app .fixtures .fxp.done { background: var(--lt-tint); }
html[data-theme="light"] .gt-app .fixtures .fxp.done:nth-child(even) { background: #EDF4E0; }
html[data-theme="light"] .gt-app .pick .team { background: var(--lt-surface); border-color: var(--lt-line-3); }
html[data-theme="light"] .gt-app .pick .team:hover { border-color: var(--lt-forest); background: var(--lt-tint); }
html[data-theme="light"] .gt-app .pick .team .crest { background: var(--lt-tint); color: var(--lt-forest); }
html[data-theme="light"] .gt-app .atable th { background: var(--lt-ink); color: #E8EFE2; }
html[data-theme="light"] .gt-app .atable td { background: var(--lt-surface); }
html[data-theme="light"] .gt-app .atable tbody tr:hover td { box-shadow: inset 0 0 0 999px rgba(196,240,0,.14); }
html[data-theme="light"] .gt-app .atable tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--lt-forest), inset 0 0 0 999px rgba(196,240,0,.14); }
html[data-theme="light"] .gt-app .atable tr.me td { background: var(--lt-sel); border-color: rgba(184,230,0,.55); }

/* THE LADDER'S COLUMN STRIPES.
   "On the ladder and leaderboard, the columns background — let's have
   something strong but that matches with white, for it to contrast and give a
   nice feel, and be good for the eyes."

   The stripe is `rgba(15,46,34,.045)`, a grey at 4.5%, chosen to sit on cream.
   On white it is not a stripe, it is a rumour — and a ladder is read ACROSS,
   so the stripe is the only thing carrying the eye from a club's name to its
   points. Green rather than grey, roughly three times the strength, and the
   Points column — the one the table exists to report — stronger again. Still
   well short of banding: the figures stay the darkest thing in the row. */
html[data-theme="light"] .gt-app .gt-board.ladder .lad-head .lad-col::before,
html[data-theme="light"] .gt-app .gt-board.ladder .lad-row .lad-col::before {
  background: rgba(23,107,69,.10);
}
html[data-theme="light"] .gt-app .gt-board.ladder .lad-col:nth-of-type(even)::before { background: transparent; }
html[data-theme="light"] .gt-app .gt-board.ladder .lad-pts::before {
  background: rgba(23,107,69,.20) !important;
}
html[data-theme="light"] .gt-app .gt-board.ladder .lad-row .lad-pts { color: var(--lt-ink); }
/* The leaderboard's own numeric columns, same reasoning. */
html[data-theme="light"] .gt-app .atable th.num,
html[data-theme="light"] .gt-app .atable td.num { background: rgba(23,107,69,.07); }
html[data-theme="light"] .gt-app .atable th.num { background: var(--lt-ink); }

/* ==========================================================================
   LEFTOVERS THAT KNEW THEY WERE ON GREEN
   ========================================================================== */
html[data-theme="light"] .gt-app.page-deep .flash-close:hover { background: rgba(15,46,34,.08); }
html[data-theme="light"] .gt-app .ch-item { background: var(--lt-surface); border-color: var(--lt-line); }
html[data-theme="light"] .gt-app .ch-item:hover { background: var(--lt-tint); border-color: rgba(23,107,69,.32); }
html[data-theme="light"] .gt-app .ch-item-name { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .ch-item-url,
html[data-theme="light"] .gt-app .ch-item-note { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .ch-item-chip { background: var(--lt-tint); color: var(--lt-forest); border-color: rgba(23,107,69,.26); }
html[data-theme="light"] .gt-app .ch-item-edit { background: var(--lt-sel); color: var(--lt-ink); }
html[data-theme="light"] .gt-app .ch-item-fix { color: var(--lt-forest); opacity: 1; }
html[data-theme="light"] .gt-app .ch-add-row { border-color: var(--lt-line-3); }
html[data-theme="light"] .gt-app .che-now,
html[data-theme="light"] .gt-app .che-refetch { background: var(--lt-surface); border-color: var(--lt-line); }
html[data-theme="light"] .gt-app .che-now-txt b,
html[data-theme="light"] .gt-app .che-refetch b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .che-now-txt small,
html[data-theme="light"] .gt-app .che-refetch small { color: var(--lt-quiet); }

/* ---- The conversation ----------------------------------------------------
   .chat paints from its own tokens, and `.page-deep .chat` swaps them for the
   on-green set. On a light page that swap is wrong twice over, so it is put
   back — one block rather than a rule per bubble part, which is the reason
   the component took tokens in the first place. */
html[data-theme="light"] .gt-app.page-deep .chat {
  --ch-mine:      #14382A;
  --ch-mine-fg:   #F1F5EC;
  --ch-theirs:    var(--lt-surface);
  --ch-theirs-fg: #17342A;
  --ch-line:      var(--lt-line);
  --ch-quiet:     var(--lt-quiet);
  --ch-accent:    var(--lt-forest);
}
html[data-theme="light"] .gt-app.page-deep .chat-composer { background: var(--lt-surface); }
html[data-theme="light"] .gt-app.page-deep .chat-composer textarea { background: var(--lt-surface-2); }
html[data-theme="light"] .gt-app.page-deep .cc-send { background: var(--lt-forest); color: #fff; }
html[data-theme="light"] .gt-app.page-deep .cc-send:hover { background: var(--lt-ink); }
html[data-theme="light"] .gt-app.page-deep .ch-back { color: var(--lt-forest); }
html[data-theme="light"] .gt-app.page-deep .ch-back:hover { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.page-deep .chat-quote { background: rgba(15,46,34,.05); }
html[data-theme="light"] .gt-app.page-deep .chat-row.mine .chat-quote { background: rgba(255,255,255,.12); }
html[data-theme="light"] .gt-app.page-deep .chat-row.mine .chat-av { background: var(--lt-accent); color: var(--lt-ink); }

/* ---- Motion ---------------------------------------------------------------
   Soft hover on the cards that are also links. Cheap, and it is most of what
   separates "premium" from "static" at this level of restraint. */
html[data-theme="light"] .gt-app a > .card,
html[data-theme="light"] .gt-app .gt-scard,
html[data-theme="light"] .gt-app .dept-card {
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
html[data-theme="light"] .gt-app a:hover > .card,
html[data-theme="light"] .gt-app .gt-scard:hover,
html[data-theme="light"] .gt-app .dept-card:hover {
  border-color: rgba(23,107,69,.30);
  box-shadow: var(--lt-lift-2);
}

/* The create-organisation wizard's panel is deliberately a slab of lime — the
   one screen in the app where the accent IS the surface, in both themes. The
   white-card rule above is broad enough to flatten it, so it is restated. */
html[data-theme="light"] .gt-app .form-lime > .card {
  background: linear-gradient(180deg, #E2F878, var(--lt-accent) 55%, var(--lt-accent-2));
  border: 1px solid rgba(15,46,34,.24);
  box-shadow: 0 30px 60px -34px rgba(15,46,34,.45), inset 0 1px 0 rgba(255,255,255,.55);
}
html[data-theme="light"] .gt-app .form-lime .card .card { background: rgba(255,255,255,.85); }

/* ---- The switch itself --------------------------------------------------- */
.an-theme {
  display: inline-flex; align-items: center; gap: 2px; flex: none;
  padding: 3px; border-radius: 999px;
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.12);
}
.an-theme button {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12px; font-weight: 600; line-height: 1;
  color: rgba(232,228,213,.72); background: transparent;
  border: 0; border-radius: 999px; padding: 6px 11px; cursor: pointer;
  transition: background .15s, color .15s;
}
.an-theme button svg { width: 13px; height: 13px; flex: none; }
.an-theme button:hover { color: #fff; }
.an-theme button.on { background: var(--lime); color: var(--deep); }
html[data-theme="light"] .an-theme { background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.12); }
html[data-theme="light"] .an-theme button { color: #6B7A6E; }
html[data-theme="light"] .an-theme button:hover { color: var(--deep); }
html[data-theme="light"] .an-theme button.on { background: var(--forest); color: #fff; }
/* Below the widths where the bar has room, the labels go and then the whole
   control does — the mobile menu carries its own copy.

   The labels go EARLY, at 1400px rather than at the width where they would
   actually collide. The link row is already squeezed between 1181 and 1360
   (see the .an-links media query above: "the fix is fewer things in the row,
   not smaller things"), and this control arrived after that budget was set —
   so it gives its ~90px back before the links have to give up anything. */
@media (max-width: 1400px) { .an-theme button span { display: none; } .an-theme button { padding: 6px 8px; } }
@media (max-width: 1060px) { .an-theme { display: none; } }

.sheet-theme { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 4px 4px; }
.sheet-theme .an-theme { display: inline-flex; }
.sheet-theme .an-theme button span { display: inline; }

/* ==========================================================================
   PAGES: PUBLIC OR PRIVATE, ONE AT A TIME
   ==========================================================================
   Two big buttons rather than a row of small tabs. There are exactly two
   choices, each needs a sentence to explain what it holds, and both are worth
   the same amount of space — which is a segmented pair, not a tab strip.
   ========================================================================== */
.pg-scope { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px; }
@media (max-width: 700px) { .pg-scope { grid-template-columns: 1fr; } }

.pg-scope-tab {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; border-radius: 14px;
  background: var(--ad-card, #fff);
  border: 1px solid var(--ad-line, rgba(15,46,34,.10));
  text-decoration: none; color: inherit;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease), background .16s var(--ease);
}
.pg-scope-tab:hover { text-decoration: none; border-color: rgba(45,122,58,.34); }
/* The chosen one is filled, not merely outlined. An outline-only "selected"
   state on a card this size is invisible next to its sibling. */
.pg-scope-tab.on {
  background: var(--deep); border-color: var(--deep);
  box-shadow: 0 14px 30px -20px rgba(15,46,34,.6);
}
.pg-scope-tab .pgs-ic {
  display: grid; place-items: center; width: 40px; height: 40px; flex: none;
  border-radius: 12px; background: var(--ad-ic-bg, rgba(45,122,58,.11)); color: var(--ad-ic-fg, #2D7A3A);
}
.pg-scope-tab .pgs-ic svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.pg-scope-tab .pgs-txt { min-width: 0; flex: 1 1 auto; }
.pg-scope-tab .pgs-txt b { display: block; font-size: 15px; color: var(--ad-ink, var(--ink)); }
.pg-scope-tab .pgs-txt small { display: block; margin-top: 2px; font-size: 12.5px; color: var(--ad-quiet, var(--muted)); }
.pg-scope-tab .pgs-n {
  flex: none; min-width: 30px; text-align: center;
  font-family: var(--score); font-weight: 800; font-size: 15px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--ad-quiet-bg, rgba(15,46,34,.06)); color: var(--ad-quiet, var(--muted));
}
/* On the filled tab everything inverts together — half-inverted is worse than
   either state on its own. */
.pg-scope-tab.on .pgs-ic { background: rgba(200,241,53,.18); color: var(--lime); }
.pg-scope-tab.on .pgs-txt b { color: #fff; }
.pg-scope-tab.on .pgs-txt small { color: rgba(232,228,213,.72); }
.pg-scope-tab.on .pgs-n { background: rgba(200,241,53,.20); color: var(--lime); }

/* ==========================================================================
   THE BELL, WHEN SOMETHING IS WAITING
   ==========================================================================
   Two problems, both reported by the client: the badge did not read as "you
   have something", and the bell never moved.

   RED, NOT LIME. Lime is this product's *approval* colour — a saved tip, a
   correct pick, the active nav item. A count wearing it says "all good" at
   the exact moment it means "three things are waiting", and on the deep-green
   nav a lime dot is one accent among several rather than the one thing that
   was not there a second ago. Red is the only colour in the palette that is
   not spent on anything else in this bar.

   AND IT NEVER RANG. `.an-bell.ring .bell-btn` has been in this file the
   whole time; the script was putting the class on the button instead of on
   the wrapper, one level too deep, so the keyframes never matched anything.
   Fixed in app_base.html — this is the state the ring leaves behind.
   ========================================================================== */
.an-bell.has-new .bell-badge {
  /* #E5372B put white at 4.27:1, and this is a 10px numeral — the one place
     on the bar where the exact figure matters. A shade deeper clears 4.9:1
     and reads as the same red. */
  background: #D62D22;
  color: #fff;
  box-shadow: 0 0 0 2px var(--gt-deep, #0C2417);
}
html[data-theme="light"] .an-bell.has-new .bell-badge { box-shadow: 0 0 0 2px #fff; }

/* A halo that breathes rather than blinks. A blink is an alarm and this is a
   notification; the point is to be findable in peripheral vision, not to
   demand the next three seconds. It stops on its own after four passes —
   an indicator that pulses forever stops being an indicator. */
.an-bell.has-new .bell-badge::after {
  content: ""; position: absolute; inset: -3px;
  border-radius: 100px; border: 2px solid #D62D22;
  animation: gt-bell-halo 1.8s var(--ease) 4;
  pointer-events: none;
}
@keyframes gt-bell-halo {
  0%   { transform: scale(.85); opacity: .85; }
  70%  { transform: scale(1.75); opacity: 0; }
  100% { transform: scale(1.75); opacity: 0; }
}
.an-bell.has-new .bell-badge { position: absolute; }
/* The bell itself stops being a dimmed piece of furniture while it is
   carrying something. */
.an-bell.has-new .bell-btn { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  /* The colour and the count still say everything the movement said. What
     goes is the movement, which is the part the setting is about. */
  .an-bell.has-new .bell-badge::after { animation: none; opacity: 0; }
}

/* ==========================================================================
   WHO A THREAD IS ADDRESSED TO
   ==========================================================================
   Sits between the subject and the first bubble, because that is where the
   question gets asked: you have opened a conversation and the next thing you
   do is write into it. A strip rather than a card — it is a label on the
   thread, not a panel of its own — and it carries the count as plain words
   rather than a badge, since "148 members" is the part that changes how you
   write and a number in a pill reads as decoration. */
.chat-to {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 10px 14px;
  border-radius: 12px;
  background: var(--gt-card, #fff);
  border: 1px solid var(--gt-hair, rgba(15,46,34,.10));
  font-size: 13px; line-height: 1.4;
}
.chat-to .ct-lab {
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); flex: none;
}
.chat-to .ct-who b { font-size: 14px; color: var(--ink); }
.chat-to .ct-more,
.chat-to .ct-count,
.chat-to .ct-org { color: var(--muted); }
.chat-to .ct-dot { color: var(--muted); opacity: .6; }
/* On the green theme the strip is on the dark sheet, so it takes the same
   translucent-panel treatment every other block up there uses. */
.page-deep .chat-to {
  background: rgba(255,255,255,.05);
  border-color: rgba(255,255,255,.10);
}
.page-deep .chat-to .ct-who b { color: #fff; }
.page-deep .chat-to .ct-lab,
.page-deep .chat-to .ct-more,
.page-deep .chat-to .ct-count,
.page-deep .chat-to .ct-org { color: rgba(232,228,213,.66); }
/* And on light it is a white card on a white page, so it needs the edge. */
html[data-theme="light"] .gt-app.page-deep .chat-to {
  background: var(--lt-surface-2);
  border-color: var(--lt-line-g);
}
html[data-theme="light"] .gt-app.page-deep .chat-to .ct-who b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.page-deep .chat-to .ct-lab,
html[data-theme="light"] .gt-app.page-deep .chat-to .ct-more,
html[data-theme="light"] .gt-app.page-deep .chat-to .ct-count,
html[data-theme="light"] .gt-app.page-deep .chat-to .ct-org { color: var(--lt-quiet); }

/* ==========================================================================
   ============ v50 — THE CONTAINER HOLDS ==================================
   ==========================================================================

   From a round of testing on other people's machines: on several screens
   "things overflowed", "the containers did not hold", and on one large, older
   display things overlapped. Four separate faults produced that, and none of
   them is a missing breakpoint — the grids in this file collapse properly
   nearly everywhere. What they share is that each one puts something in a box
   that CANNOT shrink, and then relies on the box being big enough.

   `html, body { overflow-x: clip }` (v7) is why this was reported as things
   being cut off rather than as a sideways scrollbar. Clip was the right call —
   it keeps the sticky nav working and kills stray side-space on a phone — but
   it also means every one of these faults fails silently: the content is not
   pushed off the side where you would see it, it is simply gone. So the fixes
   below are all about making the content FIT, not about hiding the evidence.
   ========================================================================== */

/* ---------- 1. THE NAV ROW ON A WIDE SCREEN -------------------------------
   The reported symptom was on a big monitor, which is the opposite of where
   you look for a layout fault, and the reason is that this row's width does
   not grow with the screen: `.an-inner` is capped at --gt-wide (1300px) and
   loses ~82px to its gutters, so from about 1360px upwards the row always has
   the same ~1218px to work in no matter how wide the display gets.

   v34 measured what goes in it — brand 74 + links 781 + right cluster 259 +
   gaps 66 = 1180 — and made all four of those `flex: none` so the link row
   could never be squeezed. That leaves ~38px, and the context chip beside
   them is `flex: none` too, at up to 460px. Two organisation names is more
   than 38px, so the row simply ran past the end of the bar and the clip took
   whatever was on the right — on a staff account, the account cluster.

   Something in that row has to yield, and the chip is the right thing: it is
   the only item that already knows how to give way gracefully. `.an-ctx-org`
   carries a 4.5em floor so the organisation can never truncate to nothing,
   and `.an-ctx-group` ellipsises before it — which is the order the rules
   above already state they want ("the group half is allowed to give way
   first"). Making the chip shrinkable is what lets those two rules do the job
   they were written for; until now nothing ever asked them to.

   `min-width: 0` on both is the load-bearing half. A flex item's automatic
   minimum size is its content, so `flex: 0 1 auto` alone shrinks nothing —
   which is the same trap `.gt-v2 .an-org` already has a note about. */
.an-ctx,
.gt-v2 .an-ctx { flex: 0 1 auto; min-width: 0; }
.an-ctx-btn { min-width: 0; }

/* ---------- 2. THE ORGANISATION MENU FELL OUT OF THE BAR -------------------
   Reported directly: the organisation/groups dropdown "is falling outside the
   container … it should be left zero so that it starts from the right of that
   container". That is exactly right, and here is why it happens.

   `.an-menu-panel` is written for the menus at the RIGHT-HAND end of the bar,
   so it anchors `right: 0` — the panel's right edge meets the button's right
   edge and it grows leftwards. The context chip is the one menu at the LEFT
   end, second item after the brand, and its panel is the widest one there is
   (two columns, min-width 480px). Anchored right, a 480px panel hung off a
   button whose right edge is ~400px from the start of the bar begins about
   80px to the LEFT of the bar — off the container, and then clipped away by
   overflow-x: clip, which is why the organisation column was the half that
   went missing.

   Anchoring it left is the whole fix: the panel now starts at the chip's own
   left edge and grows the way there is room to grow. The viewport cap is the
   belt to that brace — the chip is on screen down to 900px, where 480px of
   panel plus the chip's offset is more than the window has. */
.an-menu-panel.an-ctx-panel {
  left: 0;
  right: auto;
  max-width: calc(100vw - 24px);
}

/* Every floating panel gets the same guarantee, for the same reason: each of
   these is positioned against a button whose place in the row moves with the
   width of everything beside it, so any of them can be pushed to an edge. A
   panel that is wider than the window cannot be read whatever it is anchored
   to. */
.an-menu-panel,
.ddx-menu,
.ac-panel,
.bell-panel {
  max-width: calc(100vw - 24px);
}

/* …and the floors have to move with it, because a max-width does NOT cap a
   min-width — min-width is applied last and wins outright. `.an-menu-panel`
   asks for 330px and the context panel for 480px, so on a 320px handset the
   cap above was inert and the left edge of an account menu still sat ~10px
   off the screen. Folding the same expression into the floor is what makes
   the cap bind, and it needs no breakpoint to do it: the panel keeps the
   width it wants at every size that has the room, and only ever gives up what
   the window does not have. */
.an-menu-panel { min-width: min(330px, calc(100vw - 24px)); }
.an-menu-panel.an-ctx-panel { min-width: min(480px, calc(100vw - 24px)); }
/* Same fault one level in: the two columns are `min-width: 220px` each, so the
   panel had a 440px floor of its own however narrow the window was. */
.an-ctx-col { min-width: min(220px, calc(50vw - 24px)); }

/* ---------- 3. LONG WORDS: ADDRESSES, ORG NAMES, LINKS --------------------
   The largest single cause, and the least obvious. An email address is one
   word to a browser. `christopher.vandenberg@wellingtoncityfc.org.nz` is 46
   unbreakable characters, and it appears in a table cell on Members, on
   Approvals, in the enquiry detail and in half the admin lists. A cell can
   never be narrower than the longest word in it, so that one address sets the
   width of the column, the column sets the width of the table, and the table
   runs past the card — which is precisely "the containers did not hold".

   `overflow-wrap: anywhere` rather than `break-word` is deliberate and is the
   difference between fixing this and only appearing to. Both break an
   over-long word at the edge; only `anywhere` also lets the browser count the
   smaller size when working out how narrow the box is ALLOWED to be. With
   `break-word` the cell still asks for 46 characters of width and the table
   still overflows — the word just wraps once it gets there.

   So: `anywhere` on the places that carry identifiers, `break-word` as the
   quiet floor everywhere else, where it only ever acts on a word that would
   otherwise leave the box. */
body { overflow-wrap: break-word; }

.atable td, .atable th,
.gt-table td, .gt-table th,
.atable small, .gt-table small,
.gt-namecell small,
dd, .gt-dl dd,
.card-lead, .app-sub, .gp-sub, .gt-sub,
.bl-meta, .ac-opt .ac-meta,
.mi-txt b, .mi-txt i,
.an-ctx-row, .an-ctx-head,
.gwp-meta small, .gt-post .gpt-meta small,
.enq-meta dd {
  overflow-wrap: anywhere;
}

/* ---------- 4. TABLES SCROLL RATHER THAN GET CUT ---------------------------
   Wrapping helps a long address; it does not help a table that genuinely has
   more columns than a phone has width. `.gt-table` has had `.gt-tablewrap`
   around it from the start. `.atable` — Members, Approvals, the org lists, the
   profile's comps — never got one, so on a narrow screen its right-hand
   columns went past the edge and the clip removed them: on Members that is the
   "Set role" control, which was not merely ugly but unreachable.

   Named rather than inlined because two templates already did this with
   `style="overflow-x:auto"`, and a class is the thing to reach for next time. */
.tbl-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ---------- 5. THE FIXTURE CELL ON A PHONE ---------------------------------
   `.match-cell` is `minmax(215px,1fr) 26px minmax(215px,1fr)` with 12px gaps —
   a 480px floor — and its team names are `white-space: nowrap`. Inside a table
   that is 480px of one cell before the rank and result columns are counted, so
   My Tips could not fit on any phone. The two-track floor is there so club
   names line up down the round, which is worth having on a screen wide enough
   to line anything up and worth nothing on one that is not. */
@media (max-width: 700px) {
  .match-cell { grid-template-columns: minmax(0,1fr) 26px minmax(0,1fr); gap: 8px; }
  .match-cell .team-cell { white-space: normal; }
}

/* ---------- 6. NOTHING IS WIDER THAN ITS PAGE -----------------------------
   The last line of defence, and only a defence: media that arrives without
   dimensions, an <iframe> in a news article, a code block in the page editor.
   These are the elements that can carry an intrinsic width from outside the
   stylesheet's control, so they are the ones worth naming.

   Tables are deliberately NOT in that list. `.gt-table` carries a 640px
   min-width on purpose and lives in a scroller that is meant to be scrolled;
   capping it at 100% of that scroller would quietly turn the scroller off. A
   table's width is handled a rule up, by letting its cells wrap. */
img, svg, video, iframe, canvas { max-width: 100%; }
pre { overflow-x: auto; max-width: 100%; }

/* ==========================================================================
   ============ v51 — WHERE THESE TIPS GO, AS A TREE =======================
   ==========================================================================

   Two faults, reported together: "I did uncheck one and that part got
   distorted", and "the organisations should have a dropdown that shows
   groups".

   THE DISTORTION was contrast, not layout. A room card is drawn by
   `.cs-room:has(input:checked)` — forest border on a pale green wash — and
   UNCHECKED it fell back to a 1.5px --gt-hair border, which is
   rgba(15,46,34,.10) on a near-white sheet. So unticking a room did not put it
   in an "off" state; it made the card stop existing, and the rows either side
   of it ran together. An off state has to be a state you can see.

   THE TREE is `.cs-org` — one organisation, its master, and a disclosure of
   the rooms inside it. The rooms keep the same names and the same posted
   values as before; what is new is the indent, the rail down the left that
   ties them to the organisation above, and the master's mixed state.
   ========================================================================== */

/* ---------- the off state, on every card that can be turned off ----------- */
.cs-room,
.cs-org,
.cs-subroom {
  transition: border-color .16s var(--ease), background .16s var(--ease),
              opacity .16s var(--ease);
}
/* A visible edge whatever the state. Checked still reads louder — it gains the
   forest and the wash — but unchecked is now a card that is off rather than a
   card that has vanished. */
.cs-room:not(:has(input[name="room"]:checked)),
.cs-org.is-off {
  border-color: rgba(15,46,34,.22);
  background: transparent;
}
/* Dimmed, not hidden: you still need to read what you have just switched off
   to know whether you meant to. */
.cs-org.is-off .cs-rooms,
.cs-org.is-off .cs-head-body { opacity: .62; }

/* An unticked box needs an edge too — it was a white square inside a
   rgba(…,.10) border on a white sheet, which is the same disappearing act one
   level down. */
.cs-head .oc-box,
.cs-sub .oc-box,
.cs-conflict .oc-box { border-color: rgba(15,46,34,.22); }

/* ---------- the organisation card ---------------------------------------- */
.cs-org {
  border: 1.5px solid var(--gt-hair);
  border-radius: 14px;
  padding: 13px 15px;
  margin-bottom: 10px;
}
.cs-org:has([data-cs-master]:checked),
.cs-org:has([data-cs-master]:indeterminate) {
  border-color: var(--forest);
  background: rgba(45,122,58,.045);
}
.cs-org.has-conflict:has([data-cs-master]:checked),
.cs-org.has-conflict:has([data-cs-master]:indeterminate) {
  border-color: #C8901F;
  background: rgba(200,144,31,.05);
}
/* Mixed is its own mark — a dash, not a tick, because "some of Acme" is not
   "all of Acme" and the two must not look alike at a glance. */
.cs-head:has([data-cs-master]:indeterminate) .oc-box {
  background: var(--deep);
  border-color: var(--deep);
}
.cs-head:has([data-cs-master]:indeterminate) .oc-box svg { opacity: 0; }
.cs-head:has([data-cs-master]:indeterminate) .oc-box::after {
  content: "";
  width: 13px; height: 3px; border-radius: 2px;
  background: var(--white);
}

/* ---------- the rooms inside it ------------------------------------------ */
.cs-rooms { margin: 10px 0 0; }
.cs-rooms > summary {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer; list-style: none;
  padding: 7px 0 7px 46px;         /* clears the master's 34px box + its gap */
  font-family: var(--mark); font-weight: 700;
  font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.cs-rooms > summary::-webkit-details-marker { display: none; }
.cs-rooms > summary .cs-chev {
  width: 15px; height: 15px; flex: none;
  stroke: currentColor; fill: none; stroke-width: 2.2;
  transition: transform .16s var(--ease);
}
.cs-rooms[open] > summary .cs-chev { transform: rotate(180deg); }

/* The rail is what says "these belong to the organisation above" — the indent
   alone reads as a margin, which is not a relationship. */
.cs-subroom {
  margin-left: 46px;
  padding: 10px 0 10px 14px;
  border-left: 2px solid var(--gt-hair);
}
.cs-subroom:has(input[name="room"]:checked) { border-left-color: var(--forest); }
.cs-subroom.has-conflict:has(input[name="room"]:checked) { border-left-color: #C8901F; }
.cs-subroom:not(:has(input[name="room"]:checked)) { opacity: .62; }

.cs-sub { display: flex; align-items: flex-start; gap: 11px; cursor: pointer; }
.cs-sub input { position: absolute; opacity: 0; pointer-events: none; }
/* Smaller than the organisation's, so the hierarchy is legible from the
   control sizes alone rather than only from the indent. */
.cs-sub .oc-box {
  flex: none;
  width: 26px; height: 26px;
  border-radius: 8px;
  border: 2px solid var(--gt-hair);
  background: var(--white);
  display: grid; place-items: center;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cs-sub .oc-box svg { width: 14px; height: 14px; stroke: #fff; fill: none; stroke-width: 3.2; opacity: 0; }
.cs-sub:has(input:checked) .oc-box { background: var(--forest); border-color: var(--forest); }
.cs-sub:has(input:checked) .oc-box svg { opacity: 1; }
.cs-sub .cs-room-name { font-size: 14px; }

/* The conflict list is a step further in again, under the room it belongs to. */
.cs-subroom .cs-conflicts { margin-left: 37px; }

/* Keyboard users get the same outline the rest of the app uses; the real
   checkbox is off-screen, so the ring has to be put on what is on screen. */
.cs-head:has(input:focus-visible) .oc-box,
.cs-sub:has(input:focus-visible) .oc-box,
.cs-conflict:has(input:focus-visible) .oc-box {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
}

/* A clip in a bubble. Sized like the screenshot beside it rather than like a
   media player: the thread is the thing being read, and a video that takes the
   width of the panel turns a conversation into a feed. Tapping it plays it
   where it is; the controls are the browser's own, which is what people
   already know how to work. */
.chat-clip {
  display: block;
  width: 240px; max-width: 100%;
  border-radius: 12px;
  background: #000;
  border: 1px solid var(--app-line, rgba(15,46,34,.12));
}
@media (max-width: 560px) {
  .chat-clip { width: 100%; }
}

/* ==========================================================================
   SEO, redirects, and the story editor's new fields
   ==========================================================================
   All of it lives in the HQ shell, so it borrows that shell's tokens rather
   than naming colours: --ink, --muted, --gt-hair, --gt-bg, --forest, --lime.
   Anything hard-coded here would come back as the dark-theme bug the manage
   screens have already been through once. */

/* `.fld-hint` is `.hint` under a different name, because these forms put the
   sentence UNDER the input and `.hint` is used beside a label elsewhere. */
.fld-hint { display: block; font-size: 12px; line-height: 1.5; color: var(--muted); margin-top: 6px; }
.fld-hint code { font-size: 11.5px; background: rgba(15,46,34,.07); padding: 1px 5px; border-radius: 5px; }
.fld-label-plain { display: block; font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }

/* ---- the folded "Search & social" block, shared by both editors ---- */
.seo-block {
  border: 1px solid var(--gt-hair); border-radius: 14px;
  background: var(--gt-bg); overflow: hidden;
}
.seo-block > summary {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  padding: 14px 16px; list-style: none; user-select: none;
}
.seo-block > summary::-webkit-details-marker { display: none; }
/* The disclosure triangle, drawn rather than inherited: the native one sits at
   a different offset in every browser and this row has an icon of its own. */
.seo-block > summary::after {
  content: ""; margin-left: auto; width: 8px; height: 8px; flex: none;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .16s var(--ease);
}
.seo-block[open] > summary::after { transform: rotate(225deg) translate(-1px, -1px); }
.seo-block > summary:hover { background: rgba(15,46,34,.03); }
.seo-sum-ic {
  width: 32px; height: 32px; flex: none; border-radius: 9px;
  display: grid; place-items: center;
  background: rgba(45,122,58,.12); color: var(--forest);
}
.seo-sum-ic svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
.seo-sum-txt { min-width: 0; }
.seo-sum-txt b { display: block; font-size: 14px; color: var(--ink); }
.seo-sum-txt small { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.seo-sum-txt + .gt-chip { flex: none; }

.seo-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px;
  padding: 4px 16px 16px; border-top: 1px solid var(--gt-hair);
}
.seo-grid .seo-wide { grid-column: 1 / -1; }
@media (max-width: 720px) { .seo-grid { grid-template-columns: 1fr; } }

.seo-check {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  padding: 10px 12px; border: 1px solid var(--gt-hair); border-radius: 11px;
  background: var(--gt-bg); margin-bottom: 8px;
}
.seo-check input { width: auto; margin-top: 2px; flex: none; }
.seo-check b { display: block; font-size: 13.5px; color: var(--ink); }
.seo-check small { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

.seo-og-now { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; flex-wrap: wrap; }
.seo-og-now img { width: 148px; height: 78px; object-fit: cover; border-radius: 9px; border: 1px solid var(--gt-hair); }
.seo-og-clear { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.seo-og-clear input { width: auto; }

/* ---- the SEO index ---- */
.seo-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px; }
@media (max-width: 780px) { .seo-stats { grid-template-columns: repeat(2, 1fr); } }
.seo-stat {
  border: 1px solid var(--gt-hair); border-radius: 14px; background: var(--gt-bg);
  padding: 14px 16px;
}
.seo-stat b { display: block; font-family: var(--display); font-size: 26px; line-height: 1; color: var(--ink); }
.seo-stat span { display: block; font-size: 12.5px; font-weight: 700; color: var(--ink); margin-top: 6px; }
.seo-stat small { display: block; font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.seo-stat.is-warn b { color: var(--gold, #7a5410); }

.seo-list { display: flex; flex-direction: column; }
.seo-row {
  display: grid; grid-template-columns: minmax(0,1.4fr) minmax(0,1fr) auto 18px;
  align-items: center; gap: 14px;
  padding: 13px 12px; border-bottom: 1px solid var(--gt-hair);
  text-decoration: none; color: inherit;
  transition: background .14s var(--ease);
}
.seo-row:last-child { border-bottom: 0; }
.seo-row:hover { background: rgba(15,46,34,.04); }
.seo-row-main { min-width: 0; }
.seo-row-main b { display: block; font-size: 14.5px; color: var(--ink); }
.seo-row-main small { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seo-row-path { min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.seo-row-path code { font-size: 12px; color: var(--muted); background: rgba(15,46,34,.06); padding: 3px 8px; border-radius: 6px; }
.seo-row-state { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
.seo-row-go svg { width: 16px; height: 16px; stroke: var(--muted); fill: none; stroke-width: 2; }
/* A page kept out of Google is the one thing on this screen somebody might not
   have meant, so the row says so at a glance rather than only in its chip. */
.seo-row.is-noindex { background: rgba(212,160,48,.06); }
@media (max-width: 780px) {
  .seo-row { grid-template-columns: minmax(0,1fr) auto; }
  .seo-row-path, .seo-row-go { display: none; }
}

/* ---- redirects ---- */
.rd-list { display: flex; flex-direction: column; }
.rd-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 4px; border-bottom: 1px solid var(--gt-hair); margin: 0;
}
.rd-row:last-child { border-bottom: 0; }
.rd-paths { display: flex; align-items: center; gap: 8px; flex: 1 1 380px; min-width: 0; }
.rd-paths input {
  flex: 1 1 0; min-width: 0; padding: 8px 11px; font-size: 13px;
  border: 1px solid var(--app-line); border-radius: 9px; background: #fff;
  color: var(--ink); font-family: var(--body);
}
.rd-arrow svg { width: 15px; height: 15px; stroke: var(--muted); fill: none; stroke-width: 2; }
.rd-note {
  flex: 1 1 170px; min-width: 0; padding: 8px 11px; font-size: 12.5px;
  border: 1px solid var(--app-line); border-radius: 9px; background: #fff; color: var(--muted);
}
.rd-perm { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--muted); }
.rd-perm input { width: auto; }
.rd-hits { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* ---- story editor: tags, publish date, address, alt text ---- */
.ned-meta-row { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 18px; }
.ned-tags { flex: 1 1 340px; margin-bottom: 0; }
.ned-when { flex: 0 1 260px; margin-bottom: 0; }
.ned-alt { margin-top: 14px; }
.ned-tagbox { display: flex; flex-wrap: wrap; gap: 8px; }
.ned-tag-check { position: relative; }
/* The box itself is hidden but still focusable and still the thing a screen
   reader announces — the chip is its label, not a replacement for it. */
.ned-tag-check input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.ned-tag-check span {
  display: inline-block; padding: 7px 15px; border-radius: 100px;
  border: 1px solid var(--gt-hair); background: var(--gt-bg);
  font-size: 13px; font-weight: 700; color: var(--muted); cursor: pointer;
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s var(--ease);
}
.ned-tag-check input:focus-visible + span { box-shadow: 0 0 0 3px var(--ad-ring, rgba(45,122,58,.25)); }
.ned-tag-check span:hover { border-color: var(--ad-line-3, rgba(15,46,34,.28)); }

/* A CHIP PER CODE, IN THE CODE'S OWN COLOUR.
   All four turned the same forest green when ticked, so a row of them was
   four identical pills you had to read one at a time. The colour makes the
   row scannable; the tick, which only appears when it is on, carries the same
   state in a second channel for anybody who cannot use the colour. */
.ned-tag-check span {
  display: inline-flex; align-items: center; gap: 0;
  --ntc: var(--forest);
}
.ntc-tick {
  width: 0; height: 14px; margin-right: 0; flex: none; overflow: hidden;
  fill: none; stroke: currentColor; stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
  transition: width .14s var(--ease), margin .14s var(--ease), opacity .14s var(--ease);
}
.ned-tag-check input:checked + span {
  background: var(--ntc); border-color: var(--ntc); color: #fff;
}
.ned-tag-check input:checked + span .ntc-tick { width: 14px; margin-right: 7px; opacity: 1; }
/* The four codes, each its own. AFL and AFLW are the two greens, NRL and NRLW
   the two blues — the pairing readers already know from the ladders. */
.ned-tag-check.t-afl  span { --ntc: #1C6B3A; }
.ned-tag-check.t-aflw span { --ntc: #407E2B; }
.ned-tag-check.t-nrl  span { --ntc: #1D5C8C; }
.ned-tag-check.t-nrlw span { --ntc: #3775A6; }
.ned-tag-check.t-news span { --ntc: #4A5750; }

.ned-url-fix { font-size: 13px; color: var(--muted); font-weight: 600; }
.ned-url-in {
  min-width: 140px; max-width: 320px; padding: 5px 10px; font-size: 13px;
  border: 1px solid var(--app-line); border-radius: 8px; background: var(--ned-surf, #fff);
  color: var(--ned-surf-ink, var(--ink)); font-family: var(--body);
}
.ned-url-in:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(45,122,58,.12); }

/* ==========================================================================
   ============ NEWS DECK — six stories that turn over =======================
   Sep 2026, client: "let it not be static, it should have a slick way of
   changing after like 5 seconds". Replaces three cards plus a repeated list
   of headlines with one rotating deck of six.
   ========================================================================== */

/* SIX CARDS OF ONE SIZE, IN TWO ROWS THAT TAKE IT IN TURNS.

   Every part of a card is fixed or clamped — the band, the tags, two lines of
   headline, the picture, two lines of teaser, the footer. That is not tidiness:
   the two faces of a flipping card share one grid cell, so the cell takes the
   height of the taller of them, and a card whose faces are different heights
   grows and shrinks on every turn. With the flip suppressed that growth is the
   ONLY thing that happens, which is what the client was seeing — "the 360
   degree auto change is not working, it's just getting longer". Nothing in
   here may be allowed to size itself off its content. */
.news-deck { margin-bottom: 22px; }

/* ---- the heading ---------------------------------------------------------
   "This should be centred and a little bit large — News & blog", and the way
   through to the list beside it. */
.nd-head {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  text-align: center; margin: 12px 0 22px;
}
.nd-title {
  font-family: var(--display); font-size: clamp(28px, 3.4vw, 40px);
  line-height: 1.04; letter-spacing: -.012em; color: var(--ink); margin: 0;
}
/* The deck sits below the cream sheet, on the page's own deep-green ground,
   so the heading takes the light ink there — and the dark ink back on the
   light theme, where that ground is white. */
.page-deep .nd-title { color: #fff; }
html[data-theme="light"] .gt-app.page-deep .nd-title { color: var(--lt-ink); }

/* IN ITS BOX, AND STILL A LINK.
   "On the dashboard where we have all the blog it looks like words that is a
   link — I need it to be in its box and look like a link." Both halves: a box
   with a visible edge and a surface of its own, and inside it an underlined
   label in the link colour, rather than the solid filled button it had become
   (a filled button reads as an action, and this goes to a page). */
.nd-all {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 12px 20px; border-radius: 14px; text-decoration: none;
  background: var(--app-card); border: 1px solid var(--app-line);
  box-shadow: 0 12px 28px -22px rgba(15,46,34,.55);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease), transform .18s var(--ease);
}
.nd-all svg { width: 17px; height: 17px; flex: none; fill: var(--forest); }
.nd-all-lab {
  font-size: 14.5px; font-weight: 700; color: var(--forest);
  text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1.5px;
}
.nd-all-go { color: var(--forest); font-weight: 800; transition: transform .15s var(--ease); }
.nd-all:hover { border-color: var(--forest); transform: translateY(-2px); box-shadow: 0 18px 34px -20px rgba(15,46,34,.6); }
.nd-all:hover .nd-all-go { transform: translateX(3px); }
.nd-all:focus-visible { outline: 3px solid var(--lime); outline-offset: 3px; }

/* ---- the two rows ------------------------------------------------------- */
.nd-row { display: grid; gap: 16px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.nd-row + .nd-row { margin-top: 16px; }
@media (max-width: 1000px) { .nd-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .nd-row { grid-template-columns: minmax(0, 1fr); } }
.nd-place { min-width: 0; }

/* THE ARCHIVE LIST (news_index) uses the same card, but it is a list rather
   than a deck: it holds everything published, so it fills and wraps instead
   of being exactly three across. Same track width either way, so a card is
   the same size on both pages. */
.nd-list {
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  align-items: stretch; margin-bottom: 8px;
}
@media (max-width: 1000px) { .nd-list { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); } }
@media (max-width: 620px)  { .nd-list { grid-template-columns: minmax(0, 1fr); } }

/* ---- a card --------------------------------------------------------------
   Codes, headline, picture, teaser, More — the client's order, top to bottom.
   One shape for both rows: the bottom row's smaller "picture beside words"
   variant is gone ("why is it small, have all of them same size"). */
.nd-card {
  position: relative; isolation: isolate;
  display: flex; flex-direction: column; height: 100%;
  padding: 0 0 14px; border-radius: 16px; overflow: hidden;
  text-decoration: none; color: inherit;
  border: 1px solid var(--app-line); background: var(--app-card);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}

/* THE COLOUR, PICKED BY COMPETITION — and a fusion for a story about more
   than one, which is most of them (a piece is filed NRL + Blog far more often
   than NRL alone).

   THE FUSION DOES NOT MIX. A gradient from the NRL's amber to the AFL's blue
   passes through a grey that belongs to neither, and that grey is the widest
   part of it. So .ndc-bar is one hard-edged segment per code, each taking its
   own [data-code] token and each an equal share of the width: the colours
   meet at a line and neither is diluted. The only blended thing on the card
   is .ndc-glow, the same segments blurred into the surface behind the top of
   the card — out of focus, so there is no edge for a muddy colour to sit on,
   and at 18% so it reads as the card being tinted rather than as a second
   coloured bar.

   Nothing here spells a colour out. The segments read the same [data-code]
   custom properties the ladder, the fixtures and the story tags read, so a
   competition's colour is defined once. */
/* z-index -1, not 0. A negative z-index child paints above its stacking
   context's own background and BELOW the in-flow content — which is exactly
   where a colour bar and a blurred halo belong. At 0 it painted above the
   text, and the only way to get the text back on top was to position the text
   too; that is what broke the whole-card link (see .ndc-h). */
.ndc-band { position: absolute; z-index: -1; inset: 0 0 auto; height: 6px; }
.ndc-bar, .ndc-glow { position: absolute; inset: 0; display: flex; }
.ndc-bar > i, .ndc-glow > i { flex: 1 1 0; min-width: 0; background: var(--code-ink, var(--lime)); }
.ndc-bar { z-index: 1; }
.ndc-glow {
  height: 86px; opacity: .18; filter: blur(26px); transform: translateY(-6px);
  pointer-events: none;
}
/* A story with no codes at all still gets the brand's own line rather than a
   6px gap where every other card has colour. */
.nd-card:not([data-code]) .ndc-band { background: var(--lime); }

/* CENTRED, TOP TO BOTTOM (Sep 2026, client: "we have NRL at the top — let it
   be centred, and not on the left ... let it basically be centred for a good
   feel"). The codes, the headline, the teaser and the footer all sit on the
   card's centre line. */
.ndc-tags {
  padding: 16px 18px 0; min-height: 24px;
  display: flex; justify-content: center;
}
/* In the flow here, not laid over a picture the way it used to be. */
.ndc-tags .nt-row { position: static; justify-content: center; }
.ndc-tags .nt { font-size: 10.5px; padding: 4px 10px; box-shadow: none; }

.ndc-h {
  /* NO z-index. `position: relative` on its own does not start a stacking
     context; `z-index: 1` does — and that one declaration is what broke the
     whole-card link. .ndc-a::after is a descendant of this element, so a
     stacking context here trapped the overlay inside it, underneath the
     picture and the footer that come later in the card. The result was a card
     where only the headline TEXT was clickable: the client's "the More button
     is not working, click it, it should take us to that blog".
     Nor may anything but the card be POSITIONED. `position: relative` here
     made this element the containing block for .ndc-a::after — `inset: 0`
     then resolved to the headline's own box, so the "whole card" overlay was
     the size of two lines of text and nothing else on the card was clickable.
     The card is the only positioned ancestor, and it has to stay that way. */
  margin: 12px 0 0; padding: 0 18px; text-align: center;
  font-family: var(--display); font-size: 19.5px; line-height: 1.2; color: var(--ink);
  /* Exactly two lines, always. A one-line headline still occupies two, so
     every card's picture starts at the same height off the top of the card. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: calc(2 * 1.2em);
}
.ndc-a { color: inherit; text-decoration: none; }
/* THE WHOLE CARD IS THE LINK. One anchor, stretched over the card, so a press
   anywhere opens the story and a screen reader is handed a single link named
   after the headline — rather than the card, the picture and "More" all being
   separate controls pointing at one address.

   z-index 5 puts it over everything else in the card, including the pips at
   2 — it has to be the topmost thing or it is not covering what it claims to
   cover. */
.ndc-a::after { content: ""; position: absolute; z-index: 5; inset: 0; border-radius: inherit; }

/* ---- the picture that turns ---------------------------------------------
   Fixed height, and the frames stacked in one grid cell rather than in a
   track: a track scrolls the block sideways and resizes it, and this is the
   one part of the card that must never change size. */
/* BIGGER (client: "make the cards a little bigger so I can see the image
   well — the image is too small"). 168px was a thumbnail; at 240 the picture
   is the thing you look at, which is what a story card is for. Still a fixed
   height, because a card that resizes is a card that cannot flip. */
.ndc-shots {
  position: relative; display: grid; flex: none;
  height: 240px; margin: 14px 0 0; overflow: hidden;
  background: var(--deep);
}
.ndc-shot {
  grid-area: 1 / 1; display: block; opacity: 0;
  background-size: cover; background-position: center 30%;
  transition: opacity .7s var(--ease), transform 3.4s var(--ease);
  transform: scale(1.04);
}
.ndc-shot.on { opacity: 1; transform: none; }
.ndc-shot video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A soft floor under the picture so the teaser below it is never reading off
   the bright edge of a photograph. */
.ndc-shots::after {
  content: ""; position: absolute; z-index: 1; inset: auto 0 0; height: 42%;
  background: linear-gradient(180deg, transparent, rgba(8,21,14,.34));
  pointer-events: none;
}
/* Which frame of how many, for a card that has more than one. Small, and in
   the corner: it is a progress mark, not a control — the card is a link, and
   a pressable dot inside a link is a trap. */
.ndc-pips {
  position: absolute; z-index: 2; left: 50%; transform: translateX(-50%); bottom: 10px;
  display: flex; gap: 5px; padding: 5px 9px; border-radius: 100px;
  background: rgba(8,21,14,.45); pointer-events: none;
}
.ndc-pips i {
  width: 5px; height: 5px; border-radius: 100px; background: rgba(255,255,255,.5);
  transition: width .25s var(--ease), background .25s var(--ease);
}
.ndc-pips i.on { width: 14px; background: var(--lime); }

.ndc-p {
  margin: 14px 0 0; padding: 0 18px; text-align: center;
  font-size: 14px; line-height: 1.5; color: var(--muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: calc(2 * 1.5em);
}

.ndc-foot {
  margin-top: auto; padding: 16px 18px 0;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
}
.ndc-date { font-size: 11.5px; font-weight: 600; color: var(--muted); }
/* "We have the more button that takes you to the whole blogs." It is a span,
   not a second link — see the template — so it has to LOOK like the control
   it is and move like one under the card's hover. */
.ndc-more {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 9px 22px; border-radius: 100px;
  background: color-mix(in srgb, var(--code-ink, var(--forest)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--code-ink, var(--forest)) 30%, transparent);
  font-size: 12.5px; font-weight: 800; color: var(--code-ink, var(--forest));
  transition: background .18s var(--ease), color .18s var(--ease), transform .18s var(--ease);
}
.ndc-more span { transition: transform .15s var(--ease); }

.nd-card:hover { border-color: color-mix(in srgb, var(--code-ink, var(--forest)) 55%, transparent); box-shadow: 0 22px 44px -26px rgba(15,46,34,.5); }
.nd-card:hover .ndc-h { color: var(--code-ink, var(--forest)); }
.nd-card:hover .ndc-more { background: var(--code-ink, var(--forest)); color: var(--code-on, #fff); }
.nd-card:hover .ndc-more span { transform: translateX(3px); }
.nd-card:hover .ndc-glow { opacity: .3; }
/* The keyboard's outline goes on the card, not on the invisible stretched
   anchor — which has no size of its own the browser can draw around. */
.nd-card:has(.ndc-a:focus-visible) { outline: 3px solid var(--lime); outline-offset: 3px; }

/* ---- the five turns ------------------------------------------------------
   Sep 2026, client: "lets have like 5 ways or design that the cards will be
   changing". Two faces share one grid cell and the one showing carries
   .is-up. A turn puts the next story on the other face and plays a pair of
   keyframe animations — .is-leaving on the old face, .is-arriving on the new
   — picked by data-turn on the place. gt-news-cards.js takes the five in
   order, one per tick, and its LASTS table must agree with the timings here.

   `perspective` is on the spinner, the faces' own parent: it only gives depth
   to an element's children, and on anything further up the 3D turns flatten
   into a squash. */
.nd-spinner { position: relative; display: grid; height: 100%; perspective: 1600px; }
.nd-face {
  grid-area: 1 / 1; display: grid; min-width: 0;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
}
.nd-face:not(.is-up):not(.is-leaving) { visibility: hidden; }
.nd-face.is-arriving { z-index: 1; }
/* Slide and iris clip to the card's own corners while they run. */
.nd-place.is-turning[data-turn="slide"] .nd-spinner,
.nd-place.is-turning[data-turn="iris"] .nd-spinner { overflow: hidden; border-radius: 16px; }

/* 1. FLIP — the card turns on its upright; the new story is on the far side. */
.nd-place[data-turn="flip"] .nd-face.is-leaving  { animation: nd-flip-out .43s cubic-bezier(.55,.06,.68,.19) both; }
.nd-place[data-turn="flip"] .nd-face.is-arriving { animation: nd-flip-in .43s .43s cubic-bezier(.22,.61,.36,1) both; }
@keyframes nd-flip-out { from { transform: rotateY(0); } to { transform: rotateY(-90deg); } }
@keyframes nd-flip-in  { from { transform: rotateY(90deg); } to { transform: rotateY(0); } }

/* 2. TUMBLE — the same turn on the horizontal, like a departures board. */
.nd-place[data-turn="tumble"] .nd-face.is-leaving  { animation: nd-tumble-out .43s cubic-bezier(.55,.06,.68,.19) both; }
.nd-place[data-turn="tumble"] .nd-face.is-arriving { animation: nd-tumble-in .43s .43s cubic-bezier(.22,.61,.36,1) both; }
@keyframes nd-tumble-out { from { transform: rotateX(0); } to { transform: rotateX(90deg); } }
@keyframes nd-tumble-in  { from { transform: rotateX(-90deg); } to { transform: rotateX(0); } }

/* 3. SLIDE — the new story pushes the old one out sideways. */
.nd-place[data-turn="slide"] .nd-face.is-leaving  { animation: nd-slide-out .7s cubic-bezier(.65,0,.35,1) both; }
.nd-place[data-turn="slide"] .nd-face.is-arriving { animation: nd-slide-in .7s cubic-bezier(.65,0,.35,1) both; }
@keyframes nd-slide-out { from { transform: translateX(0); } to { transform: translateX(-104%); } }
@keyframes nd-slide-in  { from { transform: translateX(104%); } to { transform: translateX(0); } }

/* 4. ZOOM — the old story falls back and fades as the new one settles in. */
.nd-place[data-turn="zoom"] .nd-face.is-leaving  { animation: nd-zoom-out .5s cubic-bezier(.4,0,1,1) both; }
.nd-place[data-turn="zoom"] .nd-face.is-arriving { animation: nd-zoom-in .6s .15s cubic-bezier(0,0,.2,1) both; }
@keyframes nd-zoom-out { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(.88); } }
@keyframes nd-zoom-in  { from { opacity: 0; transform: scale(1.1); } to { opacity: 1; transform: scale(1); } }

/* 5. IRIS — the new story opens out of the middle of the old one. */
.nd-place[data-turn="iris"] .nd-face.is-leaving  { animation: nd-iris-under .8s ease both; }
.nd-place[data-turn="iris"] .nd-face.is-arriving { animation: nd-iris-in .8s cubic-bezier(.65,0,.35,1) both; }
@keyframes nd-iris-under { from { transform: scale(1); opacity: 1; } to { transform: scale(.95); opacity: .55; } }
@keyframes nd-iris-in {
  from { -webkit-clip-path: circle(0% at 50% 45%); clip-path: circle(0% at 50% 45%); }
  to   { -webkit-clip-path: circle(150% at 50% 45%); clip-path: circle(150% at 50% 45%); }
}

/* THE FADE — every turn, for a reader whose system asks for reduced motion.
   The deck still changes every five seconds (the client's instruction, and a
   change of content rather than a movement); nothing travels. */
.nd-place[data-turn="fade"] .nd-face.is-leaving  { animation: nd-fade-out .4s ease both; }
.nd-place[data-turn="fade"] .nd-face.is-arriving { animation: nd-fade .4s ease both; }
@keyframes nd-fade     { from { opacity: 0; } to { opacity: 1; } }
@keyframes nd-fade-out { from { opacity: 1; } to { opacity: 0; } }

/* The news page's copy of the deck sits above the full archive list. */
.news-top { margin-top: 6px; }
.news-all-h { text-align: center; font-size: 22px; margin: 30px 0 16px; }

/* ---- the dots ------------------------------------------------------------ */
.nd-foot { display: flex; justify-content: center; margin-top: 16px; }
.nd-dots { display: flex; align-items: center; gap: 8px; }
.nd-dot {
  width: 9px; height: 9px; padding: 0; border: 0; border-radius: 100px;
  background: rgba(15,46,34,.22); cursor: pointer;
  transition: background .2s var(--ease), width .25s var(--ease);
}
.nd-dot:hover { background: rgba(15,46,34,.42); }
/* The one you are on is a lozenge rather than a bigger circle — it reads as
   "you are here" at a glance, and it survives being the only distinguishing
   mark for someone who cannot separate the two greens. */
.nd-dot.on { width: 26px; background: var(--forest); }
.page-deep .nd-dot { background: rgba(232,228,213,.26); }
.page-deep .nd-dot:hover { background: rgba(232,228,213,.5); }
.page-deep .nd-dot.on { background: var(--lime); }
.nd-dot:focus-visible { outline: 3px solid var(--lime); outline-offset: 3px; }

/* Asked not to have things move: the deck and the card pictures still change,
   but every change is a cross-fade in place. The script already asks for the
   "fade" turn under this setting; this catches anything that slips through,
   whichever movement a place was last given. */
@media (prefers-reduced-motion: reduce) {
  .nd-place .nd-face.is-leaving  { animation: nd-fade-out .4s ease both; }
  .nd-place .nd-face.is-arriving { animation: nd-fade .4s ease both; }
  .ndc-shot { transition: opacity .45s ease; transform: none; }
}

/* ==========================================================================
   ============ THE READER — one story, as a page of its own =================
   Sep 2026, client: a story opened from the public news page dropped a
   signed-in member into their own dashboard. It is now one template for
   everyone (news_reader.html) with no app nav on it at all, and a visitor who
   is not signed in reads the opening third.

   The layout is the app's sheet idea without the app: deep-green ground, the
   two rotating photograph strips down the sides, and the story on a cream
   sheet between them. Everything here is scoped to `.reader`, so none of it
   can reach the member pages that share these class prefixes.
   ========================================================================== */
.reader {
  min-height: 100vh;
  background:
    radial-gradient(900px 560px at 78% -10%, rgba(200,241,53,.12), transparent 60%),
    radial-gradient(720px 520px at -10% 112%, rgba(45,122,58,.4), transparent 62%),
    var(--deep);
  color: var(--ink);
  font-family: var(--body);
}
/* The strips get the same green glass the member pages give them, so the
   photographs sit behind the page rather than beside it. */
.reader .page-scenes .scene.left::after,
.reader .page-scenes .scene.right::after {
  content: ""; position: absolute; inset: 0;
}
.reader .page-scenes .scene.left::after { background: linear-gradient(90deg, rgba(15,46,34,.06), rgba(15,46,34,.32) 55%, rgba(15,46,34,.85)); }
.reader .page-scenes .scene.right::after { background: linear-gradient(270deg, rgba(15,46,34,.06), rgba(15,46,34,.32) 55%, rgba(15,46,34,.85)); }
.reader .page-scenes .scene { width: clamp(120px, 12vw, 230px); }

/* ---- the bar ------------------------------------------------------------
   Four items, no menu. See the template for why. Sticky, because the way back
   to the list is the one control a reader wants half way down a long story. */
.rd-bar {
  position: sticky; top: 0; z-index: 40;
  background: rgba(11,33,24,.86);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.rd-bar-in {
  max-width: 1300px; margin: 0 auto;
  display: flex; align-items: center; gap: 18px;
  height: 64px; padding: 0 clamp(16px, 3vw, 44px);
}
.rd-mark {
  font-family: var(--mark); font-weight: 900; font-size: 21px; letter-spacing: .01em;
  color: #fff; text-transform: uppercase; text-decoration: none; flex: none;
}
.rd-mark span { color: var(--lime); }
.rd-mark .dot { color: var(--lime); }
.rd-back {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--cream); opacity: .72; text-decoration: none;
  font-size: 14px; font-weight: 600; padding: 8px 12px; border-radius: 9px;
  transition: opacity .15s var(--ease), background .15s var(--ease);
}
.rd-back:hover { opacity: 1; background: rgba(255,255,255,.07); }
.rd-back svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.rd-bar-end { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.rd-ghost {
  border: 1.5px solid rgba(255,255,255,.26); border-radius: 100px;
  padding: 8px 16px; font-size: 13.5px; font-weight: 700;
  color: var(--cream); text-decoration: none; white-space: nowrap;
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
.rd-ghost:hover { border-color: var(--lime); color: var(--lime); }
.rd-cta {
  background: var(--lime); color: var(--deep); border-radius: 100px;
  padding: 9px 18px; font-size: 13.5px; font-weight: 800;
  text-decoration: none; white-space: nowrap;
  transition: filter .15s var(--ease), transform .15s var(--ease);
}
.rd-cta:hover { filter: brightness(1.06); transform: translateY(-1px); }
@media (max-width: 560px) {
  .rd-back span { display: none; }
  .rd-bar-in { gap: 10px; }
}

/* ---- the sheet ----------------------------------------------------------
   The centre container the client asked to be right: one column, measured for
   reading, with the photographs showing at both margins.

   CREAM INTO PURE WHITE (Sep 2026, client: "on the whole blog I want a clean
   design — let's have the cream to white, pure white"). The top of the sheet
   keeps the brand cream, so the page still arrives as GoodTip rather than as
   a white rectangle, and it has gone to #FFF by the time the first paragraph
   starts. Everything from there down is body text on pure white, which is
   both what was asked for and the highest contrast this page can have.

   The gradient is measured in pixels rather than percent on purpose: a
   percentage stretches with the length of the story, so a long read would
   fade over three screens and a short one over half of them. 380px is the
   headline block, whatever is underneath it.

   --rd-ink and friends are declared here rather than left to the app's
   tokens: this sheet is white in EVERY theme, and a token that moves with
   the theme would put dark-mode ink on it. */
.reader {
  --rd-sheet: #FFFFFF;
  --rd-cream: #F5F2E7;
  --rd-hair: rgba(15,46,34,.13);
  --rd-tile: #FFFFFF;
}
.rd-main {
  position: relative; z-index: 1;
  max-width: 1180px; margin: 0 auto;
  background: linear-gradient(180deg, var(--rd-cream) 0, var(--rd-sheet) 380px) var(--rd-sheet);
  padding: clamp(28px, 5vw, 60px) clamp(18px, 4vw, 64px) 72px;
  box-shadow: 0 0 90px -20px rgba(0,0,0,.55);
}
/* 68ch, not the container width. A line of body text past about 75 characters
   is measurably harder to come back to at the start of the next one, and the
   whole point of this page is that it is for reading. */
.rd-art { max-width: 68ch; margin: 0 auto; }

.rd-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 16px; }
.rd-tag {
  background: var(--lime); color: var(--deep);
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase;
  padding: 4px 11px; border-radius: 100px;
}
.rd-title {
  font-family: var(--display);
  font-size: clamp(30px, 4.6vw, 50px); line-height: 1.06;
  letter-spacing: -.015em; color: var(--ink);
}
.rd-lead {
  margin-top: 16px; font-size: clamp(17px, 1.7vw, 20px); line-height: 1.55;
  color: var(--muted); font-weight: 500;
}
.rd-byline {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px;
  margin-top: 22px; padding-bottom: 22px;
  border-bottom: 1px solid rgba(15,46,34,.12);
  font-size: 13.5px; color: var(--muted);
}
.rd-who { display: inline-flex; align-items: center; gap: 9px; }
.rd-who b { color: var(--ink); font-size: 14px; }
.rd-av {
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: var(--forest); color: #fff; overflow: hidden;
  display: grid; place-items: center;
  font-family: var(--mark); font-weight: 800; font-size: 13px;
}
.rd-av img { width: 100%; height: 100%; object-fit: cover; }

.rd-hero {
  position: relative; overflow: hidden;
  margin: 28px 0 32px; border-radius: 18px;
  aspect-ratio: 16 / 9;
  background-size: cover; background-position: center 32%;
  background-color: var(--deep);
  box-shadow: 0 30px 60px -34px rgba(15,46,34,.55);
}
.rd-hero-shots .shot {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  opacity: 0; transition: opacity 1.6s var(--ease);
}
.rd-hero-shots .shot.on { opacity: 1; }

/* ---- the featured set, uncropped ----------------------------------------
   Every frame is shown WHOLE. The old hero was a 16:9 background with
   `cover`, and the featured pictures are 3:2 and 4:3 infographics, so `cover`
   cut a quarter of each one off — usually a heading or a row of logos. Here
   the frame is a stack of real <img>/<video> elements in one grid cell, each
   `contain`ed on the deep green: nothing is cut, and a picture narrower than
   the frame sits centred with a clean green margin rather than being zoomed.

   The frame's height is the first picture's own shape, capped at 72vh so a
   tall portrait image does not push the story off the first screen. Every
   frame shares the cell, so a second picture of a different shape does not
   make the frame jump when it turns in. */
.rd-gallery {
  display: grid; aspect-ratio: auto; background: var(--deep);
  background-image: none;
}
.rd-gallery .rd-frame {
  grid-area: 1 / 1; display: grid; place-items: center;
  opacity: 0; transform: none;
  transition: opacity .8s var(--ease);
}
.rd-gallery .rd-frame.on { opacity: 1; }
.rd-gallery .rd-frame img,
.rd-gallery .rd-frame video {
  display: block; width: 100%; height: auto; max-height: 72vh;
  object-fit: contain; border-radius: 0;
}
/* Frames after the first must not set the height: they are laid out in the
   same cell, and a taller second picture would otherwise stretch the frame
   the moment the page loads, before it is even showing. */
.rd-gallery .rd-frame:not(:first-child) img,
.rd-gallery .rd-frame:not(:first-child) video {
  position: absolute; inset: 0; margin: auto; height: 100%;
}
.rd-gallery .rd-frame:not(:first-child) { position: absolute; inset: 0; }
.rd-pips { bottom: 14px; }
@media (prefers-reduced-motion: reduce) { .rd-gallery .rd-frame { transition: none; } }

/* ---- pictures and clips inside the story ---------------------------------
   "When I attach an image or a video, let it be a nice size — right now it
   looks horrible." A body image had exactly two rules — a radius and
   display:block — and no width at all, so a 1536px upload ran straight out of
   the 900px column, and a phone clip filled the screen top to bottom.

   Now: the column's width and never more, a sensible ceiling on height so a
   portrait photo does not become a wall, centred, on a soft frame, with room
   above and below so it reads as a figure in the story rather than a thing
   that fell into it. A caption (the editor's alt text, or a real
   <figcaption>) sits under it in the quiet ink. */
.rd-prose img,
.rd-prose video {
  display: block; max-width: 100%; height: auto; max-height: 70vh;
  margin: 1.8em auto; border-radius: 16px;
  object-fit: contain; background: #F2F4EF;
  box-shadow: 0 1px 2px rgba(15,46,34,.06), 0 24px 48px -30px rgba(15,46,34,.45);
}
.rd-prose video { width: 100%; background: #000; }
.rd-prose figure { margin: 1.8em 0; }
.rd-prose figure img, .rd-prose figure video { margin: 0 auto; }
.rd-prose figcaption {
  margin-top: 10px; text-align: center;
  font-size: 13.5px; line-height: 1.5; color: var(--muted);
}
/* A slideshow is already a sized, framed block of its own. */
.rd-prose .gt-slides img, .rd-prose .gt-slides video {
  margin: 0; max-height: none; border-radius: 0; box-shadow: none; background: none;
}

/* ---- the story ---------------------------------------------------------- */
.rd-prose { font-size: 17.5px; line-height: 1.72; color: var(--ink); }
.rd-prose > * + * { margin-top: 1.15em; }
.rd-prose p { color: rgba(15,46,34,.86); }
.rd-prose h2 {
  font-family: var(--display); font-size: clamp(22px, 2.6vw, 30px);
  line-height: 1.15; color: var(--ink); margin-top: 1.7em;
}
.rd-prose h3 { font-family: var(--display); font-size: 21px; color: var(--ink); margin-top: 1.5em; }
.rd-prose a { color: var(--forest); font-weight: 600; text-underline-offset: 3px; }
.rd-prose a:hover { color: var(--deep); }
.rd-prose ul, .rd-prose ol { padding-left: 1.35em; }
.rd-prose li + li { margin-top: .45em; }
.rd-prose img { border-radius: 14px; display: block; }
.rd-prose blockquote {
  border-left: 3px solid var(--lime); padding: 4px 0 4px 20px;
  font-family: var(--display); font-size: 21px; line-height: 1.4; color: var(--forest);
}
.rd-prose hr { border: 0; border-top: 1px solid rgba(15,46,34,.14); margin: 2em 0; }

/* WHAT YOU CAN SEE BUT NOT READ.
   Real text, blurred and faded — so the page reads as an article carrying on
   rather than as grey placeholder bars, which is the difference between
   "there is more here" and "this is still loading".

   Three things make it a tease rather than an inconvenience:
   the blur, the fade-out towards the gate, and being unselectable — dragging
   across it to copy the words out is the one interaction that would make the
   whole thing pointless. (It is also aria-hidden in the markup: a screen
   reader must not be handed two paragraphs that stop mid-sentence.)

   The mask is the fade. A ::after gradient in the sheet's colour would work
   on cream and be wrong the moment anything else is behind it; a mask fades
   the element itself, whatever it is sitting on. */
.rd-tease {
  position: relative;
  margin-top: 1.15em;
  font-size: 17.5px; line-height: 1.72; color: var(--ink);
  filter: blur(4.5px);
  opacity: .5;
  user-select: none; -webkit-user-select: none;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.35) 55%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.35) 55%, transparent 100%);
  /* Capped so a long teased run cannot push the gate off the first screen —
     the point is to see that there IS more, not to scroll through a blur. */
  max-height: 30vh; overflow: hidden;
}
.rd-tease > * + * { margin-top: 1.15em; }
.rd-tease p { color: rgba(15,46,34,.86); }
/* Headings inside the teased run would be blurred bold text, which reads as a
   rendering fault rather than as an article. Flattened to body weight. */
.rd-tease h2, .rd-tease h3 { font-family: var(--body); font-size: 17.5px; font-weight: 600; }

/* ---- the gate -----------------------------------------------------------
   Pulled up under the blurred run so the two read as one moment: the story
   goes out of focus and this is why. */
.rd-gate {
  margin-top: -6px; text-align: center;
  border-radius: 20px; padding: clamp(28px, 4vw, 40px) clamp(20px, 4vw, 44px);
  background: linear-gradient(165deg, var(--deep), var(--deep-2));
  border: 1px solid rgba(200,241,53,.28);
  box-shadow: 0 40px 80px -40px rgba(15,46,34,.7);
  color: var(--cream);
}
.rdg-ic {
  display: inline-grid; place-items: center; width: 52px; height: 52px;
  border-radius: 16px; margin-bottom: 16px;
  background: rgba(200,241,53,.14); border: 1px solid rgba(200,241,53,.4);
}
.rdg-ic svg { width: 24px; height: 24px; fill: var(--lime); }
.rd-gate h2 {
  font-family: var(--display); font-size: clamp(24px, 3.2vw, 32px);
  line-height: 1.1; color: #fff;
}
.rd-gate > p { margin-top: 12px; font-size: 16px; line-height: 1.6; color: rgba(232,228,213,.82); max-width: 46ch; margin-inline: auto; }
.rdg-actions {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 12px; margin-top: 24px;
}
.rdg-primary {
  background: var(--lime); color: var(--deep); border-radius: 100px;
  padding: 14px 28px; font-size: 15px; font-weight: 800; text-decoration: none;
  transition: filter .15s var(--ease), transform .15s var(--ease);
}
.rdg-primary:hover { filter: brightness(1.06); transform: translateY(-1px); }
.rdg-ghost {
  color: var(--lime); font-weight: 700; font-size: 14.5px;
  text-decoration: none; padding: 13px 18px; border-radius: 100px;
  border: 1.5px solid rgba(200,241,53,.34);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.rdg-ghost:hover { border-color: var(--lime); background: rgba(200,241,53,.1); }
.rdg-fine { margin-top: 18px; font-size: 12.5px; line-height: 1.55; color: rgba(232,228,213,.55); max-width: 52ch; margin-inline: auto; }

/* ---- share bar, using the shared partial's {{ prefix }}-share names ------ */
.rd-share {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 36px; padding-top: 24px;
  border-top: 1px solid rgba(15,46,34,.12);
}
.rd-share .as-label {
  font-family: var(--mark); font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; font-size: 11.5px; color: var(--muted); margin-right: 4px;
}
.rd-share .as-btn {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid rgba(15,46,34,.16); background: #fff; cursor: pointer;
  border-radius: 100px; padding: 9px 14px;
  font-family: var(--body); font-size: 13px; font-weight: 700; color: var(--ink);
  text-decoration: none;
  transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.rd-share .as-btn svg { width: 15px; height: 15px; fill: currentColor; }
.rd-share .as-btn:hover { border-color: var(--forest); color: var(--forest); background: rgba(15,46,34,.04); }
.rd-sources { margin-top: 28px; font-size: 14px; color: var(--muted); }
.rd-sources h4 { font-family: var(--mark); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--forest); margin-bottom: 8px; }
.rd-sources ol { padding-left: 1.3em; }
.rd-sources a { color: var(--forest); }

/* ---- more stories -------------------------------------------------------
   Full sheet width, not the 68ch column: the article is over, and a row of
   three cards squeezed into a reading measure looks like a mistake. */
.rd-more { margin-top: 56px; padding-top: 36px; border-top: 1px solid rgba(15,46,34,.12); }
.rd-more-h { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.rd-more-h h2 { font-family: var(--display); font-size: clamp(20px, 2.4vw, 26px); color: var(--ink); }
.rd-more-h a { font-size: 13.5px; font-weight: 700; color: var(--forest); text-decoration: none; white-space: nowrap; }
.rd-more-h a:hover { text-decoration: underline; }
.rd-more-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
@media (max-width: 900px) { .rd-more-grid { grid-template-columns: 1fr; } }
/* The "more from GoodTip" row renders the same story card the dashboard and
   the news list render (partials/_news_deck_card.html), so there is one story
   card in the product. What it needs here is the sheet's own white and
   hairline rather than the app's cream card tone — the sheet is pure white on
   every theme, and a cream card on it reads as a smudge. */
.reader .nd-card { background: var(--rd-tile); border-color: var(--rd-hair); }
.reader .nd-card:hover { box-shadow: 0 22px 44px -26px rgba(15,46,34,.4); }

/* ---- the social band ----------------------------------------------------
   "Where we have our socials, make it small — just a little in terms of the
   height, up to down. Let's have it as green, and the icons to be the real
   colours, like YouTube be the red we know, because currently it turns to the
   real colour when highlighted. Now it just magnifies when we hover."

   Three changes from the tall cream version:

   * SHORTER. The band was 36/40px of padding around 76px tiles stacked over
     labels — most of a phone screen for five links. The tile and its label sit
     on one line now, so the whole band is about half the height it was, and
     the row is a row rather than five columns of a grid.

   * GREEN. It is a band at the foot of a white sheet; cream on cream read as
     more article. The deep green is the same ground the page already sits on
     either side of the sheet, so the story now ends on the site rather than on
     another panel.

   * THE REAL COLOURS, AT REST. Each glyph is its network's own colour on a
     white tile from the moment the page loads — YouTube's red is YouTube's
     red without being hovered. That was the complaint, and it was also an
     accessibility problem: colour that only appears on hover does not exist
     for a keyboard or a touchscreen, which is most of the traffic.

   Which leaves hover with one job, and it is the one that was asked for:
   MAGNIFY. */
.rd-socials {
  margin-top: 40px; padding: 22px clamp(16px, 3vw, 30px) 24px;
  border-radius: 20px; text-align: center;
  background: linear-gradient(135deg, #123A2B, #0C2A1F 60%, #12402D);
  border: 1px solid rgba(200,241,53,.16);
}
.rd-socials h2 {
  font-family: var(--display); font-size: clamp(18px, 2vw, 22px);
  color: #FFFFFF; margin: 0;
}
/* On the deep green, the muted ink is unreadable — this is one of the places
   the client meant by "make sure on the different themes all text can be
   seen". Cream at 82% is 9.4:1 on this ground, and it is a literal rather
   than a token because the band is deep green in EVERY theme: a token that
   moves with the theme would take the text off this fixed ground. */
.rd-socials > p { margin-top: 4px; font-size: 13.5px; color: rgba(232,228,213,.82); }
.rd-social-row {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px; margin-top: 16px;
}
.rd-social {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 16px 7px 7px; border-radius: 100px;
  text-decoration: none; color: #FFFFFF;
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.14);
  transition: transform .18s var(--ease), background .18s var(--ease), border-color .18s var(--ease);
}
.rds-ic {
  display: grid; place-items: center; flex: none;
  width: 36px; height: 36px; border-radius: 50%;
  background: #FFFFFF;
}
/* THE GLYPH IS THE BRAND COLOUR, always. --net is set per network below, once,
   and both the glyph and the hover ring read it — so a network's colour is
   written in exactly one place. */
.rds-ic svg { width: 19px; height: 19px; fill: var(--net, var(--forest)); }
.rds-lab { font-size: 13.5px; font-weight: 700; color: #FFFFFF; }
.rd-social.s-email    { --net: #0F2E22; }
.rd-social.s-linkedin { --net: #0A66C2; }
.rd-social.s-facebook { --net: #1877F2; }
.rd-social.s-instagram{ --net: #C13584; }
.rd-social.s-youtube  { --net: #FF0000; }
/* Email's own dark green is the one glyph that would disappear into a white
   tile at small sizes on a dark band, so it gets the forest rather than the
   near-black deep. */
.rd-social.s-email .rds-ic svg { fill: #176B45; }

/* "It just magnifies when we hover." One transform, nothing else moving. */
.rd-social:hover, .rd-social:focus-visible {
  transform: scale(1.09);
  background: rgba(255,255,255,.13); border-color: rgba(255,255,255,.3);
}
.rd-social:focus-visible { outline: 3px solid var(--lime); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .rd-social:hover, .rd-social:focus-visible { transform: none; } }

/* ---- the footer, on the deep ground below the sheet --------------------- */
.reader .footer { position: relative; z-index: 1; }

/* THE READER'S LIGHT THEME.
   The sheet is already cream-on-green in both themes — that IS the design,
   and it is the one page where the member app's light palette would fight the
   photographs rather than help them. What light mode changes is the ground
   behind the sheet: a softer green, so the page does not sit as a bright
   panel in a dark hole for somebody who has asked for light. */
html[data-theme="light"] .reader {
  background:
    radial-gradient(900px 560px at 78% -10%, rgba(200,241,53,.16), transparent 60%),
    radial-gradient(720px 520px at -10% 112%, rgba(45,122,58,.28), transparent 62%),
    #12362A;
}

/* ==========================================================================
   ============ CHAT: reactions, voice notes, and the row's controls ========
   ==========================================================================
   These belong to partials/_chat.html, which is shared by the member's
   messages screen and the organisation admin's support-thread page. The
   messages screen loads gt-messages.css and restates them for its dark
   panels; this is the base, painted for the cream card the admin's page
   draws them on. Without it the admin's end of a thread renders unstyled
   reaction chips and a bare audio player.
   ========================================================================== */

/* ---- the row's hover controls ---- */
.chat-rowacts { display: flex; align-items: center; gap: 2px; }
.chat-pinform { display: flex; }
.chat-pinbtn {
  width: 30px; height: 30px; display: grid; place-items: center;
  background: none; border: 0; cursor: pointer; border-radius: 8px;
  color: var(--muted); opacity: 0;
  transition: opacity .15s var(--ease), color .15s var(--ease);
}
.chat-pinbtn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chat-row:hover .chat-pinbtn, .chat-pinbtn:focus-visible { opacity: 1; }
.chat-pinbtn:hover { color: var(--forest); }
/* The pinned one stays visible: its button now means "unpin", and a control
   that only appears on hover cannot say so. */
.chat-pinbtn.on { opacity: 1; color: var(--forest); }

/* ---- reactions ---- */
.chat-reacts { margin-top: 5px; }
.rx { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.rx-chip {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  background: var(--app-card-2, #F4F1E6); border: 1px solid var(--app-line, rgba(15,46,34,.12));
  border-radius: 100px; padding: 3px 9px;
  font-family: var(--body); font-size: 12px; color: var(--muted);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.rx-chip:hover { border-color: rgba(15,46,34,.28); }
/* The one you are in is drawn as pressed, because pressing it again takes you
   back out — a chip that looks the same either way makes the toggle invisible. */
.rx-chip.mine { background: #EEF8C8; border-color: rgba(23,107,69,.34); color: #3F6208; }
.rx-e { font-size: 13px; line-height: 1; }
.rx-n { font-weight: 700; font-variant-numeric: tabular-nums; }

.rx-add { position: relative; }
.rx-add summary {
  list-style: none; cursor: pointer; width: 26px; height: 26px;
  display: grid; place-items: center; border-radius: 100px;
  color: var(--muted); opacity: 0; transition: opacity .15s var(--ease);
}
.rx-add summary::-webkit-details-marker { display: none; }
.rx-add summary svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; }
.chat-row:hover .rx-add summary, .rx-add[open] summary, .rx-add summary:focus-visible { opacity: 1; }
.rx-add summary:hover { color: var(--forest); background: rgba(15,46,34,.06); }
.rx-menu {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 20;
  display: flex; gap: 2px; padding: 5px; border-radius: 12px;
  background: var(--app-card, #FCFBF5); border: 1px solid var(--app-line, rgba(15,46,34,.14));
  box-shadow: 0 20px 40px -22px rgba(15,46,34,.5);
}
/* Your own bubbles sit on the right, so a menu anchored left opens off the
   edge of the conversation. */
.chat-row.mine .rx-menu { left: auto; right: 0; }
.rx-opt {
  width: 32px; height: 32px; display: grid; place-items: center; cursor: pointer;
  background: none; border: 0; border-radius: 9px; font-size: 17px; line-height: 1;
  transition: transform .12s var(--ease), background .12s var(--ease);
}
.rx-opt:hover { background: rgba(15,46,34,.07); transform: scale(1.15); }

/* ---- voice notes ----
   A player of our own rather than <audio controls>, which is a different grey
   box in every browser and looks like nothing else on the page. */
.chat-vn { display: flex; align-items: center; gap: 11px; min-width: 220px; max-width: 300px; padding: 4px 2px; }
.cvn-play {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center;
  cursor: pointer; border: 0; border-radius: 50%;
  background: var(--forest); color: #fff;
}
.cvn-play svg { width: 15px; height: 15px; fill: currentColor; }
.cvn-i-pause { display: none; }
.chat-vn.is-playing .cvn-i-play { display: none; }
.chat-vn.is-playing .cvn-i-pause { display: block; }
.cvn-wave { position: relative; flex: 1; height: 26px; cursor: pointer; overflow: hidden; }
/* A repeating gradient, not the real waveform: drawing the real one means
   downloading and decoding every clip in a thread on load, which is exactly
   what preload="metadata" exists to avoid. It reads as a voice note, which is
   the whole job. */
.cvn-bars {
  position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, rgba(15,46,34,.24) 0 2px, transparent 2px 6px);
  -webkit-mask-image: linear-gradient(180deg, transparent 18%, #000 18%, #000 82%, transparent 82%);
  mask-image: linear-gradient(180deg, transparent 18%, #000 18%, #000 82%, transparent 82%);
}
.cvn-fill {
  position: absolute; inset: 0 auto 0 0; width: 0;
  background: repeating-linear-gradient(90deg, var(--forest) 0 2px, transparent 2px 6px);
  -webkit-mask-image: linear-gradient(180deg, transparent 18%, #000 18%, #000 82%, transparent 82%);
  mask-image: linear-gradient(180deg, transparent 18%, #000 18%, #000 82%, transparent 82%);
  transition: width .1s linear;
}
.cvn-time { flex: none; font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--muted); }

/* ==========================================================================
   ============ THE APP BAR ACTUALLY FITS ===================================
   ==========================================================================
   Reported: "the AquaFlow organisation is overtaken and overlapped by the
   word Dashboard — let every item on the menu fit properly."

   IT IS NOT A POSITIONING BUG, IT IS ARITHMETIC. v34 sized this row for seven
   items and a plain text label for the organisation. It now carries eight
   (Dashboard, My Tips, Leaderboard, Ladder, The Wall, Messages, Manage,
   Admin) plus a CHIP for the organisation — a bordered control with padding,
   a chevron and a group name — and the chip was `flex: none`, so it could not
   give way. Measured at the 1300px cap: brand 110 + links ~880 + right
   cluster ~230 + gaps ~34 leaves the chip about nothing, and a flex item that
   cannot shrink and has no room left simply overlaps its neighbour.

   Two changes, and the second is the one that matters.

   1. The chip becomes the row's shrinkable item, the way `.an-org` was before
      it. Everything else stays immovable, so the deficit lands on the one
      thing that can ellipsise and stay useful.

   2. Below 1300px the chip stops competing for the row at all and takes its
      own strip underneath. Shrinking it further was the alternative and it is
      a worse answer: the name of the organisation you are tipping in is the
      whole point of the control, and "AquaF…" is not that. There is no room
      for both on a 1280px laptop, so they stop sharing.
   ========================================================================== */

/* HOW TALL THE BAR IS, as a number anything below it can read.

   The Messages screen is sized to fill the window exactly (it does not
   scroll; its panels do), so it has to subtract the bar's height — and with
   the chip taking its own strip at mid widths that height is no longer one
   number. Published here rather than restated in gt-messages.css, so the two
   cannot drift and a future change to the bar does not silently leave a
   40px gap at the bottom of a conversation. */
.gt-v2 { --gt-navh: 68px; }

/* 1. The chip gives way first. `min-width: 0` is not optional — a flex item
      will not shrink below its own content without it, ellipsis or no. */
.gt-v2 .an-ctx { flex: 0 1 auto; min-width: 0; }
.gt-v2 .an-ctx-btn { max-width: 100%; min-width: 0; }
/* The floor moves off the org name and onto the button. A 4.5em floor on the
   name meant the chip could not shrink past ~72px of text however tight the
   row was, which is exactly the pixels that were coming out of Dashboard. */
.gt-v2 .an-ctx-org { min-width: 0; }
.gt-v2 .an-ctx-btn > svg { flex: none; }

/* 2. Its own strip, below 1300px.
      `order` plus a full-width basis on a wrapping row: brand, links and the
      account cluster keep row one in source order, and the chip is pushed to
      row two. The nav is sticky, so the strip sticks with it — it is a
      permanent "where am I", which is what the control is for. */
@media (max-width: 1300px) {
  .gt-v2 { --gt-navh: 112px; }        /* 68 row one + 44 strip */
  .gt-v2 .app-nav .an-inner {
    flex-wrap: wrap;
    height: auto;
    row-gap: 0;
    padding-bottom: 0;
  }
  /* Row one keeps its own height, or the bar collapses to the text height. */
  .gt-v2 .app-nav .an-inner > :not(.an-ctx) { align-self: center; }
  .gt-v2 .an-brand,
  .gt-v2 .an-links,
  .gt-v2 .an-right { min-height: 68px; display: flex; align-items: center; }
  /* A FIXED 44px, not "whatever the chip happens to measure". --gt-navh
     above is an exact subtraction, and a strip that grows with a long
     organisation name would put the composer that many pixels off the bottom
     of the Messages screen. */
  .gt-v2 .an-ctx {
    order: 9;
    flex: 1 0 100%;
    height: 44px;
    margin: 0 0 0 -4px;
    padding-bottom: 8px;
  }
  .gt-v2 .an-ctx-btn { max-width: min(100%, 520px); }
  /* Anchored to the strip's own left edge, not to the row above it. */
  .gt-v2 .an-ctx .an-menu-panel { top: calc(100% + 4px); }
}

/* Below 900px the chip was already hidden (the drawer's header names the
   organisation instead), so the strip does not apply and the bar is one row
   again. Restated because the block above sets flex-wrap on a wider query. */
@media (max-width: 900px) {
  .gt-v2 { --gt-navh: 68px; }
  .gt-v2 .app-nav .an-inner { flex-wrap: nowrap; height: 68px; }
  .gt-v2 .an-brand,
  .gt-v2 .an-links,
  .gt-v2 .an-right { min-height: 0; }
}
@media (max-width: 760px) {
  .gt-v2 { --gt-navh: 60px; }
  .gt-v2 .app-nav .an-inner { height: 60px; }
}

/* The "Profile" word next to the avatar, at every width rather than only
   above 1361px. The argument for hiding it there holds everywhere: a ringed
   avatar in the corner of an app bar is the most legible profile control
   there is, it carries a title and an aria-label, and the phone drawer spells
   it out in full under the user's own name. It was ~51px of the deficit. */
.gt-v2 .an-plabel { display: none; }

/* ==========================================================================
   ============ THE STORY SCHEDULER =========================================
   ==========================================================================
   Asked for as: "a new look and design on the blog timers where we schedule
   uploads — make that look modern, neat and cool."

   It was a `datetime-local` with a caption. The redesign is not decoration:
   the field could only ever tell you the moment somebody typed, and the thing
   an editor needs to know is the STATE — published here means `is_published
   AND published_at <= now`, so a date in the future is a schedule and looks
   identical to a mistake. The card says which, in words, in colour, and with
   a countdown; the presets exist because the four times anybody actually
   picks are now, this evening, tomorrow morning and next Monday.
   ========================================================================== */
.ned-when { flex: 0 1 320px; }
.sched {
  --sc: var(--muted);
  --sc-bg: var(--ad-quiet-bg, rgba(15,46,34,.06));
  padding: 14px 16px 15px; border-radius: 14px;
  background: var(--gt-card, #fff);
  border: 1px solid var(--gt-hair, rgba(15,46,34,.10));
  box-shadow: 0 1px 2px rgba(15,46,34,.04);
}
/* One variable per state, so every part of the card follows in step and there
   is no rule that can be updated for one state and forgotten for another. */
.sched[data-state="live"]      { --sc: var(--ad-ok-fg, #1C5E30); --sc-bg: rgba(45,122,58,.10); }
.sched[data-state="scheduled"] { --sc: var(--ad-gold-fg, #8A6508); --sc-bg: var(--ad-tint-gold, rgba(244,196,48,.12)); }
.sched[data-state="draft"]     { --sc: var(--muted); --sc-bg: var(--ad-quiet-bg, rgba(15,46,34,.06)); }
.sched[data-state="new"]       { --sc: var(--muted); --sc-bg: var(--ad-quiet-bg, rgba(15,46,34,.06)); }

.sched-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 13px; padding: 6px 10px; border-radius: 100px;
  background: var(--sc-bg);
}
.sched-dot {
  width: 8px; height: 8px; flex: none; border-radius: 50%;
  background: var(--sc);
}
/* Only a story that is waiting gets the pulse. A live one is not doing
   anything and a draft is not either; motion on all three would be noise
   rather than a signal. */
.sched[data-state="scheduled"] .sched-dot { animation: schedPulse 1.8s ease-in-out infinite; }
@keyframes schedPulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }
@media (prefers-reduced-motion: reduce) { .sched-dot { animation: none !important; } }
.sched-state {
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--sc);
}
.sched-count {
  margin-left: auto; font-size: 11.5px; font-weight: 700; color: var(--sc);
  font-variant-numeric: tabular-nums; opacity: .85;
}
.sched-label {
  display: block; font-size: 11.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 6px;
}
.sched-input { position: relative; }
.sched-input > svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; pointer-events: none;
  fill: none; stroke: var(--muted); stroke-width: 1.7; stroke-linecap: round;
}
.sched-input input {
  width: 100%; font-family: var(--body); font-size: 14px; color: var(--ink);
  background: var(--ad-field, #fff);
  border: 1px solid var(--gt-hair, rgba(15,46,34,.14)); border-radius: 10px;
  padding: 11px 12px 11px 38px;
}
.sched-input input:focus {
  outline: none; border-color: var(--sc);
  box-shadow: 0 0 0 3px var(--sc-bg);
}
/* The native picker glyph is a black square on the dark theme and sits under
   our own calendar icon on the light one. */
.sched-input input::-webkit-calendar-picker-indicator {
  opacity: .55; cursor: pointer; filter: var(--sched-pick, none);
}
.sched-input input:hover::-webkit-calendar-picker-indicator { opacity: 1; }

.sched-presets { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.sched-presets button {
  cursor: pointer; border-radius: 100px; padding: 6px 12px;
  font-family: var(--body); font-size: 12px; font-weight: 700;
  color: var(--muted); background: transparent;
  border: 1px solid var(--gt-hair, rgba(15,46,34,.14));
  transition: color .14s var(--ease), border-color .14s var(--ease), background .14s var(--ease);
}
.sched-presets button:hover {
  color: var(--sc); border-color: var(--sc); background: var(--sc-bg);
}
.sched-hint {
  display: block; margin-top: 11px; font-size: 12px; line-height: 1.5;
  color: var(--muted);
}
/* The picker glyph inverts on the green theme, where it is drawn black. */
html[data-theme="dark"] .sched { --sched-pick: invert(1) brightness(1.6); }
@media (prefers-color-scheme: dark) {
  html[data-theme="auto"] .sched, html:not([data-theme]) .sched { --sched-pick: invert(1) brightness(1.6); }
}

/* The countdown on a scheduled story's row in the list. Amber, like the chip
   beside it and the scheduler card in the editor, so "waiting on a clock" is
   one colour wherever it appears. */
.np-in { font-weight: 700; color: var(--ad-gold-fg, #7a5410); white-space: nowrap; }

/* ============================================================================
   COMPETITION COLOURS
   ----------------------------------------------------------------------------
   ASKED FOR BY THE CLIENT: four competitions rendered as four identical grey
   chips over four identical grey fixtures. On a league tipping two codes the
   slate interleaves them by kickoff, so AFL and AFLW arrive alternating down
   one list — one letter apart in the heading, and that letter decides which
   ladder the round counts toward. Colour is the only difference that survives
   being read at a glance.

   THE SPLIT IS BY CODE, NOT BY SEX. Each women's competition gets a shade of
   its own and each men's competition gets its own — AFLW is not "AFL, but
   pink", it is its own competition with its own colour, and the two women's
   codes are as different from each other as the two men's are. What the
   categories do is provide the FALLBACK, so a competition we do not name here
   (Super League, Super Netball) is coloured from the day it appears rather
   than rendering grey until somebody writes a rule for it.

   THREE TOKENS PER CODE, because these appear on two very different beds:
     --code      vivid. For the deep-green round-header bar.
     --code-ink  darkened. For text and fills on a cream or white card, where
                 the vivid version fails contrast.
     --code-on   what sits ON a --code-ink fill.
   The cards are cream in the green theme and white in the light one, so
   --code-ink serves both and only the deep bar needs the vivid one.

   Category rules come first and code rules after, deliberately: same
   specificity, so the specific code wins by being later. Do not reorder.
   ========================================================================= */
[data-cat="mens"]           { --code: #8FA3B8; --code-ink: #3E5468; --code-on: #FFFFFF; }
[data-cat="womens"]         { --code: #FF9E7A; --code-ink: #B34418; --code-on: #FFFFFF; }
[data-cat="representative"] { --code: #F4C64E; --code-ink: #8A6206; --code-on: #FFFFFF; }

/* Aussie Rules: the men's blue, the women's rose. */
[data-code="afl"]   { --code: #5AA9FF; --code-ink: #12558F; --code-on: #FFFFFF; }
[data-code="aflw"]  { --code: #FF8FB4; --code-ink: #B01F55; --code-on: #FFFFFF; }
/* Rugby League: the men's amber, the women's orchid. Deliberately far from
   the AFL pair — a member tipping all four sees four colours, not two pairs. */
[data-code="nrl"]   { --code: #FFB25C; --code-ink: #9A5A05; --code-on: #FFFFFF; }
[data-code="nrlw"]  { --code: #C79BFF; --code-ink: #6B34B8; --code-on: #FFFFFF; }
/* STATE OF ORIGIN GETS ITS OWN, and had to. It was falling through to the
   representative category's gold, which sits a few degrees from the NRL's
   amber — "why do the NRL and State of Origin have the same shade of colour,
   that should not be it". They are also the two most likely to appear in the
   same list, since Origin rounds belong to the NRL competition. Teal is the
   furthest thing on the wheel from the other four. */
[data-code="state-of-origin"] { --code: #3FD0C9; --code-ink: #0B6E70; --code-on: #FFFFFF; }
/* THE OTHER TWO SERIES THAT ACTUALLY EXIST. Super Netball and Super League are
   rows in the Series table and have been since the codes were seeded, and
   neither had a token — so anything keyed on their slug fell through to the
   lime, which is the app's own colour and not a competition's. Nobody noticed
   while the only things reading these were the filter chips, which those two
   rarely reach. The loader found it immediately: the Super Netball splash came
   up lime, the one code in the set wearing GoodTip's colour instead of its own.

   Coral for the netball, which is the furthest thing left from the five
   football hues; a deeper slate-blue for Super League, which has to sit beside
   the NRL's amber and the AFL's sky without being mistaken for either. */
[data-code="super-netball"] { --code: #FF7A5C; --code-ink: #9E3418; --code-on: #FFFFFF; }
[data-code="super-league"]  { --code: #6E8CD8; --code-ink: #2F4A8F; --code-on: #FFFFFF; }
/* THE TWO STORY TAGS THAT ARE NOT CODES. A story is filed under News or Blog
   as well as, or instead of, a competition — and once every code on a story
   card wore its own colour, the two that did not read as unfinished. News
   takes GoodTip's own green, Blog a quiet slate: neither is a competition, and
   neither should look like a sixth one. Used only by the story tags
   (NewsPost.tag_pairs); no Series has either slug. */
[data-code="news"]  { --code: #7ED99A; --code-ink: #1F7A45; --code-on: #FFFFFF; }
[data-code="blog"]  { --code: #A9B8C8; --code-ink: #44586C; --code-on: #FFFFFF; }
/* And the men's fallback moved off teal in the same breath, or Super League
   would have arrived wearing Origin's colour. Five codes, five hues: AFL sky,
   AFLW rose, NRL amber, NRLW orchid, Origin teal — with slate and coral held
   back for anything we have not named. */

/* ---- the filter chips --------------------------------------------------
   Unselected: the code's colour on the rim and in the word, so the row reads
   as four competitions before anything is pressed. Selected: filled, which is
   what the client asked for — "those colours should also be the infill".

   "All competitions" has no code and keeps the lime it always had. It is not
   a competition, and giving it one of these colours would make it look like a
   fifth one. */
/* FILLED, ALL OF THEM. "On the filter let it be infill — with that button we
   should have that colour and let it be 100%." A tinted rim on a white pill is
   a hint that a colour exists; a filled pill IS the colour, and the row reads
   as four competitions before anything is pressed.

   Which one is selected then has to be carried by something other than fill,
   since fill is now spent: the chosen chip gets a ring drawn outside its own
   edge and the rest step back a little. Selection stays the loudest thing in
   the row without any chip going grey. */
.sfilter-chip[data-code] {
  background: var(--code-ink); border-color: var(--code-ink);
  color: var(--code-on); font-weight: 700;
}
.sfilter-chip[data-code]:hover { background: var(--code-ink); border-color: var(--code-ink); filter: brightness(1.12); }
.sfilter-chip[data-code]:not(.on) { opacity: .74; }
.sfilter-chip[data-code].on {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--code) 60%, transparent);
}
.sfilter-chip[data-code]:focus-visible { outline-color: var(--code-ink); }

/* ---- the round heading -------------------------------------------------
   This bar is --deep in both themes, so it takes the vivid token. The round
   NUMBER keeps the lime: it is the product's colour and the code's colour
   answering two different questions, and swapping the number to the code's
   shade as well would leave the bar with nothing on it that is GoodTip. */
.fxr-series.is-code {
  color: var(--code, rgba(232,228,213,.8));
  font-weight: 800; letter-spacing: .02em;
}
.fx-roundhead[data-code] { box-shadow: inset 0 -2px 0 color-mix(in srgb, var(--code) 55%, transparent); }

/* ---- the card ----------------------------------------------------------
   A tag in the meta row, and the leading edge.

   The left border already carries two meanings that outrank the competition —
   lime for a fixture you have picked, red for one in play — so those are
   restated here at a specificity that beats [data-code] rather than left to
   win on source order. Which competition a fixture belongs to matters less
   than whether it is live or whether you have tipped it. */
/* BIG ENOUGH TO READ ACROSS THE ROOM. "This part I feel this is too small for
   a user to know the competition... I want to see that it's NRL, have a nice
   share of that colour; if it's NRLW the same."

   10px uppercase on a 13% tint was a footnote about the single most
   consequential fact on the card — which competition's ladder this fixture
   counts toward, and in a two-code league that is the difference between the
   men's round and the women's. So: filled with the code's own colour rather
   than a wash of it, and sized to be read rather than found. */
.fxc-code {
  font-family: var(--mark); font-weight: 900; font-size: 13px;
  letter-spacing: .08em; text-transform: uppercase; line-height: 1;
  padding: 6px 12px; border-radius: 100px; flex: none;
  color: var(--code-on, #fff);
  background: var(--code-ink, var(--forest));
}
.fxc[data-code] { border-left-color: color-mix(in srgb, var(--code-ink) 45%, transparent); }
/* THE SIDE YOU PRESSED WEARS THE COMPETITION'S COLOUR, at 70%. Asked for
   exactly that way — "on the fixtures where we say this vs this let it be 70%
   infill, that button that I press should have that colour."

   70% and not 100% on purpose, and not only because it was asked for: the club
   name and crest sit on this fill, and a solid AFL blue behind dark ink is the
   point at which a pick stops being readable. Mixed toward white rather than
   dropped in opacity, so the tick in the corner keeps its own contrast.

   Lime was the old fill and stays as the fallback for a card with no
   competition on it — the confirmation screen renders some of these without
   a round attached. */
.fxc[data-code] .fxc-team.sel {
  background: color-mix(in srgb, var(--code) 70%, #fff);
  border-color: color-mix(in srgb, var(--code-ink) 55%, transparent);
}
.fxc[data-code] .fxc-team.sel .fxc-name { color: var(--ink); }
.fxc[data-code] .fxc-team.sel .fxc-tick { background: var(--code-ink); }
.fxc[data-code] .fxc-team.sel .fxc-tick svg { stroke: #fff; }
.fxc[data-code].is-picked { border-left-color: var(--lime); }
.fxc[data-code].phase-live { border-left-color: #c0392b; }

/* Light theme: the bar under the round heading stays deep green, so the vivid
   token still applies there. The chips and tags sit on white instead of cream,
   where the same --code-ink reads slightly hotter than it should; nothing else
   changes, so this is a tint adjustment rather than a second palette. */
/* THE CHIPS KEEP THEIR CODE'S COLOUR IN LIGHT MODE TOO.
   Reported as: "the colour is white — let them carry their colours in the white
   theme; second, the text should be visible, it's not."

   Both halves were one line. This rule repainted the chip near-white and left
   `color: var(--code-on)`, which is #FFFFFF — white type on a white chip, so
   AFL / AFLW / NRL / NRLW were four blank pills. The fill was overridden on the
   reasoning that --code-ink "reads slightly hotter on white than on cream",
   which is a tint judgement that cost the control its entire legibility.

   So the override goes: the chip is its code's ink in both themes, with white
   type on it, which is the same contrast the dark theme has always had. */
/* The tag is a solid fill in both themes now, so there is nothing here to
   soften — it was a tint that needed a different bed and no longer is one. */

/* ============================================================================
   HOW IT FINISHED — the score on the club, and who won
   ----------------------------------------------------------------------------
   ASKED FOR: results on a past round, readable whether or not you tipped it.
   The score line in the state strip says "12–8"; this says which of the two
   clubs those numbers belong to and which one won.
   ========================================================================= */
/* Inside the side now, not under it — so it is placed in the button's own
   grid rather than floated below the card. Column 2, under the club name,
   which is where the eye already is once it has read who this side is. */
.fxc-team.has-outcome { grid-template-rows: auto auto; align-items: start; }
.fxc-team.has-outcome .fxc-crest { grid-row: 1 / 3; align-self: center; }
.fxc-outcome {
  grid-column: 2; display: flex; align-items: baseline; gap: 7px; margin-top: 5px;
  padding: 3px 9px; border-radius: 9px; width: fit-content;
  background: rgba(15,46,34,.055);
}
.fxc-outcome .fxc-pts {
  font-family: var(--score, var(--mark)); font-weight: 900; font-size: 17px;
  line-height: 1; color: var(--forest);
}
.fxc-outcome i {
  font-style: normal; font-family: var(--mark); font-weight: 800; font-size: 9.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.fxc-outcome.is-won { background: rgba(46,125,50,.12); }
.fxc-outcome.is-won .fxc-pts { color: #1f6b28; }
.fxc-outcome.is-won i { color: #1f6b28; }
.fxc-outcome.is-drew i { color: #8a6206; }
.fxc-outcome.is-lost { opacity: .72; }
/* .is-dim fades the whole side, including the score that is now inside it.
   A result nobody can read is not a result, so the losing side is dimmed
   less than it was and its outcome pulls back to full. */
.fxc-team.is-dim.has-outcome { opacity: .78; }
.fxc-team.is-dim.has-outcome .fxc-outcome { opacity: 1; }

/* ============================================================================
   ROLE BADGES — what you are in this organisation
   ----------------------------------------------------------------------------
   The nav already drops the Manage menu when you switch into an organisation
   you do not run. Nothing said so on the way in, and a quieter nav reads as a
   page that failed to load rather than as a different role. See
   partials/_role_badge.html.
   ========================================================================= */
/* The chip's marker is icon-only at every width (see app_base.html: the word
   crowded out the name it was annotating). The panel's badges keep theirs —
   there is room in a list row, and "Admin" read before a switch is the whole
   value of it. */
.an-ctx-role { gap: 0; padding: 4px 6px; }
.gt-role, .an-ctx-role {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-family: var(--mark); font-weight: 800; font-size: 9.5px;
  letter-spacing: .09em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 100px; white-space: nowrap;
}
.gt-role svg, .an-ctx-role svg { width: 10px; height: 10px; fill: currentColor; }
/* Admin wears the product's own colour; member wears the quiet one. Not two
   shades of the same thing — the point is that they are distinguishable
   without reading them. */
.gt-role.is-admin, .an-ctx-role.is-admin { background: var(--lime); color: var(--deep); }
.gt-role.is-member, .an-ctx-role.is-member {
  background: rgba(232,228,213,.16); color: rgba(232,228,213,.9);
}
/* On a cream list bed (the switcher rows are dark; the dashboard header is
   not) the member badge needs ink rather than cream. */
.gt-app .card .gt-role.is-member, .gt-app .app-head .gt-role.is-member {
  background: rgba(15,46,34,.09); color: var(--forest);
}
.an-ctx-row .gt-role { margin-left: auto; }
.an-ctx-head .gt-role { margin-left: 6px; vertical-align: middle; }

/* ============================================================================
   THE NAV, WITH THE ORGANISATION AT LEFT ZERO
   ----------------------------------------------------------------------------
   Two faults reported together: "these two are still overlapping, Admin /
   Dashboard", and "this part feels a little bit squeezed, make it wider".

   THE OVERLAP WAS A FLEX ROW THAT COULD NOT SHRINK. .an-ctx was flex:none, the
   link row is nowrap by design, and .an-right takes margin-left:auto — so once
   the content exceeded the bar there was nothing allowed to give, and the row
   overflowed instead of compressing. The cluster on the right then painted over
   the last link, which is the "Admin" sitting on top of "Dashboard". Two fixes,
   because either alone leaves a band where it still happens: everything in the
   row may now shrink (min-width:0 is the part that actually enables it — a flex
   item's default min-width:auto is what pins it to its content), and the links
   collapse into the burger at a width the bar can honestly hold rather than one
   that assumed a narrower chip.

   AND THE CHIP IS THE FIRST THING IN THE ROW, holding the position the wordmark
   used to. It gets the width that was being rationed: the organisation and the
   group are what every other item in the bar is relative to.
   ========================================================================= */
.app-nav .an-inner > * { min-width: 0; }
.an-ctx { margin-left: 0; }
/* Roomier than the 460px it was given as a squeezed middle child, and a floor
   so it cannot be compressed to an ellipsis by the row around it. */
.gt-v2 .an-ctx-btn, .an-ctx-btn { max-width: 520px; min-width: 0; }
.an-ctx-org { min-width: 0; }
/* THE CHIP GIVES WAY, NOT THE LINKS. Reversed from the first attempt, which
   collapsed the whole link row into the burger at 1320px to buy the chip its
   width — and took Manage and Admin off the bar with it on every laptop under
   that. Reported straight back: "I have picked an organisation that I am an
   admin, I cannot see the admin and the manage."

   The links are destinations and their absence is unexplainable; the chip is a
   name that can ellipsise and still be read, and it carries the full one in its
   title attribute either way. So the chip absorbs the pressure, down to a floor
   wide enough to stay a control, and the row collapses at 1180px exactly where
   it always did. */
.gt-v2 .an-ctx, .an-ctx { flex: 1 1 auto; min-width: 118px; }
.gt-v2 .an-links, .an-links { flex: none; }
.an-right { flex: none; }
/* Between 1180 and 1400 the row is at its tightest — every link, the account
   cluster and the chip. The wordmark shortens to its mark there so the two
   things that cannot shorten do not have to. */
@media (min-width: 1181px) and (max-width: 1400px) {
  .gt-v2 .an-brand, .an-brand { font-size: 0; letter-spacing: 0; }
  .gt-v2 .an-brand::before, .an-brand::before { content: "G"; font-size: 20px; color: #fff; }
  .gt-v2 .an-brand::after,  .an-brand::after  { content: "T"; font-size: 20px; color: var(--lime); }
}

/* ============================================================================
   THE NAV CHIP AT NARROW WIDTHS
   ----------------------------------------------------------------------------
   It used to be `display: none` below 900px. Which organisation you are in and
   what you are in it are the two things the chip exists to say, and hiding it
   is hiding the answer on exactly the screens where the rest of the nav has
   already collapsed into a burger and there is nothing else left to say it.

   The client's instruction was explicit — "even if it means we push the GOODTIP
   logo at the left to make sure that is captured". So the wordmark gives way
   instead: below 900px it renders as GT, and below 560px the chip drops the
   organisation's name and keeps the group's, which is the half that decides
   which ladder a tip lands on.
   ========================================================================= */
@media (max-width: 900px) {
  .an-ctx { display: inline-flex; margin-left: 6px; min-width: 0; }
  .an-ctx-btn { max-width: min(62vw, 340px); padding: 6px 10px; font-size: 13.5px; }
  /* The mark, not the wordmark, on a phone: the league name beside it is what
     the reader needs and GOODTIP is six characters of a product they have
     already signed in to. .gt-v2 in the selector because the app shell sets
     `.gt-v2 .an-brand` outside any media query, and that beats a bare
     `.an-brand` however late in the file it appears. */
  .gt-v2 .an-brand, .an-brand { font-size: 0; letter-spacing: 0; }
  .gt-v2 .an-brand::before, .an-brand::before { content: "G"; font-size: 19px; color: #fff; }
  .gt-v2 .an-brand::after,  .an-brand::after  { content: "T"; font-size: 19px; color: var(--lime); }
  .gt-v2 .app-nav .an-inner, .app-nav .an-inner { gap: 10px; padding: 0 14px; }
}
@media (max-width: 560px) {
  /* ONLY when there is a group to fall back on. Written first as a flat
     `.an-ctx-org { display: none }`, which on a phone in an organisation with
     no groups left the chip reading "Member" and nothing else — the name of
     the place you are in removed in the name of fitting the chip that exists
     to name it. :has() makes the rule say what was meant: drop the org when
     the group is already naming where you are. */
  .an-ctx-btn:has(.an-ctx-group) .an-ctx-org,
  .an-ctx-btn:has(.an-ctx-group) .an-ctx-sep { display: none; }
  .an-ctx-role { padding: 3px 5px; }
}

/* ============================================================================
   THE DOCKED CONFIRM
   ----------------------------------------------------------------------------
   ASKED FOR: "after I scroll down and I have been making tips, I have to
   scroll up again to confirm." The real button stays where it is — beside the
   count it describes — and this follows you down. It presses the real one, so
   there is still exactly one way to submit a slate.
   ========================================================================= */
.slip-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(180deg, rgba(11,32,23,0), rgba(11,32,23,.92) 42%);
  pointer-events: none;
}
.slip-dock-in {
  max-width: 640px; margin: 0 auto; pointer-events: auto;
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 10px 12px 10px 18px; border-radius: 100px;
  background: var(--deep); border: 1px solid rgba(200,241,53,.28);
  box-shadow: 0 18px 40px -20px rgba(0,0,0,.75);
}
.slip-dock .sd-count {
  font-family: var(--mark); font-weight: 700; font-size: 12px;
  letter-spacing: .1em; text-transform: uppercase; color: rgba(232,228,213,.75);
}
.slip-dock .sd-count b { font-size: 17px; color: var(--lime); margin-right: 5px; letter-spacing: 0; }
.slip-dock .abtn { flex: none; }
/* Nothing behind it is reachable through the gradient, but the gradient itself
   must not eat clicks on the fixtures under it — hence pointer-events on the
   pill only. Printing drops it with the rest of the furniture. */
@media print { .slip-dock { display: none !important; } }

/* Light theme, nav only. The bar is pale here, so the member badge's cream-on
   translucent-cream becomes invisible — the exact failure this codebase keeps
   hitting when one set of colours is asked to serve a dark bar and a light one.
   Admin keeps the lime pill: it reads on both. */
html[data-theme="light"] .gt-app .an-ctx-role.is-member {
  background: rgba(15,46,34,.08); color: #2C4C3B;
}
html[data-theme="light"] .gt-app .an-ctx-btn { border-color: rgba(15,46,34,.14); background: rgba(15,46,34,.03); }
html[data-theme="light"] .gt-app .an-ctx-org { color: #46554B; opacity: 1; }

/* ============================================================================
   THE FIXTURE'S META ROW, READ AT A GLANCE
   ----------------------------------------------------------------------------
   "That place is too tiny... even that Open and Closed and I think Ongoing, too
   small for one to see."

   The row carries four facts and every one of them was set below body size: the
   competition, when and where the game is, and whether it is open to tip. Those
   are the things somebody scanning a round is scanning FOR — the club names
   below them are already 15px and bold, so the card was loudest about the part
   the reader already knows and quietest about the parts they came to find.

   The state chip gets the same treatment as the code tag: filled rather than
   tinted, and sized to be legible. Three states that must never be mistaken for
   one another, so they are three colours and not three shades — open is the
   product's lime, in play is the live red the card's edge already uses, and
   finished is a settled grey.
   ========================================================================= */
.fxc-meta { gap: 10px 12px; font-size: 13.5px; }
.fxc-when { font-size: 13.5px; font-weight: 700; }
.fxc-venue { font-size: 13px; }
.fxc-venue svg { width: 14px; height: 14px; }

.fxc-state {
  font-size: 11.5px; font-weight: 900; letter-spacing: .07em;
  padding: 5px 11px; line-height: 1;
}
/* Open is an invitation and wears the colour the product uses for "do this".
   It was ink on a 7% wash, which beside a filled competition tag read as the
   disabled one of the two. */
.fxc-state.is-open   { background: var(--lime); color: var(--deep); }
.fxc-state.is-tipped { background: var(--deep); color: var(--lime); }
.fxc-state.is-missed { background: #9a2b2b; color: #fff; }
.fxc-state.is-shut   { background: rgba(15,46,34,.16); color: #41523f; }
.fxc-state.is-hit    { background: #1f6b28; color: #fff; }
.fxc-state.is-miss   { background: rgba(154,43,43,.16); color: #8f2626; }
/* A game in play is the one state that should catch the eye unprompted, so it
   is the only one that moves. Held still for anybody who has asked for that. */
.fxc.phase-live .fxc-state { background: #c0392b; color: #fff; animation: fxcLivePulse 2.2s var(--ease) infinite; }
@keyframes fxcLivePulse { 0%, 100% { opacity: 1; } 50% { opacity: .62; } }
@media (prefers-reduced-motion: reduce) {
  .fxc.phase-live .fxc-state { animation: none; }
}

/* The state chip is pushed right by margin-left:auto and the code tag now leads
   the row; on a narrow card the two would collide over the date, so the row
   wraps with the tag and the chip holding the ends of the first line. */
@media (max-width: 560px) {
  .fxc-meta { row-gap: 8px; }
  .fxc-when { width: 100%; order: 3; }
  .fxc-venue { width: 100%; order: 4; }
  .fxc-code { order: 1; }
  .fxc-state { order: 2; }
}

/* ============================================================================
   ALREADY CONFIRMED — the slip bar with nothing to do
   ----------------------------------------------------------------------------
   "If I have already confirmed my picks and we are into the weekend of games,
   do I need Confirm my picks still sitting there?"

   This stands where the button stood, so the bar keeps its shape and does not
   jump when a pick is changed and the button comes back. Quiet on purpose: it
   is a receipt, not a call to action, and the only loud thing in this bar
   should be a thing worth pressing.
   ========================================================================= */
.slip-allin {
  display: inline-flex; align-items: center; gap: 10px; flex: none;
  padding: 10px 16px; border-radius: 100px;
  background: rgba(31,107,40,.10); border: 1px solid rgba(31,107,40,.24);
}
.slip-allin svg {
  width: 17px; height: 17px; flex: none; fill: none; stroke: #1f6b28;
  stroke-width: 3.2; stroke-linecap: round; stroke-linejoin: round;
}
.slip-allin b {
  font-family: var(--mark); font-weight: 800; font-size: 13.5px;
  letter-spacing: .01em; color: #1f6b28; line-height: 1.2;
}
.slip-allin i {
  font-style: normal; font-size: 12px; color: var(--muted); line-height: 1.2;
}
@media (max-width: 700px) {
  /* The second line is the part that can go: "all your tips are in" is the
     message, "change any pick until the round locks" is the reassurance, and
     the bar above it already says so. */
  .slip-allin i { display: none; }
}
/* On the deep sheet the forest green disappears into the background, so the
   receipt takes the lime the rest of that surface uses for "done". */
.page-deep .slip-allin, html[data-theme="dark"] .gt-app .slip-allin {
  background: rgba(200,241,53,.12); border-color: rgba(200,241,53,.3);
}
.page-deep .slip-allin b, html[data-theme="dark"] .gt-app .slip-allin b { color: var(--lime); }
.page-deep .slip-allin svg, html[data-theme="dark"] .gt-app .slip-allin svg { stroke: var(--lime); }
.page-deep .slip-allin i, html[data-theme="dark"] .gt-app .slip-allin i { color: rgba(232,228,213,.66); }

/* ============================================================================
   THE COMPETITION FILTER on the ranking pages, and the tables it colours
   ----------------------------------------------------------------------------
   "Even on the leaderboard, let's say I tip in all competitions — a nice way to
   filter, so I can see who is leading in the NRL... and on the ladder we have
   the dropdown, which that button is too small and far end. Let the table also
   pick the colour of that competition; that would give a vibe of distinction."

   Chips and a select, both real and both posting the same URL — see
   partials/comp_filter.html. The chips are the control: full width, in the
   code's own colour, pressable without aiming. The select sits beside them for
   a list-picker and for narrow screens, where a row of chips would wrap into a
   block taller than the thing it filters.
   ========================================================================= */
.compfilter {
  display: flex; align-items: center; gap: 14px 18px; flex-wrap: wrap;
  margin: 0 0 20px;
}
.cf-chips { display: flex; gap: 9px; flex-wrap: wrap; flex: 1 1 auto; }
.cf-chip {
  font-family: var(--mark); font-weight: 800; font-size: 13.5px;
  letter-spacing: .05em; text-transform: uppercase;
  padding: 10px 18px; border-radius: 100px; text-decoration: none;
  white-space: nowrap; border: 1.5px solid transparent;
  transition: box-shadow .15s var(--ease), filter .15s var(--ease), opacity .15s var(--ease);
}
/* Filled, like the dashboard's filter and for the same reason: a tinted rim is
   a hint that a colour exists, a filled pill is the colour. Selection is a ring
   drawn outside the edge, because fill is already spent saying which code this
   is. */
.cf-chip[data-code] {
  background: var(--code-ink); border-color: var(--code-ink); color: var(--code-on, #fff);
}
.cf-chip[data-code]:not(.on) { opacity: .72; }
.cf-chip[data-code]:hover { opacity: 1; filter: brightness(1.12); }
.cf-chip[data-code].on {
  opacity: 1; box-shadow: 0 0 0 3px color-mix(in srgb, var(--code) 60%, transparent);
}
/* "All competitions" is not a competition and must not look like a fifth one,
   so it keeps the product's own colours rather than borrowing a code's. */
.cf-chip.cf-all { background: rgba(232,228,213,.12); color: var(--cream); border-color: rgba(232,228,213,.2); }
.cf-chip.cf-all:hover { background: rgba(232,228,213,.2); }
.cf-chip.cf-all.on { background: var(--lime); color: var(--deep); border-color: var(--lime); box-shadow: none; }
.cf-chip:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }

.cf-pick { display: flex; align-items: center; gap: 9px; flex: none; }
.cf-lab {
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase; color: rgba(232,228,213,.6);
}
/* Below this the chips would wrap into a block taller than the table they
   filter, so the select becomes the whole control. */
@media (max-width: 720px) {
  .compfilter .cf-chips { display: none; }
  .cf-pick { flex: 1 1 auto; }
  .cf-pick .gt-select { flex: 1 1 auto; }
}
/* And above it the select is the alternate, not the headline — the chips say
   what the choices ARE, which a closed dropdown cannot. */
@media (min-width: 721px) {
  .cf-lab { display: none; }
}
/* These pages are the deep sheet; on the light theme the "all" chip and the
   label need ink rather than cream. */
html[data-theme="light"] .gt-app .cf-chip.cf-all { background: rgba(15,46,34,.07); color: #2C4C3B; border-color: rgba(15,46,34,.14); }
html[data-theme="light"] .gt-app .cf-chip.cf-all.on { background: var(--forest); color: #fff; border-color: var(--forest); }
html[data-theme="light"] .gt-app .cf-lab { color: var(--muted); }

/* ---- the table wears the competition ------------------------------------
   Restrained on purpose. The header band and the rank discs are the two places
   the eye lands first and neither carries data of its own, so colouring them
   says which competition this is without competing with the numbers. The rows
   stay cream: a table of coloured rows is a table you cannot read down. */
.gt-board[data-code] .gb-head { background: var(--code-ink); }
.gt-board[data-code] .gb-rank { background: var(--code-ink); }
.gt-board[data-code] .gb-row:hover { border-color: color-mix(in srgb, var(--code-ink) 45%, transparent); }
/* Your own row keeps its lime edge — where you are outranks which comp it is —
   but the tint under it follows the code so the row belongs to this table. */
.gt-board[data-code] .gb-row.me {
  background: color-mix(in srgb, var(--code) 16%, #fff);
  border-color: color-mix(in srgb, var(--code-ink) 50%, transparent);
  box-shadow: inset 4px 0 0 var(--code-ink);
}
.gt-board[data-code] .gb-row.me .gb-rank { background: var(--code-ink); }
.gt-board[data-code] .gb-correct { color: var(--code-ink); }
/* The ladder's own numeric column stripe, keyed the same way. */
.gt-board.ladder[data-code] .lad-col { background: color-mix(in srgb, var(--code) 9%, transparent); }

/* ============================================================================
   THE RANKING PAGES: hierarchy, a readable table, and a real podium
   ----------------------------------------------------------------------------
   Four reports, all on the leaderboard and the ladder.
   ========================================================================= */

/* ---- the filter bar ------------------------------------------------------
   Round and competition, side by side and the same weight, because they are
   the same kind of decision: which slice of the season, and which competition.
   They sat as a small select in the header's top-right corner and a chip row
   further down, which read as one control and one decoration. */
.boardbar {
  display: flex; align-items: center; gap: 14px 22px; flex-wrap: wrap;
  margin: 0 0 22px; padding: 14px 16px;
  background: rgba(255,255,255,.05); border: 1px solid rgba(232,228,213,.12);
  border-radius: 16px;
}
.boardbar .compfilter { margin: 0; flex: 1 1 340px; }
.bb-round { display: flex; align-items: center; gap: 8px; flex: none; }
.bb-lab {
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase; color: rgba(232,228,213,.6);
  margin-right: 2px;
}
/* Big enough to hit without aiming — the whole point of arrows over a
   dropdown is that stepping costs one press and no reading. */
.bb-arrow {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center;
  border-radius: 11px; background: rgba(232,228,213,.1);
  border: 1px solid rgba(232,228,213,.16); color: var(--cream);
  text-decoration: none; transition: background .14s var(--ease), border-color .14s var(--ease);
}
.bb-arrow:hover { background: var(--lime); border-color: var(--lime); color: var(--deep); }
.bb-arrow svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
/* The end of the sequence is shown, not removed: a control that disappears at
   the edges makes the row jump and leaves you wondering what you pressed. */
.bb-arrow.is-off { opacity: .3; pointer-events: none; }
.bb-select { min-width: 148px; }
@media (max-width: 720px) {
  .boardbar { padding: 12px 13px; }
  .bb-round { flex: 1 1 100%; }
  .bb-select { flex: 1 1 auto; }
}
html[data-theme="light"] .gt-app .boardbar { background: rgba(15,46,34,.04); border-color: rgba(15,46,34,.1); }
html[data-theme="light"] .gt-app .bb-lab { color: var(--muted); }
html[data-theme="light"] .gt-app .bb-arrow { background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.12); color: #2C4C3B; }

/* The ladder's chips are the only control on that page, so they sit centred
   under a centred title rather than hugging an edge nothing else uses. */
.compfilter.is-centred { justify-content: center; }
.compfilter.is-centred .cf-chips { flex: 0 1 auto; justify-content: center; }

/* ---- the context that used to flank the table ---------------------------
   Was a 320px sticky sidebar, which took a third of the width from the four
   columns the page exists to show. Under the board it is a row of cards. */
.gt-side.board-foot {
  position: static; margin-top: 26px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* ---- the table, at a size it can be read at -----------------------------
   "Make them bold, and a little bit larger for me to be able to read it."
   Column heads were 11.5px at weight 700 and the names 15px, on a table that
   is now the full width of the page. */
.gt-board .gb-head {
  font-size: 13px; font-weight: 800; letter-spacing: .1em;
  padding: 16px 22px;
}
.gt-board .gb-row { padding: 15px 22px; }
.gt-board .gb-name { font-size: 16.5px; font-weight: 700; }
.gt-board .gb-correct { font-size: 16px; }
.gt-board .gb-pts { font-size: 19px; }
.gt-board .gb-rank { width: 38px; height: 38px; font-size: 15px; }
.gt-board .gb-av { width: 38px; height: 38px; font-size: 14px; }

/* THE LADDER'S COLUMN HEADS WERE DARK ON DARK, and have been since the header
   band went green — .lad-num sets `color: var(--ink)` and it beats the cream
   .gb-head sets, so P W L D % Pts were painted in near-black on a near-black
   bar. Reported as "the titles are not being seen with the colour background",
   which is exactly what it is; the competition colour only made it easier to
   notice. They inherit now, so they follow whatever the band is. */
.gt-board.ladder .lad-head .lad-num { color: inherit; font-size: 13px; font-weight: 800; }
.gt-board.ladder .lad-num { font-size: 15.5px; }
.gt-board.ladder .lad-pts { font-size: 17px; }
/* The numeric columns widen with the figures in them — 46px was set for
   14.5px text and clips a three-digit percentage at 15.5px. The club name
   column is minmax(0,1fr) and absorbs the difference. */
.gt-board.ladder .lad-head,
.gt-board.ladder .lad-row { grid-template-columns: 46px minmax(0,1fr) repeat(6, 54px); }
/* The column stripe belongs to the ROWS. Over the header band it was a wash of
   tint across a colour that is already carrying meaning, which is what made
   the headings muddy on top of being the wrong colour. */
.gt-board.ladder[data-code] .lad-col { background: transparent; }
.gt-board.ladder[data-code] .gb-rows .lad-col::before {
  background: color-mix(in srgb, var(--code) 11%, transparent);
}
.gt-board.ladder[data-code] .lad-head .lad-col::before { background: rgba(255,255,255,.07); }

/* ---- gold, silver, bronze ------------------------------------------------
   "Number 2 and 3 have the same shade of colour, why?" Because the cards were
   #E4EDE6 and #E7EFD8 — two greens a couple of percent apart, with the medal
   colours confined to the little rank disc. The metals now run the whole card:
   its ground, its border and its glow. Second and third are a cool grey and a
   warm copper, which are opposite in temperature and cannot be confused at a
   glance or by anyone reading them in greyscale. */
.gt-pod.p2 {
  background: linear-gradient(165deg, #FBFCFD, #DDE2E8 70%, #C6CCD4);
  border-color: rgba(154,160,168,.7);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 12px 26px -22px rgba(60,68,78,.7);
}
.gt-pod.p3 {
  background: linear-gradient(165deg, #FDF3EA, #F0D6BE 70%, #DDB68E);
  border-color: rgba(178,126,78,.65);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 12px 26px -22px rgba(120,78,40,.7);
}
.gt-pod.p2 .gp-name { color: #2F3740; }
.gt-pod.p3 .gp-name { color: #4A3220; }
.gt-pod.p2 .gp-pts { color: #3D454E; }
.gt-pod.p3 .gp-pts { color: #6B4726; }
/* The medal is named as well as coloured, for the same reason the state chips
   on a fixture are three colours and not three shades — and so the distinction
   survives a screenshot, a printout and colour blindness. */
.gt-pod .gp-medal {
  display: block; font-family: var(--mark); font-weight: 800; font-size: 10px;
  letter-spacing: .16em; text-transform: uppercase; margin-bottom: 8px;
}
.gt-pod.p1 .gp-medal { color: #E4B33C; }
.gt-pod.p2 .gp-medal { color: #6C757F; }
.gt-pod.p3 .gp-medal { color: #91653A; }

/* ============================================================================
   THE BOARDS, SECOND PASS
   ========================================================================= */

/* ---- centred page heads --------------------------------------------------
   There is nothing beside these any more — both filters moved to their own
   bars — so a left-aligned title with empty space to its right reads as a
   layout that lost something rather than one that was designed. */
.gt-head.is-centred { justify-content: center; text-align: center; }
.gt-head.is-centred > div { max-width: 62ch; }
.gt-head.is-centred .gh-lead, .gt-head.is-centred .gh-sub { margin-left: auto; margin-right: auto; }
/* ---- THE ORGANISATION OUTRANKS THE WORD ---------------------------------
   "This AquaFlow Water Co · AFL + NRL 2026 should be bigger than the bottom
   text, that is Leaderboard."

   The two lines swap jobs, not places. The organisation and its codes are what
   the member came to look at and the only line that differs between one comp
   and the next; "Leaderboard" labels the screen, which the nav has already
   said. So the organisation takes the display face at title size and the word
   takes the small lime capitals the organisation used to wear — the same two
   colours and the same order, with the emphasis reversed.

   Scoped to .is-orgfirst rather than changed on .gt-head, because the Wall, the
   Ladder and My Tips all use this header and the word IS the subject on those.

   `.sheet-page .gt-head .gh-eyebrow` paints the eyebrow lime at 14px and sits
   later in this file than the base rules, so these carry .sheet-page too — a
   three-class selector would lose to it however far down it were written. */
.sheet-page .gt-head.is-orgfirst .gh-eyebrow,
.gt-head.is-orgfirst .gh-eyebrow {
  font-family: var(--display); font-weight: 400;
  /* SMALLER THAN THE FIRST ATTEMPT, TO KEEP IT ON ONE LINE — "this should only
     be one line, to make it neat, so reduce the size a little bit."

     At 44px, "AquaFlow Water Co · AFL + NRL 2026" ran past the 62ch cap that
     .gt-head.is-centred puts on the title column and broke over two lines,
     which is exactly what an eyebrow must not do: it is one fact, and a fact
     split across two lines reads as two. Thirty is where a name of that length
     and four codes still fit the column on a laptop. */
  font-size: clamp(19px, 2.6vw, 30px);
  letter-spacing: -.015em; text-transform: none; line-height: 1.15;
  color: #fff; opacity: 1;
}
.sheet-page .gt-head.is-orgfirst h1,
.gt-head.is-orgfirst h1 {
  font-family: var(--mark); font-weight: 700;
  font-size: clamp(14px, 1.7vw, 18px);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--lime); margin-top: 12px;
}
.gt-head.is-orgfirst .gh-lead { margin-top: 8px; }
/* No green is left under these in the light theme. */
html[data-theme="light"] .gt-app.sheet-page .gt-head.is-orgfirst .gh-eyebrow { color: var(--lt-ink); }
html[data-theme="light"] .gt-app.sheet-page .gt-head.is-orgfirst h1 { color: var(--lt-accent-ink); }

/* ---- THE SENTENCE THAT SAYS WHOSE BOARD THIS IS, CENTRED -----------------
   Only the line. The segmented control below it scrolls sideways when an
   organisation has a dozen groups, and centring a scrolling track leaves its
   first option off the left edge with nothing to scroll back with. */
.room-switch.is-centred .rw-top {
  justify-content: center; text-align: center;
}

/* THE PAGE'S ACTION, between the sentence and the rooms, hard right.
   "At the furthest right, not centre." Its own row rather than a corner of
   .rw-top, so the sentence keeps the full width to wrap in and the button
   cannot be pushed under it on a narrow screen. */
.room-switch .rw-action {
  display: flex; justify-content: flex-end; margin: 0 0 10px;
}
/* On a phone the far right of a 320px column is nowhere useful, and a button
   with empty space either side of it reads as something that failed. */
@media (max-width: 620px) {
  .room-switch .rw-action > .abtn { flex: 1; justify-content: center; }
}

/* ---- AN ACTION AFTER THE ROOM, NOT INSIDE THE HEADER ---------------------
   "That button should come after that line that I have said — have it centred."

   Outside .gt-head, so it needs its own row: the room switcher now stands
   between the header and this, and a child of the header cannot be drawn below
   something that is not in it. */
.gh-actions.gh-after-room {
  display: flex; justify-content: center; align-items: center; gap: 12px;
  margin: 0 0 24px;
}
/* On a phone a button that is 40% of the width with empty space either side of
   it reads as something that failed to load. */
@media (max-width: 620px) {
  .gh-actions.gh-after-room > .abtn { flex: 1; justify-content: center; }
}
/* "Waita Realty Group · 2026" in 12.5px capitals over a 40px title made the
   league look like a footnote to the word Ladder, when the league is the whole
   point of the page. Set at the title's own scale, one step down. */
.gt-head .gh-eyebrow.is-title {
  font-family: var(--display); font-size: clamp(22px, 3.1vw, 32px);
  letter-spacing: -.01em; text-transform: none; font-weight: 400;
  color: var(--forest); line-height: 1.1;
}
.gt-head .gh-eyebrow.is-title + h1 { margin-top: 2px; }

/* ---- the round strip -----------------------------------------------------
   Five rounds at a time, the one you are on filled, and a page-at-a-time walk
   backwards through the season. See partials/round_strip.html. */
.rstrip { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rs-showing {
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .1em; text-transform: uppercase; color: rgba(232,228,213,.6);
  margin-right: 4px;
}
.rs-step {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px;
  border-radius: 10px; text-decoration: none; font-size: 12.5px; font-weight: 700;
  color: var(--cream); background: rgba(232,228,213,.1);
  border: 1px solid rgba(232,228,213,.16);
}
.rs-step:hover { background: rgba(232,228,213,.2); }
.rs-step svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.rs-step.is-off { opacity: .3; pointer-events: none; }
.rs-nums { display: flex; gap: 6px; }
/* Square, and big enough to hit — these are the targets, the rest of the strip
   is the frame around them. */
.rs-num {
  min-width: 42px; height: 38px; padding: 0 10px; display: grid; place-items: center;
  border-radius: 10px; text-decoration: none;
  font-family: var(--score); font-weight: 800; font-size: 15px;
  color: var(--cream); background: rgba(232,228,213,.08);
  border: 1px solid rgba(232,228,213,.14);
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s var(--ease);
}
.rs-num:hover { background: rgba(232,228,213,.2); }
/* "On the round like 26 it must be highlighted so I know I am there." Filled
   and lime, which is the one colour on these pages that means "this is where
   you are" — not an outline, which at this size is a hint rather than a state. */
.rs-num.on {
  background: var(--lime); border-color: var(--lime); color: var(--deep);
  box-shadow: 0 0 0 3px rgba(200,241,53,.22);
}
.rs-all {
  margin-left: 4px; font-size: 12.5px; font-weight: 700; color: var(--lime);
  text-decoration: none; padding: 8px 10px;
}
.rs-all:hover { text-decoration: underline; }
.boardbar-round.is-centred { justify-content: center; }
.ladder-asat {
  text-align: center; margin: -8px 0 18px; font-size: 13.5px;
  color: rgba(232,228,213,.72);
}
.ladder-asat a { color: var(--lime); font-weight: 700; text-decoration: none; }
html[data-theme="light"] .gt-app .rs-showing { color: var(--muted); }
html[data-theme="light"] .gt-app .rs-step,
html[data-theme="light"] .gt-app .rs-num { background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.12); color: #2C4C3B; }
html[data-theme="light"] .gt-app .rs-num.on { background: var(--forest); border-color: var(--forest); color: #fff; box-shadow: 0 0 0 3px rgba(45,122,58,.2); }
html[data-theme="light"] .gt-app .rs-all { color: var(--forest); }
html[data-theme="light"] .gt-app .ladder-asat { color: var(--muted); }

/* ---- a real podium -------------------------------------------------------
   "Why does the size of the 3rd bronze look large? Make sure 1 gold is
   largest, have 2 larger than 3." The three cards were 1fr / 1.08fr / 1fr, so
   second and third were identical and only the middle stood out. They step
   down now — and they sit on a shared baseline, which is what makes a podium
   read as a podium rather than as three cards in a row. */
.gt-podium { grid-template-columns: 1fr 1.2fr .88fr; align-items: end; }
.gt-pod.p1 { padding: 40px 22px 34px; }
.gt-pod.p2 { padding: 32px 20px 27px; }
.gt-pod.p3 { padding: 25px 18px 21px; }
.gt-pod.p1 .gp-avwrap { width: 74px; height: 74px; }
.gt-pod.p2 .gp-avwrap { width: 60px; height: 60px; }
.gt-pod.p3 .gp-avwrap { width: 50px; height: 50px; }
.gt-pod.p2 .gp-name { font-size: 17px; }
.gt-pod.p3 .gp-name { font-size: 15px; }
.gt-pod.p1 .gp-pts { font-size: 40px; }
.gt-pod.p2 .gp-pts { font-size: 32px; }
.gt-pod.p3 .gp-pts { font-size: 27px; }
@media (max-width: 820px) {
  /* Stacked, so a size ladder means nothing — give them all the same room and
     let the medal word and colour carry the order. */
  .gt-podium { grid-template-columns: 1fr; align-items: stretch; }
  .gt-pod.p1, .gt-pod.p2, .gt-pod.p3 { padding: 26px 20px 22px; }
}

/* ---- the table's headings, on a coloured band ----------------------------
   "Those titles are not visible with the colours — make sure they are white
   when we have the competition colours, and make them a little bit larger."

   White outright rather than the cream the band used, because --code-ink runs
   from a mid blue to a deep plum and cream is a tint that reads as faded on
   the lighter of them. This also has to beat the light theme, which repaints
   .gb-head for a pale page and would otherwise put dark text on a dark
   competition colour — the same dark-on-dark this pass fixed on the ladder. */
.gt-board[data-code] .gb-head,
html[data-theme="light"] .gt-app .gt-board[data-code] .gb-head {
  color: #FFFFFF; font-size: 13.5px; font-weight: 800;
}
.gt-board[data-code] .gb-head span,
html[data-theme="light"] .gt-app .gt-board[data-code] .gb-head span,
.gt-board.ladder[data-code] .lad-head .lad-num,
html[data-theme="light"] .gt-app .gt-board.ladder[data-code] .lad-head .lad-num {
  color: #FFFFFF;
}

/* ---- the leaderboard's two new columns ----------------------------------- */
.gt-board .gb-head, .gt-board .gb-row {
  grid-template-columns: 62px minmax(0,1fr) 104px 94px 82px 84px;
}
.gt-board .gb-head .num, .gt-board .gb-acc, .gt-board .gb-behind { text-align: right; }
.gt-board .gb-acc {
  font-family: var(--score); font-weight: 800; font-variant-numeric: tabular-nums;
  font-size: 15.5px; color: var(--ink);
}
.gt-board .gb-behind {
  font-family: var(--score); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: 15px; color: var(--muted);
}
.gt-board .gb-acc i, .gt-board .gb-behind i { font-style: normal; color: var(--muted); opacity: .6; }
.gt-board[data-code] .gb-acc { color: var(--code-ink); }
/* Detail goes before the table does. Rank, name and points ARE the board; the
   rest is what you read when you have room for it. */
@media (max-width: 1000px) {
  .gt-board .gb-head, .gt-board .gb-row { grid-template-columns: 58px minmax(0,1fr) 96px 84px 80px; }
  .gt-board .h-behind, .gt-board .gb-behind { display: none; }
}
@media (max-width: 820px) {
  .gt-board .gb-head, .gt-board .gb-row { grid-template-columns: 54px minmax(0,1fr) 92px 78px; }
  .gt-board .h-acc, .gt-board .gb-acc { display: none; }
}
@media (max-width: 620px) {
  .gt-board .gb-head, .gt-board .gb-row { grid-template-columns: 44px minmax(0,1fr) 70px; }
  .gt-board .gb-correct, .gt-board .gb-head .h-correct { display: none; }
}

/* ---- the ladder's seventh column ----------------------------------------- */
.gt-board.ladder .lad-head,
.gt-board.ladder .lad-row { grid-template-columns: 46px minmax(0,1fr) repeat(7, 52px); }
.gt-board.ladder .lad-left { color: var(--muted); }
.gt-board.ladder .lad-left i { font-style: normal; opacity: .55; }
@media (max-width: 900px) {
  /* The first thing to go, because it is the one column that is about the
     future rather than about what has happened. */
  .gt-board.ladder .lad-head,
  .gt-board.ladder .lad-row { grid-template-columns: 46px minmax(0,1fr) repeat(6, 50px); }
  .gt-board.ladder .lad-left { display: none; }
}

/* ============================================================================
   STATS — a member's season, and a club's
   ----------------------------------------------------------------------------
   "I should have stats... some small visualisation, some charts and cards, that
   shows my performance generally: have I grown or have I dropped, what is my
   strongest competition, my weakness."

   Everything here is drawn from markup and inline SVG rather than a charting
   library: four small pictures of at most a season of rounds do not justify
   60-90KB, a second palette for the dark theme, and a render that happens after
   the page instead of with it. It also means every one of these prints, is
   readable to a screen reader, and takes the competition's colour from the same
   --code tokens as everything else.
   ========================================================================= */

/* ---- the four cards ------------------------------------------------------ */
.statcards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px; margin: 0 0 22px;
}
.statcard {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r-sm); padding: 18px 20px 16px;
  border-top: 3px solid var(--code-ink, var(--forest));
}
.sc-lab {
  display: block; font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}
.sc-fig {
  display: block; margin: 6px 0 4px; font-family: var(--score); font-weight: 900;
  font-size: 38px; line-height: 1; color: var(--code-ink, var(--ink));
  font-variant-numeric: tabular-nums;
}
.sc-fig small { font-size: 20px; margin-left: 1px; }
.sc-note { display: block; font-size: 12.5px; color: var(--muted); line-height: 1.35; }

/* ---- the sentence version of the trend -----------------------------------
   Said in words as well as drawn. A line going up is a shape; this is the
   claim, with the arithmetic behind it stated rather than left to be inferred
   from a slope. */
.trendline {
  margin: 0 0 22px; padding: 14px 18px; border-radius: 14px; font-size: 15px;
  background: rgba(232,228,213,.08); border: 1px solid rgba(232,228,213,.14);
  color: var(--cream);
}
.trendline b { font-weight: 800; }
.trendline.is-up { background: rgba(31,107,40,.16); border-color: rgba(31,107,40,.4); }
.trendline.is-down { background: rgba(154,43,43,.14); border-color: rgba(154,43,43,.34); }
html[data-theme="light"] .gt-app .trendline { color: var(--ink); background: rgba(15,46,34,.05); border-color: rgba(15,46,34,.12); }

/* ---- the panels ---------------------------------------------------------- */
.statgrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px; margin-bottom: 20px;
}
.statpanel {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r-sm); padding: 20px 22px 22px;
  color: var(--code-ink, var(--forest));
}
.statpanel h2 {
  font-family: var(--display); font-size: 20px; color: var(--ink);
  margin: 0; line-height: 1.15;
}
.sp-sub { margin: 5px 0 16px; font-size: 13px; color: var(--muted); }
.sp-empty { font-size: 13.5px; color: var(--muted); font-style: italic; }
.sp-verdict { margin: 14px 0 0; font-size: 13.5px; color: var(--ink); }
.sp-verdict b { color: var(--code-ink, var(--forest)); }
.sp-foot {
  margin: 0; padding: 13px 18px; border-radius: 12px; font-size: 13.5px;
  background: rgba(232,228,213,.07); border: 1px solid rgba(232,228,213,.13);
  color: var(--cream);
}
.sp-foot b { color: var(--lime); }
.sp-foot-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
html[data-theme="light"] .gt-app .sp-foot { color: var(--ink); background: rgba(15,46,34,.05); border-color: rgba(15,46,34,.12); }
html[data-theme="light"] .gt-app .sp-foot b { color: var(--forest); }
.board-more { margin: 0 0 14px; text-align: right; }

/* ---- the line chart ------------------------------------------------------
   currentColor throughout, so the line, its fill and its end point all take
   the panel's competition colour without a single hard-coded hex. */
.sp-chart { position: relative; }
.gt-spark { display: block; width: 100%; height: 120px; overflow: visible; }
.sp-scale {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-family: var(--score); font-size: 11.5px; color: var(--muted);
}
.sp-two { display: grid; }
.sp-two > span { grid-area: 1 / 1; display: block; }
.sp-two .sp-against { opacity: .42; }
.sp-key { display: flex; gap: 16px; margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.sp-key span::before {
  content: ""; display: inline-block; width: 14px; height: 3px; border-radius: 3px;
  background: currentColor; margin-right: 6px; vertical-align: middle;
}
.sp-key .k-for { color: var(--code-ink, var(--forest)); }
.sp-key .k-against { opacity: .55; }

/* ---- bars, in markup rather than on a canvas ---------------------------- */
.sp-bars {
  display: flex; align-items: flex-end; gap: 4px; height: 130px;
  padding-top: 6px;
}
.spb { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; min-width: 0; height: 100%; }
.spb i {
  display: block; background: var(--code-ink, var(--forest)); border-radius: 4px 4px 0 0;
  min-height: 2px; opacity: .85; transition: opacity .14s var(--ease);
}
.spb:hover i { opacity: 1; }
.spb span {
  font-size: 9.5px; color: var(--muted); text-align: center; margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.sp-rows { display: grid; gap: 10px; }
.spr { display: grid; grid-template-columns: 88px minmax(0,1fr) 46px 54px; align-items: center; gap: 10px; }
.spr-lab { font-size: 13.5px; font-weight: 700; color: var(--code-ink, var(--ink)); }
.spr-track { height: 10px; border-radius: 100px; background: rgba(15,46,34,.09); overflow: hidden; }
.spr-track i { display: block; height: 100%; background: var(--code-ink, var(--forest)); border-radius: 100px; }
.spr-val { font-family: var(--score); font-weight: 800; font-size: 14px; text-align: right; color: var(--ink); }
.spr-n { font-size: 11.5px; color: var(--muted); text-align: right; }
@media (max-width: 520px) { .spr { grid-template-columns: 74px minmax(0,1fr) 42px; } .spr-n { display: none; } }

/* ---- the ring ------------------------------------------------------------ */
.sp-donut { position: relative; width: 120px; margin: 6px auto 0; }
.sp-donut svg { display: block; width: 120px; height: 120px; }
.sp-donut-mid {
  position: absolute; inset: 0; display: grid; place-content: center; text-align: center;
}
.sp-donut-mid b {
  display: block; font-family: var(--score); font-weight: 900; font-size: 25px;
  color: var(--code-ink, var(--ink)); line-height: 1;
}
.sp-donut-mid span { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }

/* ---- the season as a strip of results ------------------------------------ */
.formrun { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 22px; }
.fr-g {
  width: 42px; padding: 7px 0 5px; border-radius: 10px; text-align: center;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
}
.fr-g b { display: block; font-family: var(--mark); font-weight: 900; font-size: 14px; line-height: 1; }
.fr-g i { display: block; font-style: normal; font-size: 9.5px; color: var(--muted); margin-top: 3px; }
.fr-g.w { background: rgba(31,107,40,.14); border-color: rgba(31,107,40,.4); }
.fr-g.w b { color: #1f6b28; }
.fr-g.l { background: rgba(154,43,43,.12); border-color: rgba(154,43,43,.32); }
.fr-g.l b { color: #9a2b2b; }
.fr-g.d b { color: #8a6206; }

/* The club's name on the ladder is a link into its season. */
.lad-link { text-decoration: none; color: var(--ink); }
.lad-link:hover { color: var(--code-ink, var(--forest)); text-decoration: underline; }

/* ============================================================================
   THE LOADER COVERS THE CONTENT, NOT THE WINDOW
   ----------------------------------------------------------------------------
   "It should not be the whole page, it should be that innermost section that
   has our content — because it looks like the whole page is loading."

   It was fixed to the viewport, so fetching one board withdrew the nav, the
   organisation switcher, the bell and the charity strip as well. None of those
   were loading. Absolute inside the sheet instead: the furniture stays usable
   and what is covered is exactly what is being replaced.

   Scoped to .loader-inset so the public site and the sign-in page keep the
   full-screen splash, where there IS no furniture to preserve.
   ========================================================================= */
.app-main .page-sheet { position: relative; }
.loader.loader-inset {
  position: absolute; inset: 0; z-index: 40;
  border-radius: inherit; overflow: hidden;
  /* Slightly transparent, so the shape of the page shows through and it reads
     as this panel working rather than as a different screen. */
  background:
    radial-gradient(700px 380px at 50% 18%, rgba(200,241,53,.12), transparent 60%),
    color-mix(in srgb, var(--deep-2) 94%, transparent);
  /* Tall enough to be a panel, short enough not to push the page around on a
     screen where the content is shorter than the splash used to be. */
  min-height: 380px;
}
.loader.loader-inset .loader-inner { transform: none; }

.loader.loader-inset .loader-word { font-size: 26px; }
/* The line that says what is being fetched. It was the smallest thing in the
   splash; it is the only part that carries information. */
.loader.loader-inset .loader-task {
  font-size: 15px; font-weight: 700; color: #FFFFFF; margin-top: 10px;
}
.loader.loader-inset .loader-sport { color: rgba(255,255,255,.72); }
/* The page no longer has to be frozen: nothing outside the sheet is covered,
   so scrolling to the nav or the charity strip is legitimate while it loads. */
body.loading:has(.loader-inset) { overflow: visible; }

/* ============================================================================
   WHITE THAT IS ACTUALLY WHITE, on the green
   ----------------------------------------------------------------------------
   "The text that are white — to me some are not even bright enough. The white
   has to be strong so that in the green theme I am not struggling to see it."

   Right, and it was systematic rather than a few stray rules: the deep sheet's
   secondary text was set in cream at .6-.72 alpha, which is a mid-grey once it
   is over a dark green and drops under the 4.5:1 contrast floor at the sizes it
   was used at. Cream at full strength is a warm off-white and reads fine; cream
   at 60% does not.

   Two changes together, because either alone leaves it hard to read: the alphas
   go up to near-solid white, and the sizes go up a step. Applied to the labels,
   the steppers and the notes — the small print of these pages, which is where
   all of it was.
   ========================================================================= */
.rs-showing, .bb-lab, .cf-lab {
  color: rgba(255,255,255,.92); font-size: 12px;
}
.rs-step { color: #FFFFFF; font-size: 13px; background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.26); }
.rs-step:hover { background: rgba(255,255,255,.26); }
.rs-num {
  color: #FFFFFF; font-size: 16px;
  background: rgba(255,255,255,.13); border-color: rgba(255,255,255,.24);
}
.rs-num:hover { background: rgba(255,255,255,.26); }
.rs-all { font-size: 13.5px; }
.ladder-asat { color: rgba(255,255,255,.9); font-size: 14.5px; }
.trendline { color: #FFFFFF; font-size: 15.5px; border-color: rgba(255,255,255,.2); }
.sp-foot { color: #FFFFFF; font-size: 14px; border-color: rgba(255,255,255,.18); }
.gt-head .gh-sub, .gt-head .gh-lead { color: rgba(255,255,255,.86); font-size: 15.5px; }
.page-deep .gt-note, .sheet-page .gt-note { color: rgba(255,255,255,.9); font-size: 14px; }
.boardbar { border-color: rgba(255,255,255,.16); }
.bb-arrow { color: #FFFFFF; background: rgba(255,255,255,.13); border-color: rgba(255,255,255,.24); }
.cf-chip.cf-all { color: #FFFFFF; background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.26); }
.cf-chip.cf-all:hover { background: rgba(255,255,255,.26); }
/* The room switcher sits on the same sheet and had the same problem. */
.room-switch.on-dark .rw-eyebrow { color: rgba(255,255,255,.9); }
.room-switch.on-dark .rw-note { color: rgba(255,255,255,.9); font-size: 14.5px; }
/* These are all deep-sheet rules; the light theme repaints the same surfaces
   pale, where solid white would vanish. Put back to ink there. */
html[data-theme="light"] .gt-app .rs-showing,
html[data-theme="light"] .gt-app .bb-lab,
html[data-theme="light"] .gt-app .cf-lab { color: var(--muted); }
html[data-theme="light"] .gt-app .rs-step,
html[data-theme="light"] .gt-app .rs-num,
html[data-theme="light"] .gt-app .bb-arrow,
html[data-theme="light"] .gt-app .cf-chip.cf-all { color: #2C4C3B; background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.13); }
html[data-theme="light"] .gt-app .ladder-asat,
html[data-theme="light"] .gt-app .trendline,
html[data-theme="light"] .gt-app .sp-foot,
html[data-theme="light"] .gt-app .gt-head .gh-sub,
html[data-theme="light"] .gt-app .gt-head .gh-lead,
html[data-theme="light"] .gt-app .gt-note,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-note,
html[data-theme="light"] .gt-app .room-switch.on-dark .rw-eyebrow { color: var(--ink); }

/* ============================================================================
   THE WINNER'S CARD IS GOLD
   ----------------------------------------------------------------------------
   "The colour of number 1 is green instead of gold — why?" Because it was:
   .gt-pod.p1 was a deep-green gradient from before the podium had medals at
   all, and when silver and bronze were given their metals first place kept the
   product's colour and became the odd one out. A podium where second and third
   are metallic and first is not reads as first place being a different kind of
   thing.

   Gold, and still the loudest card: it is the only one with a dark ground
   underneath the metal, which is what keeps the lime crown and the white name
   readable at the size this card is drawn at.
   ========================================================================= */
.gt-pod.p1 {
  background:
    linear-gradient(165deg, #FFF0B8 0%, #F0CE62 38%, #C9992C 72%, #A87B18 100%);
  border-color: rgba(168,123,24,.85);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75), 0 16px 34px -22px rgba(120,86,10,.85);
}
/* The ink on gold is dark, not the cream this card used when it was green —
   cream on gold is the same brightness twice and reads as a smudge. */
.gt-pod.p1 .gp-name { color: #3F2E06; }
.gt-pod.p1 .gp-pts { color: #4A3608; }
.gt-pod.p1 .gp-pts small { color: #6B5111; }
.gt-pod.p1 .gp-chip { background: rgba(63,46,6,.14); color: #3F2E06; border-color: rgba(63,46,6,.24); }
.gt-pod.p1 .gp-medal { color: #6B5111; }
.gt-pod.p1 .crown svg { stroke: #4A3608; }
.gt-pod.p1 .gp-av { background: rgba(63,46,6,.12); color: #3F2E06; border-color: rgba(63,46,6,.35); }
/* The disc was the one gold thing on a green card. Now that the card is gold it
   has to invert, or it disappears into its own background. */
.gt-pod.p1 .gp-rank { background: linear-gradient(145deg,#3F2E06,#241A03); color: #F0CE62; border-color: rgba(255,255,255,.5); }
.gt-pod.p1::after { display: none; }

/* THIRD, SMALLER AGAIN. "Make number 3 a little bit smaller than that, so we
   can see the difference more easily." The step from second to third was there
   and was too fine to read at a glance. */
.gt-podium { grid-template-columns: 1fr 1.28fr .8fr; }
.gt-pod.p3 { padding: 20px 15px 17px; }
.gt-pod.p3 .gp-avwrap { width: 44px; height: 44px; }
.gt-pod.p3 .gp-name { font-size: 14px; }
.gt-pod.p3 .gp-pts { font-size: 23px; }
.gt-pod.p3 .gp-medal { font-size: 9px; }
@media (max-width: 820px) {
  /* Stacked, a size ladder means nothing; the medal word and colour carry it. */
  .gt-podium { grid-template-columns: 1fr; }
  .gt-pod.p3 { padding: 26px 20px 22px; }
  .gt-pod.p3 .gp-avwrap { width: 58px; height: 58px; }
  .gt-pod.p3 .gp-name { font-size: 17px; }
  .gt-pod.p3 .gp-pts { font-size: 30px; }
}

/* ============================================================================
   THE LADDER'S FULL-WORD HEADINGS AND ITS STATISTICS COLUMN
   ----------------------------------------------------------------------------
   "The names of the clubs are not that big, so we do have space — I want the
   other column names in full. Instead of P I want to see POINTS."

   Words need width that single letters did not, so the numeric columns widen
   and the headings drop a size and wrap rather than clipping. GAMES LEFT is two
   words on two lines on purpose: one long column would have taken the room the
   other six needed.
   ========================================================================= */
.gt-board.ladder .lad-head,
.gt-board.ladder .lad-row {
  grid-template-columns: 46px minmax(0,1fr) repeat(7, minmax(58px, 74px)) 84px;
}
.gt-board.ladder .lad-head .lad-num {
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em;
  line-height: 1.15; white-space: normal; word-break: normal;
  text-align: right;
}
.lad-stats-h {
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .06em; text-align: right; color: #FFFFFF;
}
/* The button, in a column that says what it is. A club name that happens to be
   underlined is not an offer. */
.lad-stats {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 11px; border-radius: 100px; text-decoration: none;
  font-family: var(--mark); font-weight: 800; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
  color: var(--code-ink, var(--forest));
  background: color-mix(in srgb, var(--code-ink, var(--forest)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--code-ink, var(--forest)) 32%, transparent);
  justify-self: end;
}
.lad-stats:hover { background: var(--code-ink, var(--forest)); color: #fff; }
.lad-stats svg { width: 13px; height: 13px; fill: currentColor; }
@media (max-width: 1080px) {
  /* The word goes before the button does — the glyph in a column headed
     STATISTICS still says what it is. */
  .lad-stats span { display: none; }
  .lad-stats { padding: 8px; }
  .gt-board.ladder .lad-head,
  .gt-board.ladder .lad-row { grid-template-columns: 42px minmax(0,1fr) repeat(7, minmax(50px, 64px)) 44px; }
}
@media (max-width: 900px) {
  .gt-board.ladder .lad-head,
  .gt-board.ladder .lad-row { grid-template-columns: 42px minmax(0,1fr) repeat(6, minmax(48px, 60px)) 44px; }
  .gt-board.ladder .lad-left { display: none; }
}

/* The key, now above the table. */
.lad-head-key { margin: 0 0 10px; }
.lad-foot .lad-key, .lad-foot .lad-when { color: rgba(255,255,255,.9); font-size: 13.5px; }
html[data-theme="light"] .gt-app .lad-foot .lad-key,
html[data-theme="light"] .gt-app .lad-foot .lad-when { color: var(--muted); }
html[data-theme="light"] .gt-app .lad-stats-h { color: #FFFFFF; }

/* ============================================================================
   THE EYEBROW ON THE DEEP SHEET, AND THE FORM RUN
   ----------------------------------------------------------------------------
   Two things reported as "in a green so it's not visible", and both were
   literally that.

   THE TITLE-SIZED EYEBROW. `.gt-head .gh-eyebrow.is-title` set --forest, which
   is a dark green, and it landed AFTER the sheet-page rule that paints the
   eyebrow lime — so on the deep sheet "Waita Realty Group · 2026" and "AFL ·
   2026" were dark green on dark green. Near-white here, with the title itself
   pure white above it, so the two read as a hierarchy rather than as one line
   that failed.

   THE FORM RUN. Its cells were tinted-transparent greens and reds with dark
   ink on them — designed for a cream card, drawn on the deep sheet, so W was
   #1f6b28 over a translucent green over dark green. Solid grounds and bright
   ink instead. A row of results nobody can read is not a form guide.
   ========================================================================= */
.sheet-page .gt-head .gh-eyebrow.is-title,
.gt-head .gh-eyebrow.is-title {
  color: rgba(255,255,255,.88); opacity: 1;
}
html[data-theme="light"] .gt-app .gt-head .gh-eyebrow.is-title { color: var(--forest); }

.formrun .fr-g {
  background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.2);
  width: 46px; padding: 8px 0 6px;
}
.formrun .fr-g b { font-size: 15px; color: #FFFFFF; }
.formrun .fr-g i { color: rgba(255,255,255,.7); font-size: 10px; }
/* Solid, not a tint: a translucent wash of the outcome colour over a dark green
   sheet is the same near-black whichever outcome it is. */
.formrun .fr-g.w { background: #1f6b28; border-color: #2c8a37; }
.formrun .fr-g.l { background: #8f2626; border-color: #ad3333; }
.formrun .fr-g.d { background: #7a5410; border-color: #9a6d18; }
.formrun .fr-g.w b, .formrun .fr-g.l b, .formrun .fr-g.d b { color: #FFFFFF; }
.formrun .fr-g.w i, .formrun .fr-g.l i, .formrun .fr-g.d i { color: rgba(255,255,255,.8); }

/* ============================================================================
   THE COMPETITION STATISTICS PAGE
   ========================================================================= */
.compfilter-wrap {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap; margin-bottom: 20px;
}
.compfilter-wrap .compfilter { margin: 0; }
.comp-stats-btn { flex: none; }
.abtn-ic { width: 16px; height: 16px; fill: currentColor; margin-right: 7px; vertical-align: -3px; }

.gt-board.compstats .gb-head,
.gt-board.compstats .gb-row {
  grid-template-columns: minmax(0,1.6fr) repeat(6, minmax(54px, 72px)) 132px 84px;
}
.gt-board.compstats .num { text-align: right; font-family: var(--score); font-variant-numeric: tabular-nums; font-size: 15px; color: var(--ink); }
.gt-board.compstats .num.strong { font-weight: 800; color: var(--code-ink, var(--forest)); }
.gt-board.compstats .num.pos { color: #1f6b28; font-weight: 700; }
.gt-board.compstats .num.neg { color: #9a2b2b; }
.cs-form-h, .cs-stats-h {
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .06em; color: #FFFFFF; text-align: right;
}
/* The last five, as five letters — the same shorthand the fixture card's form
   strip uses, so a reader learns it once. */
.cs-form { display: flex; gap: 3px; justify-content: flex-end; }
.cs-form i {
  font-style: normal; width: 21px; height: 21px; border-radius: 6px;
  display: grid; place-items: center; color: #fff;
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
}
.cs-form i.w { background: #1f6b28; }
.cs-form i.l { background: #9a2b2b; }
.cs-form i.d { background: #8a6206; }
@media (max-width: 1100px) {
  .gt-board.compstats .gb-head,
  .gt-board.compstats .gb-row { grid-template-columns: minmax(0,1.4fr) repeat(6, minmax(46px, 60px)) 44px; }
  .cs-form, .cs-form-h { display: none; }
  .gt-board.compstats .lad-stats span { display: none; }
}
@media (max-width: 780px) {
  .gt-board.compstats .gb-head,
  .gt-board.compstats .gb-row { grid-template-columns: minmax(0,1fr) 52px 52px 58px 44px; }
  .gt-board.compstats .gb-head span:nth-child(5),
  .gt-board.compstats .gb-head span:nth-child(6),
  .gt-board.compstats .gb-head span:nth-child(7),
  .gt-board.compstats .cs-row .num:nth-of-type(4),
  .gt-board.compstats .cs-row .num:nth-of-type(5),
  .gt-board.compstats .cs-row .num:nth-of-type(6) { display: none; }
}

/* ============================================================================
   PREFECT — the review desk
   ----------------------------------------------------------------------------
   Deliberately plain. This is a screen where somebody decides whether a
   colleague gets a warning, and it should read like a form rather than like a
   dashboard: no scores in large type, no red until an actual consequence, and
   the least severe option first and easiest.
   ========================================================================= */
.pf-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 18px; }
.pf-tab {
  padding: 9px 16px; border-radius: 100px; text-decoration: none;
  font-weight: 700; font-size: 13.5px; color: var(--forest);
  background: rgba(15,46,34,.06); border: 1px solid rgba(15,46,34,.12);
}
.pf-tab.on { background: var(--forest); color: #fff; border-color: var(--forest); }
.pf-tab i { font-style: normal; margin-left: 5px; font-weight: 800; }

.pf-list { display: grid; gap: 9px; margin-bottom: 26px; }
.pf-row {
  display: flex; align-items: center; gap: 14px; padding: 14px 18px;
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: 14px; text-decoration: none; color: inherit;
}
a.pf-row:hover { border-color: rgba(45,122,58,.4); }
.pf-row.is-cleared, .pf-row.is-actioned { opacity: .72; }
.pf-src {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .07em; text-transform: uppercase;
  padding: 6px 11px; border-radius: 100px;
}
.pf-src svg { width: 12px; height: 12px; fill: currentColor; }
.pf-src.is-prefect { background: rgba(45,122,58,.12); color: var(--forest); }
.pf-src.is-member  { background: rgba(47,93,168,.12); color: #2F5DA8; }
.pf-src.is-warn    { background: rgba(138,98,6,.14); color: #7a5410; }
.pf-src.is-susp    { background: rgba(154,43,43,.12); color: #9a2b2b; }
.pf-txt { flex: 1 1 auto; min-width: 0; }
.pf-txt b { display: block; font-size: 15.5px; color: var(--ink); }
.pf-txt i { font-style: normal; font-size: 12.5px; color: var(--muted); }
.pf-terms { display: flex; gap: 5px; flex: none; }
.pf-terms i {
  font-style: normal; font-size: 11px; padding: 3px 8px; border-radius: 6px;
  background: rgba(15,46,34,.07); color: var(--muted);
}
/* Small on purpose. It is a rank so the worst sorts first, not a verdict, and
   a big number invites somebody to act on it without reading. */
.pf-score { font-family: var(--score); font-weight: 700; font-size: 13px; color: var(--muted); flex: none; }
.pf-go { flex: none; font-weight: 700; font-size: 13px; color: var(--forest); }
.pf-none { padding: 26px; text-align: center; color: var(--muted); background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 14px; }
.pf-h { font-family: var(--display); font-size: 22px; margin: 26px 0 12px; color: var(--ink); }
.pf-sanction { cursor: default; }
.pf-allow { display: flex; flex-wrap: wrap; gap: 8px; }
.pf-allowchip {
  display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px;
  border-radius: 100px; background: rgba(15,46,34,.06);
  border: 1px solid rgba(15,46,34,.12); font-size: 13px;
}
.pf-allowchip button { background: none; border: 0; cursor: pointer; font-size: 15px; color: var(--muted); line-height: 1; }

/* ---- one case ---- */
.pf-back { margin: 0 0 14px; }
.pf-back a { color: var(--forest); font-weight: 700; text-decoration: none; }
.pf-case { background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 18px; padding: 22px 24px; margin-bottom: 18px; }
.pf-case-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 14px; }
.pf-case-head h2 { font-family: var(--display); font-size: 24px; margin: 0; color: var(--ink); }
.pf-case-head p { margin: 3px 0 0; font-size: 13px; color: var(--muted); }
.pf-why { font-size: 14px; color: var(--ink); background: rgba(15,46,34,.05); padding: 12px 14px; border-radius: 12px; }
.pf-prior { font-size: 13.5px; color: #7a5410; background: rgba(138,98,6,.1); padding: 10px 14px; border-radius: 12px; }
/* The conversation around it, which is the whole point of this screen. */
.pf-thread { display: grid; gap: 6px; margin-top: 16px; }
.pf-msg {
  padding: 10px 14px; border-radius: 12px; background: rgba(15,46,34,.04);
  border: 1px solid transparent; font-size: 14px;
}
.pf-msg b { display: block; font-size: 12.5px; color: var(--forest); }
.pf-msg span { display: block; color: var(--ink); margin: 2px 0; word-break: break-word; }
.pf-msg i { font-style: normal; font-size: 11px; color: var(--muted); }
.pf-msg.is-it { background: rgba(138,98,6,.12); border-color: rgba(138,98,6,.35); }
.pf-msg.is-after { opacity: .75; }

.pf-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.pf-act { background: var(--gt-card); border: 1px solid var(--gt-hair); border-radius: 18px; padding: 20px 22px; }
.pf-act-clear { border-color: rgba(31,107,40,.35); }
.pf-act h3 { font-family: var(--display); font-size: 19px; margin: 0 0 4px; color: var(--ink); }
.pf-act p { font-size: 13.5px; color: var(--muted); margin: 0 0 14px; }
.pf-field { display: block; margin-bottom: 14px; border: 0; padding: 0; }
.pf-field > span, .pf-field legend { display: block; font-weight: 700; font-size: 13px; margin-bottom: 6px; color: var(--ink); }
.pf-field label { display: block; font-size: 13.5px; margin: 4px 0; }
.pf-field textarea { width: 100%; border-radius: 12px; border: 1px solid var(--gt-hair); padding: 10px 12px; font: inherit; }
.pf-learn { display: block; font-size: 13.5px; margin: 10px 0 6px; }
.pf-learn-note { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 0 0 12px; }
.pf-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.abtn-danger { background: #9a2b2b; color: #fff; border-color: #9a2b2b; }
.abtn-sm { padding: 7px 13px; font-size: 12.5px; }
.pf-done { padding: 16px 18px; border-radius: 14px; background: rgba(15,46,34,.05); color: var(--muted); }

/* The member's own report control, beside reply and pin. */
.chat-repform { display: inline-flex; }
.chat-repbtn {
  background: none; border: 0; cursor: pointer; padding: 4px; opacity: 0;
  color: var(--muted); transition: opacity .14s var(--ease), color .14s var(--ease);
}
.chat-row:hover .chat-repbtn, .chat-repbtn:focus-visible { opacity: .8; }
.chat-repbtn:hover { color: #9a2b2b; opacity: 1; }
.chat-repbtn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* ==========================================================================
   THE BAR MAKES ROOM FOR THE ORGANISATION'S NAME
   ==========================================================================
   "We can move the GOODTIP more to the left, because I want to have AquaFlow
   Water Co at least be seen in full."

   Two things were taking the width the name needed: the bar's own outer
   padding, which grew to 44px on a wide screen, and a chip capped at 24vw that
   truncated anything longer than about fifteen characters.

   The mark moves left by giving the bar a smaller left inset than right — it is
   a badge, and a badge sitting nearer the edge still reads as one — and the
   chip is allowed a third of the bar before it starts to ellipsis. Both are
   caps rather than fixed widths, so a short name still leaves the links where
   they were. */
.gt-v2 .app-nav .an-inner { padding-left: clamp(12px, 1.2vw, 20px); }
.an-ctx-btn { max-width: clamp(190px, 33vw, 480px); }
/* The name is the point of the control, so it gets the room inside it: the
   role glyph and the chevron are fixed-width and the name takes the rest. */
.an-ctx-org { min-width: 0; flex: 1 1 auto; }

/* ---- THE ORGANISATION PANEL, WIDER --------------------------------------
   "Up to down it's good, that does not need change, but the sideways left to
   right we can increase it a little, because it kinda looks squeezed."

   Only the horizontal measure moves. The rows keep their heights, their
   padding and their order — what changes is that an organisation name and its
   "You run this one" no longer have to share 220px with a role badge. */
.an-menu-panel.an-ctx-panel { min-width: 640px; }
.an-ctx-col { min-width: 290px; max-width: 360px; padding: 9px; }
@media (max-width: 1100px) {
  .an-menu-panel.an-ctx-panel { min-width: 0; }
  .an-ctx-col { min-width: 0; max-width: none; }
}

/* ==========================================================================
   MY TIPS — the split, and the two full-width bars above it
   ========================================================================== */

/* "The table that showcases the results ... then this one which will take like
   1/3, Your season."

   .gt-shell alone gives the sidebar a fixed 320px, so on a 1300px sheet the
   fixtures get 74% and on a 1000px one they get 66% — the proportion moves with
   the window instead of being the proportion that was asked for. Two thirds and
   one third, stated. */
.gt-shell.gt-shell-thirds { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
@media (max-width: 1080px) { .gt-shell.gt-shell-thirds { grid-template-columns: 1fr; } }

/* ---- the round bar --------------------------------------------------------
   The stepper takes its own row across the page and the picker takes the next,
   because eight numbers, two arrows, a dropdown and a lock time do not fit one
   line at any width worth designing for — and wrapped, they wrap in a different
   place on every screen. */
.boardbar-round .bb-round-pick {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.bb-form { display: flex; align-items: center; gap: 6px; margin: 0; }
/* The Go button is the no-JavaScript path. Once the script that submits on
   change is running it has nothing to do, so it takes itself off the row
   rather than sitting there as a second way to do what already happened. */
.bb-form.is-live .bb-go { display: none; }
.bb-go {
  font-family: var(--mark); font-size: 12px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 9px 14px; border-radius: 10px; cursor: pointer;
  background: var(--lime); color: var(--deep); border: 0;
}
.bb-when {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: rgba(232,228,213,.72); margin-left: auto;
}
.bb-when svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }
html[data-theme="light"] .gt-app .bb-when { color: var(--lt-quiet); }

/* MY TIPS ONLY. Eight buttons rather than the leaderboard's five, so the strip
   takes the bar's whole first line and the picker takes the second. Two rows
   beat one that wraps in a different place on every screen — and this must NOT
   reach .boardbar-round on the leaderboard and the ladder, which are asked to
   be one line and are narrow enough to be one. */
.boardbar-tips { flex-direction: column; align-items: stretch; }
/* All-competitions mode: what you are looking at, and the two steps either
   side of it. */
.bb-multi { font-size: 14px; color: var(--cream); }
html[data-theme="light"] .gt-app .bb-multi { color: var(--lt-ink); }
.bb-chip {
  display: inline-block; font-family: var(--mark); font-size: 10.5px;
  font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 100px; margin-left: 6px;
  background: rgba(255,255,255,.09); color: rgba(232,228,213,.8);
}
html[data-theme="light"] .gt-app .bb-chip { background: rgba(15,46,34,.07); color: var(--lt-quiet); }
/* The competition filter is a card in its own right and needs the full measure
   under it, not the sliver a flex row gives its content. */
.sfilter { width: 100%; }
.boardbar-tips .rstrip { width: 100%; }
.boardbar-tips .rs-nums { flex-wrap: wrap; }
.boardbar-tips .bb-round-pick { flex: 1 1 100%; }

/* THE ORGANISATION OUTRANKS THE WORD on the groups hero too. `.gh-eyebrow
   .is-title` is scoped to .gt-head, which this hero is not, so the size has to
   be stated here or the line stays 12px capitals under a 44px "Groups". */
.dept-hero .gh-eyebrow.is-title {
  font-family: var(--display); font-weight: 400; text-transform: none;
  font-size: clamp(24px, 3.4vw, 38px); letter-spacing: -.015em; line-height: 1.12;
  color: #fff; opacity: 1;
}
html[data-theme="light"] .gt-app .dept-hero .gh-eyebrow.is-title { color: var(--lt-ink); }

/* ==========================================================================
   MEMBERS — the header, and the two cards that lead it
   ========================================================================== */

/* THE ORGANISATION OUTRANKS THE WORD, centred. Same swap as the leaderboard's
   .gt-head.is-orgfirst, on the other header component: "there is no way this
   can be smaller than Members — have this AquaFlow Water Co larger." */
.app-head.is-centred { flex-direction: column; align-items: center; text-align: center; }
.app-head.is-centred .app-sub { max-width: 58ch; margin-left: auto; margin-right: auto; }
.app-head.is-orgfirst .app-eyebrow {
  font-family: var(--display); font-weight: 400; text-transform: none;
  font-size: clamp(22px, 3.2vw, 36px); letter-spacing: -.015em; line-height: 1.12;
  color: #fff;
}
.app-head.is-orgfirst .app-h1 {
  font-family: var(--mark); font-weight: 700; text-transform: uppercase;
  font-size: clamp(13px, 1.5vw, 17px); letter-spacing: .16em;
  color: var(--lime); margin-top: 10px;
}
.app.page-deep .app-head.is-orgfirst .app-eyebrow { color: #fff; }
.app.page-deep .app-head.is-orgfirst .app-h1 { color: var(--lime); }
html[data-theme="light"] .gt-app .app-head.is-orgfirst .app-eyebrow { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .app-head.is-orgfirst .app-h1 { color: var(--lt-accent-ink); }
.app-head-act { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }

/* ---- FOUR SMALL SQUARE CARDS, ONE COLOUR EACH ---------------------------
   "I need a card that is small, a square card — not this big thing with all
   these details ... all should be the same size, different colours ... if I
   click an item let it carry the colour."

   A fixed square rather than a stretched row: four equal targets read as one
   control, and the count inside each is the whole reason the strip is at the
   top — "what if we had 200 members, will I scroll down to know we have that?"

   The colour is per card and lives on --pc. Off, it tints the icon and the
   border; on, it fills the card. One custom property, five cards. */
.pcards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 12px; margin-bottom: 22px;
}
.pcard {
  --pc: var(--forest);
  display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between;
  gap: 8px; text-decoration: none; aspect-ratio: 1 / .82; padding: 14px;
  border-radius: var(--gt-r-sm); color: var(--ink);
  background: var(--gt-card); border: 1.5px solid var(--gt-hair);
  transition: transform .14s var(--ease), border-color .14s var(--ease);
}
.pcard:hover { transform: translateY(-2px); border-color: var(--pc); }
.pcard-ic {
  width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: 10px; background: color-mix(in srgb, var(--pc) 14%, transparent);
  color: var(--pc);
}
.pcard-ic svg { width: 17px; height: 17px; fill: currentColor; }
.pcard b { font-size: 14px; font-weight: 800; line-height: 1.2; }
/* The number is the point of the card, so it is the biggest thing on it. */
.pcard i {
  font-style: normal; font-family: var(--display); font-size: 24px;
  line-height: 1; color: var(--pc);
}
/* THE ONE YOU ARE ON WEARS ITS COLOUR. "Let it carry the colour." */
.pcard.on {
  background: var(--pc); border-color: var(--pc); color: #fff;
}
.pcard.on .pcard-ic { background: rgba(255,255,255,.2); color: #fff; }
.pcard.on i { color: rgba(255,255,255,.9); }
.pcard.c-members { --pc: #2D7A3A; }
.pcard.c-groups  { --pc: #1F6FA8; }
.pcard.c-orgs    { --pc: #8A5A17; }
.pcard.c-team    { --pc: #6B3FA0; }
.pcard.c-invite  { --pc: #A83254; }

/* ---- THE PANEL UNDER THEM ------------------------------------------------
   One box, whatever the cards point at. It keeps its own frame so the strip
   above always reads as a switch attached to something. */
.ppanel {
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  border-radius: var(--gt-r); padding: 18px 20px 22px; margin-bottom: 22px;
}
.ppanel-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 16px;
}
/* ---- THE LADDER'S BOARD, WITH THREE COLUMNS INSTEAD OF FOUR --------------
   `.gt-board .gb-head` is two classes, so a plain `.mb-board .gb-head` lost to
   it and the members board kept the ladder's `58px 1fr 110px 90px` — four
   tracks for three cells. Every row landed in the wrong column and the last
   heading fell off the end, which is why the table did not look like a table
   and its headings could not be found.

   `.gt-board.mb-board` is three, so it wins. Stated once for the head and the
   rows together, because a head on a different grid from its rows is the same
   bug wearing a different hat. */
/* The bed is not stripped any more — see "PANELS WEAR THEIR CARD'S COLOUR" at
   the end of this file, where every board inside a panel gets a white one. */
.gt-board.mb-board .gb-head,
.gt-board.mb-board .gb-row { grid-template-columns: minmax(0, 1fr) auto 120px; }
/* The last column is right-aligned in the head as well as the rows — it was an
   inline style on the heading alone, so the two disagreed the moment the rows
   were restyled. */
.gt-board.mb-board .gb-head > :last-child { text-align: right; }
.mb-roles { display: flex; gap: 5px; flex-wrap: wrap; justify-content: flex-end; }
.mb-when { text-align: right; font-size: 13px; white-space: nowrap; }
.gt-board.mb-board .gb-name { display: flex; flex-direction: column; gap: 1px; }
.gt-board.mb-board .gb-name small { font-weight: 400; font-size: 12px; opacity: .72; }
@media (max-width: 700px) {
  .gt-board.mb-board .gb-head,
  .gt-board.mb-board .gb-row { grid-template-columns: minmax(0, 1fr) auto; }
  .gt-board.mb-board .mb-when { display: none; }
}

/* ---- THE ORGANISATION → GROUPS NAVIGATOR --------------------------------
   "The first one been the organisations I have, then if I click that
   organisation, under it is the groups." Two rows, the second following the
   first — the same shape as the room switcher, so the gesture is learned. */
.pnav-row + .pnav-row { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--gt-hair); }
.pnav-lab {
  display: block; margin-bottom: 10px;
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.pnav-opts { display: flex; gap: 8px; flex-wrap: wrap; }
.pnav-opt {
  display: inline-flex; align-items: center; gap: 7px; text-decoration: none;
  padding: 9px 15px; border-radius: 100px; font-size: 13.5px; font-weight: 600;
  color: var(--ink); background: rgba(45,122,58,.06); border: 1px solid var(--gt-hair);
}
.pnav-opt:hover { border-color: var(--forest); }
.pnav-opt.on { background: var(--forest); border-color: var(--forest); color: #fff; }
.pnav-opt i { font-style: normal; font-size: 11.5px; opacity: .65; }
.pnav-opt.is-add { background: none; color: var(--forest); font-weight: 700; }
.pnav-none { font-size: 13px; color: var(--muted); }

/* ---- THE INVITE PANEL ---------------------------------------------------- */
.pinvite { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 26px; }
.pinvite-link { padding-left: 26px; border-left: 1px solid var(--gt-hair); }
@media (max-width: 700px) { .pinvite-link { padding-left: 0; border-left: 0; } }
.tm-nominate { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--gt-hair); max-width: 560px; }

/* ==========================================================================
   GROUPS — a centred hero with both of its decisions under the sentence
   ========================================================================== */
.dept-hero.is-centred { grid-template-columns: 1fr; text-align: center; justify-items: center; }
.dept-hero.is-centred .dh-lead { margin-left: auto; margin-right: auto; }
/* The organisation outranks the word here too. .gh-eyebrow.is-title already
   sets the display face at title size for the ladder; the word underneath
   steps down to the small lime capitals. */
.dept-hero.is-centred .dh-word {
  font-family: var(--mark); font-weight: 700; text-transform: uppercase;
  font-size: clamp(13px, 1.5vw, 17px); letter-spacing: .16em;
  color: var(--lime); margin: 10px 0 12px;
}
html[data-theme="light"] .gt-app .dept-hero.is-centred .dh-word { color: var(--lt-accent-ink); }
/* BOTH DECISIONS, SAME SIZE, UNDER THE SENTENCE. Switch-groups-off was a small
   ghost button at the very foot of the page, under the list and under the
   create sheet — an admin had to scroll past every group to learn it existed.
   It is a decision about this screen, so it sits where the other decision about
   this screen is. Ghost against solid still says which one is the quiet one. */
.dept-hero .dh-acts {
  display: flex; align-items: center; justify-content: center;
  gap: 14px; flex-wrap: wrap; margin-top: 22px;
}
.dept-hero .dh-acts form { margin: 0; }
@media (max-width: 620px) {
  .dept-hero .dh-acts { flex-direction: column; align-items: stretch; }
  .dept-hero .dh-acts .abtn { width: 100%; justify-content: center; }
}

/* ==========================================================================
   THE BOARD BARS STAY ON ONE LINE
   ==========================================================================
   "Round / All rounds / Rounds 23–27 / Earlier 23 24 25 26 27 Later — this must
   be in one line", and the same for the row of competition chips.

   They were a wrapping flex row, so on anything narrower than about 1200px the
   strip dropped under the dropdown and the bar grew a second line — which moved
   the table under it every time the window changed. One line that SCROLLS
   sideways instead: the row keeps its shape at every width, and the few pixels
   past the edge are reachable by the same gesture people already use on the
   filter tabs in Messages.

   Not applied to .boardbar-tips, which is deliberately two rows: eight round
   buttons plus a dropdown plus a lock time do not belong on one line at any
   width worth designing for. */
.boardbar-round:not(.boardbar-tips),
.boardbar:not(.boardbar-round) {
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
}
.boardbar-round:not(.boardbar-tips)::-webkit-scrollbar,
.boardbar:not(.boardbar-round)::-webkit-scrollbar { display: none; }
.boardbar-round:not(.boardbar-tips) .rstrip,
.boardbar-round:not(.boardbar-tips) .rs-nums { flex-wrap: nowrap; }
.boardbar-round:not(.boardbar-tips) .bb-round { flex: none; }
/* The chips are the whole content of the competition bar, so they carry the
   nowrap too or the row inside the row wraps instead. */
.boardbar:not(.boardbar-round) .cf-chips { flex-wrap: nowrap; }

/* THE LADDER'S ROUND STRIP, CENTRED. `.boardbar-round.is-centred` centres the
   bar's own children; the strip is one child and centres its contents
   separately, so without this the numbers sat left inside a centred box. */
.boardbar-round.is-centred .rstrip { justify-content: center; flex: 1 1 auto; }

/* ==========================================================================
   GROUPS — the lead sentence in light mode
   ==========================================================================
   ".dh-lead" is rgba(232,228,213,.76) — a near-white tuned for the deep green
   sheet. In light mode there is no green left under it and the sentence was
   near-white on cream: "when it's in light mode they cannot be seen." */
html[data-theme="light"] .gt-app .dept-hero .dh-lead { color: var(--lt-quiet); }
html[data-theme="light"] .gt-app .dept-hero .dh-copy h1 { color: var(--lt-ink); }

/* ==========================================================================
   MY TIPS / LADDER — the headers people asked to have centred
   ========================================================================== */

/* "That text, let it be centered, and the words My tips you can increase its
   size a little bit." The org line already outranks it (is-orgfirst); this
   lifts the word itself so the pair reads as a title and a subtitle rather than
   a title and a caption. */
.gt-head.is-orgfirst .gh-lead { margin-left: auto; margin-right: auto; }
.sheet-page .gt-head.is-orgfirst h1,
.gt-head.is-orgfirst h1 { font-size: clamp(16px, 2vw, 22px); letter-spacing: .14em; }

/* ---- THE LADDER'S KEY ----------------------------------------------------
   Centred, and in two short parts: what the table IS, then what the shading
   means. "Top 8, finals places" sat above the rows and read as a description of
   them — as though eight were all that had been listed. */
.lad-head-key.is-centred {
  display: flex; justify-content: center; align-items: center;
  gap: 8px 18px; flex-wrap: wrap; text-align: center;
}
.lad-all { font-weight: 700; color: var(--cream); }
html[data-theme="light"] .gt-app .lad-all { color: var(--lt-ink); }

/* ==========================================================================
   THE PLANS — five plans, five identities
   ==========================================================================
   "The five pricing cards must each have a completely different, tasteful
   background colour ... do NOT make any of them green, lime, pure white, cream
   or beige."

   They were five shades of the same green, deepening across the row, with the
   featured one inverted to the deep sheet. Five options that differ only in
   saturation are five rows of a table wearing card borders — and the one job of
   a pricing page is to make five choices feel like five choices.

   So each takes a soft pastel of its own, none of them a brand colour, and the
   brand keeps every MARK on top of them: the tick, the price, the border, the
   badge and the button. That is also what stops five colours reading as five
   different products — the ink is the same on all of them.

   THE COLOURS CARRY ACROSS BOTH THEMES, which the client asked for explicitly.
   They are set once here on the plain selector rather than in a light block and
   a dark block, so there is nothing to keep in step. */
.tier[data-tier] {
  --tier-bg: #EAF1FA; --tier-edge: #9DBBDD; --tier-ink: #123A5E;
}
.tier:nth-child(1) { --tier-bg: #E8F1FB; --tier-edge: #9CBEE2; --tier-ink: #12496F; }  /* sky      */
.tier:nth-child(2) { --tier-bg: #EFEAFA; --tier-edge: #B4A5E0; --tier-ink: #45327D; }  /* lavender */
.tier:nth-child(3) { --tier-bg: #FCEEE6; --tier-edge: #E6B79C; --tier-ink: #8A4620; }  /* peach    */
.tier:nth-child(4) { --tier-bg: #E9EEF2; --tier-edge: #A9BCC9; --tier-ink: #2C4655; }  /* mist     */
.tier:nth-child(5) { --tier-bg: #FBEAEE; --tier-edge: #E3A8B7; --tier-ink: #8A2F47; }  /* rose     */

/* Rewritten rather than patched: the old rules painted the background on
   :nth-child and the featured card on .featured, and a patch would have left
   two systems fighting over the same box. */
.tier[data-tier] {
  background: var(--tier-bg);
  border: 1px solid var(--tier-edge);
  border-top: 4px solid var(--tier-edge);
  border-radius: 20px; padding: 28px 22px 24px;
  box-shadow: 0 10px 24px -18px rgba(15,46,34,.5);
  color: var(--tier-ink);
}
.tier[data-tier] .tier-name { color: var(--tier-ink); font-size: 19px; }
.tier[data-tier] .tier-for { color: var(--tier-ink); opacity: .72; }
/* The price is the biggest thing on the card, and it is the one place the
   forest green appears at size — so the row reads as one product however many
   colours it is wearing. */
.tier[data-tier] .tier-price { color: var(--forest); font-size: 40px; }
.tier[data-tier] .tier-price span,
.tier[data-tier] .tier-per { color: var(--tier-ink); opacity: .66; }
.tier[data-tier] .tier-feats li { color: var(--tier-ink); }
.tier[data-tier] .tk { color: var(--forest); font-weight: 800; }
.tier[data-tier] .tier-cta {
  background: var(--forest); color: #fff; border-radius: 12px;
  padding: 13px; font-size: 12.5px;
}
.tier[data-tier] .tier-cta:hover { background: var(--deep); }

/* THE FEATURED PLAN KEEPS ITS OWN COLOUR and stands out by weight instead —
   "make Growth the featured plan, but do NOT give it a green background;
   instead make it stand out with a stronger dark-green border, subtle shadow,
   and a MOST POPULAR badge in GoodTip lime." */
.tier[data-tier].featured {
  border-color: var(--forest); border-top-color: var(--forest); border-width: 2px;
  box-shadow: 0 18px 40px -22px rgba(15,46,34,.75);
  transform: translateY(-6px);
}
.tier[data-tier].featured .tier-cta { background: var(--deep); }
.tier[data-tier] .tier-flag {
  background: var(--lime); color: var(--deep);
  font-family: var(--mark); font-weight: 800; font-size: 10px;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 100px;
}
/* Equal heights, and enough air between them that five cards read as five. */
.tier-cards { align-items: stretch; gap: 18px; }
.tier[data-tier] .tier-feats { flex: 1 1 auto; }
@media (max-width: 900px) {
  .tier[data-tier].featured { transform: none; }
}

/* On the deep sheet the pastels are the lightest thing on the page, which is
   what makes them read as cards rather than as panels. Only the shadow needs
   to change: a dark shadow under a pale card on a dark ground does nothing. */
.sheet-page .tier[data-tier] { box-shadow: 0 14px 30px -18px rgba(0,0,0,.6); }
.sheet-page .tier[data-tier].featured { box-shadow: 0 20px 44px -20px rgba(0,0,0,.75); }

/* ==========================================================================
   CHARITIES — the table, and the elections panel
   ========================================================================== */
.pnav-rooms { margin-bottom: 18px; }
.ch-backing {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 14px 16px; margin-bottom: 18px;
  border-radius: var(--gt-r-sm); background: rgba(45,122,58,.07);
  border: 1px solid var(--gt-hair);
}
.cht-name { display: flex; align-items: center; gap: 10px; }
.cht-logo {
  width: 30px; height: 30px; flex: none; border-radius: 8px; object-fit: contain;
  background: #fff;
}
.cht-initials {
  display: grid; place-items: center; font-size: 11px; font-weight: 800;
  background: hsl(var(--ch-hue, 140) 45% 92%); color: hsl(var(--ch-hue, 140) 55% 28%);
}
.cht-web a { color: var(--forest); font-size: 13px; }
.ch-inline-form { max-width: 560px; }
.ch-inline-form .msg-fld { margin-bottom: 16px; }

/* ---- ONE ELECTION -------------------------------------------------------
   "When I click it I will see the votes, when the election was started, ended,
   a bar and pie chart of the results and all." */
.elec { padding: 18px 0; border-top: 1px solid var(--gt-hair); }
.elec:first-of-type { border-top: 0; padding-top: 6px; }
.elec-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 14px;
}
.elec-when { font-size: 13px; color: var(--muted); }
.elec-body {
  display: grid; grid-template-columns: 148px minmax(0, 1fr);
  gap: 22px; align-items: center;
}
@media (max-width: 640px) { .elec-body { grid-template-columns: 1fr; justify-items: center; } }
/* The wheel is one conic-gradient built in services._pie_stops — each slice has
   to begin where the last ended, which a template cannot accumulate. The hole
   in the middle is a mask rather than a second element on top, so the card's
   own background shows through whatever it is. */
.elec-pie {
  width: 148px; height: 148px; border-radius: 50%;
  -webkit-mask: radial-gradient(circle, transparent 46%, #000 47%);
  mask: radial-gradient(circle, transparent 46%, #000 47%);
}
.elec-bars { display: grid; gap: 9px; }
.elec-bar { display: grid; grid-template-columns: minmax(0, 1fr) 3fr auto; gap: 12px; align-items: center; }
.eb-name { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.eb-track { height: 10px; border-radius: 100px; background: rgba(15,46,34,.09); overflow: hidden; }
.eb-track i { display: block; height: 100%; border-radius: 100px; }
.eb-n { font-size: 13px; font-weight: 700; white-space: nowrap; }
.eb-n small { font-weight: 400; color: var(--muted); }
.elec-facts {
  display: flex; gap: 8px 22px; flex-wrap: wrap; margin-top: 16px;
  font-size: 13px; color: var(--muted);
}
.elec-facts b { color: var(--ink); font-size: 15px; }
.elec-tie {
  display: flex; align-items: center; gap: 9px; margin-top: 14px;
  padding: 11px 13px; border-radius: 10px; font-size: 13px; line-height: 1.5;
  background: rgba(200,241,53,.14); border: 1px solid rgba(45,122,58,.24);
}
.elec-tie svg { width: 16px; height: 16px; flex: none; fill: currentColor; }
.elec-open { font-size: 13.5px; color: var(--muted); line-height: 1.6; }

/* ---- LEGIBILITY, STATED FOR BOTH THEMES ----------------------------------
   "When it's green the text should be white and huge; if light theme have black
   and let them be huge." Reported as 80% unreadable in light and 20% in green,
   which is what happens when a panel written for a cream card is dropped onto
   the deep sheet and then half-corrected: some inks were fixed for the green
   and the light theme inherited the fixes.

   So the panel names its own ink ONCE, as a pair of tokens, and everything
   inside reads them. One place to change, and neither theme can be left behind
   when the other is adjusted. */
.ppanel {
  --pp-ink: var(--ink);
  --pp-quiet: var(--muted);
  --pp-line: var(--gt-hair);
  color: var(--pp-ink);
}
.sheet-page .ppanel {
  --pp-ink: #FFFFFF;
  --pp-quiet: rgba(232,228,213,.86);
  --pp-line: rgba(255,255,255,.16);
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14);
}
html[data-theme="light"] .gt-app .ppanel,
html[data-theme="light"] .gt-app.sheet-page .ppanel {
  --pp-ink: #10241B;
  --pp-quiet: #47554D;
  --pp-line: rgba(15,46,34,.14);
  background: #FFFFFF; border-color: rgba(15,46,34,.12);
}

/* Everything that carries words. Bigger as asked — these were 13px captions on
   a screen people are reading a list from. */
.ppanel, .ppanel .card-h,
.ppanel .atable td, .ppanel .atable th,
.ppanel .gb-row, .ppanel .gb-name,
.eb-name, .eb-n, .elec-facts b { color: var(--pp-ink); }
.ppanel .card-h { font-size: 17px; }
.ppanel .atable td, .ppanel .gb-row { font-size: 14.5px; }
.ppanel .card-lead, .ppanel .app-sub, .ppanel .hint,
.elec-when, .elec-facts, .elec-open, .eb-n small,
.cht-from, .mb-when, .gb-name small { color: var(--pp-quiet); font-size: 13.5px; }
.ppanel .atable tr, .ppanel .gb-row { border-color: var(--pp-line); }
.eb-track { background: color-mix(in srgb, var(--pp-ink) 16%, transparent); }

/* The rows used to be a translucent film on the panel, which put the panel's
   white ink on a near-white surface. They are paper now — see "PANELS WEAR
   THEIR CARD'S COLOUR" at the end of this file. */
html[data-theme="light"] .gt-app .cht-web a { color: var(--forest); }

/* The charity board's own tracks — logo and name, website, where from, and the
   edit button. `.gt-board .gb-head` is two classes, so this needs three. */
.gt-board.ch-board .gb-head,
.gt-board.ch-board .gb-row { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 130px 80px; }
.cht-web { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.cht-act { text-align: right; }
.ch-board .gb-av img { width: 100%; height: 100%; object-fit: contain; background: #fff; border-radius: 6px; }
@media (max-width: 820px) {
  .gt-board.ch-board .gb-head,
  .gt-board.ch-board .gb-row { grid-template-columns: minmax(0, 1fr) 80px; }
  .ch-board .cht-web, .ch-board .cht-from { display: none; }
}

/* THE ROOM NAVIGATOR AND THE CARDS, on the deep sheet. */
.sheet-page .pnav-opt { background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.16); color: #fff; }
.sheet-page .pnav-opt.on { background: var(--lime); border-color: var(--lime); color: var(--deep); }
.sheet-page .pnav-lab { color: rgba(232,228,213,.72); }
.sheet-page .pnav-row + .pnav-row { border-color: rgba(255,255,255,.14); }
.sheet-page .pnav-none { color: rgba(232,228,213,.7); }
.sheet-page .ch-backing { background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.14); }

/* The panel while its replacement is in flight. A dim rather than a spinner:
   the box is already the right shape and in the right place, and what is
   changing is its contents. */
.ppanel.is-swapping { opacity: .55; transition: opacity .12s var(--ease); }

/* ---- THE CARDS KEEP THEIR COLOURS ON THE GREEN SHEET ---------------------
   "Let them be also of different colours, to make it pop up a little bit."

   They already had one each — and a blanket `.sheet-page .pcard` rule painted
   every one of them the same translucent white, so on the deep sheet all five
   were identical grey boxes and the colour only appeared on the card you were
   standing on. The tint is now mixed FROM the card's own colour, so the strip
   reads as five things at rest and not only once pressed. */
.sheet-page .pcard {
  background: color-mix(in srgb, var(--pc) 24%, #0E2018);
  border-color: color-mix(in srgb, var(--pc) 55%, transparent);
  color: #FFFFFF;
}
.sheet-page .pcard:hover { border-color: color-mix(in srgb, var(--pc) 85%, transparent); }
.sheet-page .pcard .pcard-ic { background: color-mix(in srgb, var(--pc) 42%, transparent); color: #FFFFFF; }
.sheet-page .pcard i { color: #FFFFFF; opacity: .92; }
.sheet-page .pcard.on {
  background: var(--pc); border-color: #FFFFFF;
  box-shadow: 0 12px 28px -16px rgba(0,0,0,.8);
}
/* On the deep sheet a bright pastel would out-shout the page, so the colours
   run a shade deeper here than the plan cards' do. */
.sheet-page .pcard.c-members { --pc: #3E9E52; }
.sheet-page .pcard.c-groups  { --pc: #3D8FD1; }
.sheet-page .pcard.c-orgs    { --pc: #C08A2E; }
.sheet-page .pcard.c-team    { --pc: #9269D6; }
.sheet-page .pcard.c-invite  { --pc: #D45A7E; }

/* ==========================================================================
   THE PLANS — three squares, then two across
   ==========================================================================
   "Because they are five, we will have three of them be on the first row, all
   perfect squares, then the last two be rectangles under the three perfect
   squares."

   Five equal columns gave every plan a tall narrow strip, which is the shape
   that makes a feature list read as a column of crumbs. Three then two: the
   first row squares, the second row the two widest cards on the page — and the
   pair carries the plans with the longest feature lists, which is where the
   extra width goes to work. */
/* SIX TRACKS, NOT THREE. Three columns cannot halve a row, so the pair
   underneath would have come out as a double-width card beside a single — one
   rectangle and one square, which is not what was asked for. On sixes the top
   three take two tracks each and the bottom two take three each: three equal
   squares, then two equal rectangles. */
.tier-cards {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 18px;
}
.tier-cards .tier[data-tier] { grid-column: span 2; }
/* A square is a square: the aspect ratio is on the card, and the content is
   free to run past it rather than being clipped by it — a plan with one more
   feature grows the row rather than losing a line. */
.tier-cards .tier[data-tier] { aspect-ratio: 1 / 1; }
.tier-cards .tier[data-tier]:nth-child(4),
.tier-cards .tier[data-tier]:nth-child(5) { aspect-ratio: auto; }
.tier-cards .tier[data-tier]:nth-child(4),
.tier-cards .tier[data-tier]:nth-child(5) { grid-column: span 3; }
/* A square stops being a good shape well before a phone. Two across, then one,
   and nothing is held to a ratio once it is that narrow. */
@media (max-width: 1000px) {
  .tier-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tier-cards .tier[data-tier],
  .tier-cards .tier[data-tier]:nth-child(4),
  .tier-cards .tier[data-tier]:nth-child(5) { aspect-ratio: auto; grid-column: span 1; }
}
@media (max-width: 640px) {
  .tier-cards { grid-template-columns: 1fr; }
}

/* ==========================================================================
   SETTINGS — the cards centre with the header above them
   ==========================================================================
   "The container that has this — that box should also be centred; that's why I
   said have everything centred."

   They were capped at 720px and left-aligned, so a centred title sat over two
   boxes hugging the left margin. The cap stays (a form line 1200px wide is
   unreadable); what changes is that the leftover width is split rather than all
   given to the right. The text inside stays left-aligned: a centred paragraph
   of body copy is harder to read, and "centre it" is about where the box sits.
   ========================================================================== */
.set-card { max-width: 720px; margin-left: auto; margin-right: auto; }
.set-card + .set-card { margin-top: 20px; }

/* ==========================================================================
   PANELS WEAR THEIR CARD'S COLOUR, AND THEIR TABLES GO BACK TO PAPER
   ==========================================================================
   "In green we have cream or white rows of the table, so most of the texts are
   not visible ... have the background of that table be white, and the text a
   colour that goes with white; then the display and shade design to be of the
   colour that the card is — like the charities card is green, so the table
   should have that green feel, the way we did the competition on the ladder.
   Add a charity, What groups back, Elections should have the feel of the
   colour we have at the card."

   WHAT WAS BROKEN. The panel is a translucent white box on the deep-green
   sheet, so it names white as its ink (--pp-ink) and everything inside reads
   that token. Two surfaces inside it never got the message:

     · `.atable td` paints itself `--app-card`, a cream, and then took white
       ink from the panel. Members' role table, the child-organisation list and
       "what each group backs" were white text on cream — invisible.
     · `.gt-board .gb-head` is recoloured to near-black on a sheet page,
       because there it sits on a cream bed. Inside a panel there is no cream
       bed, so "Member / Roles / Joined" and "Charity / Website / Where from"
       were near-black on green — invisible for the same reason in reverse.

   THE FIX IS ONE DECISION, NOT SIX PATCHES. A table is paper. Wherever a
   panel holds one, it lays down a white sheet, and inside that sheet the ink
   goes dark again — the row redefines --pp-ink and --pp-quiet for its own
   subtree, so `.gb-name`, the addresses, the "joined" dates and the cell text
   all follow without being listed one by one.

   AND THE COLOUR IS THE CARD'S. Each panel is opened by a card that already
   has an identity colour; the panel takes it as --pc and spends it exactly
   where the ladder spends a competition's colour — the heading band, a whisper
   of tint on alternating rows, the hover, the avatar wells, the rule beside a
   title. The card you pressed and the thing it opened are then visibly one
   object rather than a coloured button above a grey box.
   ========================================================================== */

/* ---- 1. the colour, taken from the card that opened the panel ------------
   The same five values as `.pcard`, restated rather than shared: a panel is
   not a card, and a selector that made them one would mean every future tweak
   to a card's chrome landed on the panel too. */
.ppanel            { --pc: #2D7A3A; }
.ppanel.c-members  { --pc: #2D7A3A; }
.ppanel.c-groups   { --pc: #1F6FA8; }
.ppanel.c-orgs     { --pc: #8A5A17; }
.ppanel.c-team     { --pc: #6B3FA0; }
.ppanel.c-invite   { --pc: #A83254; }

/* The panel's own ink for anything sitting on PAPER rather than on the panel.
   Stated on .ppanel so both themes inherit one pair of values: paper is white
   in the green theme and in the light theme alike, so its ink does not need to
   differ between them. */
.ppanel {
  --pp-paper:       #FFFFFF;
  --pp-paper-ink:   #10241B;
  --pp-paper-quiet: #55625A;
}
/* The accent edge. Both themes already set `border-color` on the panel as a
   shorthand, which repaints all four sides — so the top is claimed back after
   them, and at their own specificity, rather than by hoping to be later. */
.gt-app .ppanel,
html[data-theme="light"] .gt-app .ppanel { border-top: 3px solid var(--pc); }

/* THE TITLE CARRIES THE COLOUR TOO, as a rule rather than as coloured type:
   these headings are read at a glance and a mid-blue or a plum heading on
   green is a harder read than a white one beside a coloured mark. */
.ppanel .card-h { display: flex; align-items: center; gap: 10px; }
.ppanel .card-h::before {
  content: ""; flex: none; width: 4px; align-self: stretch; min-height: 18px;
  border-radius: 100px; background: var(--pc);
}

/* ---- 2. the bed every board sits on -------------------------------------
   The ladder's shape exactly: a bed, white rows lifting off it, a coloured
   band across the top. What changes is only what the bed is made of — cream
   there, a wash of the panel's own colour here, so "the table has that green
   feel" on Charities and a blue one on Groups without a second component.

   Both themes, one rule: the sheet's cream bed and the light theme's beige one
   are the two this replaces, and either left in place would put a warm surface
   under a coloured table. */
.sheet-page .ppanel .gt-board,
.sheet-page .ppanel .gt-listwrap,
html[data-theme="light"] .gt-app .ppanel .gt-board,
html[data-theme="light"] .gt-app .ppanel .gt-listwrap {
  --pp-bed: color-mix(in srgb, var(--pc) 9%, #FFFFFF);
  background: var(--pp-bed);
  border: 1px solid color-mix(in srgb, var(--pc) 26%, transparent);
  border-radius: 16px;
  padding: 12px;
  box-shadow: 0 18px 44px -30px rgba(15,46,34,.7);
}
/* One bed, not two, if a board is ever nested inside a listwrap of its own. */
.sheet-page .ppanel .gt-listwrap .gt-board,
html[data-theme="light"] .gt-app .ppanel .gt-listwrap .gt-board {
  background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
}
/* The "there is more below" fade has to fade into the bed it is drawn on —
   it is a hard-coded cream otherwise, which on a blue or plum bed is a smear. */
.ppanel .gt-listwrap::after {
  background: linear-gradient(180deg, transparent,
                              color-mix(in srgb, var(--pc) 9%, #FFFFFF));
}

/* ---- 3. the heading band ------------------------------------------------
   Back to a plate, in the panel's colour, with white on it. Beats both the
   sheet-page rule that flattens it for a cream bed and the light theme's own,
   which is why the selector names the panel. */
.sheet-page .ppanel .gt-board .gb-head,
html[data-theme="light"] .gt-app .ppanel .gt-board .gb-head {
  background: var(--pc);
  color: #FFFFFF;
  border: 0; border-radius: 11px;
  padding: 13px 18px;
  font-size: 12.5px; font-weight: 800; letter-spacing: .1em;
}
.sheet-page .ppanel .gt-board .gb-head > span,
html[data-theme="light"] .gt-app .ppanel .gt-board .gb-head > span { color: #FFFFFF; }

/* ---- 4. the rows -------------------------------------------------------
   White, with the ink redefined FOR THE SUBTREE. Every caption inside a row —
   the member's address, the date they joined, where a charity came from —
   reads `--pp-ink` or `--pp-quiet` from wherever it can find them, so
   restating the pair on the row itself turns all of them dark at once. */
.sheet-page .ppanel .gb-row,
html[data-theme="light"] .gt-app .ppanel .gb-row {
  --pp-ink: var(--pp-paper-ink);
  --pp-quiet: var(--pp-paper-quiet);
  background: var(--pp-paper);
  border-color: color-mix(in srgb, var(--pc) 20%, transparent);
  color: var(--pp-paper-ink);
}
/* The ladder stripes its numeric columns so a row can be tracked across. A
   member list has no numeric columns to stripe, so the same job is done down
   the rows instead — at a tint light enough to stay a texture, and lighter
   than the bed so the rows still read as sitting ON it. */
.sheet-page .ppanel .gb-rows > .gb-row:nth-child(even),
html[data-theme="light"] .gt-app .ppanel .gb-rows > .gb-row:nth-child(even) {
  background: color-mix(in srgb, var(--pc) 4%, var(--pp-paper));
}
.sheet-page .ppanel .gb-row:hover,
html[data-theme="light"] .gt-app .ppanel .gb-row:hover {
  border-color: var(--pc);
  box-shadow: 0 12px 26px -20px color-mix(in srgb, var(--pc) 70%, transparent);
}
.sheet-page .ppanel .gb-av,
html[data-theme="light"] .gt-app .ppanel .gb-av {
  background: color-mix(in srgb, var(--pc) 14%, #FFFFFF);
  color: var(--pc);
}
.ppanel .gb-empty {
  background: var(--pp-paper);
  border-color: color-mix(in srgb, var(--pc) 20%, transparent);
  color: var(--pp-paper-quiet);
}
/* A website on white is a link in the panel's colour, not the lime that was
   picked for it when the row was a translucent film on the green sheet. */
.sheet-page .ppanel .cht-web a,
html[data-theme="light"] .gt-app .ppanel .cht-web a { color: var(--pc); }

/* ---- 5. the plain tables, given the same treatment ----------------------
   `.atable` is still the right component for the two lists that are genuinely
   a grid of small facts — the child organisations and what each group backs.
   It gets the colour and the paper, so a table means one thing on this screen
   whichever panel drew it. */
.gt-app .ppanel .atable th,
html[data-theme="light"] .gt-app .ppanel .atable th {
  background: var(--pc); color: #FFFFFF;
}
.gt-app .ppanel .atable td,
html[data-theme="light"] .gt-app .ppanel .atable td {
  --pp-ink: var(--pp-paper-ink);
  --pp-quiet: var(--pp-paper-quiet);
  background: var(--pp-paper);
  color: var(--pp-paper-ink);
  border-color: color-mix(in srgb, var(--pc) 18%, transparent);
}
.gt-app .ppanel .atable tbody tr:nth-child(even) td {
  background: color-mix(in srgb, var(--pc) 6%, var(--pp-paper));
}
.gt-app .ppanel .atable td:first-child { box-shadow: inset 3px 0 0 var(--pc); }
.gt-app .ppanel .atable tbody tr:hover td {
  box-shadow: inset 0 0 0 999px color-mix(in srgb, var(--pc) 8%, transparent);
}
.gt-app .ppanel .atable tbody tr:hover td:first-child {
  box-shadow: inset 3px 0 0 var(--pc), inset 0 0 0 999px color-mix(in srgb, var(--pc) 8%, transparent);
}
/* The captions inside a cell. These were inline `color: var(--muted)` styles
   aimed at a cream card, which an inline style means no stylesheet could ever
   correct — so they are classes now (`.pp-quiet`) and this is what they read. */
.ppanel .atable td small,
.ppanel .pp-quiet { color: var(--pp-quiet); }

/* ---- 6. the role pills, back on a pale surface --------------------------
   `.page-deep .pill-role` paints them white-on-translucent-white for the deep
   sheet. Inside a row that is now white paper, which made Owner / Manager /
   Captain vanish along with everything else. */
.ppanel .gb-row .pill-role,
.ppanel .atable td .pill-role,
html[data-theme="light"] .gt-app.page-deep .ppanel .gb-row .pill-role,
html[data-theme="light"] .gt-app.page-deep .ppanel .atable td .pill-role {
  background: color-mix(in srgb, var(--pc) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--pc) 26%, transparent);
  color: color-mix(in srgb, var(--pc) 78%, #06140D);
}
.ppanel .gb-row .pill-role.owner,
.ppanel .atable td .pill-role.owner,
html[data-theme="light"] .gt-app.page-deep .ppanel .gb-row .pill-role.owner,
html[data-theme="light"] .gt-app.page-deep .ppanel .atable td .pill-role.owner {
  background: var(--lime); border-color: var(--lime); color: var(--deep);
}

/* ---- 7. form labels on the panel itself ---------------------------------
   These sit on the panel, not on paper, so they take the panel's ink. Both
   were written `color: var(--ink)` / `var(--deep)` for a cream card: "Email
   addresses" and "Add a note (optional)" were dark green on dark green, and
   the charity form's two labels with them. */
.ppanel .fld > label,
.ppanel .msg-fld > label { color: var(--pp-ink); }
.ppanel .msg-fld > label svg { stroke: var(--pc); }
.ppanel .fld-optional { color: var(--pp-quiet); font-weight: 400; }
.ppanel .msg-fld input:focus,
.ppanel .msg-fld textarea:focus,
.ppanel .fld input:focus,
.ppanel .fld textarea:focus {
  border-color: var(--pc);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--pc) 22%, transparent);
}

/* ---- 7b. the small buttons that live ON the paper -----------------------
   `.sheet-page .page-sheet .abtn-ghost` is cream type on a hairline, for the
   deep sheet. Save, Edit, Election and Make admin all sit inside a row, which
   is now white — so they would have gone the way of the text they sit beside.
   On paper a ghost button takes the panel's colour, which also makes it the
   one coloured thing in the row and therefore findable. */
.sheet-page .page-sheet .ppanel .gb-row .abtn-ghost,
.sheet-page .page-sheet .ppanel .atable .abtn-ghost,
html[data-theme="light"] .gt-app .ppanel .gb-row .abtn-ghost,
html[data-theme="light"] .gt-app .ppanel .atable .abtn-ghost {
  color: var(--pc);
  border-color: color-mix(in srgb, var(--pc) 36%, transparent);
  background: color-mix(in srgb, var(--pc) 7%, transparent);
}
.sheet-page .page-sheet .ppanel .gb-row .abtn-ghost:hover,
.sheet-page .page-sheet .ppanel .atable .abtn-ghost:hover,
html[data-theme="light"] .gt-app .ppanel .gb-row .abtn-ghost:hover,
html[data-theme="light"] .gt-app .ppanel .atable .abtn-ghost:hover {
  color: #FFFFFF; background: var(--pc); border-color: var(--pc);
}
/* EVERY FIELD IN A PANEL IS PAPER. A text box is white whatever is behind it,
   so it needs dark ink — and several of these inherited the panel's white
   instead, having been written when the panel was a cream card. The "new
   admin" address on the organisations panel and the "nominate a manager" one
   were white on white: you could type in them and see nothing. */
.ppanel input[type="text"],
.ppanel input[type="email"],
.ppanel input[type="url"],
.ppanel input[type="search"],
.ppanel textarea,
.ppanel select {
  background: #FFFFFF; color: var(--pp-paper-ink);
  border-color: color-mix(in srgb, var(--pc) 30%, transparent);
}
.ppanel ::placeholder { color: #8A948D; opacity: 1; }

/* ---- 8. the rest of the panel's chrome, in the same colour -------------- */
.ppanel .elec { border-color: var(--pp-line); }
.sheet-page .ppanel .ch-backing {
  background: color-mix(in srgb, var(--pc) 20%, transparent);
  border-color: color-mix(in srgb, var(--pc) 40%, transparent);
}
.ppanel .pinvite-link { border-color: var(--pp-line); }
.ppanel .tm-nominate { border-color: var(--pp-line); }

/* ==========================================================================
   TEAM MANAGEMENT, DRAWN AS A BOARD
   ==========================================================================
   "Lots of things are not visible ... and I do not like the design, find a
   nice design for that display."

   Three tracks: who, what they are now, and the control that changes it. The
   control keeps the right-hand end, which is where every other action on this
   product's lists lives. */
.gt-board.tm-board .gb-head,
.gt-board.tm-board .gb-row {
  grid-template-columns: minmax(0, 1fr) auto minmax(240px, 290px);
  gap: 16px;
}
.gt-board.tm-board .gb-head > :last-child { text-align: right; }
.gt-board.tm-board .gb-name { display: flex; flex-direction: column; gap: 1px; }
.gt-board.tm-board .gb-name small { font-weight: 400; font-size: 12px; }
.tm-set { display: flex; align-items: center; gap: 8px; justify-content: flex-end; margin: 0; }
.tm-set .ddx, .tm-set select { flex: 1 1 auto; min-width: 0; }
.tm-set .abtn { flex: none; }
/* NO HOVER NUDGE ON THIS BOARD. Every other row slides 3px under the pointer;
   this one holds a dropdown, and a transformed ancestor changes what
   `position: fixed` is measured against — which is how the open menu escapes
   a scrolling container (see gt-ui.js). A row that moves under the cursor you
   are aiming at a Save button is not much of a loss. */
.gt-board.tm-board .gb-row:hover { transform: none; }
@media (max-width: 820px) {
  .gt-board.tm-board .gb-head,
  .gt-board.tm-board .gb-row { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .gt-board.tm-board .gb-head > span:not(:first-child) { display: none; }
  .gt-board.tm-board .mb-roles { justify-content: flex-start; }
  .tm-set { justify-content: flex-start; }
}

/* ==========================================================================
   THE WAY BACK, AS A BUTTON AT THE RIGHT-HAND END
   ==========================================================================
   "Back to the leaderboard should be a button on the right side and in the
   end" — and the same on both statistics pages off the ladder.

   Its own row after the header rather than a corner of it: the headers on
   these three pages are centred blocks, and a control pinned into the corner
   of a centred block reads as something that fell off the title. */
.gh-backrow { display: flex; justify-content: flex-end; margin: -6px 0 22px; }
.abtn.gh-back { gap: 10px; }
.gh-back-ic {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .16s var(--ease);
}
.abtn.gh-back:hover .gh-back-ic { transform: translateX(3px); }
@media (max-width: 560px) {
  .gh-backrow { justify-content: stretch; }
  .abtn.gh-back { width: 100%; justify-content: center; }
}

/* ===========================================================================
   THE MANAGE HERO, CENTRED — and led by the eyebrow
   ---------------------------------------------------------------------------
   `.gt-hero` is a flex row built around a text block on the left and the admin
   chip on the right. With the chip gone (admin_base.html says why) there is
   one child, and the row becomes a centred column.

   The eyebrow takes `.is-title` but not the rule that already exists for it.
   `.gt-head .gh-eyebrow.is-title` is tuned for the cream sheet and sets
   --forest, which on this deep-green panel is the "green on green" failure
   already fixed once further up this file. Here it keeps the lime it always
   had and only grows — the panel's colours are unchanged.
   ========================================================================= */
.gt-hero.is-centred { justify-content: center; text-align: center; }
.gt-hero.is-centred .gh-mid { max-width: 62ch; margin: 0 auto; }
.gt-hero.is-centred p { margin-left: auto; margin-right: auto; }

.gt-hero .gh-eyebrow.is-title {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(21px, 2.5vw, 28px);
  letter-spacing: -.005em; text-transform: none; line-height: 1.1;
  color: var(--lime);
}
/* The page's own name still leads — it is the line that differs between
   Overview, Organisations, Approvals and Prefect, and the eyebrow is the line
   that does not. Tightened up under a kicker that is now more than twice the
   size it was, so the two read as one stacked title rather than as a label
   with a heading a long way below it. */
.gt-hero .gh-eyebrow.is-title + h1 { margin-top: 2px; }
@media (max-width: 560px) {
  .gt-hero .gh-eyebrow.is-title { font-size: 20px; }
}

/* ===========================================================================
   THE ORGANISATIONS DESK — cards over a panel
   ---------------------------------------------------------------------------
   Six cards, six colours, one panel. A card's colour arrives as a `c-` class
   and lands in `--mc`, and that pair of custom properties is the ONLY thing
   that differs between the six.

   WHY THE COLOUR IS AN EDGE AND NOT A GROUND. /manage/ is drawn on the deep
   green in one theme and on white in the other, and a card tinted with its own
   hue has to be legible on both — which in practice means six pairs of
   background-and-ink to keep in step, and the half-moved palette in this file
   is exactly what that goes wrong as. So an unselected card is the ordinary
   `--gt-card` surface every other panel here uses, and the colour arrives as
   a top rail, an icon badge and the number. Only the SELECTED card takes the
   full fill — and once it does, the ground is the card's rather than the
   theme's, so the ink on it is `--mc-on` and is decided per theme below.
   ========================================================================= */
/* The six hues, twice. --mc is the colour as TYPE and as the selected fill;
   --mc-on is the ink that goes on top of that fill. On cream the fills are
   saturated and the ink is white; on the green the fills are the light end of
   the same hue and the ink is near-black, because white on #7DC24A is 2.2:1
   and a selected card nobody can read is worse than no selection at all. */
.mcard { --mc: var(--forest); --mc-on: #fff; }
.mcard.c-organisations { --mc: #2D7A3A; }
.mcard.c-groups        { --mc: #12798C; }
.mcard.c-members       { --mc: #4B5FC4; }
.mcard.c-rounds        { --mc: #A96A12; }
.mcard.c-charity       { --mc: #B23F65; }
.mcard.c-create        { --mc: #56800A; }

html[data-theme="dark"] body.manage-app .mcard { --mc-on: #08170F; }
html[data-theme="dark"] body.manage-app .mcard.c-organisations { --mc: #7DC24A; }
html[data-theme="dark"] body.manage-app .mcard.c-groups        { --mc: #63CBDE; }
html[data-theme="dark"] body.manage-app .mcard.c-members       { --mc: #9FACF7; }
html[data-theme="dark"] body.manage-app .mcard.c-rounds        { --mc: #F0CB63; }
html[data-theme="dark"] body.manage-app .mcard.c-charity       { --mc: #F59BB4; }
html[data-theme="dark"] body.manage-app .mcard.c-create        { --mc: #C8F135; }

.mcards {
  display: grid; grid-template-columns: repeat(6, 1fr);
  gap: 14px; margin-bottom: 22px;
}
@media (max-width: 1180px) { .mcards { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 720px)  { .mcards { grid-template-columns: repeat(2, 1fr); } }

.mcard {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 2px;
  padding: 18px 16px 16px; border-radius: var(--gt-r);
  background: var(--gt-card); border: 1px solid var(--gt-hair);
  text-decoration: none; color: inherit;
  transition: transform .16s ease, box-shadow .16s ease, background .16s ease;
}
/* The rail. 4px of the card's own colour along the top, so six cards read as
   six things at a glance without six coloured grounds. */
.mcard::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 4px;
  background: var(--mc);
}
.mcard:hover { transform: translateY(-2px); box-shadow: 0 18px 34px -26px rgba(10,32,20,.55); }
.mcard:focus-visible { outline: 2px solid var(--mc); outline-offset: 2px; }

.mcard .mc-ic {
  width: 38px; height: 38px; border-radius: 11px; margin-bottom: 8px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--mc) 16%, transparent);
  color: var(--mc);
}
.mcard .mc-ic svg { width: 19px; height: 19px; fill: currentColor; }
/* The Create card wears ic-org-add, which is one of the stroke icons — filled
   it collapses into a solid blob. The other five are the ic-f- set and are
   drawn as fills by design. */
.mcard.is-cta .mc-ic svg {
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.mcard .mc-num {
  font-family: var(--display); font-size: 26px; line-height: 1;
  color: var(--mc);
}
.mcard .mc-lab { font-weight: 700; font-size: 14px; color: var(--ink); margin-top: 4px; }
.mcard .mc-sub { font-size: 12px; color: var(--muted); line-height: 1.35; }

/* Selected. The card becomes its colour and everything on it flips to
   --mc-on, the number included — which is the one thing that was already
   wearing the colour rather than sitting on it. */
.mcard.on {
  background: var(--mc); border-color: var(--mc);
  box-shadow: 0 20px 38px -26px color-mix(in srgb, var(--mc) 70%, transparent);
}
.mcard.on::before { background: color-mix(in srgb, var(--mc-on) 45%, transparent); }
.mcard.on .mc-ic { background: color-mix(in srgb, var(--mc-on) 16%, transparent); color: var(--mc-on); }
.mcard.on .mc-num,
.mcard.on .mc-lab { color: var(--mc-on); }
.mcard.on .mc-sub { color: color-mix(in srgb, var(--mc-on) 78%, transparent); }

/* The one card that is an action rather than a count. */
.mcard.is-cta .mc-num { font-size: 30px; }

/* ---- the panel ---------------------------------------------------------- */
.mpanel { display: grid; grid-template-columns: 1fr; gap: 18px; }
/* A panel that wants a sidebar puts .mp-side on its second section and the
   grid follows; one that does not stays full width without saying anything.
   `.gt-panel + .gt-panel` adds a 20px top margin between stacked panels
   elsewhere, which inside a gapped grid is 20px of nothing — zeroed here. */
.mpanel:has(.mp-side) { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; }
@media (max-width: 1080px) { .mpanel:has(.mp-side) { grid-template-columns: 1fr; } }
.mpanel .gt-panel + .gt-panel { margin-top: 0; }

/* While a fetch is in flight. Not a spinner: the old panel stays readable and
   simply stops accepting clicks, because the new one usually lands inside a
   frame or two and a spinner that flashes is worse than no spinner. */
.mpanel.is-loading { opacity: .55; pointer-events: none; transition: opacity .12s ease .08s; }

/* Rows — the shape the non-table panels use. */
.mp-rows { list-style: none; margin: 0; padding: 0; }
.mp-rows li {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  /* No side padding: .gt-panel around these already pads to its content edge,
     and a second inset would step the rows in from their own heading. */
  padding: 13px 0; border-top: 1px solid var(--gt-hair);
}
.mp-rows li:first-child { border-top: 0; }
.mp-rows li > span:first-child { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mp-rows b { font-size: 14px; color: var(--ink); }
.mp-rows small { font-size: 12.5px; color: var(--muted); display: flex; align-items: center; gap: 5px; }
/* .mp-rows b is the row's name, at 14px. A <b> INSIDE the small line is
   emphasis within that line and has to stay its size. */
.mp-rows small b { font-size: inherit; }
.mp-rows .mp-ic {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.mp-rows .mp-nested { padding-left: 2px; opacity: .9; }
.mp-rows-lg li { padding: 16px 0; }
.mp-waiting { color: var(--forest); font-weight: 700; }

/* The wizard's steps, as a ladder rather than a form. */
.mp-steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;
  counter-reset: step;
}
.mp-steps li {
  counter-increment: step;
  border: 1px solid var(--gt-hair); border-radius: var(--gt-r-sm);
  padding: 12px 14px 12px 40px; position: relative;
}
.mp-steps li::before {
  content: counter(step);
  position: absolute; left: 12px; top: 12px;
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 700;
  background: color-mix(in srgb, var(--forest) 14%, transparent); color: var(--forest);
}
/* --forest is #2D7A3A on cream and #7DC24A on the green, so the ink that goes
   ON it cannot be a literal white in both. Same split as the cards' --mc-on. */
.mp-steps { --step-ink: #fff; }
html[data-theme="dark"] body.manage-app .mp-steps { --step-ink: #08170F; }
.mp-steps li.done::before { content: "\2713"; background: var(--forest); color: var(--step-ink); }
.mp-steps li.now { border-color: var(--forest); }
.mp-steps li.now::before { background: var(--forest); color: var(--step-ink); }
.mp-steps b { display: block; font-size: 13px; color: var(--ink); }
.mp-steps small { font-size: 11.5px; color: var(--muted); }
.mp-note { margin: 16px 0 0; font-size: 12.5px; color: var(--muted); max-width: 68ch; }
/* .gt-go-btn is `width: 100%` because everywhere else it is the last thing in
   a form. In a panel head it is one item in a row, and it is a link. */
.gt-panel-head .gt-go-btn { width: auto; text-decoration: none; }

/* Nothing else needs a dark rule. .mcard, .mp-rows and .mp-steps paint from
   --gt-card, --gt-hair, --ink and --muted, and gt-shell.css already moves all
   four on body.manage-app under the dark palette — which is the whole reason
   they are written as tokens here rather than as the literals the older
   components in this file are still being unpicked from. */


/* ==========================================================================
   STORY TAGS, IN THEIR CODES' COLOURS — partials/_news_tags.html
   Sep 2026, client: "if it's a blog that is NRL it should carry its colour".
   Every tag on a story, not just the first, each on the same token its code
   wears on the ladder and the fixtures ([data-code] above). One row laid over
   the picture, so a story with two codes shows two chips side by side rather
   than one stacked on top of the other.
   ========================================================================== */
.nt-row {
  position: absolute; z-index: 1; top: 12px; left: 12px; right: 12px;
  display: flex; flex-wrap: wrap; gap: 6px; pointer-events: none;
}
.nt {
  font-family: var(--mark); font-weight: 700; font-size: 11px;
  letter-spacing: .05em; text-transform: uppercase; line-height: 1.4;
  padding: 4px 11px; border-radius: 100px;
  background: var(--lime); color: var(--deep);
  box-shadow: 0 2px 10px -3px rgba(0,0,0,.45);
}
.nt[data-code] { background: var(--code-ink); color: var(--code-on, #fff); }
/* Above the story on the reader page the row is in the flow, not over a
   picture, and a little larger — it is the first thing on the page. */
.rd-tags .nt-row { position: static; }
.rd-tags .nt { font-size: 12px; padding: 5px 13px; box-shadow: none; }

/* ==========================================================================
   THE NEWS & BLOG FILTER — partials/news_filter.html
   The ladder's chips-and-select (.compfilter / .cf-chip), on the two news
   lists. Only the "All stories" chip needs its own rule: the ladder's assumes
   the deep sheet, and the public news page is cream, so this one takes the
   colour of the text around it instead of assuming either.
   ========================================================================== */
.newsfilter { margin: 24px 0 26px; }
.newsfilter .cf-chip.cf-all {
  background: color-mix(in srgb, currentColor 8%, transparent);
  color: inherit; border-color: color-mix(in srgb, currentColor 22%, transparent);
}
.newsfilter .cf-chip.cf-all.on { background: var(--lime); color: var(--deep); border-color: var(--lime); }
.newsfilter .cf-lab { color: inherit; opacity: .7; }
/* A closed dropdown cannot show a row of colours, so it wears the chosen
   one, and each option carries its own where the browser will draw it. */
/* Its own look rather than leaning on .gt-select's: that one reads its
   surface and hairline from tokens only the member app defines, so on the
   public news page it came out as bare text with no box around it. */
.newsfilter .nf-select {
  /* Every part of the background spelled out. .gt-select sets it as one
     shorthand around var(--gt-card), and where that token is undefined the
     whole shorthand is thrown away at computed-value time — taking the
     no-repeat and the position with it, so the chevron vanished, and on the
     coloured state below it tiled across the box. */
  background-color: var(--gt-card, #fff);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F2E22' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
  border: 1.5px solid var(--gt-hair, rgba(15,46,34,.16));
  border-radius: 100px; padding: 10px 40px 10px 18px;
  font-size: 13.5px; font-weight: 700; color: var(--ink);
}
.nf-select[data-code] {
  background-color: var(--code-ink); border-color: var(--code-ink); color: var(--code-on, #fff); font-weight: 700;
  /* The chevron in white, on the code's colour. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
}
.nf-select option { background: #fff; color: #0F2E22; }
.nf-select option[data-code] { color: var(--code-ink); font-weight: 700; }

/* ==========================================================================
   THE READER, WIDER — Sep 2026, client: "the centre looks good but we have
   squeezed it; the text from left to right needs to be increased and made
   wider." The 68ch measure is the textbook one, and it was the complaint: on a
   desktop it left a narrow column with a lot of cream either side. 900px at a
   slightly larger size keeps a line comfortably under a hundred characters
   while using the sheet the page already draws.
   ========================================================================== */
.rd-main { padding-inline: clamp(18px, 3.4vw, 48px); }
.rd-art { max-width: 900px; }
.rd-prose, .rd-tease { font-size: 18.5px; }

/* ==========================================================================
   SHARE — partials/_news_share.html

   IT OPENS SIDEWAYS. "When I click it it should be like it's opening from
   left to right", with the feel of a menu collapsing open. So the platforms
   sit on the same row as the button, to its right, and the strip they are in
   goes from no width to exactly-as-wide-as-they-are.

   HOW THE WIDTH IS ANIMATED. A transition needs two lengths, and "as wide as
   the content" is not a length — width:auto cannot be animated, and a
   max-width guess either clips the last platform or leaves a hole where the
   guess was too big. `grid-template-columns: 0fr -> 1fr` on a one-column grid
   IS animatable and resolves to the content's own width, which is why there
   are two wrappers: .shr-reveal is that grid, and .shr-clip inside it is the
   overflow:hidden box, so the buttons are laid out once at their full size
   and the box wipes across them rather than reflowing them as it opens.
   ========================================================================== */
.rd-share.shr {
  display: block; margin-top: 44px; padding-top: 30px;
  border-top: 1px solid var(--rd-hair, rgba(15,46,34,.12));
}
.shr-k {
  font-family: var(--mark); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; font-size: 12px; color: var(--muted); margin: 0 0 14px;
}
/* The row the whole thing happens on. It wraps rather than scrolls, so on a
   narrow screen the platforms drop under the button instead of disappearing
   off the edge — the one failure a share control must not have. */
.shr-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.shr-btn {
  display: inline-flex; align-items: center; gap: 12px; flex: none;
  padding: 15px 26px; border: 0; border-radius: 100px; cursor: pointer;
  background: var(--deep); color: var(--lime);
  font-family: var(--body); font-size: 16px; font-weight: 800;
  box-shadow: 0 18px 36px -20px rgba(15,46,34,.75);
  transition: transform .15s var(--ease), filter .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.shr-btn > svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.shr-chev { width: 16px; height: 16px; transition: transform .35s var(--ease); }
.shr-btn:hover { transform: translateY(-2px); filter: brightness(1.12); }
.shr-btn[aria-expanded="true"] { background: var(--lime); color: var(--deep); }
/* The chevron points the way the strip is about to move, and turns to point
   back when it is open — the only part of the button that says "press me
   again to put this away". */
.shr-btn[aria-expanded="true"] .shr-chev { transform: rotate(180deg); }
.shr-btn:focus-visible { outline: 3px solid var(--lime); outline-offset: 3px; }

.shr-reveal {
  display: grid; grid-template-columns: 0fr;
  transition: grid-template-columns .42s cubic-bezier(.4, 0, .2, 1);
  min-width: 0;
}
.shr-reveal.is-open { grid-template-columns: 1fr; }
/* WITH NO SCRIPT, IT IS SIMPLY OPEN. Closed is the default state in CSS, so
   there is no flash of eight platforms at full width before gt-share.js runs
   — but a reader with JavaScript off would then have a Share button that
   collapses nothing and platforms they can never reach, so for them the strip
   starts open and stays open. `no-js` is on <html> from the first byte and
   swapped to `js` by an inline script in the head; see news_reader.html. */
html.no-js .shr-reveal { grid-template-columns: 1fr; }
html.no-js .shr-go { opacity: 1; transform: none; }
html.no-js .shr-chev { display: none; }
/* min-width:0 is load-bearing, not tidiness. A `0fr` grid track still takes
   its item's AUTOMATIC MINIMUM SIZE, which is min-content — so the closed
   strip would be "as narrow as eight buttons can be squeezed" rather than
   nothing at all, and the open animation would be a small jiggle. Zeroing the
   minimum is what lets the track actually reach nought. */
.shr-clip { overflow: hidden; min-width: 0; }
.shr-grid { display: flex; align-items: center; gap: 8px; padding: 4px 2px; }

.shr-go {
  --brand: var(--forest);
  display: inline-flex; align-items: center; gap: 9px; flex: none;
  padding: 7px; border-radius: 100px; cursor: pointer;
  border: 1px solid var(--rd-hair, rgba(15,46,34,.12)); background: var(--rd-tile, #fff);
  color: var(--ink); text-decoration: none; white-space: nowrap;
  font-family: var(--body); font-size: 13.5px; font-weight: 700;
  /* Each one arrives a beat after the one before, left to right, so the strip
     reads as opening rather than as appearing. The delay is only on the way
     IN — closing, they all go at once, because a staggered close is a control
     that takes half a second to admit it has been pressed. */
  opacity: 0; transform: translateX(-14px);
  transition: opacity .28s var(--ease), transform .28s var(--ease),
              border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.shr-reveal.is-open .shr-go { opacity: 1; transform: none; }
.shr-reveal.is-open .shr-go:nth-child(1) { transition-delay: .06s; }
.shr-reveal.is-open .shr-go:nth-child(2) { transition-delay: .10s; }
.shr-reveal.is-open .shr-go:nth-child(3) { transition-delay: .14s; }
.shr-reveal.is-open .shr-go:nth-child(4) { transition-delay: .18s; }
.shr-reveal.is-open .shr-go:nth-child(5) { transition-delay: .22s; }
.shr-reveal.is-open .shr-go:nth-child(6) { transition-delay: .26s; }
.shr-reveal.is-open .shr-go:nth-child(7) { transition-delay: .30s; }
.shr-reveal.is-open .shr-go:nth-child(8) { transition-delay: .34s; }
.shr-go[hidden] { display: none; }
.shr-go:hover { border-color: var(--brand); box-shadow: 0 14px 26px -18px rgba(15,46,34,.5); }
.shr-go:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }
.shr-ic {
  width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; flex: none;
  background: var(--brand); color: #fff;
}
.shr-ic svg { width: 19px; height: 19px; fill: currentColor; }

/* THE PLATFORM NAMES ARE READ, NOT SHOWN — on this layout only.

   "It should be like it's opening from left to right", on the row the Share
   button is already on. Eight pills carrying both a mark and a word come to
   about 860px, and the reading column is 900px: with the button in front of
   them they cannot fit, so they wrapped onto a line of their own and the
   strip stopped being beside the button at all.

   Dropping the word is what makes the row work, and it costs nothing that
   matters: a share row is the one place where the marks ARE the labels — the
   blue f, the green phone, the black X — and every one of them carries an
   aria-label plus a title, so a screen reader and a hover both still name it.
   The phone layout below puts the words back, because there the strip opens
   downward into a grid that has room for them. */
.shr-lab {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* Copy link's feedback has to survive the label going: the icon flashes lime
   instead of the word changing. See .shr-cp.copied below. */
.shr-ic.is-line svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.shr-fb { --brand: #1877F2; }
.shr-wa { --brand: #25D366; }
.shr-x  { --brand: #0F1419; }
.shr-li { --brand: #0A66C2; }
.shr-tg { --brand: #26A5E4; }
.shr-em { --brand: #4A5750; }
.shr-cp { --brand: var(--forest); }
.shr-more { --brand: var(--deep); }
.shr-cp.copied .shr-ic { background: var(--lime); color: var(--deep); }

/* On a phone the strip has nowhere to go sideways, so it opens downward
   instead — the same collapse, on the other axis — and the platforms become a
   grid rather than a line that would wrap into a ragged block. */
@media (max-width: 720px) {
  .shr-row { flex-direction: column; align-items: stretch; }
  .shr-btn { justify-content: center; }
  .shr-reveal {
    grid-template-columns: 1fr; grid-template-rows: 0fr;
    transition: grid-template-rows .42s cubic-bezier(.4, 0, .2, 1);
  }
  .shr-reveal.is-open { grid-template-rows: 1fr; }
  /* The row equivalent of the min-width:0 above, and just as load-bearing: a
     `0fr` track still takes its item's automatic minimum size, which is
     min-content — so without this the strip collapses to "as short as eight
     buttons can be stacked" rather than to nothing. */
  .shr-clip { min-height: 0; }
  .shr-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; padding-top: 10px; }
  /* Room for the words again, so they come back. */
  .shr-lab {
    position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip: auto; clip-path: none;
  }
  .shr-go { transform: translateY(-8px); padding: 7px 16px 7px 7px; justify-content: flex-start; }
  .shr-reveal.is-open .shr-go { transform: none; }
}

/* Asked not to have things move: the strip is simply there or not. */
@media (prefers-reduced-motion: reduce) {
  .shr-reveal { transition: none; }
  .shr-go { opacity: 1; transform: none; transition-delay: 0s !important; }
  .shr-chev { transition: none; }
}

/* ==========================================================================
   SLIDESHOWS IN A STORY — written by the story editor, run by gt-slides.js
   Up to ten pictures or short videos, one at a time. Without the script this
   is a row you can swipe along, which is a perfectly good way to see ten
   pictures; with it, .is-live stacks them and turns them over.
   ========================================================================== */
.gt-slides {
  position: relative; margin: 1.6em 0; border-radius: 16px; overflow: hidden;
  background: #0B1A13;
}
.gt-slides:not(.is-live) .gs-track {
  display: flex; gap: 4px; overflow-x: auto; scroll-snap-type: x mandatory;
}
.gt-slides:not(.is-live) .gs-slide { flex: 0 0 100%; aspect-ratio: 16 / 10; scroll-snap-align: start; }
.gt-slides.is-live {
  aspect-ratio: 16 / 10; outline: none; touch-action: pan-y;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.gt-slides.is-live:focus-visible { box-shadow: 0 0 0 3px var(--lime); }
.gt-slides.is-live .gs-track { position: absolute; inset: 0; }
.gt-slides.is-live .gs-slide {
  position: absolute; inset: 0; opacity: 0; transform: scale(1.03); pointer-events: none;
  transition: opacity .6s var(--ease), transform .9s var(--ease);
}
.gt-slides.is-live .gs-slide.on { opacity: 1; transform: none; pointer-events: auto; }
.gt-slides .gs-slide img, .gt-slides .gs-slide video {
  display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 0;
}
/* A phone clip is often upright; cropping it to 16:10 would cut off most of
   it. Pictures fill the frame, videos fit inside it. */
.gt-slides .gs-slide video { object-fit: contain; background: #000; }
.gs-bar {
  position: absolute; z-index: 2; top: 10px; left: 12px; right: 12px; height: 3px;
  border-radius: 3px; background: rgba(255,255,255,.28); overflow: hidden;
}
.gs-fill { display: block; height: 100%; background: var(--lime); transform-origin: left; transform: scaleX(0); }
@keyframes gs-fill { from { transform: scaleX(var(--gs-from, 0)); } to { transform: scaleX(1); } }
.gs-nav {
  position: absolute; z-index: 2; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center;
  background: rgba(8,21,14,.55); color: #fff; opacity: .85;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  transition: opacity .2s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.gs-nav svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.gs-prev { left: 12px; }
.gs-next { right: 12px; }
.gs-nav:hover, .gs-nav:focus-visible { background: var(--lime); color: var(--deep); opacity: 1; }
@media (hover: hover) {
  .gt-slides.is-live .gs-nav { opacity: 0; }
  .gt-slides.is-live:hover .gs-nav, .gt-slides.is-live:focus-within .gs-nav { opacity: .85; }
}
.gs-dots {
  position: absolute; z-index: 2; bottom: 12px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 7px; padding: 6px 10px; border-radius: 100px; background: rgba(8,21,14,.42);
  transition: opacity .2s var(--ease);
}
.gs-dot { width: 8px; height: 8px; padding: 0; border: 0; border-radius: 100px; cursor: pointer; background: rgba(255,255,255,.5); transition: width .25s var(--ease), background .2s var(--ease); }
.gs-dot.on { width: 22px; background: var(--lime); }
.gs-count {
  position: absolute; z-index: 2; top: 20px; right: 14px;
  font-family: var(--body); font-size: 12px; font-weight: 700; color: #fff;
  background: rgba(8,21,14,.5); padding: 3px 9px; border-radius: 100px;
}
.gs-sound {
  position: absolute; z-index: 2; bottom: 12px; right: 12px;
  width: 38px; height: 38px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: rgba(8,21,14,.55); color: #fff;
}
.gs-sound[hidden] { display: none; }
.gs-sound svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Held still to look at: the controls step back out of the picture. */
.gt-slides.is-held .gs-nav, .gt-slides.is-held .gs-dots { opacity: 0; }
/* In the blurred run under a gated story there is nothing to press. */
.rd-tease .gs-nav, .rd-tease .gs-dots, .rd-tease .gs-sound { display: none; }
@media (prefers-reduced-motion: reduce) {
  .gt-slides.is-live .gs-slide { transition: none; transform: none; }
}

/* ---- in the story editor ------------------------------------------------
   The editor shows a slideshow as what it is to the author: a strip of every
   slide, labelled, that opens its manager when pressed. Nothing in it can be
   typed into — it is contenteditable="false" — so the caret goes round it. */
.ned-surface .gt-slides {
  position: relative; margin: 14px 0; padding: 34px 10px 10px; cursor: pointer;
  background: color-mix(in srgb, var(--lime) 10%, transparent);
  border: 2px dashed color-mix(in srgb, var(--forest) 45%, transparent);
  border-radius: 14px;
}
.ned-surface .gt-slides::before {
  content: "Slideshow \00B7  press to change it";
  position: absolute; top: 9px; left: 12px;
  font-family: var(--mark); font-size: 11px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--forest);
}
.ned-surface .gt-slides:hover { border-color: var(--forest); }
.ned-surface .gt-slides .gs-track { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: none; }
.ned-surface .gt-slides .gs-slide { flex: 0 0 auto; width: 132px; height: 88px; aspect-ratio: auto; border-radius: 9px; overflow: hidden; background: #0B1A13; }
.ned-surface .gt-slides .gs-slide img, .ned-surface .gt-slides .gs-slide video { width: 100%; height: 100%; object-fit: cover; margin: 0; pointer-events: none; }

.ned-tool-txt { font-size: 12.5px; font-weight: 700; margin-left: 5px; }
.ned-sdlg {
  width: min(720px, calc(100vw - 32px)); max-height: calc(100vh - 48px);
  padding: 0; border: 0; border-radius: 18px; overflow: auto;
  background: var(--ad-card, #fff); color: var(--ad-ink, #0F2E22);
  box-shadow: 0 40px 90px -30px rgba(8,21,14,.6);
}
.ned-sdlg::backdrop { background: rgba(8,21,14,.55); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.nsd-head { padding: 22px 24px 6px; }
.nsd-head h3 { font-family: var(--display); font-size: 22px; margin: 0 0 6px; color: inherit; }
.nsd-head p { font-size: 14px; line-height: 1.55; margin: 0; color: var(--ad-quiet, #5B6B62); }
.nsd-list { list-style: none; margin: 14px 0 0; padding: 0 24px; display: grid; gap: 10px; counter-reset: nsd; }
.nsd-item {
  counter-increment: nsd;
  display: grid; grid-template-columns: 120px minmax(0, 1fr) auto; align-items: center; gap: 12px;
  padding: 8px; border-radius: 12px; border: 1px solid var(--ad-line, rgba(15,46,34,.12));
}
.nsd-thumb { position: relative; width: 120px; height: 76px; border-radius: 8px; overflow: hidden; background: #0B1A13; }
.nsd-thumb::before {
  content: counter(nsd); position: absolute; z-index: 1; top: 5px; left: 5px;
  min-width: 20px; height: 20px; border-radius: 100px; display: grid; place-items: center;
  background: var(--lime); color: var(--deep); font-size: 11px; font-weight: 800;
}
.nsd-thumb img, .nsd-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
.nsd-badge {
  position: absolute; bottom: 5px; left: 5px; font-size: 10.5px; font-weight: 700;
  color: #fff; background: rgba(8,21,14,.65); padding: 2px 7px; border-radius: 100px;
}
.nsd-spin { display: none; }
.nsd-item.is-loading .nsd-spin {
  display: block; position: absolute; inset: 0; background: rgba(8,21,14,.45);
}
.nsd-item.is-loading .nsd-spin::after {
  content: ""; position: absolute; top: 50%; left: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px;
  border-radius: 50%; border: 3px solid rgba(255,255,255,.35); border-top-color: var(--lime);
  animation: nsd-spin .8s linear infinite;
}
@keyframes nsd-spin { to { transform: rotate(360deg); } }
.nsd-alt {
  width: 100%; padding: 9px 11px; border-radius: 9px; font: inherit; font-size: 13.5px;
  border: 1px solid var(--ad-line, rgba(15,46,34,.16)); background: var(--ad-field, #fff); color: inherit;
}
.nsd-acts { display: flex; gap: 4px; }
.nsd-acts button {
  width: 32px; height: 32px; border-radius: 8px; cursor: pointer; font-size: 16px; line-height: 1;
  border: 1px solid var(--ad-line, rgba(15,46,34,.14)); background: transparent; color: inherit;
}
.nsd-acts button:hover:not(:disabled) { border-color: var(--forest); color: var(--forest); }
.nsd-acts button:disabled { opacity: .35; cursor: default; }
.nsd-acts [data-drop]:hover { border-color: #B3261E !important; color: #B3261E !important; }
.nsd-empty {
  margin: 14px 24px 0; padding: 26px; text-align: center; border-radius: 12px;
  border: 2px dashed var(--ad-line, rgba(15,46,34,.18)); color: var(--ad-quiet, #5B6B62); font-size: 14px;
}
.nsd-empty[hidden] { display: none; }
.nsd-add { display: flex; align-items: center; gap: 12px; padding: 16px 24px 0; }
.nsd-count { font-size: 13px; font-weight: 700; color: var(--ad-quiet, #5B6B62); }
.nsd-msg { margin: 10px 24px 0; font-size: 13.5px; font-weight: 600; color: #B3261E; min-height: 1em; }
.nsd-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 16px; padding: 16px 24px 20px; border-top: 1px solid var(--ad-line, rgba(15,46,34,.12));
}
.nsd-sp { flex: 1; }
.nsd-remove, .nsd-cancel {
  padding: 10px 16px; border-radius: 10px; cursor: pointer; font: inherit; font-size: 14px; font-weight: 700;
  border: 1px solid var(--ad-line, rgba(15,46,34,.16)); background: transparent; color: inherit;
}
.nsd-remove { color: #B3261E; border-color: rgba(179,38,30,.35); }
.nsd-remove[hidden] { display: none; }
.nsd-ok { width: auto; }
.nsd-ok:disabled { opacity: .5; cursor: default; }
@media (max-width: 560px) {
  .nsd-item { grid-template-columns: 96px minmax(0, 1fr); }
  .nsd-thumb { width: 96px; height: 64px; }
  .nsd-acts { grid-column: 1 / -1; justify-content: flex-end; }
}

/* ==========================================================================
   THE STORY EDITOR, REDRESSED — templates/manage/news_editor.html
   Sep 2026, client: "give it a nice design; have the subsection titles in caps
   and huge ... the cream background on the admin when we create the blog —
   let that cream background and that centre part find a clean way to have
   that nice blend and distinction."
   ========================================================================== */

/* ---- the sheet -----------------------------------------------------------
   The editor sat straight on the HQ shell's cream, so the writing surface and
   the page behind it were the same colour and the only edges were the
   toolbars. It is a sheet now: white, lifted off the cream by a long soft
   shadow and a hairline, which is the DISTINCTION. The BLEND is the top of
   the sheet, which starts as a pale green wash and has become white by the
   first section — so the sheet grows out of the page rather than being cut
   out of it. Same move the story reader makes with its cream-to-white. */
.ned-shell {
  --ned-wash: #F1F7E8;
  --ned-sheet: var(--ad-card, #FFFFFF);
  --ned-rule: var(--gt-hair, rgba(15,46,34,.10));
  max-width: 980px; margin: 4px auto 0;
  padding: clamp(22px, 3.6vw, 46px) clamp(18px, 4vw, 54px) clamp(26px, 4vw, 46px);
  border-radius: 28px;
  background: linear-gradient(180deg, var(--ned-wash) 0, var(--ned-sheet) 240px) var(--ned-sheet);
  border: 1px solid var(--ned-rule);
  box-shadow: 0 1px 0 rgba(255,255,255,.7) inset,
              0 2px 4px rgba(15,46,34,.03),
              0 50px 90px -60px rgba(15,46,34,.45);
}
html[data-theme="dark"] .ned-shell { --ned-wash: #173A26; }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .ned-shell { --ned-wash: #173A26; } }

/* ---- section titles: in capitals, and large ------------------------------
   "Have the subsection titles be in caps and huge." They were 13px bold
   labels — the same weight as the hints under them — so the page read as
   one long form rather than as five parts of a story. Each is a numbered
   chapter heading now, with a rule above it that separates it from the
   section before; the explanatory line under it stays in sentence case and
   the quiet ink, because a paragraph in capitals is not readable. */
.ned-sec-h {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 14px;
  margin: 40px 0 16px; padding-top: 30px;
  border-top: 1px solid var(--ned-rule);
  font-family: var(--display); font-weight: 400;
  font-size: clamp(24px, 2.6vw, 34px); line-height: 1; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink);
}
.ned-sec-n {
  align-self: center; flex: none;
  display: inline-grid; place-items: center;
  min-width: 38px; height: 28px; padding: 0 8px; border-radius: 100px;
  background: var(--ned-fill, var(--deep)); color: var(--ned-fill-ink, var(--lime));
  font-family: var(--mark); font-size: 12px; letter-spacing: .06em;
}
.ned-sec-h small {
  flex-basis: 100%; margin-top: 8px;
  font-family: var(--body); font-size: 13.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none; line-height: 1.45; color: var(--muted);
}
/* The first one opens the sheet; a rule above it would be a line under
   nothing. */
.ned-meta-row + .ned-sec-h { border-top: 0; padding-top: 6px; margin-top: 28px; }

/* ---- the top row: tags beside when-it-goes-out -------------------------- */
.ned-meta-row { align-items: stretch; gap: 18px; }
.ned-meta-row .ned-tags { flex: 1 1 360px; }
.ned-when { flex: 0 1 330px; }

/* ==========================================================================
   WHEN IT GOES OUT — a state line, a button, and a calendar behind it
   ========================================================================== */
.pub {
  --sc: var(--muted);
  --sc-bg: rgba(15,46,34,.06);
  position: relative; height: 100%;
  padding: 16px 18px; border-radius: 18px;
  background: var(--ad-card, #fff);
  border: 1px solid var(--ned-rule, rgba(15,46,34,.10));
  box-shadow: 0 1px 2px rgba(15,46,34,.04);
}
.pub[data-state="now"]       { --sc: #1C5E30; --sc-bg: rgba(45,122,58,.10); }
.pub[data-state="live"]      { --sc: #1C5E30; --sc-bg: rgba(45,122,58,.10); }
.pub[data-state="scheduled"] { --sc: #7E5D07; --sc-bg: rgba(244,196,48,.16); }
.pub[data-state="back"]      { --sc: #45566C; --sc-bg: rgba(69,86,108,.10); }
.pub-k {
  margin: 0 0 10px; font-family: var(--mark); font-size: 11.5px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.pub-line {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 13px; border-radius: 100px; background: var(--sc-bg);
}
.pub-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--sc); flex: none; }
.pub[data-state="scheduled"] .pub-dot { animation: schedPulse 1.8s ease-in-out infinite; }
.pub-state { font-size: 14px; font-weight: 800; color: var(--sc); }
.pub-when { margin: 9px 0 0; min-height: 1.3em; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.pub-acts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.pub-btn {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 11px 17px; border-radius: 12px; border: 0;
  background: var(--ned-fill, var(--deep)); color: var(--ned-fill-ink, var(--lime));
  font-family: var(--body); font-size: 14px; font-weight: 800;
  box-shadow: 0 12px 24px -16px rgba(15,46,34,.8);
  transition: transform .15s var(--ease), filter .15s var(--ease);
}
.pub-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; }
.pub-btn:hover { transform: translateY(-1px); filter: brightness(1.15); }
/* Open is a ring, not a colour swap: on the dark palette the fill is
   already lime, so swapping to lime would show no change at all. */
.pub.is-open .pub-btn { box-shadow: 0 0 0 3px var(--ad-card, #fff), 0 0 0 6px var(--lime); }
.pub-btn:focus-visible { outline: 3px solid var(--lime); outline-offset: 3px; }
.pub-reset {
  cursor: pointer; border: 0; background: none; padding: 6px 2px;
  font-family: var(--body); font-size: 13px; font-weight: 700; color: var(--forest);
  text-decoration: underline; text-underline-offset: 3px;
}
.pub-reset[hidden] { display: none; }

/* ---- the calendar -------------------------------------------------------- */
.pub-pop {
  position: absolute; z-index: 40; top: calc(100% + 10px); right: 0;
  width: min(360px, calc(100vw - 40px));
  padding: 16px; border-radius: 20px;
  background: var(--ad-card, #fff); color: var(--ink);
  border: 1px solid var(--ned-rule, rgba(15,46,34,.12));
  box-shadow: 0 40px 80px -30px rgba(8,21,14,.55), 0 2px 6px rgba(8,21,14,.08);
  animation: ppIn .18s var(--ease) both;
}
.pub-pop[hidden] { display: none; }
@keyframes ppIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .pub-pop { animation: none; } }

.pp-presets { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; margin-bottom: 14px; }
.pp-presets button {
  cursor: pointer; padding: 9px 10px; border-radius: 11px;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.12)); background: transparent;
  font-family: var(--body); font-size: 13px; font-weight: 700; color: var(--ink);
}
.pp-presets button:hover { border-color: var(--forest); color: var(--forest); background: rgba(45,122,58,.06); }

.pp-month { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.pp-month b { font-family: var(--display); font-weight: 400; font-size: 17px; letter-spacing: .02em; }
.pp-nav {
  width: 34px; height: 34px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.12)); background: transparent;
  font-size: 20px; line-height: 1; color: var(--ink);
}
.pp-nav:hover { border-color: var(--forest); color: var(--forest); }
.pp-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
.pp-dow {
  text-align: center; padding: 4px 0 6px;
  font-family: var(--mark); font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.pp-day {
  aspect-ratio: 1; border: 0; border-radius: 10px; cursor: pointer; background: transparent;
  font-family: var(--body); font-size: 13.5px; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.pp-day:hover { background: rgba(45,122,58,.10); }
.pp-day.is-past { color: var(--muted); }
.pp-day.is-today { box-shadow: inset 0 0 0 1.5px var(--forest); }
.pp-day.is-picked { background: var(--ned-fill, var(--deep)); color: var(--ned-fill-ink, var(--lime)); font-weight: 800; }
.pp-day:focus-visible, .pp-nav:focus-visible, .pp-presets button:focus-visible,
.pp-ok:focus-visible, .pp-cancel:focus-visible { outline: 3px solid var(--lime); outline-offset: 2px; }

.pp-time {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--ned-rule, rgba(15,46,34,.10));
}
.pp-k {
  margin-right: auto; font-family: var(--mark); font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.pp-time select {
  width: auto; padding: 8px 10px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.14));
  background: var(--ad-field, #fff); color: var(--ink);
  font-family: var(--body); font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.pp-ampm { display: inline-flex; padding: 3px; border-radius: 10px; background: rgba(15,46,34,.07); }
.pp-ampm button {
  cursor: pointer; border: 0; background: transparent; border-radius: 8px; padding: 6px 11px;
  font-family: var(--body); font-size: 13px; font-weight: 800; color: var(--muted);
}
.pp-ampm button.on { background: var(--ned-fill, var(--deep)); color: var(--ned-fill-ink, var(--lime)); }

.pp-sum {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 12px;
  background: var(--ned-sum-bg, rgba(244,196,48,.14)); color: var(--ned-sum-ink, #6E5106);
  font-size: 13px; font-weight: 700;
}
.pp-sum.is-past { background: var(--ned-past-bg, rgba(69,86,108,.10)); color: var(--ned-past-ink, #3E4E63); }
.pp-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.pp-cancel {
  cursor: pointer; padding: 10px 14px; border-radius: 11px; background: transparent;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.14));
  font-family: var(--body); font-size: 13.5px; font-weight: 700; color: var(--ink);
}
.pp-ok {
  cursor: pointer; padding: 10px 18px; border-radius: 11px; border: 0;
  background: var(--lime); color: var(--deep);
  font-family: var(--body); font-size: 13.5px; font-weight: 800;
}
.pp-ok:hover { filter: brightness(1.05); }

/* The dark palette: the calendar's own tints lifted so they read on a dark
   panel rather than disappearing into it. */
html[data-theme="dark"] .pp-ampm { background: rgba(232,228,213,.08); }
html[data-theme="dark"] .pub[data-state="now"],
html[data-theme="dark"] .pub[data-state="live"]      { --sc: #A7DE7E; }
html[data-theme="dark"] .pub[data-state="scheduled"] { --sc: #F0CB63; }
html[data-theme="dark"] .pub[data-state="back"]      { --sc: #C3D2E0; }

/* ==========================================================================
   FEATURED MEDIA — three pictures and three videos, in order
   ========================================================================== */
.ned-fm {
  position: relative; padding: 16px; border-radius: 20px;
  background: var(--ad-card, #fff);
  border: 1.5px dashed var(--ned-rule, rgba(15,46,34,.16));
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.ned-fm.has-items { border-style: solid; }
.ned-fm.is-dropping { border-color: var(--forest); background: rgba(45,122,58,.06); }

.ned-fm-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px;
}
.ned-fm-grid:empty { display: none; }
.ned-fm-item {
  display: flex; flex-direction: column; gap: 8px; padding: 8px;
  border-radius: 16px; background: var(--ad-card, #fff);
  border: 1px solid var(--ned-rule, rgba(15,46,34,.12));
  cursor: grab; transition: box-shadow .15s var(--ease), transform .15s var(--ease), opacity .15s;
}
.ned-fm-item.is-lead { border-color: var(--forest); box-shadow: 0 0 0 1px var(--forest); }
.ned-fm-item.is-dragging { opacity: .45; }
.ned-fm-item.is-over { transform: scale(1.03); box-shadow: 0 0 0 2px var(--lime); }
.ned-fm-thumb {
  position: relative; aspect-ratio: 3 / 2; border-radius: 11px; overflow: hidden;
  background: var(--deep);
}
.ned-fm-thumb img, .ned-fm-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.ned-fm-badge {
  position: absolute; top: 7px; left: 7px; padding: 3px 9px; border-radius: 100px;
  background: rgba(8,21,14,.72); color: #fff;
  font-family: var(--mark); font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
}
.ned-fm-item.is-lead .ned-fm-badge { background: var(--lime); color: var(--deep); }
.ned-fm-kind {
  position: absolute; bottom: 7px; left: 7px; padding: 2px 8px; border-radius: 100px;
  background: rgba(8,21,14,.62); color: #fff; font-size: 10.5px; font-weight: 700;
}
.ned-fm-spin { position: absolute; inset: 0; background: rgba(8,21,14,.5); }
.ned-fm-spin::after {
  content: ""; position: absolute; top: 50%; left: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border-radius: 50%; border: 3px solid rgba(255,255,255,.3); border-top-color: var(--lime);
  animation: nsd-spin .8s linear infinite;
}
.ned-fm-alt {
  width: 100%; padding: 8px 10px; border-radius: 10px; font: inherit; font-size: 13px;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.14)); background: var(--ad-field, #fff); color: var(--ink);
}
.ned-fm-acts { display: flex; gap: 5px; }
.ned-fm-acts button {
  flex: 1; height: 32px; border-radius: 9px; cursor: pointer; font-size: 15px; line-height: 1;
  border: 1px solid var(--ned-rule, rgba(15,46,34,.14)); background: transparent; color: var(--ink);
}
.ned-fm-acts button:hover:not(:disabled) { border-color: var(--forest); color: var(--forest); }
.ned-fm-acts button:disabled { opacity: .35; cursor: default; }
.ned-fm-acts button.is-drop:hover:not(:disabled) { border-color: var(--ned-danger, #B3261E); color: var(--ned-danger, #B3261E); }

.ned-fm-empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center;
  padding: 26px 16px 20px; color: var(--muted);
}
.ned-fm-empty[hidden] { display: none; }
.ned-fm-empty svg { width: 30px; height: 30px; stroke: currentColor; fill: none; stroke-width: 1.5; margin-bottom: 4px; }
.ned-fm-empty b { font-size: 15px; color: var(--ink); }
.ned-fm-empty span { font-size: 13.5px; max-width: 48ch; }

.ned-fm-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.ned-fm-add {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 10px 14px; border-radius: 12px; border: 0;
  background: var(--lime); color: var(--deep);
  font-family: var(--body); font-size: 14px; font-weight: 800;
}
.ned-fm-add.is-video { background: var(--ned-fill, var(--deep)); color: var(--ned-fill-ink, var(--lime)); }
.ned-fm-add svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.ned-fm-add b {
  padding: 2px 8px; border-radius: 100px; background: rgba(8,21,14,.12);
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.ned-fm-add.is-video b { background: rgba(200,241,53,.18); }
.ned-fm-add:disabled { opacity: .45; cursor: not-allowed; }
.ned-fm-add:not(:disabled):hover { filter: brightness(1.08); }
.ned-fm-msg { margin: 0 0 0 auto; font-size: 13px; font-weight: 700; color: var(--forest); }
.ned-fm-msg.is-bad { color: var(--ned-danger, #B3261E); }

/* ---- the save row -------------------------------------------------------- */
.ned-publish-row {
  margin-top: 36px !important; padding: 18px 20px; border-radius: 18px;
  background: linear-gradient(135deg, #123A2B, #0C2A1F 70%);
  border: 0;
}
.ned-publish-row label { color: #FFFFFF; }
.ned-publish-row input[type="checkbox"] { accent-color: var(--lime); }

@media (max-width: 640px) {
  .ned-shell { border-radius: 20px; }
  .pub-pop { left: 0; right: auto; }
  .ned-fm-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ==========================================================================
   ============ THE BAR FITS THE SCREEN IT IS ON (Sep 2026) =================
   ==========================================================================
   Client: "containers and certain things fall out of place depending on the
   laptop or pad in use ... people with Mac where things are not holding, and
   on my HP everything is intact".

   Measured, not guessed (Chromium, 8px steps, a staff account with all eight
   items): the full bar needs 1,474px against a 1,300px cap, its compact tier
   only started at 1,400px, and even that needs 1,330px. So every screen from
   ~1,360 to ~1,650px wide pushed the profile and log-out controls off the
   right-hand edge — and 1,440, 1,470, 1,512 and 1,536px are the widths
   MacBooks run at by default, where an HP laptop is usually 1,366 or 1,920.
   That was "falls apart on a Mac, fine on my HP".

   A breakpoint cannot fix that for good, because the width the bar needs
   depends on things no media query can see: how many items this account
   has, how long the league's name is, the machine's text scaling and zoom,
   which fonts it ended up with. So the bar measures itself (the script right
   after the nav in app_base.html) and steps down a tier at a time until the
   account cluster is on screen and on the first row:

     nav-tight   the wordmark becomes GT, the theme switch drops its words,
                 the links tighten (the old 1,181–1,400px tier, on demand)
     nav-tight2  the links drop their glyphs
     nav-burger  the links go to the drawer, as they do under 1,181px

   The media queries stay as the starting point, so on a screen where the bar
   already fits nothing moves.
   ========================================================================== */
.gt-v2 .an-inner.nav-tight .an-brand,
.an-inner.nav-tight .an-brand { font-size: 0; letter-spacing: 0; }
/* No colour on the G: it inherits the wordmark's own ink, which is white on
   the green bar and dark on the light one. */
.gt-v2 .an-inner.nav-tight .an-brand::before,
.an-inner.nav-tight .an-brand::before { content: "G"; font-size: 20px; }
.gt-v2 .an-inner.nav-tight .an-brand::after,
.an-inner.nav-tight .an-brand::after { content: "T"; font-size: 20px; color: var(--lime); }
html[data-theme="light"] .gt-app .an-inner.nav-tight .an-brand::after { color: var(--lt-accent-ink); }
.an-inner.nav-tight .an-theme button span { display: none; }
.an-inner.nav-tight .an-theme button { padding: 6px 8px; }
.gt-v2 .an-inner.nav-tight .an-links a,
.an-inner.nav-tight .an-menu-btn { padding: 9px 10px; font-size: 13.5px; }
.gt-v2 .an-inner.nav-tight2 .an-links .an-ic,
.an-inner.nav-tight2 .an-menu-btn .an-ic { display: none; }
.gt-v2 .an-inner.nav-burger .an-links,
.an-inner.nav-burger .an-org { display: none; }
.an-inner.nav-burger .an-burger { display: inline-grid; }

/* THE LINKS LEAVE UNDER 1,181px, AS THEY WERE ALWAYS MEANT TO. The chip's
   own strip (max-width: 1300px, above) sets `.gt-v2 .an-links { display:
   flex }` to give row one its height, and at two classes deep that outranked
   the one-class rules hiding the row at 1,180 and 760px. So on every tablet
   and phone the whole row of links stayed on the bar beside the burger,
   could not shrink (flex: none), and pushed the page up to 724px wider than
   the screen. */
@media (max-width: 1180px) {
  .gt-v2 .an-links, .gt-v2 .an-org { display: none; }
}

/* The short mark in the LIGHT theme. Its "G" was hard-coded white, which on
   the light bar is white on cream: the logo vanished between 1,181 and
   1,400px and on every phone. It takes the wordmark's ink instead, and the
   "T" the light theme's accent ink. */
html[data-theme="light"] .gt-app .an-brand::before { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .an-brand::after { color: var(--lt-accent-ink); }

/* ==========================================================================
   ============ EVERY WORD READABLE IN BOTH THEMES (Sep 2026 audit) =========
   ==========================================================================
   Client, Sep 2026: "go page by page checking each and every text in both
   green and light theme ... I do not want to go to a page and when it's in
   green theme I can see and when it's in the light mode I cannot see certain
   things, and vice versa."

   Found by measuring, not by eye: every page a member, a league admin and an
   HQ admin can open (59 of them), in both themes, at laptop and phone widths,
   in Chromium and WebKit. For each piece of text the browser was asked what is
   actually painted underneath it, and the ratio computed; anything under WCAG
   AA (4.5:1, or 3:1 for large type) is answered below. The worst were not
   subtle — about 1:1, invisible — and nearly all were one of three mistakes:

     1. A rule written for the green SHEET leaking into the cream CARD inside
        it. `.sheet-page .page-sheet .app-sub` is cream, so was every hint in
        every card on the profile, invite and charity pages.
     2. A dark-theme rule with no light-theme partner — the new-league
        wizard's rail and footer, the Manage captions, the org picker menus:
        cream words on the light theme's white.
     3. Opacity on text. 72% of a grey is a paler grey; the chips, counts and
        stamps below keep their colour and lose the opacity.

   `html:not([data-theme="light"])` scopes the green-only rules: the green is
   the product, so "auto" and an unset key are green, as everywhere else.
   ========================================================================== */

/* ---- 1. the green sheet, and the cream card inside it -------------------- */
.sheet-page .page-sheet .card .app-sub,
.sheet-page .page-sheet .card .hint { color: var(--muted); }
.sheet-page .page-sheet .card .app-eyebrow { color: var(--forest); }
/* Straight on the green sheet, text inherited the app's dark ink — dark on
   dark (the group charity ballot, "All organisations" on league search). */
html:not([data-theme="light"]) .sheet-page .page-sheet > .hint { color: rgba(232,228,213,.82); }
html:not([data-theme="light"]) .sheet-page .page-sheet > .hint a { color: var(--lime); }
html:not([data-theme="light"]) .sheet-page .browse-title .card-h { color: #fff; }
html:not([data-theme="light"]) .sheet-page .browse-count { color: var(--lime); background: rgba(200,241,53,.12); }
html:not([data-theme="light"]) .sheet-page .browse-sort label { color: rgba(232,228,213,.8); }
/* The charity "backing" strip is a tint over the sheet, not a card. */
html:not([data-theme="light"]) .sheet-page .ch-backing .ch-name { color: #fff; }
html:not([data-theme="light"]) .sheet-page .ch-backing .ch-url,
html:not([data-theme="light"]) .sheet-page .ch-backing .ch-current-label { color: rgba(232,228,213,.84); }
html[data-theme="light"] .gt-app .ch-backing .ch-url,
html[data-theme="light"] .gt-app .ch-backing .ch-current-label { color: #3F4A43; }

/* Role pills. Written as white on glass for a DARK ground, but keyed on the
   page being dark (.page-deep), so they were white-on-white wherever a pill
   sat in a table or card on such a page: profile, league search, "You" on
   the leaderboard. On a light surface they take their own colours again. */
.page-deep :is(.card, .atable, .gt-board, .bl-row, .gb-row) .pill-role { color: #1C5E30; background: rgba(15,46,34,.08); }
.page-deep :is(.card, .atable, .gt-board, .bl-row, .gb-row) .pill-role.manager { color: #1C5E30; background: rgba(45,122,58,.16); }
.page-deep :is(.card, .atable, .gt-board, .bl-row, .gb-row) .pill-role.captain { color: #245B66; background: rgba(46,110,122,.16); }
.page-deep :is(.card, .atable, .gt-board, .bl-row, .gb-row) .pill-role.owner { color: var(--deep); background: var(--lime); }
/* ...except on the dashboard banner's photograph, where they are glass, now
   darker glass: the right-hand end of the banner is the bright end of it. */
.page-deep .dash-banner .pill-role:not(.owner),
.dash-banner .pill-role:not(.owner),
html[data-theme="light"] .gt-app .dash-banner .pill-role:not(.owner) {
  color: #fff; background: rgba(8,21,14,.55);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}

/* ---- 2. dark-only rules that needed a light partner ---------------------- */
/* The new-league wizard: rail, draft chip and footer note were cream on the
   light theme's white. */
html[data-theme="light"] .gt-app .wz-label,
html[data-theme="light"] .gt-app .wiz-step.current .wz-label { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .wz-sub,
html[data-theme="light"] .gt-app .wiz-step.current .wz-sub { color: #4A5850; }
html[data-theme="light"] .gt-app .wiz-step:not(.current) .wz-num { color: #3D4B43; background: rgba(15,46,34,.06); border-color: rgba(15,46,34,.2); }
html[data-theme="light"] .gt-app .draft-chip,
html[data-theme="light"] .gt-app .wiz-foot { background: var(--lt-surface); border-color: var(--lt-line); }
html[data-theme="light"] .gt-app .draft-chip .dc-txt b,
html[data-theme="light"] .gt-app .wf-txt b,
html[data-theme="light"] .gt-app .wf-note b { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .draft-chip .dc-txt i,
html[data-theme="light"] .gt-app .wf-txt i { color: #4A5850; }
html[data-theme="light"] .gt-app .hl-lime { color: var(--lt-accent-ink); }

/* The org picker's options on billing, charities and Manage ▸ Charity were
   white on the light theme's white. */
html[data-theme="light"] .gt-app.sheet-page .pnav-opt { color: var(--lt-ink); background: rgba(15,46,34,.05); }
html[data-theme="light"] .gt-app.sheet-page .pnav-lab { color: #4A5850; }

/* The dashboard's season-points card is deliberately dark in the light
   theme, but a nested-card rule painted it white under its cream words. */
html[data-theme="light"] .gt-app .card .card .stat-card.accent,
html[data-theme="light"] .gt-app .stat-card.accent { background: linear-gradient(160deg, #14382A, var(--lt-ink)); }
/* The round eyebrow on the banner photograph stays lime: the light theme's
   eyebrow colour is a dark olive meant for white paper, not for the photo. */
.sheet-page .page-sheet .dash-banner .db-eyebrow,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .dash-banner .db-eyebrow { color: var(--lime); opacity: 1; }
/* Words on a photograph carry their own shadow, whatever the photograph is. */
.dash-banner .db-h1, .dash-banner .dp-txt, .dash-banner .db-eyebrow, .dash-banner .db-rank,
.gt-hero h1, .gt-hero p, .gt-hero .gh-eyebrow { text-shadow: 0 1px 2px rgba(0,0,0,.35), 0 2px 18px rgba(0,0,0,.45); }
.gt-hero p { opacity: .94; }

/* Manage (the league admin) in the light theme: its captions and ghost
   buttons were written for the green shell only — cream on the light sheet. */
html[data-theme="light"] body.manage-app .app-sub { color: #4A5850; }
html[data-theme="light"] body.manage-app .app-eyebrow { color: var(--forest); }
html[data-theme="light"] body.manage-app .btn-ghost { color: var(--forest); border-color: rgba(15,46,34,.25); }
/* Breadcrumbs, which were inline lime / cream in the templates. */
.crumb-link { color: var(--lime); font-weight: 700; text-decoration: none; }
.crumb-link:hover { text-decoration: underline; }
.crumb-sep { color: rgba(245,241,230,.55); }
html[data-theme="light"] .crumb-link { color: var(--forest); }
html[data-theme="light"] .crumb-sep { color: rgba(15,46,34,.72); }
/* Manage in green: dropdowns kept a white button under the shell's light ink,
   so a chosen team or status was invisible; the Prefect tab was white on its
   pale-green highlight. */
html[data-theme="dark"] body.manage-app .ddx-btn { background: var(--ad-field, #0C2417); border-color: rgba(255,255,255,.16); color: var(--ad-ink, #E9F0EA); box-shadow: none; }
html[data-theme="dark"] body.manage-app .ddx-val.is-placeholder { color: rgba(233,240,234,.68); }
html[data-theme="dark"] body.manage-app .ddx-menu { background: var(--ad-card, #102A1D); border-color: rgba(255,255,255,.14); }
html[data-theme="dark"] body.manage-app .ddx-opt { color: var(--ad-ink, #E9F0EA); }
html[data-theme="dark"] body.manage-app .ddx-opt:hover,
html[data-theme="dark"] body.manage-app .ddx-opt.on { background: rgba(200,241,53,.14); color: #fff; }
html[data-theme="dark"] body.manage-app .pf-tab.on { color: #0C2417; }

/* The news page's "Your move" card is dark in both themes. It was dark by an
   inline background the light theme's card rule could not see past, and so
   turned white under its cream words. */
.news-cta,
.sheet-page .page-sheet .card.news-cta,
html[data-theme="light"] .gt-app.sheet-page .page-sheet .card.news-cta { background: #0F2E22; border-color: #0F2E22; }
html[data-theme="light"] .gt-app.sheet-page .page-sheet .news-cta .abtn-ghost { color: #E8E4D5; background: transparent; border-color: rgba(255,255,255,.35); }

/* The nav in light: the org name, the Manage menu and the theme switch's
   idle half were 3.5–4.2:1. */
html[data-theme="light"] .gt-app .an-ctx-org { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .an-links a,
html[data-theme="light"] .gt-app .an-menu-btn { opacity: 1; }
html[data-theme="light"] .gt-app .an-theme button:not(.on) { color: #3D4B43; }
html[data-theme="light"] .gt-app .charity-strip .cs-right { opacity: .92; }

/* The group wall's rotating cards sit on white in both themes, but their
   times, club lines and ghost chips were still cream (1.1–2.6:1). */
.sheet-page .wr-card .wr-when,
.sheet-page .wr-card .wr-who small { color: var(--muted); opacity: 1; }
.sheet-page .wr-card .wr-who b { color: var(--ink); }
.sheet-page .wr-card .wr-chip { color: var(--forest); background: rgba(15,46,34,.07); }
.sheet-page .wr-card .wr-chip.ghost { color: var(--muted); background: transparent; box-shadow: inset 0 0 0 1px rgba(15,46,34,.16); opacity: 1; }
html[data-theme="light"] .gt-app .wr-card .wr-when,
html[data-theme="light"] .gt-app .wr-card .wr-who small,
html[data-theme="light"] .gt-app .wr-card .wr-chip.ghost { color: #4A5850; }

/* ---- 3. opacity on text -------------------------------------------------- */
/* A switched-off competition chip was the whole chip at 72%: white at 3.7:1
   on NRL's amber. Off is now told by the colour going quiet, with the words
   at full strength. */
.cf-chip[data-code]:not(.on),
.sfilter-chip[data-code]:not(.on) { opacity: 1; filter: saturate(.4) brightness(.9); }
.ndc-more {
  color: color-mix(in srgb, var(--code-ink, var(--forest)) 80%, #000);
  background: color-mix(in srgb, var(--code-ink, var(--forest)) 9%, transparent);
}
.gt-toast .gtt-x { opacity: .8; }
.rw-opt > i { opacity: .9; }
.cf-pick .cf-lab { opacity: 1; }
.wr-when, .wf-reply small, .wf-fine { opacity: .74; }
.wr-chip.ghost { opacity: .82; }
/* My Tips: the losing side is dimmed as a whole, so its words start darker. */
.fxc-outcome i { color: #26332B; opacity: 1; }
.fxc-outcome .fxc-pts,
.fxc-outcome.is-won i,
.fxc-outcome.is-won .fxc-pts { color: #14532D; opacity: 1; }
.gt-filter i { opacity: 1; }
.gfx-stamp { opacity: 1; }
.gfx-stamp.is-auto { color: #6B5410; }
.gt-strip .gsp-sub, .gt-stat .gs-sub { opacity: .8; }
/* Ladder and leaderboard. A step that cannot be taken is exempt from the
   contrast bar, but it should still be legible as "not now". */
.rs-step.is-off { opacity: .55; }
.gt-board.ladder .lad-left i { opacity: .85; }
.gt-board .gb-acc i, .gt-board .gb-behind i { opacity: 1; }
.gt-board.mb-board .gb-name small { opacity: 1; }
.gt-board .gb-av { color: #1F5C2B; }
/* Billing tiers. */
.tier[data-tier] .tier-price span, .tier[data-tier] .tier-per { opacity: .9; }
.tier[data-tier] .tier-for { opacity: .92; }
.tier[data-tier].featured .tier-feats .tk { color: var(--tier-ink); }
/* Charity / members cards: white on the members green was 3.4:1. */
.pcard.on, .sheet-page .pcard.on { background: color-mix(in srgb, var(--pc) 78%, #000); }
.mcard.on .mc-sub { color: color-mix(in srgb, var(--mc-on) 92%, transparent); }
html:not([data-theme="dark"]) .gt-chip.needs { color: #6B480C; }
/* HQ bits drawn by this file. */
.np-sep { opacity: .7; }
.gt-choice .gc-txt p { opacity: .82; }
.enq-tabs a { opacity: .86; }

/* Placeholders nobody styled were the browser's own #757575 — 2.7:1 on the
   dark fields. They take the field's own ink at 70%, which reads on either
   ground. Zero specificity, so every placeholder that WAS styled keeps its
   colour. */
::placeholder { color: color-mix(in srgb, currentColor 70%, transparent); opacity: 1; }

/* ---- the light theme's sheet covers the photo strips ----------------------
   In light the sheet was made transparent so "the page becomes the ground".
   But that ground is only the column between the two photo strips, and the
   sheet is wider than the column — so everything near its edges sat on the
   photographs: "YOUR ORGANISATION", "STEP 1 OF 7", the banner's corners. The
   green sheet is opaque, which is why it never showed there. The light sheet
   now takes the page's own colour: still no visible box, and the strips stay
   outside it. */
html[data-theme="light"] .gt-app.sheet-page .page-sheet { background: var(--lt-page); border-radius: 22px; }

/* ==========================================================================
   ============ NOTHING WIDER THAN ITS SCREEN (Sep 2026 audit) ===============
   ==========================================================================
   The same pages at eleven widths — the HP and MacBook laptop widths, iPads
   both ways up, a 390px phone — in Chromium, Firefox and WebKit (Safari's
   engine). Nearly every break was the same CSS default: a grid or flex child
   will not shrink below its content (min-width: auto), so one long team name
   or a 220px dropdown pushes its row off the side of a phone. What follows is
   the missing min-width: 0 / minmax(0, 1fr), at each place it was found.
   ========================================================================== */
.grid-app > * { min-width: 0; }
@media (max-width: 820px) { .g-2, .g-3 { grid-template-columns: minmax(0, 1fr); } }
.statgrid { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.gwh-stack { grid-template-columns: minmax(0, 1fr); }
.gwh-slot { min-width: 0; }
.mpanel { grid-template-columns: minmax(0, 1fr); }
.mpanel > * { min-width: 0; }

/* Fixture cards: each team's side sized its column to its content — 120px of
   it inside a 70px slot on a phone. */
.fxc-side { grid-template-columns: minmax(0, 1fr); }
.fxc-form { min-width: 0; flex-wrap: wrap; }
@media (max-width: 480px) {
  .fxc-team { grid-template-columns: 30px minmax(0, 1fr); column-gap: 6px; }
  .fxc-outcome { grid-column: 1 / -1; justify-self: start; max-width: 100%; flex-wrap: wrap; }
}
/* Tip a round: the two team buttons either side of the "v". */
.pick { grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1fr); }
.pick .team { min-width: 0; }
@media (max-width: 480px) { .pick .team { padding: 12px 10px; } }
/* The dashboard's league picker had a 220px floor. */
@media (max-width: 560px) {
  .dash-ctl .dc-pick { flex-wrap: wrap; }
  .dash-ctl .dc-pick .ddx, .dash-ctl .dc-pick > select { min-width: 0; flex: 1 1 100%; }
}
/* The new-league wizard: seven steps in a rail that could not wrap, and two
   footer buttons that would not give up their padding. */
@media (max-width: 620px) {
  .wiz-rail { overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .wiz-rail::-webkit-scrollbar { display: none; }
  .wiz-foot .wiz-nav .abtn { min-width: 0; padding-left: 14px; padding-right: 14px; }
}
/* Competition statistics on a phone: five number columns left the club name
   no width at all. */
.gt-board { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 480px) {
  .gt-board.compstats .gb-head, .gt-board.compstats .gb-row { grid-template-columns: minmax(0, 1fr) 40px 40px 46px 38px; column-gap: 6px; }
}
/* Manage: two pages set their two columns inline; on a phone they stack. */
@media (max-width: 820px) {
  .manage-app .grid-app[style*="grid-template-columns"] { grid-template-columns: minmax(0, 1fr) !important; }
}
/* A table wider than a phone scrolls inside its panel instead of pushing the
   page; and the organisations table's "Manage" heading gets its width back. */
@media (max-width: 760px) {
  .gt-panel .gt-table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
.gt-table.gt-t-orgs { table-layout: auto; }

/* HQ on a phone — the staff console was never laid out below a laptop. Not a
   redesign: nothing is allowed to be wider than the screen. */
@media (max-width: 600px) {
  .gt-shell > *, .gta-grid-2 > *, .gta-cats > *, .gta-cards > * { min-width: 0; max-width: 100%; }
  .gta-grid-2, .gta-cats, .gta-cards { grid-template-columns: minmax(0, 1fr) !important; }
  .gta-card-body.flush { overflow-x: auto; }
  .gts-top .gts-actions { flex-wrap: wrap; justify-content: flex-end; }
  .gt-panel-head .gp-right { flex-wrap: wrap; min-width: 0; }
}

/* ---- the podium's ink, on every part of its metal -------------------------
   Gold runs from pale butter to a dark bronze-gold, silver and bronze each
   end on their darkest stop; the medal words, the "pts" and the chips were a
   mid-tone of their own metal and fell under 3:1 wherever they met that dark
   end. Each card's ink is now dark enough for its darkest stop, and gold's
   last stop is lifted a little so its dark ink clears 4.5:1 there too. */
.gt-pod.p1 { background: linear-gradient(165deg, #FFF0B8 0%, #F0CE62 38%, #D4A640 72%, #BB8F2E 100%); }
.gt-pod.p1 .gp-name, .gt-pod.p1 .gp-pts, .gt-pod.p1 .gp-pts small,
.gt-pod.p1 .gp-medal, .gt-pod.p1 .gp-chip, .gt-pod.p1 .gp-av { color: #241A03; }
.gt-pod.p2 .gp-name, .gt-pod.p2 .gp-pts, .gt-pod.p2 .gp-pts small,
.gt-pod.p2 .gp-medal, .gt-pod.p2 .gp-chip, .gt-pod.p2 .gp-av, .gt-pod.p2 .gp-rank { color: #252B32; }
.gt-pod.p3 .gp-name, .gt-pod.p3 .gp-pts, .gt-pod.p3 .gp-pts small,
.gt-pod.p3 .gp-medal, .gt-pod.p3 .gp-chip, .gt-pod.p3 .gp-av { color: #3A220C; }
.gt-pod.p3 .gp-rank { color: #FFF6EC; }

/* ---- the members board on a phone ----------------------------------------
   Under 700px the roles column was `auto`, which takes its max-content — every
   pill on one line — and left the name column nothing: names and emails were
   0px wide. The roles column is capped and wraps; on a phone it goes under the
   name. */
@media (max-width: 700px) {
  .gt-board.mb-board .gb-head,
  .gt-board.mb-board .gb-row { grid-template-columns: minmax(0, 1fr) fit-content(46%); }
}
@media (max-width: 440px) {
  .gt-board.mb-board .gb-head { display: none; }
  .gt-board.mb-board .gb-row { grid-template-columns: minmax(0, 1fr); row-gap: 8px; }
  .gt-board.mb-board .mb-roles { justify-content: flex-start; }
}

/* ---- the public site's bar fits its screen too ----------------------------
   Signed in, the public bar carries its nine links plus the account actions,
   and between its drawer breakpoint (1,120px) and ~1,650px that row ran
   224px past its container — the same MacBook-width failure as the app bar.
   The same answer: public/base.html measures the row and steps down a tier
   (is-tight, then is-drawer, which is the drawer the bar already has under
   1,120px) until it fits. */
.nav.is-tight .nav-links a { padding-left: 9px; padding-right: 9px; font-size: 13.5px; }
.nav.is-tight .nav-auth { margin-left: 2px; padding-left: 8px; }
.nav.is-drawer .nav-toggle { display: block; }
.nav.is-drawer .nav-links {
  position: absolute; top: 66px; left: 0; right: 0;
  background: var(--deep); flex-direction: column; align-items: stretch;
  gap: 0; padding: 8px var(--gutter) 20px;
  border-bottom: 1px solid var(--line-d);
  transform: translateY(-8px); opacity: 0; pointer-events: none;
  transition: .2s var(--ease);
  max-height: calc(100vh - 66px); overflow-y: auto;
}
.nav.is-drawer .nav-links a { padding: 14px 0; border-bottom: 1px solid var(--line-d); }
.nav.is-drawer .nav-links .btn-nav { text-align: center; margin-top: 12px; }
.nav.is-drawer[data-open="true"] .nav-links { transform: none; opacity: 1; pointer-events: auto; }
.nav.is-drawer .nav-auth { margin-left: 0; padding-left: 0; border-left: 0; border-top: 1px solid var(--line-d); }

/* The recap card's competition colour, on the sheet pages too. The sheet's
   own post card (`.sheet-page .gt-feed .gt-post`, and its light twin) paints
   every post white with a hairline edge, and it outranked the recap's colour,
   so on the Wall a recap for NRLW looked like a recap for anything. */
.sheet-page .gt-feed .gt-post.is-recap[data-code],
html[data-theme="light"] .gt-app.sheet-page .gt-feed .gt-post.is-recap[data-code] {
  border-left: 5px solid var(--code-ink);
  background: #fff;
  background: linear-gradient(110deg, color-mix(in srgb, var(--code) 16%, #fff), #fff 55%);
}

/* ---- second pass, after re-measuring (Sep 2026) ---------------------------- */
/* The news page is not a sheet page, so its "Your move" card answers to the
   light theme's plain `.gt-app .card` rule, not the sheet one. */
html[data-theme="light"] .gt-app .card.news-cta { background: #0F2E22; border-color: #0F2E22; }
/* Bronze's rank disc runs from pale peach to copper; no one ink read on both
   ends of that. The disc keeps its metal on a narrower, lighter range. */
.gt-pod.p3 .gp-rank { background: linear-gradient(145deg, #F2C9A2, #CF955C); color: #2A1606; }
/* My Tips: the losing side is faded as a block, and its result badge cannot
   escape the fade — so the fade is lighter where there is a result to read,
   and the badge's ink starts darker. */
.fxc-team.is-dim.has-outcome { opacity: .9; }
.fxc-outcome i { color: #111A15; }
.fxc-outcome .fxc-pts,
.fxc-outcome.is-won i,
.fxc-outcome.is-won .fxc-pts { color: #0B3D20; }
.gt-filter i { color: #1B4D25; }
.gt-filter.on i { color: inherit; }
/* The group switcher's sub-labels and the wizard's footer note faded
   themselves under the bar. */
.room-switch .rw-opt i, .room-switch .rw-opt.on i { opacity: 1; }
.wf-note { opacity: 1; }
html:not([data-theme="light"]) .wf-txt i,
html:not([data-theme="light"]) .draft-chip .dc-txt i { color: rgba(245,241,230,.76); }
/* Members' initials in the coloured panels take a darker cut of the panel's
   own colour. */
.sheet-page .ppanel .gb-av,
html[data-theme="light"] .gt-app .ppanel .gb-av { color: color-mix(in srgb, var(--pc) 72%, #000); }
.np-sep { opacity: 1; }
.gt-board.ladder .lad-left i { opacity: 1; }
.gt-choice .gc-txt p { opacity: .92; }
/* The public wall: the green emphasis inside a white heading on a near-black
   stage, and the "for you" cards' tags and copy. */
.wall-stage .section-heading em { color: var(--lime); }
.for-card .for-tag { box-shadow: inset 0 0 0 100px rgba(0,0,0,.2); }
.for-card p { color: #4D5A4C; }
.tab-btn { color: #4D5A4C; }

/* THE MENU BUTTON YOU COULD NOT SEE. Its icon is three strokes (M3 6h18 …) and
   the button's svg had no stroke — the paths were painted with a black fill
   that a line does not have. So on every phone and tablet, where this button
   is the only way to the rest of the app, it was an empty 42px square that
   worked if you happened to press it. It draws in the bar's own ink now, and in
   the light theme's ink on the light bar. */
.an-burger svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
html[data-theme="light"] .gt-app .an-burger { color: var(--lt-ink); }

/* ---- third pass, after re-measuring ---------------------------------------- */
/* The "Read" tick in your own bubble sat at 60% of its pale blue. */
.gtm-chat .chat-row.mine .chat-bubble .chat-tick,
.gtm-chat .chat-row.mine .chat-bubble .chat-tick i { opacity: 1; }
/* The news card's ghost button took the light theme's white button fill under
   its cream words. On the dark card it stays a ghost. */
html[data-theme="light"] .gt-app .news-cta .abtn-ghost { background: transparent; }
/* The idle half of the theme switch on the two shells whose bars stay dark in
   both themes — Manage in light, HQ in dark — was 3.6 and 4.5:1. */
html[data-theme="light"] body.manage-app .an-theme button:not(.on) { color: #A9B8AD; }
html[data-theme="dark"] .gts-theme button:not(.on) { color: #B4C4B8; }

/* ---- phone widths, second pass ------------------------------------------- */
/* The ladder kept nine columns at 390px, so the club name got 0px. On a phone
   the ladder scrolls sideways inside its own box, as a table would, with the
   club column wide enough to read. */
@media (max-width: 620px) {
  .gt-board.ladder { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .gt-board.ladder > * { min-width: 470px; }
  .gt-board.ladder .lad-head,
  .gt-board.ladder .lad-row { grid-template-columns: 34px minmax(120px, 1fr) repeat(6, 42px) 40px; }
}
/* Invite: the address field kept its built-in width beside the Copy button. */
.copy-field > :not(.abtn) { min-width: 0; flex: 1 1 auto; }
/* Tip a round: a long club name beside its crest. */
.pick .team > span:not(.crest) { min-width: 0; flex: 1 1 auto; overflow-wrap: anywhere; }
@media (max-width: 480px) { .pick .team .crest { width: 26px; } }
/* The public wall's hero stacks on a tablet; 1fr alone would not shrink. */
@media (max-width: 820px) { .hero-split { grid-template-columns: minmax(0, 1fr); } }
/* A member's email line ends in an ellipsis rather than a hard cut. */
.gt-board.mb-board .gb-name small { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

/* ---- public pages: section intros centred ---------------------------------
   Client, Sep 2026: "since we have had things centered in the private page,
   also in the public [pages] certain text have it centered". The private pages
   centre their page heads (`head-center`); here the matching text is each
   section's intro — the eyebrow, the heading and the line under it — which the
   public templates always write as direct children of `.section > .wrap`.
   Split layouts (story-split, hero-split, the wall's feed head, the contact
   shell) keep their intros inside their own columns, so the child combinator
   leaves them left-aligned beside their pictures and forms, where centred text
   would float away from what it describes. The heading and the line under it
   have a max-width, so they are centred as blocks as well as in their text. */
.section > .wrap > .eyebrow,
.section > .wrap > .section-heading,
.section > .wrap > .section-sub { text-align: center; margin-left: auto; margin-right: auto; }
/* The public bar's middle tier. Tightening the spacing alone saves ~60px and
   the row needed ~150px at MacBook widths (1,440–1,536), so it fell straight
   through to the drawer and hid the whole menu. Dropping the link icons there
   buys ~200px, which keeps every link on the bar; the drawer is now reached
   only where even that does not fit. */
.nav.is-tight2 .nav-links a > svg { display: none; }
.nav.is-tight2 .nav-links a { padding-left: 8px; padding-right: 8px; }

/* The centred news filter (Sep 2026, client: "this part — All stories, AFL,
   AFLW, NRL, NRLW, News, Blog — have it centered"). The row holds the chips AND
   the "Show" dropdown, and centring the row centred the PAIR, which put the
   chips ~145px left of centre. Both stay (the client asked for chip and
   dropdown): the chips get a centred line of their own, the dropdown the
   centred line under it. Below 720px the chips are hidden and the dropdown is
   the whole control, as before. */
.compfilter.is-centred { flex-wrap: wrap; row-gap: 12px; }
.compfilter.is-centred .cf-chips { flex: 1 1 100%; justify-content: center; }
.compfilter.is-centred .cf-pick { flex: 1 1 100%; justify-content: center; }

/* ==========================================================================
   THE RECOMMENDED PLAN, INSIDE THE SIGN-UP WIZARD
   ==========================================================================
   Client, 17 Sep 2026: "where in the process do they select their plan or get
   a recommended package — is it when they get asked the size of the group?"
   It is now, under that field, and again on the review step.

   Styled as a READOUT and not as a price card. A bordered card with a big
   number in it, on a screen with a Continue button, reads as a thing being
   sold — and nothing is being sold here: the organisation is free to create
   and the plan is chosen afterwards. So it sits flat on the panel, tinted
   rather than boxed, with the fine print doing the reassuring. */
.plan-pick {
  margin-top: 10px; padding: 12px 14px;
  border-radius: 12px;
  background: rgba(45,122,58,.06);
  border: 1px solid rgba(45,122,58,.16);
}
.plan-pick .pp-lab {
  display: block; font-size: 11px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--forest); margin-bottom: 6px;
}
.plan-pick .pp-row {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
}
.plan-pick .pp-row b { font-size: 17px; color: var(--ink); }
.plan-pick .pp-price { font-size: 17px; font-weight: 800; color: var(--forest); }
.plan-pick .pp-price i { font-style: normal; font-size: 12px; font-weight: 700; opacity: .7; }
.plan-pick .pp-seats {
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 2px 9px; border-radius: 100px;
  background: rgba(45,122,58,.1); color: var(--forest);
}
.plan-pick small { display: block; margin-top: 7px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.plan-pick .pp-fine { font-size: 11.5px; opacity: .88; }
.plan-pick a { color: var(--forest); font-weight: 700; }
.plan-pick.is-final { margin-top: 0; }

/* On the deep green sheet the forest tint disappears into the panel and the
   forest type is unreadable. Lime does both jobs there, as it does everywhere
   else in the member app. */
.page-deep .plan-pick,
html[data-theme="dark"] .gt-app .plan-pick {
  background: rgba(200,241,53,.08);
  border-color: rgba(200,241,53,.22);
}
.page-deep .plan-pick .pp-lab,
.page-deep .plan-pick .pp-price,
.page-deep .plan-pick a,
html[data-theme="dark"] .gt-app .plan-pick .pp-lab,
html[data-theme="dark"] .gt-app .plan-pick .pp-price,
html[data-theme="dark"] .gt-app .plan-pick a { color: var(--lime); }
.page-deep .plan-pick .pp-seats,
html[data-theme="dark"] .gt-app .plan-pick .pp-seats {
  background: rgba(200,241,53,.16); color: var(--lime);
}
.page-deep .plan-pick .pp-row b,
html[data-theme="dark"] .gt-app .plan-pick .pp-row b { color: var(--cream); }

/* ==========================================================================
   THE SPONSORED-PLACES BAND, UNDER THE PRICES
   ==========================================================================
   Client, 20 Sep 2026: "we do have the sponsorship page, so in the pricing I
   think we should have that so someone can see the sponsorship and all."

   Deliberately the one band on this page that does not look like a plan. The
   table above it is five rows of boxes with prices in them, so another boxed
   thing with a heading would be read as a sixth plan and skipped by anybody
   who has already decided about the first five. It sits on the deep green
   instead, full width, with the lime doing the talking — a change of surface
   is what makes a reader's eye stop, and stopping is the entire job here. */
.sponsor-band { background: var(--deep); color: var(--cream); }
.sb-card {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 32px; align-items: center;
}
.sb-main h2 {
  font-family: var(--display); font-size: clamp(24px, 3.4vw, 36px);
  line-height: 1.12; color: var(--cream); margin: 14px 0 12px;
}
.sb-main > p { font-size: 16px; line-height: 1.65; color: rgba(232,228,213,.84); max-width: 60ch; }
.sb-pill { align-self: flex-start; }

/* The three reassurances, as a list rather than a paragraph. They are the
   three objections somebody raises to themselves before deciding not to ask,
   and answering them one line each is what makes them answerable at a glance —
   buried in a sentence they read as terms. */
.sb-points { list-style: none; margin: 18px 0 24px; display: grid; gap: 10px; }
.sb-points li {
  display: flex; align-items: flex-start; gap: 11px;
  font-size: 14.5px; line-height: 1.55; color: rgba(232,228,213,.9);
}
.sb-points svg {
  width: 18px; height: 18px; flex: none; margin-top: 2px;
  fill: none; stroke: var(--lime); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.sb-ctas { display: flex; flex-wrap: wrap; gap: 12px; }

/* The aside is decoration and says so — aria-hidden in the markup, because a
   screen reader arriving at a pull quote that repeats nothing is being given
   an extra thing to get past on the way to the button. */
.sb-aside {
  display: flex; flex-direction: column; align-items: flex-start; gap: 18px;
  padding: 28px; border-radius: var(--radius);
  background: rgba(200,241,53,.07); border: 1px solid rgba(200,241,53,.22);
}
.sb-glyph {
  width: 52px; height: 52px; display: grid; place-items: center;
  border-radius: 50%; background: var(--lime);
}
.sb-glyph svg { width: 26px; height: 26px; fill: var(--deep); stroke: none; }
.sb-quote {
  font-family: var(--display); font-size: 19px; line-height: 1.4;
  color: var(--cream); margin: 0;
}

@media (max-width: 860px) {
  .sb-card { grid-template-columns: 1fr; gap: 24px; }
  /* The quote goes above nothing and below the buttons — it is the least
     useful thing here on a phone, so it is the last thing scrolled past. */
  .sb-aside { order: 2; }
}

/* ==========================================================================
   THE EIGHT LOADER SCENES — FOUR AFL, FOUR NRL
   ==========================================================================
   Client, 20 Sep 2026: "let's only have NRL and AFL ... they will be like 8
   loaders. For the NRLW, either male or female, on their loader we can add the
   sign of male or female."

   Markup and the reasoning per scene: templates/partials/_loader_scenes.html.
   Eight motions, no two alike — arc, bounce, catch, wave, arc-over, count,
   place, chain — with the CODE choosing the scene and its colour, and a ♀/♂
   mark in the corner saying which side of that code it is.

   --lc is the one alias: the competition's colour from the [data-code] token
   block, with the lime as a floor. No rule below names a colour. */
#loader { --lc: var(--code, var(--lime)); }

.lstage { position: relative; width: 240px; height: 150px; margin: 0 auto 26px; }
.lscene { display: none; position: absolute; inset: 0; }
#loader[data-scene="afl-set"]     .lscene[data-lscene="afl-set"],
#loader[data-scene="afl-bounce"]  .lscene[data-lscene="afl-bounce"],
#loader[data-scene="afl-mark"]    .lscene[data-lscene="afl-mark"],
#loader[data-scene="afl-flags"]   .lscene[data-lscene="afl-flags"],
#loader[data-scene="nrl-convert"] .lscene[data-lscene="nrl-convert"],
#loader[data-scene="nrl-count"]   .lscene[data-lscene="nrl-count"],
#loader[data-scene="nrl-try"]     .lscene[data-lscene="nrl-try"],
#loader[data-scene="nrl-pass"]    .lscene[data-lscene="nrl-pass"] { display: block; }
/* Scripts off, or a scene name we do not hold: the set shot stands in. A real
   scene rather than an empty box — this element exists to cover the moment
   before a page is ready, and covering it with nothing is worse than covering
   it with the wrong code. */
#loader:not([data-scene]) .lscene[data-lscene="afl-set"] { display: block; }

.lsvg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.lsvg .lposts, .lsvg .lhands, .lsvg .lmates circle { stroke: var(--lc); }
.lsvg .lfaint { stroke: var(--lc); opacity: .24; }
.lground { stroke: var(--lc); stroke-width: 2; opacity: .4; stroke-linecap: round; }

/* ---- the ♀ / ♂ mark ---------------------------------------------------
   Bottom-right and deliberately small: it qualifies the scene, it is not the
   subject of it. Both marks exist in the markup and [data-sex] shows one —
   drawing it only on the women's scenes would make the women's competition
   read as the variant and the men's as the default, which is not how these
   two sit in the product. */
.lsex {
  position: absolute; right: 2px; bottom: 6px; width: 26px; height: 26px;
  stroke: var(--lc); opacity: .85; overflow: visible;
}
.lsex g { display: none; }
#loader[data-sex="w"] .lsex .lsex-w,
#loader[data-sex="m"] .lsex .lsex-m { display: block; }
#loader:not([data-sex]) .lsex { display: none; }

/* ---- the ball --------------------------------------------------------- */
.lball {
  position: absolute; left: 50%; top: 50%;
  width: 26px; height: 40px; margin: -20px 0 0 -13px;
  will-change: transform;
  filter: drop-shadow(0 6px 8px rgba(0,0,0,.45));
  background: radial-gradient(120% 120% at 35% 30%, #F2EFE6, #cfcab6);
  border-radius: 50% / 42%;
  border: 1.5px solid rgba(7,47,30,.28);
}
/* The lacing, which is what makes 26px of pale oval read as a football rather
   than as a pill. Both codes use an oval ball, so there is only one now — the
   round one went with Super Netball. */
.lball::before {
  content: ""; position: absolute; left: 50%; top: 14%; bottom: 14%;
  width: 2px; margin-left: -1px; background: var(--forest);
}
.lball::after {
  content: ""; position: absolute; left: 50%; top: 26%; width: 10px; height: 1.5px;
  margin-left: -5px; box-shadow: 0 5px var(--forest), 0 10px var(--forest), 0 15px var(--forest);
}

/* ============ 1. AFL — THE SET SHOT (arc) =============================
   From the boot, low and left, up through the goal mouth and away. It shrinks
   because it is going away from the viewer, not as a fade trick, and the spin
   keeps accelerating so it reads as still travelling when it leaves. */
.l-arc { animation: lArc 1.7s cubic-bezier(.22,.61,.36,1) infinite; }
@keyframes lArc {
  0%   { transform: translate(-118px, 62px) rotate(-10deg) scale(1);   opacity: 0; }
  6%   { transform: translate(-108px, 56px) rotate(6deg)   scale(1);   opacity: 1; }
  11%  { transform: translate(-98px, 48px)  rotate(30deg)  scale(1.1, .86); }
  32%  { transform: translate(-58px, 4px)   rotate(230deg) scale(.94); }
  60%  { transform: translate(-22px, -26px) rotate(430deg) scale(.78); }
  86%  { transform: translate(0px, -46px)   rotate(590deg) scale(.5);  opacity: .9; }
  100% { transform: translate(10px, -58px)  rotate(700deg) scale(.28); opacity: 0; }
}
#loader[data-scene="afl-set"] .lposts { animation: lFlash 1.7s cubic-bezier(.22,.61,.36,1) infinite; }
/* THE FLAGS ARE THE POINT. A ball leaving the top of the frame is ambiguous;
   two flags going up after it is a goal. */
.lflags { stroke: var(--lc); opacity: 0; }
#loader[data-scene="afl-set"] .lflags { animation: lFlags 1.7s cubic-bezier(.2,.9,.3,1) infinite; }
@keyframes lFlags {
  0%, 62%  { opacity: 0; transform: translateY(20px); }
  72%      { opacity: 1; transform: translateY(-2px); }
  78%, 96% { opacity: 1; transform: translateY(0); }
  100%     { opacity: 0; transform: translateY(20px); }
}

/* ============ 2. AFL — THE CENTRE BOUNCE (bounce) =====================
   Driven down into the circle, up high, down again. Two bounces, the second
   lower, because that is what a ball does. The only scene where the ball
   comes back towards the viewer. */
.l-bounce { animation: lBounce 1.9s cubic-bezier(.36,0,.66,-.24) infinite; }
@keyframes lBounce {
  0%   { transform: translate(0, -76px) rotate(0deg)   scale(.86); opacity: 0; }
  8%   { transform: translate(0, -58px) rotate(40deg)  scale(.9);  opacity: 1; }
  24%  { transform: translate(0, 0)     rotate(150deg) scale(1.08, .82); }
  27%  { transform: translate(0, 4px)   rotate(166deg) scale(1.2, .68); }
  52%  { transform: translate(0, -64px) rotate(320deg) scale(.88); }
  74%  { transform: translate(0, 2px)   rotate(470deg) scale(1.12, .84); }
  90%  { transform: translate(0, -30px) rotate(560deg) scale(.94); }
  100% { transform: translate(0, -12px) rotate(610deg) scale(.98); opacity: 0; }
}
.lping { stroke: var(--lc); opacity: 0; transform-origin: 120px 72px; }
#loader[data-scene="afl-bounce"] .lping { animation: lPing 1.9s cubic-bezier(0,.55,.45,1) infinite; }
@keyframes lPing {
  0%, 24%  { transform: scale(.7); opacity: 0; }
  27%      { transform: scale(.82); opacity: .8; }
  46%      { transform: scale(1.6); opacity: 0; }
  74%      { transform: scale(.82); opacity: .65; }
  92%,100% { transform: scale(1.55); opacity: 0; }
}

/* ============ 3. AFL — THE MARK (catch) ===============================
   The ball drops out of the sky, the arms come up and take it, and it stops
   dead. The only scene where something REACHES for the ball rather than
   sending it somewhere. */
.l-fall { animation: lFall 1.8s cubic-bezier(.5,0,.75,.4) infinite; }
@keyframes lFall {
  0%   { transform: translate(0, -128px) rotate(0deg)   scale(.7);  opacity: 0; }
  10%  { transform: translate(0, -104px) rotate(40deg)  scale(.76); opacity: 1; }
  52%  { transform: translate(0, -24px)  rotate(220deg) scale(.94); }
  /* Taken. It stops on the spot rather than easing to a halt — a mark is an
     interception, and easing would read as the ball settling on its own. */
  58%  { transform: translate(0, -8px)   rotate(250deg) scale(1); }
  62%  { transform: translate(0, -6px)   rotate(252deg) scale(1.06, .92); }
  92%  { transform: translate(0, -6px)   rotate(252deg) scale(1); opacity: 1; }
  100% { transform: translate(0, -6px)   rotate(252deg) scale(1); opacity: 0; }
}
.lhands { opacity: .45; }
#loader[data-scene="afl-mark"] .lhands { animation: lGrab 1.8s cubic-bezier(.3,.8,.4,1) infinite; }
@keyframes lGrab {
  0%, 44%  { opacity: .45; transform: translateY(14px); }
  58%      { opacity: 1;   transform: translateY(0); }
  66%      { opacity: 1;   transform: translateY(2px); }
  92%,100% { opacity: .45; transform: translateY(14px); }
}
#loader[data-scene="afl-mark"] .lburst { animation: lBurst 1.8s cubic-bezier(0,.55,.45,1) infinite; }

/* ============ 4. AFL — THE GOAL UMPIRE (wave) =========================
   Two flags waved from the boundary and the six going up on the board. NO
   BALL: this is what happens after one, and it is the only motion of the
   eight that is a person signalling rather than an object moving. */
.lwave-l, .lwave-r { stroke: var(--lc); transform-origin: 80px 126px; }
.lwave-r { transform-origin: 160px 126px; }
#loader[data-scene="afl-flags"] .lwave-l { animation: lWaveL 1.5s ease-in-out infinite; }
#loader[data-scene="afl-flags"] .lwave-r { animation: lWaveR 1.5s ease-in-out infinite; }
@keyframes lWaveL {
  0%, 100% { transform: rotate(-16deg); }
  50%      { transform: rotate(10deg); }
}
@keyframes lWaveR {
  0%, 100% { transform: rotate(16deg); }
  50%      { transform: rotate(-10deg); }
}
/* The scoreboard between them, ticking over as the flags go. */
.lsix { stroke: var(--lc); opacity: .35; transform-origin: 120px 77px; }
#loader[data-scene="afl-flags"] .lsix { animation: lSix 1.5s cubic-bezier(.4,0,.3,1) infinite; }
@keyframes lSix {
  0%, 40%  { opacity: .35; transform: scaleY(1); }
  52%      { opacity: 1;   transform: scaleY(.15); }
  64%      { opacity: 1;   transform: scaleY(1); }
  100%     { opacity: .35; transform: scaleY(1); }
}

/* ============ 5. NRL — THE CONVERSION (arc over) ======================
   Off the tee, OVER the crossbar, between the uprights. Flatter than the AFL
   arc and it clears a bar rather than threading a gap — which, with the H
   behind it, is what stops the two reading as the same scene recoloured. */
.l-convert { animation: lConvert 1.7s cubic-bezier(.24,.58,.4,1) infinite; }
@keyframes lConvert {
  0%   { transform: translate(-92px, 56px) rotate(-16deg) scale(1);   opacity: 0; }
  7%   { transform: translate(-84px, 52px) rotate(0deg)   scale(1);   opacity: 1; }
  12%  { transform: translate(-74px, 44px) rotate(34deg)  scale(1.12, .84); }
  40%  { transform: translate(-36px, -10px) rotate(240deg) scale(.92); }
  64%  { transform: translate(-8px, -34px)  rotate(400deg) scale(.76); }
  88%  { transform: translate(14px, -46px)  rotate(560deg) scale(.46); opacity: .88; }
  100% { transform: translate(24px, -54px)  rotate(650deg) scale(.26); opacity: 0; }
}
#loader[data-scene="nrl-convert"] .lposts { animation: lFlash 1.7s cubic-bezier(.24,.58,.4,1) infinite; }

/* ============ 6. NRL — THE TACKLE COUNT (count) =======================
   LIT ALMOST IMMEDIATELY, and the STAGGER does the counting. This lit at 60%
   of each stroke's own cycle, which put every delay on top of the same long
   wait: all five sat dim for two thirds of the loop and then lit within a few
   frames of each other — a count you could not count. At 4% each one lands a
   quarter-second after the last, which is the rhythm somebody recognises. */
.ltally line { stroke: var(--lc); opacity: .16; }
#loader[data-scene="nrl-count"] .lt1 { animation: lTally 2.1s steps(1, end) infinite;       }
#loader[data-scene="nrl-count"] .lt2 { animation: lTally 2.1s steps(1, end) infinite .26s;  }
#loader[data-scene="nrl-count"] .lt3 { animation: lTally 2.1s steps(1, end) infinite .52s;  }
#loader[data-scene="nrl-count"] .lt4 { animation: lTally 2.1s steps(1, end) infinite .78s;  }
#loader[data-scene="nrl-count"] .lt5 { animation: lTally 2.1s steps(1, end) infinite 1.04s; }
@keyframes lTally {
  0%, 3%   { opacity: .16; }
  /* Held at full for the rest of the loop: a count that fades is a count you
     cannot read the total of. */
  4%, 100% { opacity: 1; }
}
.lt6 { stroke-dasharray: 170; stroke-dashoffset: 170; }
#loader[data-scene="nrl-count"] .lt6 { animation: lStrike 2.1s cubic-bezier(.4,0,.2,1) infinite; }
@keyframes lStrike {
  0%, 62%   { stroke-dashoffset: 170; opacity: 0; }
  64%       { stroke-dashoffset: 170; opacity: 1; }
  78%, 92%  { stroke-dashoffset: 0;   opacity: 1; }
  100%      { stroke-dashoffset: 0;   opacity: 0; }
}
/* The play-the-ball: it rolls back along the ground as the set restarts. */
.l-roll { animation: lRoll 2.1s cubic-bezier(.3,.4,.3,1) infinite; }
@keyframes lRoll {
  0%, 76% { transform: translate(64px, 44px) rotate(0deg);     opacity: 0; }
  82%     { transform: translate(52px, 44px) rotate(-40deg);   opacity: 1; }
  100%    { transform: translate(-64px, 44px) rotate(-520deg); opacity: 1; }
}

/* ============ 7. NRL — THE TRY (place) ================================
   In low from the left and grounded over the line. It ends on the deck and
   STAYS there — the only scene of the eight that finishes at rest with the
   ball still on screen, because a try is a ball being put somewhere. */
.l-place { animation: lPlace 1.9s cubic-bezier(.3,.5,.35,1) infinite; }
@keyframes lPlace {
  0%   { transform: translate(-130px, 4px) rotate(-20deg) scale(.9);  opacity: 0; }
  8%   { transform: translate(-114px, 8px) rotate(10deg)  scale(.94); opacity: 1; }
  40%  { transform: translate(-40px, 20px) rotate(190deg) scale(1); }
  62%  { transform: translate(22px, 28px)  rotate(330deg) scale(1.02); }
  70%  { transform: translate(46px, 32px)  rotate(370deg) scale(1.16, .8); }
  74%  { transform: translate(46px, 32px)  rotate(374deg) scale(1.22, .74); }
  92%  { transform: translate(46px, 32px)  rotate(374deg) scale(1.04); opacity: 1; }
  100% { transform: translate(46px, 32px)  rotate(374deg) scale(1);   opacity: 0; }
}
#loader[data-scene="nrl-try"] .ltryline { animation: lFlashLate 1.9s cubic-bezier(.3,.5,.35,1) infinite; }
#loader[data-scene="nrl-try"] .lburst { animation: lBurst 1.9s cubic-bezier(0,.55,.45,1) infinite; }

/* ============ 8. NRL — THE PASS (chain) ===============================
   Down the line, each player lighting as the ball reaches them. Built out of
   a SEQUENCE across the width rather than one flight, which is what separates
   it from the other three even though a ball is moving in both. */
.l-chain { animation: lChain 2s cubic-bezier(.35,0,.45,1) infinite; }
@keyframes lChain {
  0%   { transform: translate(-72px, -30px) rotate(0deg)   scale(.9);  opacity: 0; }
  8%   { transform: translate(-72px, -22px) rotate(20deg)  scale(.94); opacity: 1; }
  /* Up and across to the second, dipping between them — a pass travels in an
     arc, and a straight slide would read as the ball being dragged. */
  26%  { transform: translate(-36px, -44px) rotate(120deg) scale(1); }
  44%  { transform: translate(0, -22px)     rotate(210deg) scale(1); }
  62%  { transform: translate(36px, -44px)  rotate(300deg) scale(1); }
  80%  { transform: translate(72px, -22px)  rotate(390deg) scale(.96); }
  100% { transform: translate(96px, -16px)  rotate(450deg) scale(.7);  opacity: 0; }
}
.lmates circle { opacity: .3; }
#loader[data-scene="nrl-pass"] .lm1 { animation: lMate 2s steps(1, end) infinite;      }
#loader[data-scene="nrl-pass"] .lm2 { animation: lMate 2s steps(1, end) infinite .38s; }
#loader[data-scene="nrl-pass"] .lm3 { animation: lMate 2s steps(1, end) infinite .76s; }
@keyframes lMate {
  0%, 3%    { opacity: .3; }
  4%, 22%   { opacity: 1; }
  23%, 100% { opacity: .3; }
}

/* ---- the shared flashes ------------------------------------------------ */
@keyframes lFlash {
  0%, 62%   { filter: none; }
  74%       { filter: drop-shadow(0 0 10px var(--lc)); }
  90%, 100% { filter: none; }
}
@keyframes lFlashLate {
  0%, 66%   { filter: none; }
  76%       { filter: drop-shadow(0 0 10px var(--lc)); }
  94%, 100% { filter: none; }
}
.lburst { stroke: var(--lc); opacity: 0; transform-origin: center; }
@keyframes lBurst {
  0%, 52%  { transform: scale(.4); opacity: 0; }
  60%      { transform: scale(.8); opacity: .9; }
  92%,100% { transform: scale(2.1); opacity: 0; }
}

/* ---- the caption and the bar wear the code too -------------------------
   Otherwise the scene is amber and the word under it is lime, which is the
   fusion the Wall's rooms were told off for. */
#loader[data-code] .loader-word span,
#loader[data-code] .loader-word .dot { color: var(--lc); }
#loader[data-code] .loader-sport { color: var(--lc); }
#loader[data-code] .loader-bar > i { background: var(--lc); }
#loader[data-code] { background:
  radial-gradient(900px 500px at 50% 12%, color-mix(in srgb, var(--lc) 14%, transparent), transparent 60%),
  var(--deep-2); }
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  #loader[data-code] { background: var(--deep-2); }
}

/* ---- reduced motion ----------------------------------------------------
   Not frozen. A still picture on the one screen whose job is to say something
   is happening reads as a broken loader, which is worse than the movement
   this query exists to suppress. Every scene keeps its own path at roughly
   half speed; what goes is the spin, which at 700 degrees is the part that is
   actually punishing. These scenes are exempt from the blanket rule near the
   top of this file as `.lanim` — without that they are frozen outright, which
   is how the first six shipped invisible. */
@media (prefers-reduced-motion: reduce) {
  .l-arc, .l-place, .l-roll, .l-fall, .l-chain { animation-duration: 3.6s; }
  .l-convert { animation-duration: 3.4s; }
  .l-bounce  { animation-duration: 3.8s; }
  .l-arc     { animation-name: lArcCalm; animation-timing-function: cubic-bezier(.3,.5,.4,1); }
  .l-convert { animation-name: lConvertCalm; animation-timing-function: cubic-bezier(.3,.5,.4,1); }
  #loader .lposts, #loader .ltryline, #loader .lflags, #loader .lhands,
  #loader .lwave-l, #loader .lwave-r, #loader .lsix { animation-duration: 3.4s; }
  #loader .lt1, #loader .lt2, #loader .lt3, #loader .lt4, #loader .lt5,
  #loader .lt6, #loader .lm1, #loader .lm2, #loader .lm3 { animation-duration: 3.6s; }
  .lping, .lburst { animation-duration: 3.8s; }
}
@keyframes lArcCalm {
  0%   { transform: translate(-110px, 55px) scale(1);   opacity: 0; }
  10%  { transform: translate(-100px, 48px) scale(1);   opacity: 1; }
  50%  { transform: translate(-46px, -4px)  scale(.9); }
  86%  { transform: translate(-2px, -46px)  scale(.56); opacity: .9; }
  100% { transform: translate(8px, -58px)   scale(.34); opacity: 0; }
}
@keyframes lConvertCalm {
  0%   { transform: translate(-86px, 52px)  scale(1);   opacity: 0; }
  10%  { transform: translate(-78px, 46px)  scale(1);   opacity: 1; }
  50%  { transform: translate(-22px, -20px) scale(.88); }
  88%  { transform: translate(16px, -46px)  scale(.5);  opacity: .88; }
  100% { transform: translate(24px, -54px)  scale(.3);  opacity: 0; }
}

/* ---- inset (the member app) -------------------------------------------- */
.loader.loader-inset .lstage { width: 180px; height: 112px; margin-bottom: 18px; }

/* ==========================================================================
   THE FOUR SCOPED LOADERS — ROTATED PER COMPETITION
   ==========================================================================
   Client, 20 Sep 2026, correcting the first attempt: "let each competition
   have like 4 types of loaders with its own type of animation ... the reason I
   said 4 in each competition is that if I have a certain loader I have seen in
   NRL, if I go to NRL again I should see another type of design and animation."

   So the four are a ROTATION, not four kinds of data. gt-veil.js keeps the
   position per competition, so NRL walks kick → climb → score → sweep → kick
   and AFLW has its own place in that cycle. The first pass mapped them to what
   was loading, which meant pressing NRL twice gave the same loader twice —
   exactly what the sentence rules out.

   FOUR DIFFERENT MOTIONS, not four arrangements of the same one: a ball over
   posts, bars overtaking, blocks flipping, chevrons chasing. Two of them in a
   row cannot be mistaken for each other, which is the whole requirement.

   --vc is the one alias: the competition's bright colour, falling back to the
   forest green for "all competitions", which has no code and should not borrow
   one. Nothing below names a colour. */
.bv-scoped { --vc: var(--code, var(--forest)); }
.gt-dark .bv-scoped, .page-deep .bv-scoped { --vc: var(--code, var(--lime)); }

/* ---- 1. KICK — a ball over a set of posts, in miniature -----------------
   The one that ties the small loaders to the full-page splash: same idea,
   two centimetres across. The posts hold still and flash as it clears them. */
.bv-kick { position: relative; display: block; width: 62px; height: 40px; }
.bv-kick-posts {
  position: absolute; left: 50%; bottom: 0; width: 34px; height: 30px;
  margin-left: -17px;
  border-left: 3px solid var(--vc); border-right: 3px solid var(--vc);
  border-top: 3px solid var(--vc);
  border-radius: 2px 2px 0 0; opacity: .45;
}
.bv-kick-ball {
  position: absolute; left: 50%; bottom: 2px; width: 13px; height: 19px;
  margin-left: -6.5px; border-radius: 50% / 42%;
  background: var(--vc);
  animation: bvKick 1.35s cubic-bezier(.24,.6,.36,1) infinite;
}
@keyframes bvKick {
  0%   { transform: translate(-26px, 4px) rotate(0deg) scale(1);   opacity: 0; }
  10%  { transform: translate(-22px, 0)   rotate(30deg) scale(1);  opacity: 1; }
  48%  { transform: translate(-4px, -24px) rotate(240deg) scale(.86); }
  80%  { transform: translate(10px, -34px) rotate(430deg) scale(.6); opacity: .9; }
  100% { transform: translate(18px, -40px) rotate(540deg) scale(.36); opacity: 0; }
}
.bv-kick .bv-kick-posts { animation: bvKickFlash 1.35s cubic-bezier(.24,.6,.36,1) infinite; }
@keyframes bvKickFlash {
  0%, 60%  { opacity: .45; }
  72%      { opacity: 1; }
  88%,100% { opacity: .45; }
}

/* ---- 2. CLIMB — four bars finding their order --------------------------
   Deliberately NOT four bars growing in step. A ladder is about which is
   above which, so they overtake; a row pulsing together is an equaliser, and
   an equaliser says "audio". The delays are out of order for that reason. */
.bv-climb { display: flex; align-items: flex-end; gap: 5px; height: 34px; }
.bv-climb i {
  width: 9px; border-radius: 3px; background: var(--vc);
  animation: bvClimb 1.5s cubic-bezier(.45,0,.4,1) infinite;
}
.bv-climb i:nth-child(1) { animation-delay: 0s;   }
.bv-climb i:nth-child(2) { animation-delay: .34s; }
.bv-climb i:nth-child(3) { animation-delay: .12s; }
.bv-climb i:nth-child(4) { animation-delay: .52s; }
@keyframes bvClimb {
  0%, 100% { height: 9px;  opacity: .34; }
  40%      { height: 32px; opacity: 1; }
  70%      { height: 16px; opacity: .6; }
}

/* ---- 3. SCORE — a scoreline settling -----------------------------------
   Two blocks turning over with the dash between them held still, so the eye
   has one fixed point. Scaling on Y rather than sliding digits: a number you
   cannot read beats a wrong one flashing up. */
.bv-score { display: flex; align-items: center; gap: 7px; height: 30px; }
.bv-score b { font-family: var(--score); font-size: 16px; color: var(--vc); opacity: .5; }
.bv-score-n {
  width: 21px; height: 28px; border-radius: 5px;
  background: var(--vc); transform-origin: center;
  animation: bvScore 1.1s cubic-bezier(.5,0,.5,1) infinite;
}
.bv-score-b { animation-delay: .18s; }
@keyframes bvScore {
  0%, 100% { transform: scaleY(1);   opacity: .3; }
  30%      { transform: scaleY(.1);  opacity: .7; }
  60%      { transform: scaleY(1);   opacity: 1; }
}

/* ---- 4. SWEEP — chevrons chasing across --------------------------------
   The only one of the four that TRAVELS, which is what stops it reading as a
   smaller version of the kick. Each chevron picks up where the last left off,
   so the run reads as one thing moving rather than four things blinking. */
.bv-sweep { display: flex; align-items: center; gap: 4px; height: 26px; }
.bv-sweep i {
  width: 9px; height: 16px; flex: none;
  border-right: 3px solid var(--vc); border-top: 3px solid var(--vc);
  transform: rotate(45deg); opacity: .2;
  animation: bvSweep 1.15s ease-in-out infinite;
}
.bv-sweep i:nth-child(1) { animation-delay: 0s;   }
.bv-sweep i:nth-child(2) { animation-delay: .1s;  }
.bv-sweep i:nth-child(3) { animation-delay: .2s;  }
.bv-sweep i:nth-child(4) { animation-delay: .3s;  }
@keyframes bvSweep {
  0%, 70%, 100% { opacity: .2;  transform: rotate(45deg) translate(0, 0); }
  30%           { opacity: 1;   transform: rotate(45deg) translate(2px, -2px); }
}

/* The label goes with them. A caption in the app's green under an amber
   loader is the fusion the Wall's rooms were told off for. */
.bv-scoped[data-code] .bv-label { color: var(--vc); }
/* And the ball, for anything that has not been through the rotation. */
.bv-scoped[data-code] .bv-ball::after { border-top-color: var(--vc); }

/* Status motion, slowed rather than stopped — these are the only thing on
   screen saying the press registered, and a frozen one says the opposite. */
@media (prefers-reduced-motion: reduce) {
  .bv-kick-ball { animation-duration: 2.7s; }
  .bv-kick .bv-kick-posts { animation-duration: 2.7s; }
  .bv-climb i  { animation-duration: 3s; }
  .bv-score-n  { animation-duration: 2.4s; }
  .bv-sweep i  { animation-duration: 2.4s; }
}

/* ==========================================================================
   MOST POPULAR, MADE TO STAND OUT
   ==========================================================================
   Client, 20 Sep 2026: "in the billing where we have Most popular, it looks
   regular to me, it does not stand out — so the Most popular must stand out."

   Right, and the reason is that the treatment it had was a set of very quiet
   signals: a border one pixel thicker, a shadow, six pixels of lift and a
   small pill in the corner. Every one of those is a difference you can only
   see by comparing it with the card beside it, and nobody compares — they
   scan, and a scan needs one thing that is a different COLOUR, not four things
   that are slightly more of the same.

   An earlier instruction is still honoured: "do NOT give it a green
   background". That rules out filling the card, not accenting it. So the lime
   goes on a band across the top and on the button at the bottom — the two
   edges of the card — and the badge comes out of the corner to sit on the band
   where it is read before the plan's name is.

   Nothing here touches the other four cards. A featured card that works by
   making its neighbours worse is a featured card that stops working the moment
   somebody looks at one of them on its own. */
.tier[data-tier].featured {
  border-color: var(--forest);
  border-top: 0;                       /* the band below replaces it */
  border-width: 2px;
  box-shadow:
    0 0 0 4px rgba(207,249,58,.28),
    0 26px 52px -24px rgba(7,47,30,.8);
  transform: translateY(-14px);
  padding-top: 34px;                   /* room for the band and the badge */
  overflow: visible;
}
/* THE BAND. A solid lime strip across the whole top edge, which is the one
   mark on the page that no amount of scanning can miss. */
.tier[data-tier].featured::before {
  content: "";
  position: absolute; left: -2px; right: -2px; top: 0; height: 8px;
  background: var(--lime);
  border-radius: var(--gt-r-sm, 14px) var(--gt-r-sm, 14px) 0 0;
}
/* THE BADGE, out of the corner and onto the band. In the corner it was
   competing with the plan's name for the same glance; here it is above it,
   which is the order somebody reads the card in anyway. */
.tier[data-tier].featured .tier-flag {
  position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  right: auto; rotate: none;
  background: var(--lime); color: var(--deep);
  font-family: var(--mark); font-weight: 800; font-size: 11.5px;
  letter-spacing: .16em; text-transform: uppercase; white-space: nowrap;
  padding: 6px 16px; border-radius: 100px;
  box-shadow: 0 6px 16px -6px rgba(7,47,30,.55);
  border: 2px solid var(--cream-2);
}
/* And the button it is trying to get pressed. The other four keep the deep
   green; this one is the only lime button in the row, which is what makes it
   the obvious end of the card. */
.tier[data-tier].featured .tier-cta {
  background: var(--lime); color: var(--deep); font-weight: 800;
}
.tier[data-tier].featured .tier-cta:hover { background: var(--lime-dk); color: var(--deep); }
/* On the deep sheet the cream ring round the badge is wrong — it is a halo of
   paper on a dark ground. It takes the sheet's own colour there. */
.sheet-page .tier[data-tier].featured .tier-flag { border-color: var(--deep); }
.sheet-page .tier[data-tier].featured {
  box-shadow: 0 0 0 4px rgba(207,249,58,.22), 0 28px 56px -22px rgba(0,0,0,.8);
}
@media (max-width: 900px) {
  /* Stacked, the lift is just a gap. The band, the badge and the lime button
     do the whole job on a phone, which is where they matter most. */
  .tier[data-tier].featured { transform: none; }
}

/* ---- and the same plan on the public price table ----------------------
   The row had a 4px lime edge on its first cell, which is four pixels on a
   table five rows tall. It now carries a wash across every cell and the tier
   name goes full-strength ink, so the row is found before it is read. */
.tier-table tr.is-popular td {
  background-image: linear-gradient(rgba(207,249,58,.16), rgba(207,249,58,.16));
}
.tier-table tr.is-popular td:first-child { box-shadow: inset 5px 0 0 var(--lime); }
.tier-table tr.is-popular:hover td:first-child {
  box-shadow: inset 5px 0 0 var(--lime), inset 0 0 0 999px rgba(207,249,58,.07);
}
.tier-table tr.is-popular .tt-name { font-weight: 800; }
.tier-table tr.is-popular .tier-badge {
  box-shadow: 0 2px 8px -2px rgba(7,47,30,.45);
  font-size: 10.5px; padding: 3px 10px;
}

/* ==========================================================================
   THE LEGAL PAGES — WHITE AND GREEN, AND NOTHING SHOWING THROUGH
   ==========================================================================
   Client, 20 Sep 2026: "on the Terms and Conditions page we need to have it
   cleaned up, have proper cards, proper flow when I scroll down, all animation
   that is needed — such as expanding of a card and colour change as I scroll
   down. Remove that translucent feel, and let's have white and green."

   WHAT THE TRANSLUCENT FEEL ACTUALLY WAS. A stadium photograph at 34% under a
   two-stop veil, behind the one page on the site whose entire job is to be
   read carefully. Nothing on it was literally see-through — the cards have
   been solid white all along — but a photograph behind a heading, a wash over
   the top of it and a cream page under that is three surfaces where a document
   needs one, and the eye reads the result as murk.

   So the photograph goes, on all three legal pages. What replaces it is not a
   flat green rectangle either: a deep green with one soft lime bloom in the
   top corner, which is the same treatment the loader's ground uses and is the
   difference between "dark" and "unfinished".

   The body stays cream and the cards stay white, which is the contrast the
   client is asking for by "white and green" — the words sit on paper, the
   furniture around them is the brand. */
/* The hero is the split component now (see the markup): the photograph is
   BESIDE the words in its own panel rather than behind them, which is what
   the client meant by both "remove the translucent feel" and "we should have
   the hero split at the top and the images on one side". The chips that used
   to sit under a full-width band sit under the left column instead. */
.hero-legal .hl-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.hero-legal .hl-chip {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: rgba(232,228,213,.86);
  background: rgba(255,255,255,.06); border: 1px solid rgba(232,228,213,.22);
  border-radius: 100px; padding: 6px 13px;
}
.hero-legal .hl-chip svg { width: 14px; height: 14px; fill: none; stroke: var(--lime); stroke-width: 1.9; }

/* ---- the cards ---------------------------------------------------------
   Solid, edged, and with somewhere for the eye to rest between them. The
   border was a 10%-black hairline, which on cream is a suggestion of an edge
   rather than an edge — "proper cards" is mostly this line being visible. */
.page-legal .legal-card {
  border: 1px solid rgba(7,47,30,.14);
  border-left: 4px solid rgba(7,47,30,.10);
  box-shadow: 0 1px 2px rgba(7,47,30,.04);
  margin-bottom: 20px;
  transition:
    border-color .35s var(--ease), border-left-color .35s var(--ease),
    box-shadow .35s var(--ease), transform .35s var(--ease);
}

/* ---- the expand, on the way in -----------------------------------------
   "Expanding of a card." Each clause arrives slightly short and settles to
   full height as it lands, so scrolling the page reads as the document
   unfolding rather than as a list that was already there.

   Height is not animated — that would relayout every frame and drag on a long
   page. scaleY from a fixed origin gets the same read for nothing, and the
   inner text is counter-scaled so the words never squash. */
.page-legal .legal-card.reveal {
  transform-origin: 50% 0;
  transform: scaleY(.965) translateY(16px);
  opacity: 0;
}
.page-legal .legal-card.reveal.in {
  transform: none; opacity: 1;
  transition: transform .5s cubic-bezier(.2,.75,.3,1), opacity .4s var(--ease);
}

/* ---- the colour change, as it passes -----------------------------------
   The clause you are reading takes the green: its left edge fills, its number
   chip inverts, and the row for it in the contents rail lights to match. That
   is the "colour change as I scroll down" — driven by which card is actually
   in the reading band, not by hover, because on a document you are not
   pointing at what you are reading. gt-legal.js sets .is-reading. */
/* ---- A COMPETITION COLOUR PER CLAUSE, AND THE WHOLE CARD TAKES IT --------
   Client, 20 Sep 2026: "on the cards I do not want a small green. I am
   thinking when we scroll, the first it turns on colours we are using in the
   competitions, then the next also gets another colour and all — and it
   should be the WHOLE card."

   So the four codes run down the document, one per clause, repeating every
   fourth: AFL sky, NRL amber, AFLW rose, NRLW orchid. Assigned by position
   with nth-child rather than by an attribute in the markup, because the
   colour is about a clause's PLACE in the sequence and not about its
   content — nothing in clause three is more AFLW than clause four, and
   writing a code into the template would say it was.

   AT REST the colour is only an edge and a chip, so eleven cards do not read
   as eleven different things. It fills as the clause reaches the reading band
   — which is the "turns on" the client is describing — and drains again as it
   leaves, so the colour marks where you are rather than decorating the page.

   --cc is the card's own hue; the rules below never name one. */
.page-legal .legal-card:nth-child(4n+1) { --cc: #5AA9FF; --cc-ink: #12558F; }  /* AFL   */
.page-legal .legal-card:nth-child(4n+2) { --cc: #FFB25C; --cc-ink: #9A5A05; }  /* NRL   */
.page-legal .legal-card:nth-child(4n+3) { --cc: #FF8FB4; --cc-ink: #B01F55; }  /* AFLW  */
.page-legal .legal-card:nth-child(4n+4) { --cc: #C79BFF; --cc-ink: #6B34B8; }  /* NRLW  */

.page-legal .legal-card {
  border-left-color: color-mix(in srgb, var(--cc) 55%, transparent);
  background: var(--white);
}
.page-legal .legal-card > h2 .lc-n {
  color: var(--cc-ink);
  background: color-mix(in srgb, var(--cc) 22%, transparent);
  transition: background .35s var(--ease), color .35s var(--ease);
}

/* THE CLAUSE YOU ARE READING. The whole card, not an accent on it. */
.page-legal .legal-card.is-reading {
  background: color-mix(in srgb, var(--cc) 12%, var(--white));
  border-color: color-mix(in srgb, var(--cc) 52%, transparent);
  border-left-color: var(--cc);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--cc) 26%, transparent),
    0 14px 36px -22px color-mix(in srgb, var(--cc-ink) 70%, transparent);
}
.page-legal .legal-card.is-reading > h2 { color: var(--cc-ink); }
.page-legal .legal-card.is-reading > h2 .lc-n { background: var(--cc-ink); color: var(--white); }
/* The rail row for it takes the same colour, so the two halves of "where am
   I" agree instead of one being green and the other amber. */
.page-legal .legal-toc a.on { background: color-mix(in srgb, var(--rc, var(--forest)) 16%, transparent); border-left-color: var(--rc, var(--forest)); color: var(--deep); }
.page-legal .legal-toc a.on i { color: var(--rc-ink, var(--forest)); }

/* No color-mix, no tint — the card still marks itself with its edge and chip,
   which is the part that has to work everywhere. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .page-legal .legal-card.is-reading { background: var(--white); border-color: var(--cc); }
  .page-legal .legal-card > h2 .lc-n { background: rgba(7,47,30,.08); }
}

/* Hover stays, quieter than the reading state so the two cannot be confused:
   one says "you are here", the other says "you could click this". */
.page-legal .legal-card:hover { border-color: rgba(45,122,58,.22); box-shadow: 0 8px 22px -18px rgba(7,47,30,.4); }

/* ---- the contents rail keeps up ---------------------------------------- */
.page-legal .legal-toc a { transition: background .25s var(--ease), color .25s var(--ease), border-left-color .25s var(--ease); }
.page-legal .legal-toc a.on { background: rgba(45,122,58,.12); border-left-color: var(--forest); }

@media (prefers-reduced-motion: reduce) {
  /* The card still arrives, it just does not stretch to do it. */
  .page-legal .legal-card.reveal { transform: none; }
  .page-legal .legal-card.reveal.in { transition: opacity .3s var(--ease); }
}

/* ==========================================================================
   THE NUDGE — a button that asks to be noticed, briefly
   ==========================================================================
   Client, 20 Sep 2026, of the sponsorship link in the pricing hero: "maybe
   vibrates so it can draw attention."

   A SHIVER EVERY EIGHT SECONDS, NOT A LOOP. Something that moves continuously
   stops being read as a control and starts being read as decoration — the eye
   files it with the background within about two passes, which is the opposite
   of the intent. A stillness of seven seconds is what makes the half-second of
   movement register at all.

   Rotation rather than translation, and small: 1.2 degrees each way pivoting
   on the centre. A button that slides changes where it is, so a pointer on its
   way to it can miss; one that rocks stays exactly where it was.

   It carries its own icon, so it is still identifiable as the sponsorship
   route when the movement is suppressed. */
.btn-nudge { animation: btnNudge 8s var(--ease) infinite; transform-origin: 50% 50%; }
.btn-nudge svg {
  width: 16px; height: 16px; margin-right: 8px; flex: none;
  fill: currentColor; stroke: none; vertical-align: -2px;
}
@keyframes btnNudge {
  0%, 88%          { transform: rotate(0deg); }
  90%              { transform: rotate(-1.2deg); }
  92%              { transform: rotate(1.2deg); }
  94%              { transform: rotate(-1deg); }
  96%              { transform: rotate(.8deg); }
  98%, 100%        { transform: rotate(0deg); }
}
/* Reduced motion gets the attention without the movement: the edge brightens
   on the same cadence instead. The reason for the control is that somebody
   might miss it, and that reason does not go away because they asked for less
   movement. */
@media (prefers-reduced-motion: reduce) {
  .btn-nudge { animation: btnNudgeCalm 8s var(--ease) infinite; }
}
@keyframes btnNudgeCalm {
  0%, 88%, 100% { box-shadow: 0 0 0 0 rgba(207,249,58,0); }
  92%           { box-shadow: 0 0 0 4px rgba(207,249,58,.3); }
}

/* ==========================================================================
   MOST POPULAR — breathing, and wearing the codes
   ==========================================================================
   Client, 20 Sep 2026: "make sure it expands and contracts and changes the
   colours to the competition colours, just to draw attention so people can be
   interested in it."

   TWO MOVEMENTS ON DIFFERENT CLOCKS, deliberately out of step. The card
   breathes on a four-second cycle and the band above it walks the four
   competition colours on a twelve-second one, so the pair never repeats the
   same frame — which is what stops a loop reading as a loop.

   THE CARD ITSELF DOES NOT CHANGE COLOUR. It is the plan somebody is being
   asked to buy, and a price panel that cycles through four hues is a price
   panel nobody trusts. The colour lives on the band, the badge and the ring —
   the furniture — and the numbers underneath stay exactly where they were.

   The scale is 1.5%, which sounds like nothing and is the point: enough that
   the eye catches movement in the periphery, small enough that the card never
   appears to overlap its neighbours or shift the row. */
.tier[data-tier].featured {
  animation: tierBreathe 4s ease-in-out infinite;
}
@keyframes tierBreathe {
  0%, 100% { transform: translateY(-14px) scale(1); }
  50%      { transform: translateY(-16px) scale(1.015); }
}
/* The band, the badge and the ring walk the four codes. Spelled out here
   rather than read from [data-code] because this element belongs to no
   competition — it is cycling THROUGH them, which is a different idea, and
   wiring it to a token would make it look like the plan was an NRL plan. */
@keyframes tierCodes {
  0%,   100% { background: #5AA9FF; }   /* AFL   sky    */
  22%        { background: #5AA9FF; }
  28%, 47%   { background: #FFB25C; }   /* NRL   amber  */
  53%, 72%   { background: #FF8FB4; }   /* AFLW  rose   */
  78%, 97%   { background: #C79BFF; }   /* NRLW  orchid */
}
.tier[data-tier].featured::before { animation: tierCodes 12s linear infinite; }
.tier[data-tier].featured .tier-flag { animation: tierCodes 12s linear infinite; }
@keyframes tierRing {
  0%,   100% { box-shadow: 0 0 0 4px rgba(90,169,255,.30),  0 26px 52px -24px rgba(7,47,30,.8); }
  28%, 47%   { box-shadow: 0 0 0 4px rgba(255,178,92,.30),  0 26px 52px -24px rgba(7,47,30,.8); }
  53%, 72%   { box-shadow: 0 0 0 4px rgba(255,143,180,.30), 0 26px 52px -24px rgba(7,47,30,.8); }
  78%, 97%   { box-shadow: 0 0 0 4px rgba(199,155,255,.30), 0 26px 52px -24px rgba(7,47,30,.8); }
}
.tier[data-tier].featured { animation: tierBreathe 4s ease-in-out infinite, tierRing 12s linear infinite; }

/* Hovering stops the lot. Somebody reading the card is no longer being
   attracted to it, and a panel that keeps moving while you try to read the
   feature list is the thing that makes people leave. */
.tier[data-tier].featured:hover,
.tier[data-tier].featured:focus-within {
  animation-play-state: paused;
  transform: translateY(-16px) scale(1.02);
}
.tier[data-tier].featured:hover::before,
.tier[data-tier].featured:hover .tier-flag { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  /* No breathing and no travel. The colour walk stays — it is a slow crossfade
     with nothing moving in it, which is not what this query exists to
     suppress, and it is the half that actually draws the eye. */
  .tier[data-tier].featured { animation: tierRing 18s linear infinite; transform: translateY(-14px); }
  .tier[data-tier].featured::before,
  .tier[data-tier].featured .tier-flag { animation-duration: 18s; }
}
@media (max-width: 900px) {
  .tier[data-tier].featured { animation: tierRing 12s linear infinite; transform: none; }
}

/* ==========================================================================
   COMPONENTS BUILT FOR THE DARK, USED ON THE LIGHT
   ==========================================================================
   Client, 20 Sep 2026, on the sponsorship form ("this is not visible") and on
   the privacy page's closing call to action ("About GoodTip, that button is
   not seen, same in the About Us page").

   ONE CAUSE, NOT THREE. `.btn-ghost` is cream type on a cream-at-40% border,
   and `.field` paints white type on a 5%-white fill — both correct, and both
   written for the deep green they were first used on. Dropped onto a cream
   section they are invisible: measured on the live pages, "About GoodTip"
   came out at 1.05:1 and every label in the apply form at 1.08:1. That is not
   "hard to read", it is a blank space where a control is.

   So the ground decides, rather than each author remembering. Anything inside
   a light section — .on-cream, .on-paper, or a .section with no ground
   modifier at all, which is also light — gets the dark-ink treatment of the
   same component. The dark sections keep what they had, so nothing that works
   today moves.

   This is why it is worth fixing here and not per page: .btn-ghost is on six
   public templates and .field on two, and the next one somebody writes would
   have been invisible again. */

/* ---- the ghost button ---- */
.section:not(.on-dark) .btn-ghost,
.on-cream .btn-ghost,
.on-paper .btn-ghost {
  color: var(--deep);
  border-color: rgba(7,47,30,.28);
  background: transparent;
}
.section:not(.on-dark) .btn-ghost:hover,
.on-cream .btn-ghost:hover,
.on-paper .btn-ghost:hover {
  border-color: var(--forest);
  background: rgba(45,122,58,.07);
  color: var(--deep);
}
/* The hero split's left panel is deep green even though the <section> around
   it carries no ground class, so it has to be put back. Same for anything
   explicitly marked dark inside a light section. */
.hero-split .btn-ghost,
.hero-lean .btn-ghost,
.on-dark .btn-ghost {
  color: var(--cream); border-color: rgba(232,228,213,.4); background: transparent;
}
.hero-split .btn-ghost:hover,
.hero-lean .btn-ghost:hover,
.on-dark .btn-ghost:hover { border-color: var(--cream); background: rgba(255,255,255,.06); color: var(--cream); }

/* ---- the floating-label form ----
   White ink on a 5% white fill is invisible on paper, and so is the label
   that floats above it. On a light ground the field becomes what it should
   be there: a white box with a real edge and dark type. */
.section:not(.on-dark) .field input,
.section:not(.on-dark) .field select,
.section:not(.on-dark) .field textarea {
  background: var(--white);
  border-color: rgba(7,47,30,.18);
  color: var(--ink);
}
.section:not(.on-dark) .field label { color: var(--muted); }
.section:not(.on-dark) .field input:focus,
.section:not(.on-dark) .field select:focus,
.section:not(.on-dark) .field textarea:focus {
  border-color: var(--forest);
  background: var(--white);
  box-shadow: 0 0 0 3px rgba(45,122,58,.14);
}
/* The floated label is the field's NAME once something is typed, so it has to
   stay readable — the lime that works on deep green is 1.5:1 on paper. */
.section:not(.on-dark) .field input:focus + label,
.section:not(.on-dark) .field input:not(:placeholder-shown) + label,
.section:not(.on-dark) .field textarea:focus + label,
.section:not(.on-dark) .field textarea:not(:placeholder-shown) + label,
.section:not(.on-dark) .field select:focus + label,
.section:not(.on-dark) .field select[data-filled="true"] + label { color: var(--forest); }
.section:not(.on-dark) .field select { color: var(--ink); }
.section:not(.on-dark) .field select option { background: var(--white); color: var(--ink); }
.section:not(.on-dark) .field select option[value=""] { color: var(--muted); }
/* The select's own chevron is drawn in white for the dark version. */
.section:not(.on-dark) .field select { background-image: none; }
.section:not(.on-dark) .field .sel-arrow,
.section:not(.on-dark) .field::after { color: var(--muted); }

/* ==========================================================================
   ABOUT US — WHITE AND GREEN, AND CARDS THAT COME UP IN COLOUR
   ==========================================================================
   Client, 20 Sep 2026: "on the About Us we also have that translucent page
   feel, work on it, remove that cream, let's use white and green, have that
   page well organised, the cards should have different colours and in full on
   the About Us page, and should expand as we scroll downwards."

   THREE THINGS, AND THE FIRST TWO ARE THE SAME THING. The cream ground plus
   two full-height photographs down the margins is what made this read as
   washed out; white sections alternating with the deep green is the contrast
   the client is asking for, and it also gives the page its organisation for
   free — every change of ground is a change of subject. */
.section.on-white { background: var(--white); }

/* ---- a colour per card, across the whole card -------------------------
   The same four codes the Terms page runs, and for the same reason: the
   colour marks a card's PLACE in a row rather than claiming anything about
   its content, so it is assigned by position and never written into the
   template.

   Unlike the legal cards these do not need a reading state — a row of three
   is taken in at a glance, not read one at a time — so they carry their
   colour permanently and simply arrive in it. */
.on-white .card-lift:nth-child(4n+1), .section:not(.on-dark) .card-lift:nth-child(4n+1) { --cc: #5AA9FF; --cc-ink: #12558F; }
.on-white .card-lift:nth-child(4n+2), .section:not(.on-dark) .card-lift:nth-child(4n+2) { --cc: #FFB25C; --cc-ink: #9A5A05; }
.on-white .card-lift:nth-child(4n+3), .section:not(.on-dark) .card-lift:nth-child(4n+3) { --cc: #FF8FB4; --cc-ink: #B01F55; }
.on-white .card-lift:nth-child(4n+4), .section:not(.on-dark) .card-lift:nth-child(4n+4) { --cc: #C79BFF; --cc-ink: #6B34B8; }

.page-about .card-lift {
  border-color: color-mix(in srgb, var(--cc, var(--forest)) 34%, transparent);
  border-top: 4px solid var(--cc, var(--forest));
  background: color-mix(in srgb, var(--cc, var(--forest)) 7%, var(--white));
  transition: transform .4s cubic-bezier(.2,.75,.3,1), box-shadow .4s var(--ease);
}
.page-about .card-lift h3, .page-about .card-lift .cl-n { color: var(--cc-ink, var(--forest)); }
.page-about .card-lift:hover {
  box-shadow: 0 18px 44px -26px color-mix(in srgb, var(--cc-ink, var(--deep)) 75%, transparent);
}
/* On the deep green section the tint would be invisible and the ink unreadable,
   so there the colour goes on the top edge and the heading only. */
.page-about .on-dark .card-lift {
  background: rgba(255,255,255,.04);
  border-color: rgba(255,255,255,.12);
  border-top-color: var(--cc, var(--lime));
}
.page-about .on-dark .card-lift h3 { color: var(--cc, var(--lime)); }

/* ---- "should expand as we scroll downwards" ---------------------------
   Each card arrives a little short and settles to full size. scaleY from a
   fixed origin rather than height: animating height relayouts the grid on
   every frame, and with three cards in a row that is three reflows a frame
   for the length of the entrance.

   The stagger comes from the reveal wave in public/base.html, so the three in
   a row expand one after another rather than together. */
.page-about .card-lift.reveal { transform: scaleY(.94) translateY(20px); transform-origin: 50% 0; opacity: 0; }
.page-about .card-lift.reveal.in { transform: none; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .page-about .card-lift.reveal { transform: none; }
}
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .page-about .card-lift { background: var(--white); border-color: rgba(7,47,30,.14); }
}

/* ---- the four codes reach every card-lift, not only the light ones -----
   The first pass scoped these to light sections, so the three cards on the
   deep green band all fell back to the lime default — which is a row of three
   identical cards, the thing the colours exist to prevent. The assignment is
   about POSITION, and position does not care what the card is standing on. */
.card-lift:nth-child(4n+1) { --cc: #5AA9FF; --cc-ink: #12558F; }
.card-lift:nth-child(4n+2) { --cc: #FFB25C; --cc-ink: #9A5A05; }
.card-lift:nth-child(4n+3) { --cc: #FF8FB4; --cc-ink: #B01F55; }
.card-lift:nth-child(4n+4) { --cc: #C79BFF; --cc-ink: #6B34B8; }

/* ---- the pull-quote band keeps its photograph and loses the murk -------
   Client, 20 Sep 2026, of this page: "we also have that translucent page
   feel." The band is a quote set over a stadium shot, and the veil under the
   words ran to 92% — close, but a photograph reading through white type at
   40px is exactly the texture being complained about.

   The left third is now effectively solid: the words sit on deep green and
   the picture carries the right of the frame, which is the same arrangement
   as the split heroes. Nothing is lost — the photograph was never legible
   behind the text anyway, it was only making the text harder to read. */
.image-band .veil {
  background: linear-gradient(90deg,
    var(--deep) 0%,
    rgba(7,47,30,.97) 34%,
    rgba(7,47,30,.72) 62%,
    rgba(7,47,30,.34) 100%);
}


/* ---- CENTRED, on the pages we built -----------------------------------
   Client, 20 Sep 2026: "make sure things are centred in all those pages that
   we created."

   The legal layout is a 232px contents rail plus the clauses, dropped into a
   wrap as wide as the widest marketing section on the site. On a large screen
   that leaves the document hard against the left and a third of the window
   empty on the right — it reads as having come loose rather than as being
   laid out.

   Capping the pair and centring it is the whole fix. The number is the rail
   plus a comfortable measure for the prose: past about 78 characters a line
   is genuinely harder to read, and a legal page is the last place to make
   somebody work for it. */
.legal-layout {
  max-width: 1020px;
  margin-inline: auto;
}
/* The banner and the short-version panel above it line up with the layout
   rather than with the section, or the document steps sideways halfway down.
   Both carry an inline max-width of 860px from when this page was built, and
   an inline style beats a stylesheet — so the width is left alone and only
   the centring is set here, which is the part that was missing. */
.page-legal .section > .wrap > .flash,
.page-legal .legal-summary { margin-inline: auto; }

/* ==========================================================================
   THE CLOSING CTA'S SECOND BUTTON
   ==========================================================================
   Client, 20 Sep 2026: "About GoodTip — that button is not seen. Have that
   button in green and the text in black so it can be visible."

   `.cta-pro` is a LIME band, and the second button on it was `.btn-ghost` —
   cream type on a cream border. Measured on the live page it came out at
   1.05:1, which is not a faint button, it is a blank space. The light-ground
   fix further up this file did not reach it because `.cta-pro` is its own
   band and not a `.section`, so it never matched.

   GREEN FILL, NEAR-BLACK TEXT, as asked — with one substitution that keeps
   the instruction rather than breaking it. Pure black on the mid forest green
   is 4.0:1 and fails; on `--green`, the vivid one kept for fills, the deep
   forest ink reads 5.3:1 and passes. That is the same pairing the eye reads
   as "green button, black writing", and it clears the bar.

   It also has to differ from the button beside it: `.btn-dark` on this band is
   deep green with lime type, so this one is the reverse — light green with
   dark type. Two buttons, two weights, neither mistakable for the other. */
.cta-pro .btn-ghost {
  background: var(--green);
  color: var(--deep);
  border-color: var(--green);
  font-weight: 800;
}
.cta-pro .btn-ghost:hover {
  background: var(--deep);
  color: var(--lime);
  border-color: var(--deep);
}

/* ==========================================================================
   THE MEMBER APP'S PAGE-TO-PAGE LOADER: A BAR ACROSS THE TOP
   ==========================================================================
   Client, 20 Sep 2026: "the loaders in the private pages are horrible ... we
   need to have a clean loader from page to page in the private page."

   The full sport splash is a front door, and it was being used as an internal
   doorway — covering the nav a member had just used, to announce a move they
   made on purpose. This is the other convention, the one every tool people
   already use has settled on.

   FIXED TO THE WINDOW, ABOVE EVERYTHING, AND COVERING NOTHING. The bar is the
   whole loader: no panel, no backdrop, no scene. It cannot hide content
   because it does not overlap any — three pixels at the very top, outside the
   flow, with pointer-events off so it can never intercept a click meant for
   the nav underneath it. */
.loader.loader-slim {
  position: fixed; inset: 0 0 auto 0; z-index: 10000;
  display: block; height: 3px;
  background: transparent; place-items: initial;
  pointer-events: none;
  transition: opacity .35s var(--ease);
}
.loader.loader-slim .loader-bar {
  width: 100%; height: 100%; margin: 0; border-radius: 0;
  background: rgba(207,249,58,.16); overflow: hidden;
}
.loader.loader-slim .loader-bar > i {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--forest), var(--lime));
  box-shadow: 0 0 10px rgba(207,249,58,.7);
  transition: width .25s var(--ease);
  border-radius: 0;
}
.loader.loader-slim.out { opacity: 0; }

/* THE PAGE MUST STILL SCROLL WHILE IT IS UP.
   `body.loading { overflow: hidden }` exists so a full-screen splash cannot be
   scrolled behind. There is nothing to scroll behind here — the loader is a
   bar — and freezing the page for a quarter of a second on every internal
   navigation is a jolt with no purpose. */
body.loading:has(#loader.loader-slim) { overflow: visible; }

/* A veil over something small carries the shape alone — see gt-veil.js. The
   shapes shrink with it, or a 34px ladder animation over a 40px reaction chip
   is a bar chart wearing a chip as a hat. */
.bv-scoped[data-veil-compact] .bv-inner { padding: 0; gap: 0; }
.bv-scoped[data-veil-compact] .bv-climb { height: 18px; gap: 3px; }
.bv-scoped[data-veil-compact] .bv-climb i { width: 5px; }
.bv-scoped[data-veil-compact] .bv-kick { width: 40px; height: 26px; }
.bv-scoped[data-veil-compact] .bv-kick-posts { width: 22px; height: 19px; margin-left: -11px; }
.bv-scoped[data-veil-compact] .bv-kick-ball { width: 9px; height: 13px; margin-left: -4.5px; }
.bv-scoped[data-veil-compact] .bv-score { height: 20px; gap: 4px; }
.bv-scoped[data-veil-compact] .bv-score-n { width: 13px; height: 18px; }
.bv-scoped[data-veil-compact] .bv-sweep { height: 16px; gap: 3px; }
.bv-scoped[data-veil-compact] .bv-sweep i { width: 6px; height: 11px; border-width: 2px; }

/* ===========================================================================
   "TELL ME WHEN IT'S READY" — the pre-launch waiting list (home, #start)
   ===========================================================================
   Client, 22 Sep 2026, for a function the following day. Two columns on a
   dark ground: the argument on the left, the form on the right.

   IT SITS ON --deep, which is why every colour below is stated rather than
   inherited. The form controls elsewhere on the public site (.field, and the
   contact form's inputs) are built for a LIGHT ground — that is the exact bug
   that made the sponsorship form measure 1.08:1 and read as blank space, and
   reusing them here would reproduce it on the one form the client is about to
   send a room of people to. */
/* THE FORM GETS MORE OF THE ROW (client, 22 Sep 2026: "this form, make it
   wider — too small"). It was a straight 1fr 1fr, so the card and the prose
   beside it had identical width and the card lost — four fields and a button
   in half a column reads cramped where four lines of text do not. 0.92fr /
   1.08fr is a small change on paper and a visible one on screen, and the card
   has its own floor besides so it cannot be squeezed by a long heading. */
.tmw {
  display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(min(100%, 420px), 1.08fr);
  gap: clamp(28px, 4.5vw, 56px); align-items: center;
}
@media (max-width: 900px) { .tmw { grid-template-columns: 1fr; } }

.tmw-points { list-style: none; margin: 22px 0 0; padding: 0; display: grid; gap: 11px; }
.tmw-points li {
  display: flex; gap: 11px; align-items: flex-start;
  color: rgba(232,228,213,.9); font-size: 15px; line-height: 1.5;
}
/* The tick is the lime; the words are cream. Lime body text on deep green
   measures fine but reads as a warning label at paragraph length. */
.tmw-points .bk {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  background: var(--lime); color: var(--deep);
  display: grid; place-items: center; font-size: 12px; font-weight: 900;
  margin-top: 1px;
}

/* ---- the band ----
   The only lime section on the site, and that is the point: this section sits
   between a deep-green founding band and a cream FAQ, and it used to be deep
   green itself directly above a deep-green contact form. Two identical
   grounds with nothing between them read as one section with two forms in it
   (client, 22 Sep 2026). A ground nothing else uses cannot be confused with
   anything, whatever ends up next to it.

   Lime as a FIELD rather than as an accent is a decision worth noting, since
   the rest of the stylesheet is careful not to do it. It works here because
   nothing is written on the lime itself — the copy sits in deep green on the
   left and the form sits on a white card on the right, so the colour is doing
   the job of a ground and never the job of ink. */
.tmw-band {
  background:
    radial-gradient(120% 90% at 12% 0%, rgba(255,255,255,.5), transparent 58%),
    linear-gradient(135deg, var(--lime) 0%, #B8EC2E 46%, var(--green) 100%);
  color: var(--deep);
  position: relative; overflow: hidden;
}
/* A hairline of the deep green top and bottom, so the band has edges rather
   than bleeding into whatever is above and below it. */
.tmw-band::before, .tmw-band::after {
  content: ""; position: absolute; left: 0; right: 0; height: 3px;
  background: var(--deep); opacity: .16;
}
.tmw-band::before { top: 0; }
.tmw-band::after  { bottom: 0; }

/* Everything in the band takes deep green. The markup carries .on-dark-era
   inline colours from when this sat on deep green; these override them. */
.tmw-band .eyebrow { color: var(--deep) !important; opacity: 1; }
.tmw-band .section-heading { color: var(--deep) !important; }
.tmw-band .section-sub { color: rgba(7,47,30,.82) !important; opacity: 1; }
.tmw-band .tmw-points li { color: rgba(7,47,30,.9); }
.tmw-band .tmw-points .bk { background: var(--deep); color: var(--lime); }
.tmw-band .gtf-veil { background: rgba(255,255,255,.88); }
/* The label takes forest as INK; the dots take it as a FILL. Written as one
   rule these set colour and background to the same value on the same element,
   which is forest text on forest — 1:1, and the contrast audit said so. */
.tmw-band .gtf-veil-label { color: var(--forest); }
.tmw-band .gtf-spin i { background: var(--forest); }
.tmw-band .gtf-done h3 { color: var(--ink); }
.tmw-band .gtf-done p { color: var(--muted); opacity: 1; }

/* ---- the card ---- */
.tmw-card {
  position: relative;
  background: var(--white);
  border-radius: 20px;
  padding: clamp(28px, 3vw, 42px);
  box-shadow: 0 40px 80px -40px rgba(0,0,0,.6), 0 2px 8px rgba(0,0,0,.14);
  border: 1px solid rgba(255,255,255,.1);
  overflow: hidden;
}
/* A lime hairline across the top edge — the same device the message card in
   Tell the Boss uses, so the two branded objects on the site agree. */
.tmw-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 4px;
  background: linear-gradient(90deg, var(--lime), var(--green) 55%, var(--lime));
}

.tmw-form { display: grid; gap: 18px; }
.tmw-rows { display: grid; gap: 15px; }
/* The two optional fields share a row, so the form reads as "two we need, two
   we'd like" rather than four equal demands. */
.tmw-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }
@media (max-width: 560px) { .tmw-pair { grid-template-columns: 1fr; } }

.tmw-field { display: grid; gap: 7px; min-width: 0; }
.tmw-lab {
  font-family: var(--mark); font-weight: 800; font-size: 12px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink);
  display: flex; align-items: baseline; gap: 7px;
}
.tmw-lab i {
  font-family: var(--body); font-style: normal; font-weight: 500;
  font-size: 10.5px; letter-spacing: .02em; text-transform: none;
  color: var(--muted);
}

.tmw-field input,
.tmw-field select {
  width: 100%; font-family: var(--body); font-size: 15px; line-height: 1.3;
  padding: 13px 15px; border-radius: 12px;
  border: 1.5px solid rgba(15,46,34,.16);
  background: #FBFCFB; color: var(--ink);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease), background .18s var(--ease);
}
.tmw-field input::placeholder { color: rgba(15,46,34,.64); }
.tmw-field input:hover,
.tmw-field select:hover { border-color: rgba(15,46,34,.3); }
.tmw-field input:focus,
.tmw-field select:focus {
  outline: none; background: var(--white);
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(42,115,55,.15);
}
/* The browser's own "this is wrong" state, but only once they have tried. */
.tmw-field input:user-invalid { border-color: rgba(190,40,40,.5); }

/* ---- the dropdowns, drawn rather than left to the OS ----
   Client, 22 Sep 2026: "make sure the dropdown contents are also looking good
   in terms of design." A bare <select> is painted by the operating system, so
   the same form is a rounded pill on a Mac, a grey rectangle on Windows and
   something else again on Android — which is also half of why the site "looks
   different on a different machine". appearance:none takes the control back;
   the chevron is ours, and it is a real SVG rather than a background image so
   it inherits colour on focus.

   The OPTION LIST itself is still drawn by the OS and cannot be styled in any
   cross-browser way — so rather than half-style it, the options are given an
   explicit colour and ground, which is the one thing that does apply
   everywhere and stops a dark-mode OS rendering white-on-white. */
.tmw-select { position: relative; display: block; }
.tmw-select select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 42px; cursor: pointer;
  /* Long labels ("We don't run a tipping comp yet") must not widen the grid. */
  text-overflow: ellipsis;
}
.tmw-select select::-ms-expand { display: none; }
.tmw-select option { background: var(--white); color: var(--ink); }
.tmw-chev {
  position: absolute; right: 14px; top: 50%; translate: 0 -50%;
  width: 17px; height: 17px; pointer-events: none;
  fill: none; stroke: var(--forest); stroke-width: 2.1;
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke .18s var(--ease), translate .18s var(--ease);
}
.tmw-select select:focus ~ .tmw-chev { stroke: var(--forest); translate: 0 -30%; }

/* ---- the button ---- */
.tmw-submit {
  width: 100%; justify-content: center; gap: 10px;
  font-size: 15px; padding: 15px 22px;
}
.tmw-submit svg {
  width: 18px; height: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .22s var(--ease);
}
.tmw-submit:hover svg { transform: translateX(4px); }

.tmw-fine { margin: 0; font-size: 12px; color: var(--muted); line-height: 1.55; text-align: center; }
.tmw-fine a { color: var(--forest); font-weight: 700; }

/* ---- the confirmation, which replaces the form ---- */
.tmw-done { text-align: center; padding: 8px 0 4px; }
.tmw-done h3 { font-family: var(--display); font-size: 22px; color: var(--ink); margin: 16px 0 8px; }
.tmw-done p { color: var(--muted); font-size: 14.5px; line-height: 1.6; margin: 0 0 20px; }
.tmw-tick {
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--lime); color: var(--deep);
  display: inline-grid; place-items: center;
}
.tmw-tick svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }

@media (prefers-reduced-motion: no-preference) {
  .tmw-tick { animation: tmwPop .42s var(--ease) both; }
}
@keyframes tmwPop {
  from { transform: scale(.6); opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}

/* ===========================================================================
   THE "MOST POPULAR" ROW ON /pricing/ — it breathes, and it walks the codes
   ===========================================================================
   Client, 22 Sep 2026: "on the pricing, the Most popular row — make sure the
   most popular expands and contracts, then that row, let it be changing its
   background colour to the colours we are using in the competition, so it can
   capture the user's attention."

   The same treatment the price CARD got on the home page (18 Sep), now on the
   table row, because the table is what /pricing/ actually shows and the card
   fix never reached it.

   TWO ANIMATIONS, DELIBERATELY OUT OF STEP. The scale is 5.5s and the colour
   walk is 18s, so they never land together and the row never looks like it is
   pulsing to a metronome. The colours are the four real competition tokens
   from the [data-code] block above — AFL blue, AFLW pink, NRL amber, NRLW
   orchid — at low alpha, so the row tints rather than changes.

   IT IS A TINT, NOT A FILL, AND THAT IS A CONTRAST DECISION. The cells carry
   body text at 4.5:1 against the table's own ground. Painting a real colour
   behind them would move that number four times a minute, and no measurement
   taken against one of the four would hold for the other three. At .07-.10
   the text's contrast is unchanged to two decimal places and the movement is
   still plainly visible.

   HOVER STOPS BOTH. A row that keeps moving while somebody is trying to read
   the sentence in it is a row they will leave. */
.tier-table tr.is-popular {
  transform-origin: center;
  will-change: transform;
}

@media (prefers-reduced-motion: no-preference) {
  .tier-table tr.is-popular {
    animation: popBreathe 5.5s var(--ease) infinite;
  }
  .tier-table tr.is-popular td {
    animation: popWalk 18s linear infinite;
  }
  /* Reading beats attention-seeking. */
  .tier-table tr.is-popular:hover,
  .tier-table tr.is-popular:hover td,
  .tier-table tr.is-popular:focus-within,
  .tier-table tr.is-popular:focus-within td {
    animation-play-state: paused;
  }
}

/* A row cannot be transformed in every engine — Safari in particular ignores
   `transform` on <tr> in some table layouts. Scaling by a hair and falling
   back to a shadow that reads as the same "lift" means the effect degrades to
   something rather than to nothing. */
@keyframes popBreathe {
  0%, 100% { transform: scale(1);      box-shadow: 0 0 0 0 rgba(200,241,53,0); }
  50%      { transform: scale(1.012);  box-shadow: 0 10px 26px -14px rgba(15,46,34,.34); }
}

@keyframes popWalk {
  0%,   100% { background-color: rgba(90,169,255,.09); }  /* AFL   */
  25%        { background-color: rgba(255,143,180,.09); } /* AFLW  */
  50%        { background-color: rgba(255,178,92,.10); }  /* NRL   */
  75%        { background-color: rgba(199,155,255,.09); } /* NRLW  */
}

/* The badge itself pulses with the row rather than sitting still on a moving
   ground, which reads as the badge being stuck on. */
@media (prefers-reduced-motion: no-preference) {
  .tier-table tr.is-popular .tier-badge { animation: popBadge 5.5s var(--ease) infinite; }
  .tier-table tr.is-popular:hover .tier-badge { animation-play-state: paused; }
}
@keyframes popBadge {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}

/* ===========================================================================
   PUBLIC FORMS — the busy veil, the thank-you, and the return
   ===========================================================================
   Paired with static/js/gt-forms.js. Client, 22 Sep 2026: a loader over the
   form's own section rather than the page, then a thank-you, then the form
   back after about three seconds. */

[data-form-shell] { position: relative; }

/* ---- 1 · busy ---- */
.gtf-veil {
  position: absolute; inset: 0; z-index: 5;
  display: grid; place-items: center;
  border-radius: inherit;
  /* Frosted rather than opaque: the form stays visible underneath, so it reads
     as "this is working" and not as "this has been replaced". */
  background: rgba(255,255,255,.82);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .22s var(--ease);
}
.gtf-veil.on { opacity: 1; }
/* On a dark section the veil takes the ground it is actually sitting on. */
.on-dark .gtf-veil,
.section.on-dark .gtf-veil { background: rgba(7,47,30,.82); }

.gtf-veil-in { display: grid; justify-items: center; gap: 13px; }
.gtf-veil-label {
  font-family: var(--mark); font-weight: 800; font-size: 12.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--forest);
}
.on-dark .gtf-veil-label { color: var(--lime); }

/* Three dots rather than a ring. The work is a single short save with no
   progress to report, and anything shaped like a percentage would be invented. */
.gtf-spin { display: flex; gap: 6px; }
.gtf-spin i {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--forest); opacity: .3;
}
.on-dark .gtf-spin i { background: var(--lime); }
@media (prefers-reduced-motion: no-preference) {
  .gtf-spin i { animation: gtfDot 1.05s var(--ease) infinite; }
  .gtf-spin i:nth-child(2) { animation-delay: .14s; }
  .gtf-spin i:nth-child(3) { animation-delay: .28s; }
}
/* Reduced motion still needs to show that something is happening, so the dots
   hold at full strength rather than freezing mid-fade at 30%. */
@media (prefers-reduced-motion: reduce) { .gtf-spin i { opacity: 1; } }
@keyframes gtfDot {
  0%, 80%, 100% { opacity: .28; transform: translateY(0); }
  40%           { opacity: 1;   transform: translateY(-5px); }
}

/* ---- 2 · the thank-you ---- */
.gtf-done {
  text-align: center; padding: clamp(22px, 3vw, 34px) 8px;
  opacity: 0; transform: translateY(8px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.gtf-done.on { opacity: 1; transform: none; }
.gtf-done h3 {
  font-family: var(--display); font-size: clamp(20px, 2.2vw, 26px);
  color: var(--ink); margin: 16px 0 9px;
}
.gtf-done p {
  font-size: 15px; line-height: 1.6; color: var(--muted);
  margin: 0 auto; max-width: 42ch;
}
.on-dark .gtf-done h3 { color: var(--white); }
.on-dark .gtf-done p { color: var(--cream); opacity: .84; }

.gtf-tick {
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--lime); color: var(--deep);
  display: inline-grid; place-items: center;
}
.gtf-tick svg {
  width: 27px; height: 27px; fill: none; stroke: currentColor;
  stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round;
}
@media (prefers-reduced-motion: no-preference) {
  .gtf-done.on .gtf-tick { animation: gtfPop .44s var(--ease) both; }
  /* The tick draws itself. 34 is the path's length to the nearest unit. */
  .gtf-done.on .gtf-tick svg path {
    stroke-dasharray: 34; stroke-dashoffset: 34;
    animation: gtfDraw .42s var(--ease) .18s forwards;
  }
}
@keyframes gtfPop  { from { transform: scale(.55); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes gtfDraw { to { stroke-dashoffset: 0; } }

/* ---- 3 · the return ---- */
.gtf-leaving  { opacity: 0; transform: translateY(-6px); transition: opacity .18s var(--ease), transform .18s var(--ease); }
.gtf-entering { opacity: 0; transform: translateY(8px); }
form[data-ajax] { transition: opacity .26s var(--ease), transform .26s var(--ease); }

/* ---- errors ---- */
.gtf-error {
  margin: 0 0 14px; padding: 11px 14px; border-radius: 11px;
  background: rgba(190,40,40,.08); border: 1px solid rgba(190,40,40,.32);
  color: #8E1F1F; font-size: 13.5px; line-height: 1.5;
}
.on-dark .gtf-error {
  background: rgba(255,120,110,.12); border-color: rgba(255,140,130,.42);
  color: #FFC9C4;
}

/* ===========================================================================
   CENTRING, AND THE FORM CARD
   ===========================================================================
   Client, 22 Sep 2026: "make sure in all public pages data is centred — like
   in the sponsorship page, things are not well centred."

   The cause was the same everywhere it happened: a block given a max-width
   inside a wider .wrap, and no auto margin. CSS does not centre a constrained
   block on its own, so every one of them sat hard left with the rest of the
   page empty beside it. These two utilities are the fix, and they are
   utilities rather than page-specific rules so the next capped column does
   not reintroduce it. */
.stack-centre {
  max-width: 720px; margin-inline: auto; text-align: center;
  display: flex; flex-direction: column; align-items: center;
}
/* .eyebrow caps itself at 18ch to keep the big new title from running the
   full width; centred, that cap has to be released or it sits off-axis. */
.stack-centre .eyebrow { max-width: none; }
.stack-centre .section-heading,
.stack-centre .section-sub { max-width: 60ch; margin-inline: auto; }

.is-centred-block { max-width: 620px; margin-inline: auto; }

/* ---- the form card ----
   Client: "have that form also have a nice elegant design as well, I do not
   like how it looks." The sponsorship and contact forms were bare fields on
   the section's own ground. Giving them a card does three things at once: it
   bounds the form so the eye knows where it ends, it gives the busy veil
   something to cover, and it stops a long form reading as the rest of the
   page. Same object as .tmw-card, so the three public forms are one design. */
.form-card {
  position: relative;
  max-width: 760px; margin-inline: auto;
  background: var(--white);
  border-radius: 20px;
  padding: clamp(24px, 3.2vw, 38px);
  border: 1px solid rgba(15,46,34,.09);
  box-shadow: 0 34px 70px -38px rgba(15,46,34,.5), 0 2px 6px rgba(15,46,34,.05);
  overflow: hidden;
}
.form-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 4px;
  background: linear-gradient(90deg, var(--lime), var(--green) 55%, var(--lime));
}
/* On a dark section the card keeps its white ground — it is the sheet you
   write on, and inverting it would take every field's contrast with it. */
.on-dark .form-card { border-color: rgba(255,255,255,.1); }

/* ---- .field inside a .form-card is ALWAYS on white ----
   The light-ground rules above key on `.section:not(.on-dark)`, which is the
   right test while the field sits directly on the section. It stops being the
   right test the moment the form gets a white card of its own: the contact
   form lives inside `.section.on-dark`, so its fields keep the dark treatment
   — white ink on a 5% white fill — and that on a white card is white on
   white. Exactly the failure the light-ground block was written to fix,
   arriving from the other direction.

   So the card restates it, keyed on the ground the field is actually painted
   on rather than on the section two levels up. Same values, so the three
   public forms look identical whichever section they happen to sit in. */
.form-card .field input,
.form-card .field select,
.form-card .field textarea {
  background: #FBFCFB;
  border: 1.5px solid rgba(15,46,34,.16);
  color: var(--ink);
}
.form-card .field label { color: var(--muted); }
.form-card .field input:hover,
.form-card .field select:hover,
.form-card .field textarea:hover { border-color: rgba(15,46,34,.3); }
.form-card .field input:focus,
.form-card .field select:focus,
.form-card .field textarea:focus {
  background: var(--white);
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(42,115,55,.15);
}
.form-card .field input:focus + label,
.form-card .field input:not(:placeholder-shown) + label,
.form-card .field textarea:focus + label,
.form-card .field textarea:not(:placeholder-shown) + label,
.form-card .field select:focus + label,
.form-card .field select[data-filled="true"] + label { color: var(--forest); }
.form-card .field select { color: var(--ink); background-image: none; }
.form-card .field select option { background: var(--white); color: var(--ink); }
.form-card .field select option[value=""] { color: var(--muted); }
.form-card .field .sel-arrow,
.form-card .field::after { color: var(--muted); }
/* gt-ui.js swaps the select for its own dropdown; that too is drawn for a
   dark panel and has to follow the card. */
.form-card .ddx-btn {
  background: #FBFCFB; border-color: rgba(15,46,34,.16); color: var(--ink);
}
.form-card .ddx-btn:hover { border-color: rgba(15,46,34,.3); }
/* The contact section's own supporting text sits OUTSIDE the card and keeps
   the dark ground, so nothing there changes. */
.on-dark .form-card .btn-primary { color: var(--deep); }

/* ===========================================================================
   CROSS-BROWSER / CROSS-DEVICE HOLD-UP LAYER
   ===========================================================================
   Client, 22 Sep 2026: "find a way to ensure the way it looks, all the content
   holds up and looks the same in different browsers ... I do not want to be in
   a position that when someone opens the site on a Mac things are not intact,
   this looks different, some things are falling over, some overlapping."

   There is no switch for that. What there is, is the specific list of things
   that actually differ between engines on this site, each handled. Everything
   below is a real divergence with a named cause, not defensive noise.

   WHAT THIS DOES NOT REPLACE: opening the pages. It removes the known classes
   of breakage; it cannot prove a layout on a device nobody has looked at. */

/* ---- 1 · iOS zooms any input under 16px on focus, and never zooms back ----
   Safari on iPhone and iPad treats a font-size below 16px in a form control as
   a signal that the page needs magnifying. It scales the whole viewport, which
   on a fixed-nav page leaves the nav off-screen and the layout apparently
   "fallen over". Every control on the public side is therefore at least 16px
   on touch, which is also the size it should have been for thumbs. */
@media (hover: none) and (pointer: coarse) {
  input, select, textarea,
  .tmw-field input, .tmw-field select,
  .field input, .field select, .field textarea { font-size: 16px; }
}

/* ---- 2 · 100vh is wrong on mobile Safari and Chrome Android ----
   vh is measured against the viewport WITHOUT the browser chrome, so a
   100vh hero is taller than the screen and its bottom content — the scoreboard
   on the home page — sits under the URL bar until the user scrolls. dvh is the
   dynamic version and is supported everywhere that matters now; the vh line
   stays first as the fallback for anything that does not know dvh. */
.hero, .hero.has-shots { min-height: 88vh; min-height: 88dvh; }
.hs-right, .hero-split { min-height: auto; }

/* ---- 3 · Safari needs the -webkit- backdrop-filter, and some engines have
   neither ----
   Every backdrop-filter on the site is paired with its prefixed twin. Where
   the filter is missing entirely the element must still be opaque enough to
   hide what is behind it, or the veil becomes a tint over live text. */
.gtf-veil, .hs-veil, .hero-veil, .veil {
  -webkit-backdrop-filter: inherit;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .gtf-veil { background: rgba(255,255,255,.97); }
  .on-dark .gtf-veil { background: rgba(7,47,30,.97); }
}

/* ---- 4 · :has() is not everywhere ----
   The About Us card reserves a corner for its numeral using :has(). Firefox
   shipped it late and some in-app browsers still do not have it. Without the
   rule the heading can run under the numeral — which is the overlap the client
   reported. The fallback reserves the space unconditionally, costing a little
   width on cards that have no numeral rather than overlapping the ones that do. */
@supports not selector(:has(*)) {
  .card-lift .cl-ic, .card-lift > h3 { padding-right: 58px; }
}

/* ---- 5 · flex and grid children refuse to shrink below their content ----
   The default min-width:auto on a flex/grid item means one long unbroken
   string — an email address, a URL, a competition name — pushes its column
   wider than the page and produces a horizontal scrollbar on narrow screens.
   This is the single most common cause of "it is fine on my machine". */
.tmw-field, .form-grid > *, .contact-shell > *, .tier-table td,
.legal-body, .legal-toc, .hs-left, .hs-right { min-width: 0; }
/* And the strings themselves break rather than overflow. */
.tmw-fine, .contact-meta, .legal-body p, .gtf-done p, .price-desc {
  overflow-wrap: anywhere; word-break: break-word;
}

/* ---- 6 · form controls do not inherit the page font anywhere by default ----
   Left alone, Chrome renders them in Arial, Safari in -apple-system and
   Firefox in the OS UI font — three different forms on three machines, which
   is precisely the complaint. */
input, select, textarea, button {
  font-family: inherit; font-size: inherit; line-height: inherit;
  color: inherit;
}
/* Safari also strips the border radius and shadow off a date/search input. */
input[type="search"], input[type="date"] { -webkit-appearance: none; appearance: none; }

/* ---- 7 · the sticky nav needs a scroll-margin or anchors land under it ----
   Following /#start on any engine scrolls the target to y=0, which is behind
   the fixed nav. Chrome's smooth scroll hides it; Safari's does not. */
[id] { scroll-margin-top: 84px; }

/* ---- 8 · text rendering ----
   Without these, the same headline is visibly heavier on a Mac than on
   Windows, which reads as a different font rather than a different renderer. */
html {
  -webkit-text-size-adjust: 100%;   /* iOS inflates text in landscape */
  text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- 9 · a printed page should not be a screenshot of a dark site ---- */
@media print {
  .nav, .footer, .gtf-veil, .loader, video { display: none !important; }
  .section, .section.on-dark { background: #fff !important; color: #000 !important; }
}

/* ===========================================================================
   LEGAL PAGES — uniform cards, and a contents rail you can see
   ===========================================================================
   Client, 22 Sep 2026, two notes on /terms/ and /privacy/. */

/* ---- 1 · every clause card is the same height ----
   "That card part that keeps changing colour and enlarges when I reach it —
   very good design. But now when I reach the one that does not have too much
   data in it, it is small. Make sure they have the same size for uniformity
   even if one has more content than the other."

   Right, and it is worth saying why it looked wrong rather than just fixing
   it: the cards scroll past one at a time, so their height is the only thing
   the reader has to judge "how much is left" by. Clause 05 at 90px after
   clause 03 at 340px reads as the document running out, not as that clause
   being short.

   A min-height, not a fixed one. Clause 03 is four paragraphs and must still
   be allowed to be four paragraphs; what has to go is the card that is barely
   taller than its own heading. The short ones now centre their content in the
   reserved space rather than sitting at the top of an obviously empty box. */
.legal-card {
  min-height: 210px;
  display: flex; flex-direction: column; justify-content: center;
}
@media (max-width: 700px) {
  /* On a phone the cards are already tall because the measure is narrow, and
     a 210px floor there just adds blank space to every one of them. */
  .legal-card { min-height: 0; display: block; }
}
/* The heading keeps its own spacing whichever way the card is laid out. */
.legal-card > h2 { flex: none; }

/* ---- 2 · the contents rail ----
   "This menu for both — 01 What GoodTip is, 02 Accounts ... it is not well
   seen."

   It was 13.5px of #4A5A4F on cream with a 2px transparent edge, which is a
   list of links doing its best to be invisible. Three changes: a real card
   under it so it reads as a component rather than as stray text, bigger and
   darker type, and a numeral that is a chip like the one on the clause it
   points at — so the rail and the document use the same marker and you can
   match them at a glance. */
.legal-toc {
  background: var(--white);
  border: 1px solid rgba(15,46,34,.10);
  border-radius: 16px;
  padding: 16px 12px 14px;
  box-shadow: 0 18px 40px -30px rgba(15,46,34,.4);
}
.legal-toc .lt-head {
  font-family: var(--mark); font-size: 11px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--forest);
  padding: 0 10px 12px; margin-bottom: 4px;
  border-bottom: 1px solid rgba(15,46,34,.08);
}
.legal-toc a {
  gap: 11px; padding: 9px 11px; margin-bottom: 1px;
  font-size: 14px; font-weight: 500; color: var(--ink);
  border-left: 3px solid transparent;
}
.legal-toc a:hover { background: rgba(45,122,58,.09); color: var(--deep); }
.legal-toc a.on {
  background: rgba(45,122,58,.13); border-left-color: var(--forest);
  color: var(--deep); font-weight: 700;
}
/* The same chip the clause wears, so the rail and the card agree. */
.legal-toc a i {
  font-family: var(--score); font-size: 11px; font-weight: 700;
  letter-spacing: .04em; color: var(--forest);
  background: rgba(45,122,58,.11); border-radius: 6px;
  padding: 2px 6px; min-width: 24px; text-align: center;
}
.legal-toc a.on i { background: var(--forest); color: var(--white); }

@media (max-width: 960px) {
  /* The phone chip-row keeps the card but loses the vertical padding. */
  .legal-toc { padding: 10px; border-radius: 14px; }
  .legal-toc a { border-left: none; border-bottom: 3px solid transparent; white-space: nowrap; }
  .legal-toc a.on { border-left: none; border-bottom-color: var(--forest); }
}

/* ===========================================================================
   THE LIVE THREAD — the Wall typing itself out
   ===========================================================================
   Paired with static/js/gt-live.js.

   EVERYTHING HERE IS SCOPED TO .lv-on, which the script adds. With JavaScript
   off the class never appears, no rule below ever matches, and the thread is
   the fully-written static feed it has always been. Nothing is hidden by CSS
   that JavaScript is then responsible for showing — that is the pattern where
   a script error leaves a blank section. */

.wall-live { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; max-width: 920px; }
@media (max-width: 820px) { .wall-live { grid-template-columns: 1fr; } }
.wall-live > .post { margin-top: 0; }

/* Posts waiting their turn. visibility, not display, so the grid keeps its
   shape and the second post does not jump into place when it arrives. */
.lv-on .lv-hidden { visibility: hidden; opacity: 0; }
.lv-on .post,
.lv-on .wf-item,
.lv-on .wall-card { transition: opacity .34s var(--ease), transform .34s var(--ease); }
.lv-on .lv-in { visibility: visible; opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .lv-on .post.lv-hidden,
  .lv-on .wf-item.lv-hidden,
  .lv-on .wall-card.lv-hidden { transform: translateY(10px); }
  .lv-on .post.lv-in,
  .lv-on .wf-item.lv-in,
  .lv-on .wall-card.lv-in { transform: none; }
}

/* The reactions arrive after the words, because that is the order it happens
   in — a room reacts to something that has been said. */
.lv-on .post-foot,
.lv-on .wr-foot { transition: opacity .3s var(--ease), transform .3s var(--ease); }
.lv-on .post-foot.lv-hidden,
.lv-on .wr-foot.lv-hidden { transform: translateY(5px); }
.lv-on .post-foot.lv-in,
.lv-on .wr-foot.lv-in { transform: none; }

/* ---- the caret ----
   On the body being typed, and only while it is being typed. An element
   carrying a blinking caret when nothing is happening reads as a bug. */
.lv-on .post.lv-in .post-body::after,
.lv-on .wf-item.lv-in .wr-body::after,
.lv-on .wall-card.lv-in .wr-body::after {
  content: ""; display: inline-block; vertical-align: -2px;
  width: 2px; height: 1.05em; margin-left: 2px;
  background: var(--lime); opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .lv-on .post.lv-in .post-body::after,
  .lv-on .wf-item.lv-in .wr-body::after,
  .lv-on .wall-card.lv-in .wr-body::after { animation: lvCaret 1s steps(1) infinite; }
}
@keyframes lvCaret { 0%, 49% { opacity: .9; } 50%, 100% { opacity: 0; } }
/* Once the post is done and the reactions have landed, the caret goes. */
.lv-on .post:has(.post-foot.lv-in) .post-body::after,
.lv-on .wf-item:has(.wr-foot.lv-in) .wr-body::after,
.lv-on .wall-card:has(.wr-foot.lv-in) .wr-body::after { animation: none; opacity: 0; }

/* ---- the "typing…" beat before a post starts ----
   Three dots in the body's place, so the post has a shape before it has words
   and the column does not resize under the reader. */
.lv-on .post.lv-typing .post-body,
.lv-on .wf-item.lv-typing .wr-body,
.lv-on .wall-card.lv-typing .wr-body { min-height: 1.6em; }
.lv-on .post.lv-typing .post-body::before,
.lv-on .wf-item.lv-typing .wr-body::before,
.lv-on .wall-card.lv-typing .wr-body::before {
  content: "\00b7\00b7\00b7";
  letter-spacing: .22em; color: var(--lime); opacity: .75;
  font-size: 1.5em; line-height: 1;
}
@media (prefers-reduced-motion: no-preference) {
  .lv-on .post.lv-typing .post-body::before,
  .lv-on .wf-item.lv-typing .wr-body::before,
  .lv-on .wall-card.lv-typing .wr-body::before { animation: lvThink 1.1s var(--ease) infinite; }
}
@keyframes lvThink { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* A live badge on the thread, so the movement reads as "this is what it looks
   like" rather than as the page loading. */
.lv-on { position: relative; }
.lv-on::before {
  content: "Live"; position: absolute; top: -30px; right: 0;
  font-family: var(--mark); font-size: 10.5px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--deep); background: var(--lime);
  padding: 3px 10px 3px 20px; border-radius: 100px;
}
.lv-on::after {
  content: ""; position: absolute; top: -23px; right: calc(100% - 46px);
  width: 6px; height: 6px; border-radius: 50%; background: var(--deep);
}
@media (prefers-reduced-motion: no-preference) {
  .lv-on::after { animation: lvPing 1.8s var(--ease) infinite; }
}
@keyframes lvPing { 0%, 100% { opacity: 1; transform: scale(1); } 55% { opacity: .35; transform: scale(.7); } }
@media (max-width: 560px) { .lv-on::before, .lv-on::after { display: none; } }

/* ===========================================================================
   LEGAL SIDE RAILS — the shared .pg-rails component, with a stricter guard
   ===========================================================================
   /terms/ and /privacy/ use .pg-rails, the same component as the rest of the
   public site, so the wash towards the content — the thing that makes the
   private pages look settled rather than collaged — comes for free. The long
   reasoning is in the templates.

   ONE MODIFIER, AND IT IS THE GUARD. .pg-rails hides itself below 1240px,
   which is correct for a page whose column is narrow. These two have a 1180px
   document, so at 1240px there are only 30px of margin each side and a 120px
   rail would run under the clauses — the exact arrangement that got rails
   removed from these pages on 20 September. 1580px is where the margin clears
   200px, so that is where these appear, and they are narrower than the
   default because a legal page should be mostly paper. */
@media (max-width: 1579.98px) {
  .legal-rails { display: none; }
}
@media (min-width: 1580px) {
  /* BACK TO THE NARROWER RAIL (client, 22 Sep 2026: "the width on the terms
     and conditions and privacy, let it go to the way it was").

     They were widened to 240-400px earlier the same day and that was too far:
     a 331px picture each side of a legal document competes with the clauses
     instead of framing them. These are the original numbers. */
  .legal-rails .rail { width: clamp(150px, calc((100vw - var(--maxw)) / 2 - 40px), 210px); }
  /* A touch further into the wash than the default: on a document the eye
     should never be pulled off the measure, and these sit beside eleven
     clauses somebody is being asked to agree to. */
  .legal-rails .rail::after {
    background: linear-gradient(90deg, rgba(244,242,234,.38), rgba(244,242,234,.92) 58%, var(--cream));
  }
  .legal-rails .rail.right::after {
    background: linear-gradient(270deg, rgba(244,242,234,.38), rgba(244,242,234,.92) 58%, var(--cream));
  }
}

/* ===========================================================================
   FORM STATES INSIDE A .form-card ALWAYS TAKE THE CARD'S GROUND
   ===========================================================================
   The .on-dark variants above are correct while the veil and the thank-you
   sit directly on a dark section. They stop being correct the moment the form
   is given a white card — and the contact form is exactly that case: a white
   .form-card inside .section.on-dark.

   Left alone, every one of them inverts against the wrong surface:
     · the thank-you heading is white on a white card — invisible
     · its paragraph is cream on white — 1.2:1
     · the veil is deep green over a white card, so "sending" looks like the
       form was replaced by a dark panel
     · the error is pale pink on white

   This is the third time the same mistake has been available in this
   stylesheet: a colour chosen from the SECTION when the thing is actually
   painted on a nearer surface. It is also what produced the 1.08:1 sponsorship
   labels and the white-on-white .field. The rule is the same each time — ask
   what the element is drawn on, not what section it is in. */
.form-card .gtf-veil,
.on-dark .form-card .gtf-veil,
.section.on-dark .form-card .gtf-veil { background: rgba(255,255,255,.86); }
.form-card .gtf-veil-label,
.on-dark .form-card .gtf-veil-label { color: var(--forest); }
.form-card .gtf-spin i,
.on-dark .form-card .gtf-spin i { background: var(--forest); }

.form-card .gtf-done h3,
.on-dark .form-card .gtf-done h3 { color: var(--ink); }
.form-card .gtf-done p,
.on-dark .form-card .gtf-done p { color: var(--muted); opacity: 1; }

.form-card .gtf-error,
.on-dark .form-card .gtf-error {
  background: rgba(190,40,40,.08);
  border-color: rgba(190,40,40,.32);
  color: #8E1F1F;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .form-card .gtf-veil,
  .on-dark .form-card .gtf-veil { background: rgba(255,255,255,.97); }
}

/* ===========================================================================
   THE ORGANISATION CHIP — its name, in full, and obviously a control
   ===========================================================================
   Client, 22 Sep 2026, with a screenshot of the bar: "look at the organisation
   dropdown — I cannot see the full name of that organisation, or even know
   it's a dropdown ... I should see the name, full, between the logo and
   Dashboard."

   It was rendering as "Aq…" for AquaFlow Water Co. The cause is a row that
   asks for more than it has: eight nav links at 14px with 14px of padding
   each, plus two theme toggles, an avatar and a sign-out, all `flex: none`.
   The chip is the one shrinkable item in the row, so it absorbed the whole
   shortfall and collapsed onto .an-ctx-org's 4.5em floor.

   THE FIX IS TO STOP MAKING THE CHIP PAY FOR THE LINK ROW. Three parts:

     1. The chip gets a floor that fits a real organisation name — 13em, about
        22 characters, which covers "Coastal Facilities Management" at the
        size it is set. Below that it is the LINKS that give way.
     2. The links become the shrinkable item instead, and tighten their
        padding before anything truncates. A nav link losing 6px of padding
        costs nothing; an organisation name losing its second word costs the
        reader the one fact the control exists to carry.
     3. The chevron is always visible and the chip reads as a control: a
        stronger border, a hover that lifts, and an open state that holds the
        lime. It had a chevron already but at .45 opacity beside a truncated
        word it did not register as one.

   Below 1240px the link labels go icon-only, which buys ~380px in one move —
   by then the icons are familiar and the organisation name is not. */

/* 1 · the name gets the room */
.gt-app .an-ctx-btn,
.gt-v2 .an-ctx-btn { max-width: clamp(13em, 30vw, 460px); }
/* The floor was 118px — enough for a control to exist, not enough for a name
   to be read. 200px fits "AquaFlow Water Co" and most of what follows it. */
.gt-app .an-ctx, .gt-v2 .an-ctx { min-width: 200px; }
@media (max-width: 1100px) {
  /* Below the burger breakpoint the links are gone anyway, so the chip stops
     competing and goes back to giving way. */
  .gt-app .an-ctx, .gt-v2 .an-ctx { min-width: 118px; }
}
.gt-app .an-ctx-org,
.gt-v2 .an-ctx-org { min-width: 0; opacity: 1; font-weight: 700; }

/* 2 · THE LINKS ARE NOT WHAT GIVES, and that is a rule with history.
   An earlier fix collapsed the row into the burger at 1320px and took Manage
   and Admin off the bar on every laptop under it — "I have picked an
   organisation that I am an admin, I cannot see the admin and the manage."
   accounts.tests.NavRowFitsTests holds that line, and it is the right line:
   a link's absence is unexplainable, where a name that ellipsises still reads
   and carries the full one in its title.

   So the room comes from PADDING, not from links. Every destination stays on
   the bar at every width; they simply sit closer together once the row is
   tight. 10px a side instead of 14px across eight links is ~64px back, which
   is most of an organisation name. */
@media (max-width: 1560px) {
  .gt-app .an-links a, .gt-v2 .an-links a { padding-left: 10px; padding-right: 10px; }
}
@media (max-width: 1400px) {
  .gt-app .an-links, .gt-v2 .an-links { gap: 0; }
  .gt-app .an-links a, .gt-v2 .an-links a { padding-left: 8px; padding-right: 8px; }
}

/* 4 · it looks like a dropdown */
.gt-app .an-ctx-btn, .gt-v2 .an-ctx-btn {
  border-color: rgba(200,241,53,.28);
  background: rgba(200,241,53,.07);
  gap: 9px;
}
.gt-app .an-ctx-btn:hover, .gt-v2 .an-ctx-btn:hover {
  border-color: rgba(200,241,53,.55);
  background: rgba(200,241,53,.13);
}
.gt-app .an-ctx-btn[aria-expanded="true"],
.gt-v2 .an-ctx-btn[aria-expanded="true"] {
  border-color: var(--lime);
  background: rgba(200,241,53,.18);
}
/* The chevron: full strength, and it turns over when the menu is open, which
   is the clearest single signal that a control opens something. */
.gt-app .an-ctx-btn .an-menu-caret,
.gt-app .an-ctx-btn svg:last-child,
.gt-v2 .an-ctx-btn svg:last-child {
  opacity: .9; flex: none; transition: transform .18s var(--ease), opacity .18s var(--ease);
}
.gt-app .an-ctx-btn[aria-expanded="true"] svg:last-child,
.gt-v2 .an-ctx-btn[aria-expanded="true"] svg:last-child { transform: rotate(180deg); opacity: 1; }

/* ===========================================================================
   THE GROUPS HERO — a split, with a picture from the media library
   ===========================================================================
   Client brief, 22 Sep 2026. Copy left, image right at ~42%, large rounded
   container, object-fit cover, subtle overlay only where it helps legibility.
   The picture comes from orgs/hero_images.py; see the template for why. */
.dept-hero.dh-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.76fr);
  gap: clamp(24px, 3.5vw, 52px);
  align-items: center;
  text-align: left;
}
.dh-split .dh-copy { text-align: left; align-items: flex-start; }
.dh-split .dh-lead { margin-left: 0; margin-right: 0; max-width: 56ch; }
.dh-split .dh-acts { justify-content: flex-start; }

/* The picture panel. aspect-ratio rather than a height, so it keeps its shape
   at every width instead of becoming a letterbox on a laptop. */
.dh-art {
  position: relative; overflow: hidden;
  border-radius: 22px;
  aspect-ratio: 4 / 3;
  min-height: 240px;
  background: var(--deep-2);
  border: 1px solid rgba(200,241,53,.16);
  box-shadow: 0 34px 70px -38px rgba(0,0,0,.75);
}
.dh-shots { position: absolute; inset: 0; }
.dh-art .shot {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
/* The overlay is deliberately light and only at the edges. The brief asked
   for it "only if needed for readability" — nothing is written on this panel,
   so its whole job is to stop a bright photograph fighting the deep green
   around it. A full veil would turn a photograph into a swatch. */
.dh-art-veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(120deg, rgba(7,47,30,.55), rgba(7,47,30,0) 46%),
    linear-gradient(0deg, rgba(7,47,30,.34), rgba(7,47,30,0) 40%);
}
/* No photographs in the library: a clean panel, never a borrowed image. */
.dh-art-plain {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: radial-gradient(90% 90% at 30% 20%, var(--panel-2), var(--deep-2));
}
.dh-art-plain span {
  font-family: var(--display); font-size: clamp(56px, 8vw, 104px);
  color: rgba(200,241,53,.22); line-height: 1;
}

@media (max-width: 900px) {
  /* The picture goes above the copy rather than beside it — a 40% panel at
     phone width is a stripe. */
  .dept-hero.dh-split { grid-template-columns: 1fr; }
  .dh-art { order: -1; aspect-ratio: 16 / 9; min-height: 0; }
}

/* ---- the standing group total beside the search ---- */
.grp-total {
  margin: 8px 0 0; font-family: var(--mark); font-weight: 700;
  font-size: 12.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--lime); opacity: .85;
}

/* ---- the list, built for many groups ----
   Brief: "the page should support 10, 20, 50+ groups without breaking the
   layout ... do not constrain the group list to only a few visible cards."
   g-3 is three columns at every width above the breakpoint, which at 50
   groups gives 17 very tall rows of cramped cards. auto-fill with a sensible
   minimum gives two wide cards on a laptop and three on a large screen, and
   the row count follows the data rather than the other way round. */
.grid-app.g-3[data-grp-list] {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
  align-items: start;
}

/* ===========================================================================
   THE APP BAR GOES FULL WIDTH — the brand to the far left
   ===========================================================================
   Client, 22 Sep 2026, with a screenshot of the bar: "we will have to move the
   menu GoodTip to the far left, so that that organisation or group name can
   have room to be long. That will make sense."

   It does, and it is the right answer rather than a workaround. The bar was
   capped at --gt-wide (1300px) and centred, which on a 1920 screen left 310px
   of empty deep green at each end while the organisation's name was being
   ellipsised to "AquaFlow Wa…" in the middle. The row was short of space
   inside a container that had 620px going spare beside it.

   So the BAR spans the window and the page beneath it keeps its measure.
   Those are different jobs: a navigation bar is chrome and belongs to the
   window, where a page of content wants a readable column. Every other app
   this size does the same thing for the same reason.

   This supersedes the padding squeeze added a few rules above: with 620px
   recovered, neither the links nor the name has to give anything up. The
   padding rules stay for genuinely narrow windows, where they still apply. */
.gt-v2 .app-nav .an-inner,
.app-nav .an-inner {
  max-width: none;
  margin: 0;
  padding-left: clamp(16px, 2vw, 32px);
  padding-right: clamp(16px, 2vw, 32px);
}

/* With the room back, the name gets a proper allowance rather than a floor it
   is permanently pinned to. */
@media (min-width: 1400px) {
  .gt-app .an-ctx, .gt-v2 .an-ctx { min-width: 260px; }
  .gt-app .an-ctx-btn, .gt-v2 .an-ctx-btn { max-width: 520px; }
  /* And the links get their breathing room back, since nothing is short now. */
  .gt-app .an-links a, .gt-v2 .an-links a { padding-left: 13px; padding-right: 13px; }
}

/* The brand sits hard left, with the chip immediately after it — the two
   together read as "where you are", which is what the left of a bar is for. */
.gt-v2 .app-nav .an-brand, .app-nav .an-brand { flex: none; }


/* The public Wall's own feed keeps ITS layout when it becomes a live thread.
   .wall-live sets a two-column grid for the home page teaser; .wallfeed is a
   single column of full-width cards with reply threads hanging off them, and
   forcing it into two columns would break the threads away from their posts. */
.wallfeed.wall-live { display: block; max-width: none; }
.wallfeed.wall-live > .wf-item { margin-top: 0; }

/* ===========================================================================
   GROUPS — the working area, matched to the reference design
   ===========================================================================
   Client, 22 Sep 2026, with a reference image: a bordered panel under the
   hero holding the search and the list, a labelled header row between them,
   and DARK cards rather than cream ones, each with a coloured icon tile, a
   description, and lime/ghost actions.

   THE CARDS FLIP GROUND, and that is the change that makes it look like the
   reference. .dept-card is a cream card because that is what it is everywhere
   else in the app; on this page it sits inside a dark panel on a dark sheet,
   and a cream block there reads as a hole. Everything below is scoped to
   .grp-grid so no other screen's cards move. */

.grp-panel {
  background: rgba(255,255,255,.028);
  border: 1px solid rgba(200,241,53,.13);
  border-radius: 24px;
  padding: clamp(18px, 2.4vw, 28px);
  margin-top: clamp(18px, 2.4vw, 28px);
}

/* ---- the header row between the search and the list ---- */
.grp-listhead {
  display: flex; align-items: center; gap: 12px;
  margin: 22px 0 16px;
}
.grp-listhead-ic {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center;
  background: rgba(200,241,53,.14); color: var(--lime);
}
.grp-listhead-ic svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.grp-listhead-t {
  font-family: var(--display); font-size: 17px; color: var(--white); flex: none;
}
.grp-listhead-rule { flex: 1 1 auto; height: 1px; background: rgba(255,255,255,.12); }
.grp-listhead-n {
  flex: none; font-size: 13.5px; font-weight: 600; color: var(--cream); opacity: .72;
}

/* ---- the cards ---- */
.grp-grid { margin-top: 0; gap: 14px; }
.grp-grid .dept-card {
  background: rgba(255,255,255,.04) !important;
  border: 1px solid rgba(255,255,255,.1) !important;
  border-radius: 16px;
  padding: 18px 18px 16px !important;
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.grp-grid .dept-card:hover {
  border-color: rgba(200,241,53,.38) !important;
  background: rgba(200,241,53,.05) !important;
  transform: translateY(-2px);
}
/* Every word on the card takes the dark ground's ink. .dc-meta and .card-lead
   are the card's cream-card colours and are unreadable here. */
.grp-grid .dc-name { color: var(--white); font-size: 16.5px; }
.grp-grid .dc-meta,
.grp-grid .card-lead { color: var(--cream) !important; opacity: .74; }
.grp-grid .dept-card .pill-role { border-color: rgba(200,241,53,.4); }

/* The icon tile keeps its colour but gains weight, the way the reference
   draws it — a filled rounded square rather than a tint. */
.grp-grid .dc-ic {
  width: 42px; height: 42px; border-radius: 12px; flex: none;
}
.grp-grid .dc-ic svg { width: 20px; height: 20px; }

/* Ghost buttons on a dark card, not on a cream one. */
.grp-grid .abtn-ghost {
  border-color: rgba(255,255,255,.22);
  color: var(--cream);
  background: transparent;
}
.grp-grid .abtn-ghost:hover {
  border-color: rgba(200,241,53,.5);
  background: rgba(200,241,53,.08);
  color: var(--white);
}

/* A group still awaiting an admin keeps its warning edge, in a colour that
   works on the dark ground. */
.grp-grid .dept-card.is-pending {
  border-color: rgba(244,198,78,.42) !important;
  background: rgba(244,198,78,.06) !important;
}

/* ---- the search bar, matched to the reference's pill ---- */
.grp-panel .grp-search-form { border-radius: 999px; }
.grp-panel .grp-search-go { border-radius: 999px; }

/* ---- the Groups hero, matched to the reference more closely ----
   Three corrections after seeing it rendered (22 Sep 2026):

   1. THE NAME OUTRANKS THE WORD. The reference sets the organisation large
      and white with a small lime "GROUPS" beneath it. Both were rendering at
      the same size, so the hero read as a two-line heading rather than as a
      name with a label under it. Same inversion the public sections got.
   2. THE PICTURE FILLS ITS COLUMN. aspect-ratio 4/3 capped the panel's height
      independently of the copy beside it, so a tall hero left a band of empty
      green under the image. It stretches to the row now, with the ratio only
      as a floor.
   3. It sits flush with the top of the copy rather than floating mid-column. */
.dh-split .gh-eyebrow.is-title {
  font-family: var(--display);
  font-size: clamp(28px, 3.4vw, 44px);
  line-height: 1.04; letter-spacing: -.01em;
  color: var(--white); text-transform: none; opacity: 1;
  margin-bottom: 4px; max-width: 18ch;
}
.dh-split .dh-word {
  font-family: var(--mark); font-weight: 900;
  font-size: 14px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--lime); margin: 0 0 14px;
}
.dept-hero.dh-split { align-items: stretch; }
.dh-split .dh-copy { justify-content: center; }
.dh-art {
  aspect-ratio: auto;
  height: 100%;
  min-height: clamp(240px, 26vw, 380px);
}

/* ---- two corrections, both specificity ----
   Measured in the browser rather than guessed at: .dh-word computed to 44px
   white Archivo Black and .grp-grid .abtn-ghost computed to rgb(15,46,34) —
   deep-green ink sitting on a dark card. The rules above were written at a
   specificity the page's own rules outrank, so neither was applying.

   THE GHOST BUTTON IS THE ONE THAT MATTERED. .abtn-ghost is declared for a
   cream card, and these cards were cream until an hour ago; nothing in the
   stylesheet knows they are dark now. scripts/check_contrast.py did not catch
   it either, because it reads rules against the ground they DECLARE, and this
   pair only meets on a card whose background a different rule changed. Worth
   remembering: that audit finds a colour written on the wrong ground, not a
   ground moved out from under a colour. */
.gt-app .dept-hero.dh-split h1.dh-word,
.dept-hero.dh-split h1.dh-word {
  font-family: var(--mark); font-weight: 900;
  font-size: clamp(13px, 1.4vw, 16px); letter-spacing: .18em;
  text-transform: uppercase; color: var(--lime);
  margin: 6px 0 14px; line-height: 1.2;
}

.gt-app .grp-grid .dept-card .abtn-ghost,
.grp-grid .dept-card .abtn-ghost,
.grp-grid .dept-card a.abtn-ghost,
.grp-grid .dept-card button.abtn-ghost {
  border: 1px solid rgba(255,255,255,.26) !important;
  color: var(--cream) !important;
  background: transparent !important;
}
.gt-app .grp-grid .dept-card .abtn-ghost:hover,
.grp-grid .dept-card .abtn-ghost:hover {
  border-color: rgba(200,241,53,.55) !important;
  background: rgba(200,241,53,.1) !important;
  color: var(--white) !important;
}
/* The "you're tipping here" pill sits among them and is the same story. */
.grp-grid .dept-card .pill-role {
  color: var(--lime) !important;
  border-color: rgba(200,241,53,.45) !important;
  background: rgba(200,241,53,.1) !important;
}

/* ===========================================================================
   THE CONTEXT CHIP, REDRAWN — two centred lines and a real chevron
   ===========================================================================
   Client, 22 Sep 2026. The reasoning is in templates/app_base.html; the short
   version is that name + glyph-pill + chevron in one row read as three
   unrelated objects, and the pill read as a toggle switch.

   The text block centres itself; the chevron sits outside it so it cannot
   pull the optical centre sideways. */
.gt-app .an-ctx-btn,
.gt-v2 .an-ctx-btn {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 6px 12px 6px 16px;
  border-radius: 14px;
  line-height: 1.15;
}
.an-ctx-text {
  display: flex; flex-direction: column; align-items: center;
  min-width: 0; flex: 1 1 auto; gap: 2px;
}
.gt-app .an-ctx-text .an-ctx-org,
.gt-v2 .an-ctx-text .an-ctx-org {
  display: block; max-width: 100%;
  font-family: var(--body); font-weight: 800; font-size: 14.5px;
  letter-spacing: .005em; color: var(--white); opacity: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; text-align: center;
}
/* The second line: what you are here, and which group if you are in one. */
.an-ctx-role {
  display: inline-flex; align-items: center; gap: 5px;
  max-width: 100%; min-width: 0;
  font-family: var(--mark); font-weight: 800; font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase;
  background: none; border: 0; padding: 0;
}
.an-ctx-role svg {
  width: 11px; height: 11px; flex: none; fill: currentColor;
}
.an-ctx-role.is-admin { color: var(--lime); }
.an-ctx-role.is-member { color: rgba(232,228,213,.72); }
.an-ctx-roleword { flex: none; }
.an-ctx-role .an-ctx-sep { opacity: .5; flex: none; }
.an-ctx-role .an-ctx-group {
  font-weight: 800; color: inherit;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}

/* The chevron. Full strength, outside the centred block, and it turns over
   when the panel opens — the clearest signal that a control opens something. */
.an-ctx-caret {
  width: 16px; height: 16px; flex: none; opacity: .8;
  transition: transform .18s var(--ease), opacity .18s var(--ease);
}
.an-ctx-btn:hover .an-ctx-caret { opacity: 1; }
.an-ctx-btn[aria-expanded="true"] .an-ctx-caret { transform: rotate(180deg); opacity: 1; }

/* Light theme: the name takes the light palette's ink. */
html[data-theme="light"] .gt-app .an-ctx-text .an-ctx-org { color: var(--lt-ink, var(--ink)); opacity: 1; }
html[data-theme="light"] .gt-app .an-ctx-role.is-member { color: rgba(15,46,34,.66); }

/* The Groups hero with no picture: one column, full width, still left-aligned.
   .dh-split's two-column grid would leave an empty second column. */
.dept-hero.dh-noart { display: block; text-align: left; }
.dh-noart .dh-copy { text-align: left; align-items: flex-start; }
.dh-noart .dh-lead { margin-left: 0; margin-right: 0; max-width: 62ch; }
.dh-noart .dh-acts { justify-content: flex-start; }
.dh-noart .gh-eyebrow.is-title {
  font-family: var(--display);
  font-size: clamp(28px, 3.4vw, 44px);
  line-height: 1.04; letter-spacing: -.01em;
  color: var(--white); text-transform: none; opacity: 1;
  margin-bottom: 4px; max-width: 20ch;
}
.gt-app .dept-hero.dh-noart h1.dh-word,
.dept-hero.dh-noart h1.dh-word {
  font-family: var(--mark); font-weight: 900;
  font-size: clamp(13px, 1.4vw, 16px); letter-spacing: .18em;
  text-transform: uppercase; color: var(--lime);
  margin: 6px 0 14px; line-height: 1.2;
}

/* ---- the role line in the CHIP is not a pill ----
   .an-ctx-role has been a filled badge since it was a glyph: line 16349 sets
   `background: var(--lime); color: var(--deep)` at specificity 0,2,0. The
   two-line rewrite gave it `background: none` on the base class at 0,1,0,
   which loses — and then set `color: var(--lime)` at 0,2,0, which wins. Lime
   text on a lime pill: the word vanished and the chip showed a solid blob.

   Measured, not guessed: the element computed to background rgb(207,249,58)
   and color rgb(207,249,58). check_contrast.py cannot see this one either —
   the two colours come from two different rules, and it pairs a colour with
   the ground its own rule declares.

   Scoped to .an-ctx-btn so the same class keeps its badge treatment on the
   panel rows below, where it IS a badge and should stay one. */
.gt-app .an-ctx-btn .an-ctx-role,
.gt-v2 .an-ctx-btn .an-ctx-role,
.an-ctx-btn .an-ctx-role.is-admin,
.an-ctx-btn .an-ctx-role.is-member {
  background: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  padding: 0 !important;
  gap: 5px !important;
}
.an-ctx-btn .an-ctx-role.is-admin { color: var(--lime) !important; }
.an-ctx-btn .an-ctx-role.is-member { color: rgba(232,228,213,.75) !important; }
.an-ctx-btn .an-ctx-role svg { width: 11px !important; height: 11px !important; fill: currentColor; }
html[data-theme="light"] .gt-app .an-ctx-btn .an-ctx-role.is-admin { color: var(--forest) !important; }
html[data-theme="light"] .gt-app .an-ctx-btn .an-ctx-role.is-member { color: rgba(15,46,34,.7) !important; }

/* ===========================================================================
   THE APP BAR — spacing, a wider wordmark, and a centred link row
   ===========================================================================
   Client, 23 Sep 2026: "it's too far on the left, there is a lot of gap
   between the Dashboard button and it, move it to the right. Then also
   increase the size of the GoodTip logo, not up to down but left to right.
   Ensure the menu is also centred."

   All three come out of the same change made yesterday: the bar went
   full-width so the organisation's name would stop truncating. That worked,
   and it left the four clusters sitting where a 1300px layout had put them —
   brand and chip jammed together at the far left, the link row starting right
   after them, and 600px of empty green in the middle of the bar.

   THE LINK ROW IS CENTRED WITH AUTO MARGINS, not with absolute positioning.
   Absolute centring would put the row at the exact middle of the window
   regardless of what is beside it, which is only correct while the left and
   right clusters happen to weigh the same — and they do not: the account
   cluster on the right is about twice the brand's width. Auto margins centre
   the row in the space that is actually free, so it sits optically centred
   between its neighbours instead of mathematically centred under a lopsided
   bar. It also degrades safely: when the row grows past the space, the
   margins collapse to zero and nothing overlaps. */
@media (min-width: 1101px) {
  /* 1 · the chip comes off the brand and sits near the links */
  .gt-app .an-ctx,
  .gt-v2 .an-ctx { margin-left: clamp(28px, 5vw, 96px); }

  /* 2 · the link row centres itself in whatever is left */
  .gt-app .an-links,
  .gt-v2 .an-links { margin-left: auto; margin-right: auto; }
}

/* 3 · THE WORDMARK GROWS SIDEWAYS ONLY.
   Two things, and the order matters. Tracking does most of the work and is
   the honest half — it adds width by opening the spaces between letters and
   leaves the letterforms alone. The scale does the rest: Barlow Condensed is
   a condensed face, so stretching it 12% moves it back towards normal
   proportions rather than distorting it, which is not true of stretching a
   regular-width face.

   transform-origin is the left edge, so the mark grows INTO the bar from
   where it is anchored rather than drifting off the gutter. font-size is
   untouched, which is what keeps the height where it was. */
.gt-app .an-brand,
.gt-v2 .an-brand {
  letter-spacing: .07em;
  transform: scaleX(1.12);
  transform-origin: left center;
  /* The transform does not affect layout, so the row has to be told about the
     width the mark now occupies or the chip beside it sits on top of the P. */
  margin-right: clamp(6px, 1.4vw, 22px);
}

/* ---- the chip hugs its own content again ----
   Measured on the dashboard at 1920: the .an-ctx box was 394px wide while the
   pill inside it drew about 210px. That slack is the "lot of gap between the
   Dashboard button and it" — the chip was not far from the links, it was
   holding 180px of nothing on its right edge.

   The floor came from yesterday's truncation fix, where a 1300px centred bar
   squeezed the name to "AquaFlow Wa…" and the chip needed a width it could
   not be pushed below. The bar spans the window now, so the pressure that
   floor existed to resist is gone, and all it does is pad the row. It stays
   below 1101px, where the link row is hidden and the chip is sharing a
   genuinely tight bar with the burger. */
@media (min-width: 1101px) {
  .gt-app .an-ctx,
  .gt-v2 .an-ctx { min-width: 0; flex: 0 0 auto; }
  /* Content-width, with a ceiling so an unusually long name cannot push the
     link row off centre. Past it the name ellipsises, as it always did. */
  .gt-app .an-ctx-btn,
  .gt-v2 .an-ctx-btn { max-width: 340px; width: auto; }
}

/* ===========================================================================
   GROUPS IN LIGHT MODE — the words come back
   ===========================================================================
   Client, 23 Sep 2026: "on the groups the texts are not being seen in the
   light mode."

   Everything this page gained yesterday was written for the dark sheet and
   said so in absolute terms: cards at rgba(255,255,255,.04), names in
   --white, supporting copy and ghost buttons in --cream. On the light theme
   the page ground turns white, so a 4%-white card IS the page and cream text
   on it is cream on white — 1.2:1, which is not faint, it is gone.

   This is the same mistake three times over now (the contact form's fields,
   the group cards' ghost buttons, the chip's role pill): a colour chosen from
   the surface it was FIRST drawn on rather than from the surface it is
   painted on. Here the surface moves with the theme, so the colours have to
   move with it too. */
html[data-theme="light"] .gt-app .grp-panel {
  background: var(--lt-surface-2);
  border-color: var(--lt-line);
}
html[data-theme="light"] .gt-app .grp-grid .dept-card {
  background: var(--lt-surface) !important;
  border-color: var(--lt-line) !important;
  box-shadow: 0 1px 2px rgba(15,46,34,.05);
}
html[data-theme="light"] .gt-app .grp-grid .dept-card:hover {
  background: var(--lt-tint) !important;
  border-color: rgba(23,107,69,.4) !important;
}
html[data-theme="light"] .gt-app .grp-grid .dc-name { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .grp-grid .dc-meta,
html[data-theme="light"] .gt-app .grp-grid .card-lead {
  color: var(--lt-quiet) !important; opacity: 1;
}
/* The ghost buttons carried !important on the dark rules, so the light ones
   have to as well — and they are the controls, so they matter most. */
html[data-theme="light"] .gt-app .grp-grid .dept-card .abtn-ghost {
  border-color: var(--lt-line) !important;
  color: var(--lt-ink) !important;
  background: var(--lt-surface) !important;
}
html[data-theme="light"] .gt-app .grp-grid .dept-card .abtn-ghost:hover {
  border-color: var(--lt-forest) !important;
  background: var(--lt-tint) !important;
  color: var(--lt-ink) !important;
}
html[data-theme="light"] .gt-app .grp-grid .dept-card .pill-role {
  color: var(--lt-forest) !important;
  border-color: rgba(23,107,69,.4) !important;
  background: var(--lt-sel) !important;
}
html[data-theme="light"] .gt-app .grp-grid .dept-card.is-pending {
  background: #FFF9E8 !important;
  border-color: rgba(154,90,5,.38) !important;
}

/* The list header and the hero above it. */
html[data-theme="light"] .gt-app .grp-listhead-t { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .grp-listhead-n { color: var(--lt-quiet); opacity: 1; }
html[data-theme="light"] .gt-app .grp-listhead-rule { background: var(--lt-line); }
html[data-theme="light"] .gt-app .grp-listhead-ic {
  background: var(--lt-sel); color: var(--lt-forest);
}
html[data-theme="light"] .gt-app .grp-total { color: var(--lt-accent-ink); opacity: 1; }
html[data-theme="light"] .gt-app .dept-hero.dh-noart .gh-eyebrow.is-title { color: var(--lt-ink); }
html[data-theme="light"] .gt-app .dept-hero.dh-noart h1.dh-word { color: var(--lt-accent-ink); }
html[data-theme="light"] .gt-app .dh-noart .dh-lead { color: var(--lt-quiet); }

/* ===========================================================================
   THE LIGHT BAR — the green and the white, turned up
   ===========================================================================
   Client, 23 Sep 2026: "in the light mode let's make that pattern on the menu
   where we have green and white, make the green and white more [pronounced]
   so I can see and get the feel of that contrast."

   The bar carries a diagonal pinstripe over a pale green-to-white sweep. Both
   halves were set so quietly that on a bright screen the whole thing read as
   one flat off-white: the stripe was 4% green on a 1.5px pitch, which is
   below what most panels resolve, and the sweep's greens were #E7F3CE to
   #FFFFFF — four steps of nearly the same value.

   Turned up on both counts, and the pattern is the part that carries it: the
   stripe goes to 9% on a wider 3px band so it is a texture you can see rather
   than one you can measure, and the sweep's green ends go deeper so the white
   in the middle reads as white BECAUSE there is something beside it. The type
   on top does not move — every colour written on this bar was measured
   against the white in the middle, which is still white. */
html[data-theme="light"] .app .app-nav {
  background:
    repeating-linear-gradient(135deg,
      rgba(23,107,69,.09) 0 3px, rgba(255,255,255,0) 3px 11px),
    linear-gradient(100deg,
      #D4EBA6 0%, #E8F5C8 16%, #F8FCEE 36%, #FFFFFF 52%, #F4FADF 70%, #E2F2B8 86%, #CFE89C 100%);
  border-bottom: 1px solid rgba(23,107,69,.3);
  box-shadow: 0 1px 0 rgba(255,255,255,.8) inset, 0 12px 24px -20px rgba(15,46,34,.45);
}
/* The links sit on the pale ends as well as the white middle now, so their
   resting colour goes a step darker to hold at both. */
html[data-theme="light"] .app .an-links a { color: #33453A; }
/* The brand keeps the same relationship it has on the dark bar: the mark in
   ink, TIP in the accent — but the accent has to be the type-safe one, since
   the fill lime is 1.4:1 on this. */
html[data-theme="light"] .app .an-brand { color: var(--lt-ink); }
html[data-theme="light"] .app .an-brand span { color: var(--lt-accent-ink); }

/* ---- the "groups are part of Workplace" panel, in light mode ----
   Same failure as the cards around it: written for the dark sheet, in cream
   and low-alpha white, so on the light theme it is pale-on-pale. It is the
   panel that explains why the Create button is disabled, which makes it the
   one block on the page a reader most needs to be able to read. */
html[data-theme="light"] .app .lock-prompt {
  background: var(--lt-surface-2);
  border-color: rgba(23,107,69,.26);
}
html[data-theme="light"] .app .lock-prompt .lk-title { color: var(--lt-ink); }
html[data-theme="light"] .app .lock-prompt .lk-plan { color: var(--lt-accent-ink); }
html[data-theme="light"] .app .lock-prompt .lk-body { color: var(--lt-ink); opacity: 1; }
html[data-theme="light"] .app .lock-prompt .lk-gets,
html[data-theme="light"] .app .lock-prompt .lk-gets li { color: var(--lt-quiet); opacity: 1; }
html[data-theme="light"] .app .lock-prompt .lk-ic {
  background: var(--lt-sel); color: var(--lt-forest);
}
html[data-theme="light"] .app .lock-prompt .abtn-ghost {
  border-color: var(--lt-line); color: var(--lt-ink); background: var(--lt-surface);
}
html[data-theme="light"] .app .lock-prompt .abtn-ghost:hover {
  border-color: var(--lt-forest); background: var(--lt-tint);
}
/* The disabled Create button sits beside it and is the thing being explained. */
html[data-theme="light"] .app .abtn.is-locked[disabled] {
  background: var(--lt-surface-2) !important;
  color: var(--lt-quiet) !important;
  border: 1px solid var(--lt-line) !important;
}
