/* ChLGA (Ontario) — site styles
   Palette is taken from the club crest: deep burgundy panel, gold figure,
   cream ground. One stylesheet, no external fonts, no frameworks. */

:root {
  /* ---- Brand colour --------------------------------------------------
     Burgundy, from the club crest, and the committee's choice after seeing
     green, yellow and blue side by side.
     The original committee note — that the site read as dark and serious —
     was answered without leaving the burgundy family: these are a brighter
     rose-red than the near-black the site started with, and the deep tone is
     no longer used as a full-page background, only for the header and footer.
     The variable NAMES are load-bearing. Over a hundred rules refer to them,
     so a future colour change means editing the three values here, not
     renaming anything. Read "burgundy" as "the brand colour". */
  /* SETTLED: the committee voted burgundy, so these are back to the crest
     colours after trials of leaf green, sunlit yellow and sky blue.
     Alternatives kept so any of them can be restored in one edit:
       leaf green #3d7a33 / #35692c / #2d5a25
       sky blue   #1f6f9e / #1a6089 / #145273                              */
  --burgundy:      #a32540;
  --burgundy-deep: #86203a;
  --burgundy-dark: #6d1a30;   /* header and footer */
  --gold:          #d3ae4f;
  --gold-bright:   #e8cd84;
  --gold-pale:     #f6ecd2;
  --gold-ink:      #8a6a12;   /* gold dark enough to read on cream */
  --blush:         #fdf4ee;   /* warm tint for the page headings */
  /* ---- Second accent -------------------------------------------------
     Two sets are defined. The four --accent-* lines at the bottom decide
     which one the site uses, so switching is a four-line change and nothing
     else in the stylesheet needs touching.
     Both are pale tints plus one solid marker. Neither is used at full
     strength beside the burgundy — saturated red with saturated green reads
     as Christmas, and with saturated yellow it reads as fast food. */

  /* Fairway green — brightened from the first attempt, which was a muted sage
     and read as tasteful rather than cheerful. More chroma, a touch cooler,
     closer to new grass than to a heritage clubhouse. */
  --green:         #3a7423;   /* leaf green — yellow blended in */
  --green-tint:    #f3faea;   /* alternating bands, lighter than before */
  --green-head:    #eef7dd;   /* page-heading gradient, warmest of the three */
  --green-line:    #dcebc4;

  /* Sky blue */
  --blue:          #1f6f9e;
  --blue-tint:     #eef7fc;
  --blue-head:     #e3f1fa;
  --blue-line:     #cbe4f3;

  /* "Bad news" red: a cancelled game, a two-stroke penalty, a disqualification.
     Close to the brand burgundy now that burgundy is back, which is fine —
     these all appear as bordered chips on pale ground, so the shape and label
     carry the meaning, not the hue alone. Kept as its own value so it survives
     any future brand change; green here would say the opposite of what it means. */
  --warn:          #b3243f;

  /* Sunlit yellow */
  --butter:        #8a6a12;   /* deep ochre — dark enough for white text */
  --butter-tint:   #fdf6e0;
  --butter-head:   #fcf2d5;
  --butter-line:   #f0e4c0;

  /* THE SWITCH — point these at butter-*, green-* or blue-*.
     Butter, because that is the accent the burgundy option carried in the
     preview picker below, and burgundy is what the committee voted for. These
     four must stay in step with :root[data-palette="burgundy"] or the live site
     would differ from the version they chose. */
  --accent:        var(--butter);
  --accent-tint:   var(--butter-tint);
  --accent-head:   var(--butter-head);
  --accent-line:   var(--butter-line);
  --cream:         #fdfaf5;
  --paper:         #ffffff;
  --ink:           #2a231f;
  --ink-soft:      #524741;
  --muted:         #776d67;
  --rule:          #e6ded3;
  --rule-soft:     #f0eae1;
  --shadow:        0 1px 2px rgba(36,31,28,.05), 0 8px 24px -12px rgba(36,31,28,.18);
  --shadow-lg:     0 2px 4px rgba(36,31,28,.06), 0 20px 48px -20px rgba(36,31,28,.28);

  --serif: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
  --sans:  system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --wrap: 68rem;
  --pad: clamp(1.15rem, 4vw, 2.5rem);

  /* The site is light only. Without this, phones set to dark mode get their own
     tinted form controls and scrollbars against our light pages. */
  color-scheme: light;
}

/* ---------------------------------------------------------------------------
   DARK MODE IS TURNED OFF.

   The site carried a full second palette behind @media (prefers-color-scheme:
   dark). Any member whose phone was set to dark mode saw it instead of the
   colours the committee chose — a near-black background where the laptop shows
   white. It had also drifted: its brand colours were left over from an earlier
   round and no longer matched the light palette at all.

   All twenty of those blocks now carry "and (min-width: 99999px)", a width no
   screen reaches, so they never apply. Nothing was deleted. To bring dark mode
   back, remove that clause from each block — and then review it properly, which
   means checking contrast across the whole site a second time.
   --------------------------------------------------------------------------- */

@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  :root {
    --burgundy:      #46a173;
    --burgundy-deep: #2e8055;
    --burgundy-dark: #14201a;
    --gold:          #d8b863;
    --gold-bright:   #eed99b;
    --gold-pale:     #3a2f1e;
    --cream:         #171315;
    --paper:         #201a1c;
    --ink:           #f2ebe4;
    --ink-soft:      #cdc2ba;
    --muted:         #9c9089;
    --rule:          #362b2e;
    --rule-soft:     #292124;
    --shadow:        0 1px 2px rgba(0,0,0,.4), 0 8px 24px -12px rgba(0,0,0,.6);
    --shadow-lg:     0 2px 4px rgba(0,0,0,.4), 0 20px 48px -20px rgba(0,0,0,.7);
  }
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font: 400 clamp(1rem, .97rem + .18vw, 1.075rem)/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--serif); font-weight: 400; line-height: 1.2; margin: 0; letter-spacing: -.01em; }
p, ul, ol, dl, table, figure { margin: 0; }
img { max-width: 100%; height: auto; display: block; }

a { color: var(--burgundy); text-decoration-thickness: 1px; text-underline-offset: .18em; }
a:hover { color: var(--burgundy-deep); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { a:hover { color: var(--gold-bright); } }

:focus-visible { outline: 2.5px solid var(--gold); outline-offset: 2px; border-radius: 2px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--burgundy); color: #fff; padding: .75rem 1.25rem; border-radius: 0 0 .5rem 0;
}
.skip:focus { left: 0; }

.wrap { width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto; }

/* only present in `perl build.pl preview` output */
.preview-bar { background: #7a5d0e; color: #fff; }
.preview-bar p {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  padding: .6rem 0; font-size: .82rem; line-height: 1.5; text-align: center;
}
.preview-bar strong { letter-spacing: .06em; text-transform: uppercase; font-size: .78rem; }

/* ====================================================== palette switching */

/* Each palette re-declares only the six values that differ. The whole site is
   built on these, so setting data-palette on <html> repaints everything.
   Used by the preview-only picker; the live site just uses whichever set is
   declared in :root above. */
:root[data-palette="blue"] {
  --burgundy: #1f6f9e; --burgundy-deep: #1a6089; --burgundy-dark: #145273;
  --accent: #1f6f9e; --accent-tint: #eef7fc; --accent-head: #e3f1fa; --accent-line: #cbe4f3;
}
:root[data-palette="green"] {
  --burgundy: #3d7a33; --burgundy-deep: #35692c; --burgundy-dark: #2d5a25;
  --accent: #3a7423; --accent-tint: #f3faea; --accent-head: #eef7dd; --accent-line: #dcebc4;
}
:root[data-palette="burgundy"] {
  --burgundy: #a32540; --burgundy-deep: #86203a; --burgundy-dark: #6d1a30;
  --accent: #8a6a12; --accent-tint: #fdf6e0; --accent-head: #fcf2d5; --accent-line: #f0e4c0;
}

/* The picker itself. Preview builds only — build.pl does not emit it for the
   real site, so none of this can reach chlgagolf.com. */
.palette-pick {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 400;
  display: flex; gap: .4rem; align-items: center;
  background: rgba(255,255,255,.96); border: 1px solid var(--rule);
  border-radius: 2rem; padding: .5rem .7rem; box-shadow: var(--shadow-lg);
  font: 600 .72rem/1 var(--sans); letter-spacing: .08em; text-transform: uppercase;
}
.palette-pick span { color: var(--muted); margin-right: .15rem; }
.palette-pick button {
  width: 1.6rem; height: 1.6rem; border-radius: 50%; cursor: pointer;
  border: 2px solid rgba(0,0,0,.15); padding: 0; font-size: 0;
}
.palette-pick button[aria-pressed="true"] { outline: 2px solid var(--ink); outline-offset: 2px; }
.palette-pick button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
@media print { .palette-pick { display: none; } }

/* ============================================================ site header */

.site-head {
  position: sticky; top: 0; z-index: 100;
  background: var(--burgundy-dark);
  border-bottom: 3px solid var(--gold);
}
/* Wider than --wrap on purpose. The header is not body text and does not need
   to sit inside the reading measure — capping it at 68rem is what left nine
   nav items with nowhere to go, no matter how wide the screen got. */
.head-inner {
  width: min(100% - var(--pad) * 2, 80rem);
  margin-inline: auto;
  display: flex; align-items: center; gap: 1rem;
  min-height: 4.5rem;
  flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: .8rem; text-decoration: none; color: #fff; padding: .7rem 0; margin-right: auto; }
.brand-mark { border-radius: 2px; box-shadow: 0 0 0 1px rgba(200,169,81,.5); flex: none; }
.brand-text { display: flex; flex-direction: column; }
.brand-name { font-family: var(--serif); font-size: clamp(.98rem, .9rem + .5vw, 1.28rem); line-height: 1.15; color: #fff; }
.brand-sub { font-size: .72rem; letter-spacing: .13em; text-transform: uppercase; color: var(--gold-bright); margin-top: .22rem; }

.nav-toggle {
  display: flex; align-items: center; gap: .55rem;
  background: transparent; border: 1px solid rgba(200,169,81,.45); color: var(--gold-bright);
  font: inherit; font-size: .82rem; letter-spacing: .08em; text-transform: uppercase;
  padding: .5rem .8rem; border-radius: .4rem; cursor: pointer;
}
.nav-toggle:hover { border-color: var(--gold); background: rgba(200,169,81,.1); }
.bars { position: relative; width: 1.05rem; height: 2px; background: currentColor; display: block; }
.bars::before, .bars::after { content: ""; position: absolute; left: 0; width: 100%; height: 2px; background: currentColor; transition: transform .2s ease, top .2s ease; }
.bars::before { top: -6px; } .bars::after  { top: 6px; }
.nav-toggle[aria-expanded="true"] .bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .bars::after  { top: 0; transform: rotate(-45deg); }

.site-nav { display: none; width: 100%; }
.site-nav.is-open { display: block; }

.nav-list, .sub-list { list-style: none; margin: 0; padding: 0; }
.nav-list { padding-bottom: .8rem; }
.nav-list > li { position: relative; border-top: 1px solid rgba(255,255,255,.09); }
.nav-list > li > a {
  display: block; padding: .82rem .2rem; color: #fff; text-decoration: none;
  font-size: .93rem; letter-spacing: .04em;
}
.nav-list > li > a:hover { color: var(--gold-bright); }
.nav-list > li.is-active > a { color: var(--gold-bright); }
.nav-list > li.is-member > a::before { content: "\1F512\00a0\00a0"; font-size: .8em; opacity: .8; }

.sub-toggle {
  position: absolute; top: .3rem; right: 0; width: 2.75rem; height: 2.5rem;
  background: transparent; border: 0; cursor: pointer; color: var(--gold);
}
.sub-toggle::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: .5rem; height: .5rem; margin: -.35rem 0 0 -.25rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .2s ease;
}
.sub-toggle[aria-expanded="true"]::after { transform: rotate(-135deg); margin-top: -.1rem; }

.sub-list { display: none; padding: 0 0 .5rem .95rem; border-left: 2px solid rgba(200,169,81,.35); margin-left: .2rem; }
.sub-list.is-open { display: block; }
.sub-list a { display: block; padding: .5rem .2rem; color: rgba(255,255,255,.82); text-decoration: none; font-size: .87rem; }
.sub-list a:hover { color: var(--gold-bright); }
.sub-list li.is-active a { color: var(--gold); }

/* 76rem: with nine top-level items the horizontal nav needs roughly 1215px
   before it stops crowding the wordmark. Below that the menu button is
   cleaner than a squashed or wrapping nav. site.js uses the same breakpoint —
   change both together or the sub-menus behave inconsistently. */
@media (min-width: 76rem) {
  .nav-toggle { display: none; }
  .site-nav { display: block; width: auto; }
  .nav-list { display: flex; gap: .1rem; padding: 0; }
  .nav-list > li { border-top: 0; }
  /* .5rem, not .6rem: with ten items the extra 1.6px a side added ~32px to the
     menu, which was just enough to push the header into two rows at 1216px —
     the exact width the horizontal menu starts at. */
  .nav-list > li > a { padding: .55rem .5rem; border-radius: .35rem; font-size: .84rem; }
  .nav-list > li.is-active > a { background: rgba(200,169,81,.16); }
  .nav-list > li.has-sub > a { padding-right: 1.5rem; }
  .sub-toggle { top: 50%; right: .25rem; width: 1rem; height: 1rem; margin-top: -.5rem; pointer-events: none; }
  .sub-toggle::after { width: .38rem; height: .38rem; border-width: 1.5px; }
  .sub-toggle[aria-expanded="true"]::after { transform: rotate(45deg); margin-top: -.35rem; }

  .sub-list {
    display: block; position: absolute; top: 100%; left: 0; min-width: 15rem;
    background: var(--burgundy-dark); border: 1px solid rgba(200,169,81,.3); border-top: 2px solid var(--gold);
    border-radius: 0 0 .45rem .45rem; padding: .35rem; margin: 0;
    box-shadow: var(--shadow-lg);
    opacity: 0; visibility: hidden; transform: translateY(-.35rem); transition: opacity .16s ease, transform .16s ease, visibility .16s;
  }
  .nav-list > li.has-sub:hover .sub-list,
  .nav-list > li.has-sub:focus-within .sub-list { opacity: 1; visibility: visible; transform: none; }
  .sub-list a { padding: .5rem .6rem; border-radius: .3rem; }
  .sub-list a:hover { background: rgba(200,169,81,.14); }
}

/* ================================================================ banner */
/* The home page banner. No photograph — the club has none on file — so this
   is built from the crest, the wordmark and the club colours. Swapping in a
   photo later means adding one background-image line to .banner. */

/* The banner stays light in both themes — the golf scene behind it is a fixed
   illustration, so its text colours are pinned rather than themed. */
.banner {
  /* Golds are darkened well past the decorative #c8a951 — over the washed
     photograph these need to clear 4.5:1, which bright gold does not. */
  /* Tracks the brand rather than being pinned to a literal. It was left holding
     a dark green after the palette trials, so the home-page title stayed green
     while the rest of the site went back to burgundy. Whatever this points at
     must stay dark enough to clear 4.5:1 over the wash — --burgundy-dark always
     is, since it also carries white text on the header and footer. */
  --b-burgundy: var(--burgundy-dark);   /* 11.4:1 on the halo */
  --b-gold:     #6f540c;   /* 5.6:1 — small letterspaced caps */
  --b-gold-mid: #806112;   /* 4.7:1 — the Chinese name */
  --b-ink:      #4a423c;   /* 7.3:1 */

  position: relative;
  overflow: hidden;
  text-align: center;
  border-bottom: 3px solid var(--gold);

  /* Our own photographs, under a cream wash. The wash is what keeps this a
     light banner with burgundy and gold type — the photo reads as a soft
     backdrop rather than fighting the words. It eases off top and bottom so
     the sky and the water still show through. */
  /* Light veil only. The heavy lifting for legibility is done by the halo on
     .banner-inner, which sits under the type alone — that way the photograph
     stays clear everywhere the words are not. */
  background-image:
    linear-gradient(180deg,
      rgba(251,249,244,.16) 0%,
      rgba(251,249,244,.42) 34%,
      rgba(251,249,244,.44) 66%,
      rgba(251,249,244,.22) 100%),
    url("banner.jpg");
  background-repeat: no-repeat;
  background-position: center, center 58%;
  background-size: cover, cover;
  background-color: #eef1e6;
}

/* On a phone the banner is tall and narrow, which crops a landscape photo down
   to a strip. The portrait shot of the green fits that shape properly — and is
   half the file size, which is where it matters most. */
@media (max-width: 40rem) {
  .banner {
    background-image:
      linear-gradient(180deg,
        rgba(251,249,244,.60) 0%,
        rgba(251,249,244,.80) 34%,
        rgba(251,249,244,.82) 64%,
        rgba(251,249,244,.66) 100%),
      url("banner-portrait.jpg");
    background-position: center, center 46%;
  }
}

.banner-inner {
  position: relative; z-index: 2;
  width: min(100% - var(--pad) * 2, 64rem);
  margin-inline: auto;
  width: min(100% - var(--pad) * 2, 64rem);
  padding: clamp(2.5rem, 7vw, 4.5rem) 0 clamp(3rem, 8vw, 5rem);

  /* A soft cream halo behind the type only — no hard edge, fading to nothing
     before the banner edges so the photograph is unobstructed where there is
     nothing to read. This is what carries the burgundy and gold, so the veil
     over the picture itself can stay light. */
  background: radial-gradient(58% 52% at 50% 46%,
    rgba(251,249,244,.88) 0%,
    rgba(251,249,244,.66) 52%,
    rgba(251,249,244,.20) 82%,
    rgba(251,249,244,0) 100%);
}

/* The old inset hairline is gone — the card has its own border now, and two
   nested frames read as a mistake. */

/* The banner uses logo.png, whose white background has been made transparent so
   the crest sits on the photograph rather than on a white card. drop-shadow is
   used instead of box-shadow because it follows the artwork's alpha rather than
   drawing a rectangle around it.
   The header and footer keep logo.jpg — on dark burgundy the crest's red
   lettering needs its white field to stay legible. */
.banner-crest {
  width: 92px; height: 131px; margin: 0 auto;
  filter: drop-shadow(0 6px 14px rgba(74,12,23,.35));
}

.banner-zh {
  margin-top: 1.5rem;
  font-size: clamp(.92rem, .84rem + .42vw, 1.25rem);
  letter-spacing: .3em; text-indent: .3em;
  color: var(--b-gold-mid);
}

/* Set on one line at every width — the size is driven by viewport width and
   capped, so it scales down rather than wrapping. */
.banner-title {
  margin-top: .7rem;
  /* Back to 4.15rem now the card is gone and the full 64rem is available.
     The nowrap below makes this a hard constraint rather than a preference —
     too large and it overflows instead of wrapping. */
  font-size: clamp(1.05rem, 5.15vw, 4.15rem);
  line-height: 1.08;
  white-space: nowrap;
  color: var(--b-burgundy);
}

.banner-rule {
  display: flex; align-items: center; justify-content: center; gap: 1rem;
  margin-top: 1.2rem;
}
.banner-rule::before, .banner-rule::after {
  content: ""; height: 1px; width: clamp(1.5rem, 11vw, 6.5rem);
  background: linear-gradient(90deg, transparent, var(--gold));
}
.banner-rule::after { background: linear-gradient(90deg, var(--gold), transparent); }
.banner-rule span {
  color: var(--b-gold);
  font-size: clamp(.68rem, .64rem + .2vw, .82rem);
  letter-spacing: .26em; text-indent: .26em; text-transform: uppercase; white-space: nowrap;
}

.banner-tag {
  margin-top: 1.4rem;
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1rem, .93rem + .45vw, 1.28rem);
  color: var(--b-ink);
  max-width: 30em; margin-inline: auto;
}

.banner-actions { display: flex; flex-wrap: wrap; gap: .7rem; justify-content: center; margin-top: 1.9rem; }

.btn {
  display: inline-block; padding: .72rem 1.35rem; border-radius: .4rem;
  background: var(--gold); color: #3a2a05; font-weight: 600; font-size: .93rem;
  text-decoration: none; border: 1px solid var(--gold); transition: background .15s ease, transform .15s ease;
}
.btn:hover { background: var(--gold-bright); color: #3a2a05; transform: translateY(-1px); }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.45); }
.btn-ghost:hover { background: rgba(255,255,255,.1); color: #fff; border-color: #fff; }

/* on the light banner the buttons need dark ink, not white */
.banner .btn {
  background: var(--burgundy-deep); border-color: var(--burgundy-deep); color: #fff;
}
.banner .btn:hover { background: var(--burgundy-dark); border-color: var(--burgundy-dark); color: #fff; }
.banner .btn-ghost {
  background: rgba(255,255,255,.6); color: var(--b-burgundy); border-color: rgba(109,26,48,.35);
}
.banner .btn-ghost:hover { background: #fff; color: var(--b-burgundy); border-color: var(--b-burgundy); }

.btn {
  display: inline-block; padding: .72rem 1.35rem; border-radius: .4rem;
  background: var(--gold); color: #3a2a05; font-weight: 600; font-size: .93rem;
  text-decoration: none; border: 1px solid var(--gold); transition: background .15s ease, transform .15s ease;
}
.btn:hover { background: var(--gold-bright); color: #3a2a05; transform: translateY(-1px); }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.4); }
.btn-ghost:hover { background: rgba(255,255,255,.1); color: #fff; border-color: #fff; }

/* .btn-ghost is white on transparent, which only works on the dark banner. Used
   in body copy — About Us has one — it was white on white and effectively
   invisible at 1.04:1. Inside .prose it takes the burgundy outline instead. */
.prose .btn-ghost { color: var(--burgundy); border-color: rgba(163,37,64,.45); }
.prose .btn-ghost:hover {
  background: rgba(163,37,64,.09); color: var(--burgundy-deep);
  border-color: var(--burgundy);
}

/* next-game band, sits directly under the banner */
.nextband { background: var(--paper); border-bottom: 1px solid var(--rule); }
.nextband-inner {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  padding: clamp(1.75rem, 4.5vw, 2.6rem) 0;
}

.nextgame { display: grid; gap: 1.25rem 2.5rem; align-items: center; }
@media (min-width: 52rem) {
  .nextgame { grid-template-columns: auto 1fr auto; }
  .nextgame .ng-note { grid-column: 1 / -1; }
}

.ng-label {
  color: var(--burgundy); text-transform: uppercase; letter-spacing: .19em; font-size: .68rem; font-weight: 600;
}
.ng-date {
  font-family: var(--serif); font-size: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
  color: var(--ink); margin-top: .35rem; line-height: 1;
}
.ng-course { color: var(--ink-soft); font-size: 1.08rem; margin-top: .4rem; }
.ng-meta { display: flex; flex-wrap: wrap; gap: 1.75rem; }
.ng-meta dt { color: var(--muted); font-size: .66rem; text-transform: uppercase; letter-spacing: .14em; font-weight: 600; }
.ng-meta dd { margin: .35rem 0 0; color: var(--ink); font-size: .97rem; }
.ng-note {
  font-size: .88rem; color: var(--ink-soft); line-height: 1.55; font-style: italic;
  padding-left: .9rem; border-left: 2px solid var(--gold);
}
.nextgame-none { color: var(--ink-soft); font-size: 1.05rem; text-align: center; }

@media (prefers-color-scheme: dark) and (min-width: 99999px) { .ng-label { color: var(--gold); } }

/* the first section after the band does not need its own top padding */
.nextband + .section { padding-top: clamp(2.5rem, 6.5vw, 4.5rem); }

/* ============================================================ page header */

/* The page heading used to be a solid deep-burgundy slab sitting directly under
   an already dark sticky header — a big block of dark on all seventeen pages
   before the reader reached a word of content. That was the main reason the
   site read as heavy. It is now a warm light band: same words, same hierarchy,
   burgundy kept as the heading colour so the identity survives. */
.page-head {
  background: linear-gradient(180deg, var(--accent-head), var(--cream));
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
}
.page-head-inner {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  padding: clamp(2rem, 5.5vw, 3.4rem) 0;
}
/* --gold on cream is about 2:1 and unreadable as small text; --gold-ink is the
   same hue taken dark enough to pass. */
.page-head .eyebrow { color: var(--gold-ink); letter-spacing: .19em; text-transform: uppercase; font-size: .74rem; margin-bottom: .8rem; }
.page-head h1 { font-size: clamp(1.75rem, 1.35rem + 2vw, 2.7rem); color: var(--burgundy-deep); }
.page-head .lede { margin-top: .9rem; color: var(--ink-soft); max-width: 46em; }
/* A gold hairline where the dark band used to end, so the heading still reads
   as its own zone rather than drifting into the page. */
.page-head { border-bottom: 2px solid var(--gold); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .page-head { background: linear-gradient(180deg, #2e2622, #262019); color: var(--cream); }
  .page-head h1 { color: var(--gold-bright); }
  .page-head .eyebrow { color: var(--gold); }
  .page-head .lede { color: rgba(255,255,255,.8); }
}

/* ================================================================ content */

.section { padding: clamp(2.5rem, 6.5vw, 4.5rem) 0; }
.section + .section { padding-top: 0; }
/* Alternating bands carry the green. Cards inside stay cream, so they still lift
   off the band exactly as they did against white. */
.section-alt { background: var(--accent-tint); border-block: 1px solid var(--accent-line); }
.section-alt + .section { padding-top: clamp(2.5rem, 6.5vw, 4.5rem); }

.prose { width: min(100% - var(--pad) * 2, 46rem); margin-inline: auto; }
.prose.wide { width: min(100% - var(--pad) * 2, var(--wrap)); }

.prose > * + * { margin-top: 1.15rem; }

/* Section headings carry a gold rule rather than a plain grey line — on the
   long rules and etiquette pages this is what stops the text reading as one
   undifferentiated block. */
.prose h2 {
  font-size: clamp(1.4rem, 1.2rem + .8vw, 1.95rem);
  margin-top: 3.4rem; padding-bottom: .6rem;
  border-bottom: 1px solid var(--rule);
  position: relative;
}
.prose h2::after {
  content: ""; position: absolute; left: 0; bottom: -1px;
  width: 2.75rem; height: 2px; background: var(--gold);
}
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: clamp(1.1rem, 1.03rem + .35vw, 1.32rem); margin-top: 2.2rem; color: var(--burgundy); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .prose h3 { color: var(--gold); } }
.prose h4 { font-size: 1rem; margin-top: 1.5rem; font-family: var(--sans); font-weight: 600; }

/* Arriving from a #link: the site header is sticky and about 5.5rem tall, so
   without this the heading lands underneath it and the reader sees the wrong
   paragraph. Applies to any heading given an id. */
.prose :is(h2, h3, h4)[id] { scroll-margin-top: 6.5rem; }

.prose ul, .prose ol { padding-left: 1.35rem; }
.prose li + li { margin-top: .6rem; }
.prose ul { list-style: none; padding-left: 0; }
.prose ul li { position: relative; padding-left: 1.4rem; }
.prose ul li::before { content: ""; position: absolute; left: .3rem; top: .68em; width: .38rem; height: .38rem; border-radius: 50%; background: var(--gold); }

/* Numbered lists get a proper hanging figure instead of a cramped marker.
   The etiquette page is almost entirely these — they need to read as steps,
   not as clauses in a contract. */
.prose ol { list-style: none; padding-left: 0; counter-reset: item; }
.prose ol > li {
  position: relative; padding-left: 2.5rem; min-height: 1.6rem;
}
.prose ol > li::before {
  counter-increment: item; content: counter(item);
  position: absolute; left: 0; top: .05em;
  width: 1.7rem; height: 1.7rem; display: grid; place-items: center;
  border-radius: 50%; background: var(--gold-pale); color: var(--burgundy-deep);
  font: 600 .8rem/1 var(--sans);
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .prose ol > li::before { background: var(--gold-pale); color: var(--gold-bright); }
}
.prose strong { font-weight: 600; }
.prose .muted { color: var(--muted); }
.lead { font-size: 1.1rem; color: var(--ink-soft); }

blockquote {
  margin: 1.75rem 0; padding: 1.1rem 1.4rem;
  border-left: 3px solid var(--gold); background: var(--paper);
  border-radius: 0 .4rem .4rem 0; color: var(--ink-soft); font-style: italic;
}
.section-alt blockquote { background: var(--cream); }

.callout {
  background: var(--paper); border: 1px solid var(--rule); border-left: 3px solid var(--burgundy);
  border-radius: .45rem; padding: 1.15rem 1.4rem; box-shadow: var(--shadow);
}
.callout h3 { margin-top: 0; }
.callout > * + * { margin-top: .7rem; }
.section-alt .callout { background: var(--cream); }

.formula {
  font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;
  font-size: .93rem; background: var(--burgundy-dark); color: var(--gold-pale);
  padding: 1rem 1.2rem; border-radius: .45rem; overflow-x: auto; line-height: 1.6;
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .formula { background: #120d0f; border: 1px solid var(--rule); } }

/* ================================================================= tables */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: .5rem; border: 1px solid var(--rule); background: var(--paper); box-shadow: var(--shadow); }
.section-alt .table-wrap { background: var(--cream); }
table { border-collapse: collapse; width: 100%; font-size: .93rem; }
thead th {
  background: var(--burgundy-deep); color: #fff; text-align: left;
  font: 600 .72rem/1.3 var(--sans); letter-spacing: .1em; text-transform: uppercase;
  padding: .8rem .9rem; white-space: nowrap;
}
tbody td { padding: .78rem .9rem; border-top: 1px solid var(--rule-soft); vertical-align: top; }
/* Neutral ink rather than a brand tint: this was left holding a green wash after
   the palette trials. A colourless stripe survives any future palette change. */
tbody tr:nth-child(even) { background: rgba(42,35,31,.03); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { tbody tr:nth-child(even) { background: rgba(255,255,255,.028); } }

/* ========================================================= season schedule */

.sched-list { list-style: none; padding: 0; margin: 1.75rem 0 0; display: grid; gap: .85rem; }
.prose .sched-list li { margin: 0; padding: 0; }
.prose .sched-list li::before { display: none; }

.sched-game {
  display: grid; grid-template-columns: 5.5rem 1fr; gap: 0;
  background: var(--paper); border: 1px solid var(--rule); border-radius: .55rem;
  box-shadow: var(--shadow); overflow: hidden;
}
.section-alt .sched-game { background: var(--cream); }

/* The date block: a calendar tile, so the eye finds the date without reading. */
.sched-when {
  background: var(--burgundy-deep); color: #fff;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 1.1rem .5rem; text-align: center; gap: .1rem;
}
.sched-mon {
  font: 600 .72rem/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: #fdf7e8;
}
.sched-day { font-family: var(--serif); font-size: 2rem; line-height: 1.05; }
/* Solid white, not translucent. At 11px this needs 4.5:1, and white at 90% over
   the butter accent on the next-game tile came to 4.45 — a hair under. The size
   difference against .sched-day carries the hierarchy instead. */
.sched-dow { font: 400 .68rem/1.2 var(--sans); letter-spacing: .06em; color: #fff; }

.sched-body { padding: 1.15rem 1.3rem; min-width: 0; }
.sched-flag {
  margin: 0 0 .4rem; display: inline-block;
  font: 700 .64rem/1 var(--sans); letter-spacing: .13em; text-transform: uppercase;
  color: var(--burgundy-dark); background: var(--gold-pale);
  border: 1px solid var(--gold); border-radius: 1rem; padding: .28rem .7rem;
}
.sched-flag.is-done { color: var(--muted); background: transparent; border-color: var(--rule); }
.prose .sched-course { margin: 0; font-size: 1.32rem; color: var(--burgundy); line-height: 1.25; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .prose .sched-course { color: var(--gold); } }
.sched-note { margin: .45rem 0 0; color: var(--muted); font-size: .86rem; font-style: italic; line-height: 1.5; }

/* Label above value, so long costs like "Members $230 / Guests $240" never
   have to share a line with their label. */
.sched-meta {
  margin: .9rem 0 0; display: flex; flex-wrap: wrap; gap: .75rem 2rem;
  padding-top: .85rem; border-top: 1px solid var(--rule-soft);
}
.sched-meta > div { min-width: 0; }
.sched-meta dt {
  font: 600 .64rem/1.4 var(--sans); letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted);
}
.sched-meta dd { margin: .1rem 0 0; font-size: .95rem; }

/* The dinner address, under the restaurant name. Smaller and quieter than the
   name — you read the name to know where you are going and the address only
   when you are actually setting off. It links to a map, because members drive
   to these and an address you can tap beats one you have to copy out. */
.dinner-where { display: block; margin-top: .15rem; font-size: .82rem; }

/* The course address, directly under the course name. Same quiet treatment as
   the dinner address so the two read as the same kind of thing — a place you
   can tap to get directions, not a headline. */
.sched-where { margin: .2rem 0 0; font-size: .85rem; }

.dinner-where a, .sched-where a { color: var(--muted); text-decoration: none; }
.dinner-where a:hover, .dinner-where a:focus,
.sched-where a:hover, .sched-where a:focus { color: var(--burgundy); }

/* An address that opens a map, marked with a pin. Members were not realising
   these were tappable at all — a dotted underline in muted grey reads as
   emphasis, not as a link. The pin says "map" before the words are read.
   inline-flex keeps the pin beside the text rather than sitting on the line
   above when the address wraps, and the dotted rule moves onto the span so it
   underlines only the words, not the icon. */
.maplink { display: inline-flex; align-items: center; gap: .32rem; max-width: 100%; }
.maplink .map-pin { flex: none; opacity: .85; }
.maplink > span { border-bottom: 1px dotted currentColor; }
.maplink:hover .map-pin, .maplink:focus .map-pin { opacity: 1; }

/* The next game is the one thing most visitors came for. */
/* The next game's date tile is green while every other tile is burgundy — the
   one place the accent carries meaning rather than decoration: green for the
   game still to play, grey for played and cancelled. */
.sched-game.is-next { border-color: var(--gold); border-width: 2px; box-shadow: var(--shadow-lg); }
.sched-game.is-next .sched-when { background: var(--accent); }
/* Translucent white rather than a tinted colour, so it works on whichever
   accent the switch above is set to. */
.sched-game.is-next .sched-mon { color: #fff; }

.sched-game.is-past { opacity: .72; }
.sched-game.is-past .sched-when { background: #6f6660; }
.sched-game.is-past .sched-mon { color: rgba(255,255,255,.9); }

/* A cancelled game. Kept on the page rather than removed — members need to see
   that it was scheduled and did not happen, not silently wonder where it went.
   The course name is struck through so the status is unmistakable. */
.sched-game.is-cancelled { opacity: .7; }
.sched-game.is-cancelled .sched-when { background: #6f6660; }
.sched-game.is-cancelled .sched-mon { color: rgba(255,255,255,.9); }
.prose .sched-game.is-cancelled .sched-course { text-decoration: line-through; color: var(--muted); }
.sched-flag.is-off {
  color: var(--warn); background: rgba(179,36,63,.08); border-color: rgba(179,36,63,.35);
}
/* The reason a game was called off, sitting beside the CANCELLED badge. It is
   inside the badge's <p>, but reset back to normal body type -- muted and not
   upper-cased -- so it reads as a sentence, not part of the label. Nudged onto
   the badge's baseline so the two line up despite the badge's padding. */
.sched-why {
  margin-left: .55rem; color: var(--muted);
  font: italic 400 .82rem/1.4 var(--sans); letter-spacing: 0; text-transform: none;
  vertical-align: middle;
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .prose .sched-game.is-cancelled .sched-course { color: var(--muted); }
  .sched-flag.is-off { color: #f0a6b4; background: rgba(240,140,160,.12); border-color: rgba(240,140,160,.4); }
}

@media (max-width: 34rem) {
  .sched-game { grid-template-columns: 1fr; }
  .sched-when {
    flex-direction: row; gap: .5rem; align-items: baseline; justify-content: flex-start;
    padding: .6rem 1.1rem; text-align: left;
  }
  .sched-day { font-size: 1.3rem; }
  .sched-body { padding: 1rem 1.1rem 1.15rem; }
  .sched-meta { gap: .7rem 1.4rem; }
}

/* Penalty badges in the quick reference. Forty-four rows of bare 0/1/2 read as
   a wall of digits; colour lets you find "no penalty" or "two strokes" at a
   glance without reading every line. Colour is a second cue only — the number
   is still there, so this works for colour-blind readers too. */
.pen {
  display: inline-grid; place-items: center;
  min-width: 2rem; padding: .25rem .5rem; border-radius: 1rem;
  font: 700 .8rem/1 var(--sans); letter-spacing: .02em;
}
.pen-0  { background: rgba(47,107,60,.10); color: #2f6b3c; border: 1px solid rgba(47,107,60,.32); }
/* Ink darkened from #8a6a12, which came to 4.16:1 on the alternating cream rows
   — under the 4.5 these small digits need. Long-standing, not from a palette
   change: both values here are literals. */
.pen-1  { background: rgba(160,116,20,.12); color: #7a5e10; border: 1px solid rgba(160,116,20,.36); }
.pen-2  { background: rgba(179,36,63,.09);  color: var(--warn); border: 1px solid rgba(179,36,63,.32); }
.pen-dq { background: var(--warn); color: #fff; border: 1px solid #8e1b2e; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .pen-0 { background: rgba(120,200,140,.14); color: #8fd6a3; border-color: rgba(120,200,140,.4); }
  .pen-1 { background: rgba(230,190,90,.14);  color: #e2c87d; border-color: rgba(230,190,90,.4); }
  .pen-2 { background: rgba(240,140,160,.13); color: #f0a6b4; border-color: rgba(240,140,160,.4); }
}
/* The penalty column only ever holds a badge, so it never needs to be wide. */
.qref td:nth-child(2), .qref th:nth-child(2) { width: 1%; white-space: nowrap; }

/* stacked table cards on narrow screens */
@media (max-width: 46rem) {
  .stackable thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .stackable tbody tr { display: block; padding: .9rem; border-top: 1px solid var(--rule); }
  .stackable tbody tr:first-child { border-top: 0; }
  .stackable tbody td { display: flex; gap: 1rem; padding: .3rem 0; border: 0; }
  .stackable tbody td:empty { display: none; }
  .stackable tbody td::before {
    content: attr(data-l); flex: none; width: 6.5rem;
    font: 600 .68rem/1.6 var(--sans); letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
  }
  .stackable .d-day { display: inline; }
}

/* =========================================================== home: story */

/* A wide, quiet opening statement — the first thing after the banner that
   speaks to a visitor rather than a member. */
.statement {
  width: min(100% - var(--pad) * 2, 42rem); margin-inline: auto; text-align: center;
}
.statement p {
  font-family: var(--serif); font-size: clamp(1.2rem, 1.05rem + .8vw, 1.65rem);
  line-height: 1.5; color: var(--ink); text-wrap: balance;
}
.statement p + p { margin-top: 1.1rem; font-family: var(--sans);
  font-size: clamp(1rem, .97rem + .2vw, 1.08rem); color: var(--ink-soft); line-height: 1.7; }

/* the numbers that make twenty-eight years feel like twenty-eight years */
.stats {
  display: grid; gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); border-radius: .6rem; overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}
.stats div { background: var(--paper); padding: 1.5rem 1.25rem; text-align: center; }
.section-alt .stats div { background: var(--cream); }
.stats dt { font-family: var(--serif); font-size: clamp(1.9rem, 1.5rem + 1.6vw, 2.7rem);
  color: var(--burgundy); line-height: 1; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .stats dt { color: var(--gold); } }
.stats dd { margin: .5rem 0 0; font-size: .78rem; letter-spacing: .11em;
  text-transform: uppercase; color: var(--muted); font-weight: 600; }

/* what a game day is actually like — narrative, numbered, not a rulebook */
.steps { display: grid; gap: 1.5rem; counter-reset: step;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
.steps > div { position: relative; padding-top: 3.6rem; }
.steps > div::before {
  counter-increment: step; content: counter(step);
  position: absolute; top: 0; left: 0;
  width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
  border-radius: 50%; background: var(--burgundy-deep); color: var(--gold);
  font-family: var(--serif); font-size: 1.2rem;
}
.steps h3 { margin: 0 0 .6rem; font-size: 1.15rem; color: var(--ink); }
.steps p { color: var(--ink-soft); font-size: .96rem; }

/* alternating image + text bands */
.split {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  display: grid; gap: clamp(1.75rem, 4vw, 3.25rem); align-items: center;
}
@media (min-width: 52rem) {
  .split { grid-template-columns: 1fr 1fr; }
  .split.flip .split-media { order: 2; }
}
.split-media img { width: 100%; border-radius: .6rem; box-shadow: var(--shadow-lg); display: block; }
/* Below the two-column breakpoint a portrait photo runs the full width and
   swamps the section, so crop it to a landscape band instead. */
@media (max-width: 51.99rem) {
  .split-media img { aspect-ratio: 4 / 3; object-fit: cover; object-position: center 40%; }
}
.split-body h2 { margin-top: 0; }

/* a slot for photographs — fill once member consent is settled */
.photo-strip {
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}
.photo-strip { align-items: start; }
.photo-strip figure { margin: 0; }
/* Whole photograph, scaled to the column — no fixed aspect ratio and no crop.
   These were shot at different proportions (2.06, 1.50, 1.77) and forcing them
   into a 4:3 box cut the ends off, which lost the clock tower from the carts
   photo. Uneven heights are the price of showing each picture complete. */
.photo-strip img { width: 100%; height: auto;
  border-radius: .5rem; box-shadow: var(--shadow); }
.photo-strip figcaption { margin-top: .5rem; font-size: .82rem; color: var(--muted); }
.photo-slot {
  aspect-ratio: 4/3; border: 1.5px dashed var(--rule); border-radius: .5rem;
  display: grid; place-items: center; text-align: center; padding: 1rem;
  color: var(--muted); font-size: .84rem; line-height: 1.5; background: var(--paper);
}
.section-alt .photo-slot { background: var(--cream); }

/* the recruiting call, and the members door — deliberately different in tone */
.invite {
  background: var(--burgundy-deep); color: #fff; border-radius: .7rem;
  padding: clamp(1.9rem, 5vw, 3.2rem); text-align: center; position: relative; overflow: hidden;
}
.invite::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(90% 80% at 50% 0%, rgba(200,169,81,.22), transparent 60%); }
.invite > * { position: relative; z-index: 1; }
.invite h2 { color: #fff; border: 0; font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.15rem); margin: 0; }
.invite p { margin-top: .9rem; color: rgba(255,255,255,.88); max-width: 34em; margin-inline: auto; }
.invite .btn { margin-top: 1.7rem; background: var(--gold); border-color: var(--gold); color: #3a2a05; }
.invite .btn:hover { background: var(--gold-bright); color: #3a2a05; }

.members-door {
  display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: center; justify-content: space-between;
  background: var(--paper); border: 1px solid var(--rule); border-left: 3px solid var(--gold);
  border-radius: .55rem; padding: 1.4rem 1.6rem;
}
.section-alt .members-door { background: var(--cream); }
.members-door h2 { margin: 0; border: 0; font-size: 1.25rem; padding: 0; }
.members-door p { color: var(--ink-soft); font-size: .93rem; margin-top: .35rem; }
.members-door .btn { flex: none; }

/* ================================================================== cards */

.grid { display: grid; gap: 1.25rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }

.card {
  background: var(--paper); border: 1px solid var(--rule); border-radius: .55rem;
  padding: 1.4rem 1.5rem; box-shadow: var(--shadow);
}
.section-alt .card { background: var(--cream); }
.card h3 { margin-top: 0; font-family: var(--serif); color: var(--ink); font-size: 1.2rem; }
.card > * + * { margin-top: .7rem; }
.card p { color: var(--ink-soft); font-size: .95rem; }
.card-link { display: block; text-decoration: none; color: inherit; transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease; }
.card-link:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: var(--gold); color: inherit; }
.card-link .more { color: var(--burgundy); font-weight: 600; font-size: .88rem; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .card-link .more { color: var(--gold); } }
.card .num { font-family: var(--serif); font-size: 2.2rem; color: var(--gold); line-height: 1; }

/* ================================================================ sponsors */

/* Wider columns so the logos have room. Most of them are width-limited, not
   height-limited, so raising the logo box alone does nothing for them — the
   card itself has to get bigger. */
.sponsor-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14.5rem), 1fr));
}
/* Tiers with only a handful of names — Prize, Cake, Bakery. Flex, not grid: an
   auto-fit grid stretches a lone card across the whole row, and auto-fill parks
   it in the first of several empty tracks. Flex centres however many there are,
   each at the Gold card's width. */
.sponsor-grid.is-few {
  display: flex; flex-wrap: wrap; justify-content: center;
  grid-template-columns: none;
}
.sponsor-grid.is-few .sponsor-card { flex: 0 1 14.5rem; }
@media (max-width: 30rem) { .sponsor-grid.is-few .sponsor-card { flex-basis: 100%; } }

/* A tier capped to the width of its own cards, so the gold banner above them
   never runs wider than the tiles it labels. The numbers are card counts:
   14.5rem a card, plus the 1rem grid gap between them. */
.tier-block { margin-inline: auto; margin-top: 1.5rem; }
.tier-block .sponsor-banner { margin-bottom: 1rem; }
.tier-block.is-1 { max-width: 14.5rem; }
/* A single-card tier is only 232px wide, and "Bakery sponsor" at the full banner
   size needs 293px of that — so it wrapped to three lines. Smaller type, tighter
   letter-spacing and closer ornaments bring it to one line without dropping the
   diamonds, which would have made these two banners odd ones out. */
.tier-block.is-1 .sponsor-banner span {
  font-size: 1rem; letter-spacing: .1em; padding-inline: .6rem; white-space: nowrap;
}
.tier-block.is-1 .sponsor-banner span::before { margin-right: .5em; }
.tier-block.is-1 .sponsor-banner span::after  { margin-left: .5em; }
.tier-block.is-2 { max-width: 30rem; }
.tier-block.is-3 { max-width: 45.5rem; }

/* Two tiers side by side, with the same 1rem gap the tiles above use, so the
   pair reads as part of the same grid rather than two stranded islands.
   margin-inline must be reset to 0: `auto` margins on a flex item swallow all
   the free space in the row, which forced the two blocks to opposite ends. */
/* 3rem between the two tiers — wider than the 1rem gap inside a tier, so the
   pair reads as two separate categories rather than one four-card row. */
.tier-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.5rem 3rem; }
/* Three or four solo tiers in one row need a tighter gap to fit. Four cards at
   14.5rem plus three 3rem gaps comes to 67rem against a 68rem column — close
   enough that a wide banner tips the last one onto its own line. 1.75rem leaves
   room while still reading wider than the 1rem gap used inside a tier. */
.tier-row.is-3-up, .tier-row.is-4-up { gap: 1.5rem 1.75rem; }
/* Pinned to one card width. Without a fixed basis each block shrinks to its own
   banner text, which left the two cards at 188px and 212px instead of matching
   the 232px cards in the tiers above. */
.tier-row .tier-block { margin: 0; }
.tier-row .tier-block.is-1 { flex: 0 0 14.5rem; max-width: 100%; }
/* Two cards plus the 1rem gap between them. A two-sponsor tier sharing a row
   has to hold exactly that, or its own cards shrink to fit whatever space is
   left and stop matching the tiles in the tiers above. */
.tier-row .tier-block.is-2 { flex: 0 0 30rem; max-width: 100%; }
/* On a phone every block takes the full width, matching the rule below that
   sends the cards inside a small tier full width too. Without this the pair
   block kept its own width while the single blocks stayed pinned at one card,
   so a Cake card came out wider than the Drink card stacked under it. */
@media (max-width: 30rem) {
  .tier-row .tier-block.is-1,
  .tier-row .tier-block.is-2 { flex-basis: 100%; }
}
/* Every card the same height, logo or name. min-height rather than relying on
   grid row-stretch, because stretch only equalises within a row — the rows made
   up entirely of name cards would still have come out short. */
.sponsor-card {
  background: var(--paper); border: 1px solid var(--rule); border-radius: .55rem;
  padding: 1.5rem 1.25rem 1.35rem; text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .9rem; min-height: 10.2rem;
  box-shadow: var(--shadow);
}
.section-alt .sponsor-card { background: var(--cream); }

/* Fixed box, contained image: the logos arrive at wildly different proportions
   (4.04 down to 1.27) and without this the wide ones would tower over the
   square ones. Height is uniform, width is whatever each logo needs. */
/* 100px, not 58px. A shared height cap makes near-square logos far smaller than
   wide ones: at 58px Comfort Zone rendered 77px across and Desjardins 73px,
   while Rogers and Calla filled the whole card. Raising the box lets the
   squarish logos grow; the wide ones are limited by card width either way. */
.sponsor-logo { height: 115px; display: grid; place-items: center; width: 100%; }
/* Fill the box and let object-fit letterbox, rather than "width: auto", which
   only ever shrank a logo and never grew one. Two small files — Justin Wong at
   172px and Choplin at 221px — were drawn at their own size and floated inside
   the tile touching no edge at all, so they read as smaller than the rest.
   Every logo now scales until it meets one edge, keeping its proportions. The
   most any file is enlarged is Justin Wong at about a third, which the artwork
   carries; nothing here is scaled far enough to go soft. */
/* width, plus a cap in pixels — not percentages. The box is a centred grid
   item, so a percentage height on the image has no definite container to
   resolve against and silently falls back to auto: Comfort Zone then drew
   173px tall inside a 115px box and spilled over its card. The pixel cap is
   what actually holds, and the .has-logo rules further down re-cap it for
   their shorter 56px and 110px boxes.
   "width: 100%" is the part that changed. It used to be "width: auto", which
   could only ever shrink a logo, never grow one — Justin Wong at 172px and
   Choplin at 221px were drawn at their own size and floated in the tile
   touching no edge. Every logo now reaches at least one edge. */
.sponsor-logo img { width: 100%; max-height: 115px; object-fit: contain; }
/* Wide logos are limited by the card's width, not its height — Great Lake is
   5.8:1 and fills the box sideways while using barely a third of it vertically.
   The card's side padding exists to keep text off the edge; a contained image
   does not need all of it, so the logo box reaches into it and every wide logo
   gains. Tall logos are height-limited and are unaffected.
   The padding is what stops the widest ones — Calla de Spa, Great Lake, Rogers
   — running clean into the card's border, which read as though the artwork had
   been cropped. Bleeding out 1.25rem and padding back 0.75rem nets half the
   original gain and keeps a visible margin. */
.sponsor-card .sponsor-logo {
  width: calc(100% + 2.5rem); margin-inline: -1.25rem; padding-inline: .75rem;
}
/* Stand-in for a business with no logo file yet — set as a wordmark so the
   card reads as a sponsor rather than an empty box. */
.sponsor-logo .wordmark {
  font-family: var(--serif); font-size: 1.05rem; line-height: 1.3;
  color: var(--ink); text-align: center;
}

/* h3 sits directly under the single "Gold sponsors" h2 — no sub-headings, so
   no skipped level. The margin override is needed because .prose h3 adds a
   large top margin to every h3. */
.sponsor-card h3,
.sponsor-card .card-name {
  font-family: var(--sans); font-size: 1.18rem; font-weight: 600;
  margin: 0; line-height: 1.3; color: var(--ink);
}
.sponsor-card p  { margin: 0; font-size: .88rem; color: var(--muted); }
.sponsor-card .zh { font-size: .92rem; color: var(--ink-soft); }

/* Individual sponsors: same card, name only. Centred vertically so a short
   name does not sit awkwardly at the top of a box sized by its logo-bearing
   neighbours. */
/* Name cards take the shared min-height from .sponsor-card — with the list now
   interleaved alphabetically, mixed heights read as ragged rather than
   deliberate. Only the type differs. */
.sponsor-card.is-name h3,
.sponsor-card.is-name .card-name {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.3rem, 1.15rem + .5vw, 1.6rem);
}
/* Tiles are narrower than gold cards, so the name steps down slightly to stay
   on one line inside 184px. */
.sponsor-grid.is-tile .sponsor-card.is-name .card-name { font-size: 1.35rem; }

/* committee */
/* 13rem, not 12rem: at 12rem the three columns could squeeze to 192px, which is
   narrower than "Florence Wong" set at the size below. Raising the floor makes
   the layout drop to two wide columns instead of three cramped ones. */
.roster { list-style: none; padding: 0; display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }
/* Names are centred both ways and set large. A fixed size, not a viewport
   clamp: the prose column stops widening at its max, so the cards settle at a
   constant width and a vw-based size would have gone on growing past it. */
.roster li {
  background: var(--paper); border: 1px solid var(--rule); border-radius: .45rem;
  padding: .95rem .85rem !important;
  font-family: var(--serif);
  font-size: 1.35rem;
  line-height: 1.25;
  display: grid; place-items: center; text-align: center;
  /* Tall enough to hold the founder's larger line without his tile growing past
     the roster's — below this the two sections come out a couple of px apart. */
  min-height: 4.4rem;
}
.roster li::before { display: none; }
/* `.prose li + li` puts a top margin on every entry but the first, which in a
   grid stretches the first item's row to match the others' box-plus-margin —
   the first card came out 9px taller than its neighbours. The grid gap is doing
   this spacing already, so the margin has to go. */
.prose .roster li + li { margin-top: 0; }
/* secondary line inside a roster entry, e.g. "Sommelier 2026" */
.roster .role { display: block; margin-top: .2rem; font-family: var(--sans);
  font-size: .78rem; letter-spacing: .06em; color: var(--muted); }
/* Two categories to a row, each heading above its own names. Drops to one
   column when there is no room for two. */
.sponsor-pairs {
  display: grid; gap: 2rem 2.5rem; margin-top: 2.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
/* No top margin when the pairs open a section — the section's own padding
   already provides the space, and stacking both left a large empty gap under
   the divider above. */
.section > .prose > .sponsor-pairs:first-child,
.section > .prose.wide > .sponsor-pairs:first-child { margin-top: 0; }
.sponsor-pairs > div { text-align: center; }

/* .prose h2 gives every heading a gold underline, a bottom border and a large
   top margin — none of which suit a heading sitting inside a column. */
.sponsor-pairs h2 {
  margin: 0; padding: 0; border: 0;
  font-size: clamp(1.15rem, 1.05rem + .4vw, 1.4rem);
}
.sponsor-pairs h2::after { display: none; }
/* Tier above, subject below. "Diamond sponsor" is the same on both cards so it
   is set small and quiet; Wine / Hors d'oeuvres carries the meaning and takes
   the display size. */
.sponsor-pairs h2 .tier {
  display: block; margin-bottom: .35rem;
  font-family: var(--sans); font-size: 1rem; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase; color: #8a6a12;
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .sponsor-pairs h2 .tier { color: var(--gold); } }

/* Wine / Hors d'oeuvres, sitting under the single "Diamond sponsors" banner.
   Selector matches .prose h3's specificity, which would otherwise win. */
.prose h3.tier-sub,
.tier-sub {
  margin: 0 0 .2rem; text-align: center;
  font-family: var(--serif); font-weight: 400; line-height: 1.15;
  font-size: clamp(1.45rem, 1.2rem + 1.1vw, 2rem);
  color: var(--burgundy);
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .tier-sub { color: var(--gold-bright); } }

/* Prize takes three quarters of the row, Cake the remaining quarter.
   minmax(0, …) rather than a bare fr: an fr track will not shrink below its
   content's minimum, and the nowrap banner pill was forcing the Cake column
   wider than a quarter. */
/* Prize is capped at the width of its own tile row (3 x 200 + 2 x 10 = 620), so
   its bar stops where the tiles stop instead of running on past them. Cake then
   takes everything left over, which gives its bar room to stay on one line. */
.sponsor-pairs.is-three-quarter {
  grid-template-columns: minmax(0, 620px) minmax(0, 1fr);
  gap: 2rem 2.5rem;
}
/* The Cake column takes all the leftover width, which on a wide screen is far
   more than one short name needs. Cap the bar itself and centre it, rather than
   capping the column — the column still has to flex so the bar never wraps on a
   narrower window. */
.sponsor-pairs.is-three-quarter > div:last-child .sponsor-banner span {
  max-width: 22rem; margin-inline: auto;
}
/* Side by side needs 620 (Prize tiles) + 40 (gap) + ~261 (Cake bar unwrapped)
   = 921px of row. Below roughly 1010px of viewport the row is narrower than
   that and the Cake bar wraps, so the two stack instead and each gets the full
   width. */
@media (max-width: 63rem) {
  .sponsor-pairs.is-three-quarter { grid-template-columns: 1fr; }
  .sponsor-pairs.is-three-quarter > div:last-child .sponsor-banner span { max-width: 22rem; }
}

/* Banner headings, after the printed sponsor card: a centred pill flanked by
   gold rules, standing in for the card's brush-stroke and laurels. Burgundy and
   gold rather than the card's green, to stay in the site palette. */
/* `.prose h2` is a class+element selector and outweighs a bare class, so the
   underline and bottom border it sets have to be undone at equal specificity. */
/* A full-width gold bar per tier, not a pill. It is louder, it separates the
   sections unmistakably, and — unlike a pill — it can never overflow a narrow
   column, which the quarter-width Cake column made a real problem.
   The selector matches .prose h2's specificity, which would otherwise reimpose
   its underline and bottom border. */
.prose h2.sponsor-banner,
.sponsor-banner {
  display: block; margin: 0 0 1.3rem; padding: 0; border: 0; text-align: center;
}
.sponsor-banner span {
  display: block; width: 100%;
  background: linear-gradient(135deg, #b8933f 0%, #e2c87d 45%, #b8933f 100%);
  color: var(--burgundy-dark);
  padding: .8rem 1rem; border-radius: .5rem;
  font-family: var(--sans);
  /* Capped at 1.2rem: above that "CAKE SPONSOR" outgrows its column on a wide
     screen and wraps to two lines. */
  font-size: clamp(1.02rem, .92rem + .4vw, 1.2rem);
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  box-shadow: 0 10px 26px -14px rgba(74,12,23,.6),
              inset 0 1px 0 rgba(255,255,255,.45);
}
/* small diamonds either side, echoing the card's flourishes */
.sponsor-banner span::before { content: "\25C6"; margin-right: .9em; font-size: .62em; vertical-align: .12em; opacity: .55; }
.sponsor-banner span::after  { content: "\25C6"; margin-left:  .9em; font-size: .62em; vertical-align: .12em; opacity: .55; }

/* Prize and Cake tiles are the same size — 200 x 125, the gold card's
   260 x 163 proportion at a smaller scale. Three across plus two 10px gaps
   needs 620px, which is what the Prize column is capped at above. */
.sponsor-grid.is-tile {
  grid-template-columns: repeat(auto-fill, 200px);
  gap: 10px; justify-content: start; margin-top: 1.1rem;
}
.sponsor-grid.is-tile .sponsor-card { min-height: 125px; padding: 1rem .8rem; }

/* Cake holds one tile, so its track centres rather than starting left.
   auto-fit, not auto-fill: auto-fill leaves empty 200px tracks in a wide
   column and the single tile sits in the first one instead of the middle. */
.sponsor-grid.is-tile-lg { grid-template-columns: repeat(auto-fit, 200px); justify-content: center; }

/* Same card size as the gold grid — these inherit .sponsor-card's min-height
   rather than overriding it. Only the top margin differs. */
.sponsor-grid.is-compact { margin-top: 1.1rem; }

/* A card carrying a logo AND a name needs a shorter logo box, or it would come
   out taller than the name-only cards beside it. 56px + name + role lands on
   the same height as a plain card.
   Scoped to .is-compact deliberately. In the wide tiers a named logo card now
   sits directly beside unnamed ones carrying the SAME picture — Carolina Lam
   and Desjardins Financial Security both use the Desjardins logo — and
   shrinking one of them to 56px made the identical artwork appear at two
   sizes side by side. There the logo stays 115px and the card simply runs
   taller; the grid stretches its row to match, so the tier stays level. */
.sponsor-grid.is-compact .sponsor-card.has-logo .sponsor-logo { height: 56px; }
.sponsor-grid.is-compact .sponsor-card.has-logo .sponsor-logo img { max-height: 56px; }

/* Diamond is the top tier and has a column to itself, so its logo gets a much
   taller box than the 56px used where cards sit alongside name-only ones. Both
   diamond cards then take a matching min-height so Wine and Hors d'oeuvres stay
   level — the logo card would otherwise be ~50px taller than the name-only one. */
.sponsor-pairs.is-diamond .sponsor-card { min-height: 13.6rem; }
.sponsor-pairs.is-diamond .sponsor-card.has-logo .sponsor-logo { height: 110px; }
.sponsor-pairs.is-diamond .sponsor-card.has-logo .sponsor-logo img { max-height: 110px; }
.sponsor-grid .sponsor-card.has-logo h3,
.sponsor-grid .sponsor-card.has-logo .card-name {
  font-family: var(--serif); font-weight: 400; font-size: 1.2rem; margin: 0;
}
.sponsor-grid.is-compact .role {
  display: block; margin-top: .25rem; font-family: var(--sans);
  font-size: .74rem; letter-spacing: .06em; color: var(--muted);
}
.section-alt .roster li { background: var(--cream); }
/* A single roster entry — the Honorary Founder. It has to come out exactly as
   wide as a Core Committee card at every width, so it uses the same track sizing
   rather than a pinned width, which only matched at the widest layout.
   auto-fill, not auto-fit: auto-fit collapses the empty tracks and the one card
   would stretch across the whole row. auto-fill keeps them, so the card gets a
   real column. */
.roster.is-single { grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr)); }
/* "Benson Sy" is a short name, so at the roster's 1.5rem it covered barely half
   its tile and read as smaller type than the longer names beside it. Sized up
   until it fills the tile to about the same extent the longest name does —
   matching the apparent weight rather than the nominal point size. */
.roster.is-single li { font-size: 1.85rem; }
/* The founder's dates. Larger than the roster's usual secondary line — at that
   size they read as a footnote, which is the wrong tone for a memorial. The
   card is allowed to stand taller than the Core Committee cards to hold them;
   it sits under its own heading, so the two are never side by side. */
.roster.is-single .role {
  font-size: .95rem; margin-top: .5rem; letter-spacing: .04em; color: var(--muted);
}
/* Three columns fit from 696px up; put the card in the middle one so it sits
   under the centred heading. */
@media (min-width: 43.5rem) { .roster.is-single li { grid-column: 2; } }
/* Two columns, roughly 461-696px: there is no middle track, so the card spans
   the row and is cut back to exactly one track's width, then centred. Below
   461px the grid is a single full-width column and needs none of this. */
@media (min-width: 28.8rem) and (max-width: 43.49rem) {
  .roster.is-single li { grid-column: 1 / -1; width: calc((100% - .5rem) / 2); justify-self: center; }
}

/* Centred section headings, with the gold rule moved from the left edge to the
   middle to follow the text. */
.prose h2.h2-center { text-align: center; }
.prose h2.h2-center::after { left: 50%; transform: translateX(-50%); }

/* ====================================== members: results and photo albums */

.results-list { display: grid; gap: 1rem; margin-top: 1.75rem; }
.result-card {
  background: var(--paper); border: 1px solid var(--rule);
  border-left: 3px solid var(--gold); border-radius: .5rem;
  padding: 1.25rem 1.35rem; box-shadow: var(--shadow);
}
.section-alt .result-card { background: var(--cream); }
.result-when {
  margin: 0; font-family: var(--sans); font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.prose .result-card h3 { margin: .35rem 0 0; font-size: 1.25rem; color: var(--burgundy); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .prose .result-card h3 { color: var(--gold); } }
.result-winners { margin: .55rem 0 0; color: var(--ink-soft); }
.result-card p:last-child { margin-bottom: 0; }
.result-pending { font-size: .9rem; font-style: italic; }
/* A game can carry several reports, so the buttons wrap rather than pushing the
   card sideways. */
.result-files { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }
/* A cancelled game: dimmed and the gold edge goes grey, so it reads as
   "nothing to see here" at a glance rather than needing to be read. */
.result-card.is-cancelled { border-left-color: var(--rule); opacity: .85; }
.result-cancelled {
  margin: .55rem 0 0; font-size: .9rem; font-style: italic; color: var(--muted);
}
.btn-small { padding: .45rem .95rem; font-size: .82rem; }
/* Sign out sits quietly under the welcome text — present when wanted, never
   competing with the results and photos the page is actually for.
   Outlined in burgundy rather than reusing .btn-ghost: that variant is white on
   transparent, built for the dark banner, and was invisible on this cream
   background. */
.signout { margin-top: 1.6rem; }

/* Sign out, sitting with the page title. Absolute rather than a flex row so it
   aligns with the eyebrow line no matter how many lines the heading runs to. */
.page-head-inner.has-signout { position: relative; padding-right: 7.5rem; }
/* top matches .page-head-inner's own padding, so the button lines up with the
   "Members only" label rather than floating against the top edge of the band. */
.page-head-inner .signout {
  position: absolute; top: clamp(2rem, 5.5vw, 3.4rem); right: 0; margin: 0;
}
/* On a narrow screen there is no room beside the title, so it drops above it
   rather than overlapping the words. */
@media (max-width: 34rem) {
  .page-head-inner.has-signout { padding-right: 0; }
  .page-head-inner .signout { position: static; margin: 0 0 1.1rem; }
}
.signout .btn {
  background: transparent; color: var(--burgundy);
  border-color: rgba(163,37,64,.45); font-weight: 600;
}
.signout .btn:hover {
  background: rgba(163,37,64,.09); color: var(--burgundy-deep);
  border-color: var(--burgundy); transform: none;
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .signout .btn { color: var(--gold); border-color: rgba(200,169,81,.45); }
  .signout .btn:hover { background: rgba(200,169,81,.12); color: var(--gold-bright); border-color: var(--gold); }
}

/* Photos leaves the site for Google Photos, so it is outlined rather than solid
   gold — it reads as "elsewhere", not as another report to download. */
.btn.is-photos {
  background: transparent; color: var(--burgundy);
  border-color: rgba(163,37,64,.45); font-weight: 600;
}
.btn.is-photos:hover {
  background: rgba(163,37,64,.09); color: var(--burgundy-deep);
  border-color: var(--burgundy); transform: none;
}
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .btn.is-photos { color: var(--gold); border-color: rgba(200,169,81,.45); }
  .btn.is-photos:hover { background: rgba(200,169,81,.12); color: var(--gold-bright); }
}

/* ========================================================= embedded form */

/* A Google Form in an iframe cannot tell the page how tall it is, so the height
   is set here. Measured against the live form: it needs 1000px at the embed's
   640px width and 1054px on a phone. The extra ~60px is headroom for the
   "this is a required question" messages, which push the form taller when
   somebody submits an incomplete answer.
   Too short and the visitor scrolls inside a small window without noticing;
   too tall and the page ends in a slab of white. */
.form-embed { width: min(100%, 40rem); margin: 1.5rem auto 0; }
.form-embed iframe {
  display: block; width: 100%; height: 1060px; border: 0;
  background: var(--paper); border-radius: .5rem;
}
@media (max-width: 40rem) { .form-embed iframe { height: 1140px; } }
/* Once submitted, Google replaces the form with a short "response recorded"
   message. Without this the frame keeps its full height and the page ends in a
   large empty white box. The transition stops the collapse being a jolt. */
.form-embed iframe { transition: height .3s ease; }
.form-embed.is-submitted iframe { height: 260px; }
@media (max-width: 40rem) { .form-embed.is-submitted iframe { height: 300px; } }
.form-fallback { margin-top: 1.25rem; text-align: center; font-size: .9rem; }
/* Its own band immediately above the footer. Centred and given less vertical
   room than a content section, so it reads as a way out rather than as another
   thing to read. */
.section.back-home { padding-block: clamp(1.75rem, 4vw, 2.75rem); text-align: center; }
.section.back-home p { margin: 0; }

/* ====================================================== roll of honour */

/* A roll of honour, not a table: two values a row, and the year wants to be the
   thing your eye runs down. Numeric year in gold, name in serif, hairline
   between — the look of a board on a clubhouse wall. */
/* The current holder, standing alone above the folded history. */
.champ-current {
  margin-top: 1.75rem; padding: 1.6rem 1.4rem; text-align: center;
  background: var(--paper); border: 1px solid var(--rule);
  border-top: 3px solid var(--gold); border-radius: .6rem; box-shadow: var(--shadow);
}
.section-alt .champ-current { background: var(--cream); }
.champ-label {
  margin: 0; font: 600 .7rem/1 var(--sans); letter-spacing: .15em;
  text-transform: uppercase; color: var(--muted);
}
.champ-holder {
  margin: .7rem 0 0; font-family: var(--serif);
  font-size: clamp(1.6rem, 1.3rem + 1.4vw, 2.2rem); line-height: 1.15; color: var(--burgundy);
}
.champ-since { margin: .35rem 0 0; font-family: var(--serif); font-size: 1.05rem; color: var(--gold-ink); }
/* Shown only when the current champion has won before. */
.champ-also {
  margin: .6rem 0 0; padding-top: .6rem; border-top: 1px solid var(--rule);
  font-size: .88rem; color: var(--muted);
}

/* The full roll, folded away. */
.champ-more { margin-top: 1.25rem; }
.champ-more > summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .55rem .95rem; border: 1px solid var(--rule); border-radius: 2rem;
  font: 600 .82rem/1 var(--sans); color: var(--burgundy); background: var(--paper);
}
.section-alt .champ-more > summary { background: var(--cream); }
.champ-more > summary::-webkit-details-marker { display: none; }
.champ-more > summary::after {
  content: ""; width: .42rem; height: .42rem; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: rotate(45deg) translate(-1px,-1px);
}
.champ-more[open] > summary::after { transform: rotate(-135deg) translate(-2px,-2px); }
.champ-more > summary:hover { border-color: var(--burgundy); }
.champ-more > summary:focus-visible { outline: 2px solid var(--burgundy); outline-offset: 2px; }

.champions { list-style: none; padding: 0; margin: 1.25rem 0 0; border-top: 1px solid var(--rule); }
.prose .champions li {
  margin: 0; padding: .85rem .4rem;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .35rem 1.25rem;
  border-bottom: 1px solid var(--rule);
}
.prose .champions li::before { display: none; }
/* --gold-ink, not --gold: the decorative gold is a border-and-badge colour and
   came to 2.03:1 as text on the light rows — barely visible. --gold-ink is the
   same hue taken dark enough to read (4.86:1 on cream). */
.champ-year {
  flex: none; width: 3.6rem;
  font-family: var(--serif); font-size: 1.12rem; color: var(--gold-ink);
  font-variant-numeric: tabular-nums;
}
.champ-name { font-family: var(--serif); font-size: 1.12rem; color: var(--ink); }
/* The current holder, so the page answers "who holds it now" immediately. */
.prose .champions li.is-latest {
  background: linear-gradient(90deg, rgba(200,169,81,.13), rgba(200,169,81,0) 70%);
  border-left: 3px solid var(--gold); padding-left: .9rem;
}
.champions li.is-latest .champ-name { color: var(--burgundy); font-size: 1.22rem; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .champ-name { color: var(--cream); }
  .champions li.is-latest .champ-name { color: var(--gold-bright); }
}

/* Holes-in-one honour roll. Built to match the leaderboard: a prominent card for
   the most recent ace, then the full roll beneath. The card mirrors .champ-current
   so the two honour rolls read as a matching pair. */
.ace-current {
  margin-top: 1.75rem; padding: 1.6rem 1.4rem; text-align: center;
  background: var(--paper); border: 1px solid var(--rule);
  border-top: 3px solid var(--gold); border-radius: .6rem; box-shadow: var(--shadow);
}
.section-alt .ace-current { background: var(--cream); }
.ace-label {
  margin: 0; font: 600 .7rem/1 var(--sans); letter-spacing: .15em;
  text-transform: uppercase; color: var(--muted);
}
.ace-holder {
  margin: .7rem 0 0; font-family: var(--serif);
  font-size: clamp(1.6rem, 1.3rem + 1.4vw, 2.2rem); line-height: 1.15; color: var(--burgundy);
}
.ace-detail { margin: .35rem 0 0; font-family: var(--serif); font-size: 1.05rem; color: var(--gold-ink); }

.aces { list-style: none; padding: 0; margin: 1.25rem 0 0; border-top: 1px solid var(--rule); }
.prose .aces li {
  margin: 0; padding: .85rem .4rem;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem 1.1rem;
  border-bottom: 1px solid var(--rule);
}
.prose .aces li::before { display: none; }
.ace-name { font-family: var(--serif); font-size: 1.12rem; color: var(--ink); }
.ace-where { color: var(--ink); }
.ace-date { color: var(--muted); font-size: .92rem; margin-left: auto; font-variant-numeric: tabular-nums; }
/* The most recent ace, marked the same way the current champion is. */
.prose .aces li.is-latest {
  background: linear-gradient(90deg, rgba(200,169,81,.13), rgba(200,169,81,0) 70%);
  border-left: 3px solid var(--gold); padding-left: .9rem;
}
.aces li.is-latest .ace-name { color: var(--burgundy); font-size: 1.2rem; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .ace-name { color: var(--cream); }
  .aces li.is-latest .ace-name { color: var(--gold-bright); }
}

/* The celebration banner at the top of the members page. Gold, congratulatory,
   and set apart from the workaday welcome text below it. Temporary -- driven by
   the celebrate-until date -- so it is allowed to be the loudest thing on the
   page, including a slow shine that sweeps across to catch the eye. */
.ace-splash {
  position: relative; overflow: hidden;
  margin: 0 0 1.75rem; padding: .85rem 1.1rem;
  background: linear-gradient(120deg, var(--gold-pale), rgba(232,205,132,.45));
  border: 1px solid var(--gold); border-radius: .7rem; text-align: center;
  animation: ace-glow 2.6s ease-in-out infinite;
}
/* The shine: a soft diagonal highlight that travels left to right, pauses, repeats. */
.ace-splash::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: -40%; width: 30%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.65), transparent);
  transform: skewX(-18deg); animation: ace-shine 3.8s ease-in-out infinite;
}
@keyframes ace-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(211,174,79,0); }
  50%      { box-shadow: 0 0 20px 2px rgba(211,174,79,.5); }
}
@keyframes ace-shine {
  0%   { left: -40%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}
/* Motion off for anyone who asks the browser to reduce it -- the banner is still
   gold and prominent, just still. */
@media (prefers-reduced-motion: reduce) {
  .ace-splash { animation: none; }
  .ace-splash::after { display: none; }
}
.ace-splash > * { position: relative; z-index: 1; }

/* Twinkling sparkle-stars, scattered across the banner behind the text. Each is a
   little four-point star that fades and swells in turn, so the whole banner keeps
   catching the light. Decorative only -- hidden entirely under reduced motion. */
.ace-splash > .ace-sparkles { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.ace-sparkles i {
  position: absolute; width: .8rem; height: .8rem;
  background: radial-gradient(circle, #fff 0 18%, var(--gold-bright) 30%, transparent 70%);
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
  opacity: 0; transform: scale(.2) rotate(0deg);
  animation: ace-twinkle 2.2s ease-in-out infinite;
}
@keyframes ace-twinkle {
  0%, 100% { opacity: 0; transform: scale(.2) rotate(0deg); }
  50%      { opacity: 1; transform: scale(1) rotate(45deg); }
}
/* Scattered positions, sizes and offset timings, so they never blink in unison. */
.ace-sparkles i:nth-child(1)  { left: 4%;  top: 26%; animation-delay: 0s;    }
.ace-sparkles i:nth-child(2)  { left: 9%;  top: 68%; animation-delay: .9s; width: .6rem; height: .6rem; }
.ace-sparkles i:nth-child(3)  { left: 15%; top: 14%; animation-delay: 1.6s; width: .55rem; height: .55rem; }
.ace-sparkles i:nth-child(4)  { left: 22%; top: 78%; animation-delay: .4s;  }
.ace-sparkles i:nth-child(5)  { left: 30%; top: 22%; animation-delay: 1.9s; width: .6rem; height: .6rem; }
.ace-sparkles i:nth-child(6)  { left: 39%; top: 82%; animation-delay: 1.1s; width: .5rem; height: .5rem; }
.ace-sparkles i:nth-child(7)  { left: 50%; top: 12%; animation-delay: .2s; width: 1rem; height: 1rem; }
.ace-sparkles i:nth-child(8)  { left: 61%; top: 84%; animation-delay: 1.4s; }
.ace-sparkles i:nth-child(9)  { left: 70%; top: 20%; animation-delay: .7s; width: .55rem; height: .55rem; }
.ace-sparkles i:nth-child(10) { left: 78%; top: 76%; animation-delay: 2s;   }
.ace-sparkles i:nth-child(11) { left: 85%; top: 16%; animation-delay: 1.2s; width: .6rem; height: .6rem; }
.ace-sparkles i:nth-child(12) { left: 91%; top: 66%; animation-delay: .5s; width: .7rem; height: .7rem; }
.ace-sparkles i:nth-child(13) { left: 96%; top: 34%; animation-delay: 1.7s; width: .5rem; height: .5rem; }
.ace-sparkles i:nth-child(14) { left: 46%; top: 62%; animation-delay: 2.3s; width: .55rem; height: .55rem; }
@media (prefers-reduced-motion: reduce) {
  .ace-splash > .ace-sparkles { display: none; }
}
.ace-splash-kicker {
  margin: 0; font: 800 clamp(1.2rem, 1rem + 1vw, 1.7rem)/1.1 var(--sans);
  letter-spacing: .16em; text-transform: uppercase; color: var(--gold-ink);
}
.ace-splash-name {
  margin: .55rem 0 0; font-family: var(--serif); font-size: clamp(1.35rem, .7rem + 2.5vw, 2.6rem);
  line-height: 1.12; color: var(--burgundy);
  white-space: nowrap;   /* the congratulations stays on one line */
}
.ace-splash-where { margin: .45rem 0 0; color: var(--ink); font-size: clamp(1rem, .95rem + .3vw, 1.2rem); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .ace-splash { background: linear-gradient(120deg, rgba(216,184,99,.16), rgba(216,184,99,.06)); }
  .ace-splash-name { color: var(--gold-bright); }
  .ace-splash-where { color: var(--cream); }
}

/* With a photo, the banner becomes two columns: the picture, then the words.
   The photo is a portrait thumbnail with a white mount, like a print laid on the
   green. On a narrow screen the two stack and re-centre. */
/* row-reverse puts the photo on the right while the text stays first in the
   markup (which the mobile column layout below relies on to keep the photo on
   top). */
.ace-splash.has-photo {
  display: flex; flex-direction: row-reverse; align-items: center;
  justify-content: center;   /* keep the text and photo together, centred as a pair */
  gap: 1.6rem; text-align: left;
}
/* flex:0 1 auto -- size to the words, do not stretch across the banner, which is
   what was leaving a wide gap between the text and the photo. */
.ace-splash.has-photo .ace-splash-text { flex: 0 1 auto; min-width: 0; }
.ace-photo {
  flex: none; width: clamp(94px, 22vw, 132px); aspect-ratio: 3 / 4; object-fit: cover;
  border-radius: .5rem; background: #fff;
  border: 4px solid #fff; box-shadow: 0 2px 10px rgba(0,0,0,.22);
  transform: rotate(2deg);   /* a slight tilt, like a snapshot dropped on the page */
}
@media (max-width: 520px) {
  .ace-splash.has-photo { flex-direction: column; gap: 1rem; text-align: center; }
}

/* The archive holds well over a hundred albums, so they are grouped by year --
   each year a collapsible block. The closed state is a tidy index of years with
   counts; open one to see its albums. Built on <details>, so no JavaScript. */
.album-archive { margin-top: 1.75rem; }
.album-year { border-bottom: 1px solid var(--rule); }
.album-year:first-child { border-top: 1px solid var(--rule); }
.album-year > summary {
  display: flex; align-items: baseline; gap: 1rem; cursor: pointer;
  padding: .85rem .35rem; list-style: none; user-select: none;
}
.album-year > summary::-webkit-details-marker { display: none; }
/* A rotating chevron in place of the default triangle. */
.album-year > summary::after {
  content: ""; width: .5rem; height: .5rem; margin-left: auto;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg); transition: transform .15s ease;
}
.album-year[open] > summary::after { transform: rotate(-135deg); }
.album-year > summary:hover { background: var(--rule-soft); }
.album-year > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.album-year-num { font-family: var(--serif); font-size: 1.35rem; color: var(--burgundy); }
.album-year-count { font-family: var(--sans); font-size: .82rem; color: var(--muted); }
@media (prefers-color-scheme: dark) and (min-width: 99999px) {
  .album-year-num { color: var(--gold-bright); }
}

/* One row per album, rather than cards: cards would turn the page into a scroll
   of near-identical boxes. Inside a year block, indented a little from the year. */
.album-list { list-style: none; padding: 0 0 .5rem .35rem; margin: 0; }
.prose .album-list li { margin: 0; padding: 0; }
.prose .album-list li::before { display: none; }
.album-list a {
  display: flex; flex-wrap: wrap; gap: .3rem 1rem;
  align-items: baseline; justify-content: space-between;
  padding: .8rem .35rem; border-bottom: 1px solid var(--rule);
  text-decoration: none; color: inherit;
}
.album-list a:hover, .album-list a:focus-visible { background: var(--rule-soft); }
.album-title { font-family: var(--serif); font-size: 1.05rem; }
.album-list a:hover .album-title { color: var(--burgundy); text-decoration: underline; }
.album-when { font-family: var(--sans); font-size: .82rem; color: var(--muted); white-space: nowrap; }

/* ============================================================== charities */

.charity-grid {
  display: grid; gap: 1rem; margin-top: 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.charity-card {
  background: var(--paper); border: 1px solid var(--rule); border-radius: .55rem;
  padding: 1.4rem 1.1rem 1.25rem; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: .85rem;
  box-shadow: var(--shadow);
}
.section-alt .charity-card { background: var(--cream); }
/* The logos arrive at wildly different proportions — 4.8:1 for Fu Hui down to
   0.65:1 for Carefirst — so a fixed box with the image contained keeps every
   card level rather than letting the tall ones tower. */
.charity-logo { height: 96px; width: 100%; display: grid; place-items: center; }
.charity-logo img { max-height: 96px; max-width: 100%; width: auto; object-fit: contain; }
.charity-card h2 {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: 1.08rem; line-height: 1.3; color: var(--ink);
  border: 0; padding: 0;
}
.charity-card h2::after { display: none; }
.charity-card .years {
  margin: 0; font-family: var(--sans); font-size: .74rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: #8a6a12;
}
/* the charity currently supported */
.charity-card.is-current { border-color: var(--gold); border-width: 2px; }

/* charities — legacy list, kept for reference */
.charity-list { list-style: none; padding: 0; columns: 2; column-gap: 2rem; }
@media (max-width: 40rem) { .charity-list { columns: 1; } }
.charity-list li { padding: .55rem 0 .55rem 1.4rem !important; break-inside: avoid; border-bottom: 1px solid var(--rule-soft); position: relative; }
.charity-list li::before { content: ""; position: absolute; left: .25rem; top: 1.15em; width: .38rem; height: .38rem; border-radius: 50%; background: var(--gold); }
.charity-list .current { color: var(--burgundy); font-weight: 600; }
@media (prefers-color-scheme: dark) and (min-width: 99999px) { .charity-list .current { color: var(--gold); } }

/* members gate */
.gate { text-align: center; max-width: 34rem; margin-inline: auto; padding: clamp(2rem, 6vw, 3.5rem) 0; }
.gate .lock { font-size: 2.5rem; line-height: 1; }
.gate h2 { border: 0; margin-top: 1rem; }
.gate p { color: var(--ink-soft); margin-top: .9rem; }

/* ================================================================ footer */

.site-foot { background: var(--burgundy-dark); color: rgba(255,255,255,.78); margin-top: clamp(3rem, 8vw, 5rem); border-top: 3px solid var(--gold); }
.foot-inner {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  padding: clamp(2.25rem, 6vw, 3.5rem) 0 2rem;
  display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.foot-brand { display: flex; gap: .85rem; align-items: flex-start; }
.foot-brand img { border-radius: 2px; flex: none; box-shadow: 0 0 0 1px rgba(200,169,81,.45); }
.foot-brand p { font-family: var(--serif); color: #fff; font-size: 1.02rem; line-height: 1.35; }
.foot-brand span { color: var(--gold-bright); font-size: .8rem; }
.foot-col h2 { font-family: var(--sans); font-size: .72rem; letter-spacing: .15em; text-transform: uppercase; color: var(--gold-bright); margin-bottom: .9rem; }
.foot-col ul { list-style: none; padding: 0; }
.foot-col li + li { margin-top: .5rem; }
.foot-col a, .foot-col p { color: rgba(255,255,255,.8); font-size: .92rem; text-decoration: none; }
.foot-col a:hover { color: var(--gold-bright); text-decoration: underline; }
.foot-col .muted { color: rgba(255,255,255,.72); font-size: .84rem; margin-top: .6rem; line-height: 1.5; }
.foot-legal {
  width: min(100% - var(--pad) * 2, var(--wrap)); margin-inline: auto;
  padding: 1.25rem 0 1.75rem; border-top: 1px solid rgba(255,255,255,.12);
  font-size: .8rem; color: rgba(255,255,255,.72);
}

@media print {
  .site-head, .site-foot, .skip, .banner-actions, .banner-scene { display: none; }
  body { background: #fff; color: #000; }
  .table-wrap { overflow: visible; border: 0; }
}
