/* Wexford Eagles — design system.
 *
 * The club plays in black and white; Wexford's county colours are purple and
 * gold. So: a black-and-white base doing the structural work (backgrounds,
 * type, rules), with purple and gold carrying every accent — eyebrows,
 * underlines, date blocks, focus rings, hero washes, buttons.
 *
 * The three --brand-* variables are overridden per-site from
 * SiteConfiguration via an inline <style> in base.html, so anything an editor
 * should be able to recolour must reference them rather than the raw values
 * below.
 */

:root {
  /* Overridden from the CMS — keep in step with SiteConfiguration's defaults
     and site_config/context_processors.py. */
  --brand-primary: #101014;
  --brand-secondary: #5b2a86;
  --brand-accent: #ffc72c;

  /* Neutrals — the black-and-white base. */
  --ink: #101014;
  --ink-soft: #2a2a33;
  --muted: #5a5a66;
  --line: #e3e3e8;
  --paper: #ffffff;
  --paper-alt: #f5f5f7;
  --white: #ffffff;

  /* Purple and gold, in the shades the accents actually need. */
  --purple: #5b2a86;
  --purple-deep: #37194f;
  --purple-light: #8b5fb5;
  --gold: #ffc72c;
  --gold-deep: #c8940a;

  --danger: #b42318;
  --success: #15803d;

  --radius: 2px;
  --font-display: "Bebas Neue", "Arial Narrow", sans-serif;
  --font-body: "Source Sans 3", "Segoe UI", sans-serif;
  --shadow: 0 12px 30px rgba(16, 16, 20, 0.1);
  --shadow-lift: 0 20px 44px rgba(55, 25, 79, 0.18);
  --max: 1120px;
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  min-height: 100vh;
  line-height: 1.55;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--purple);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

a:hover {
  color: var(--purple-deep);
}

/* Every interactive element gets a visible ring. .btn sets appearance:none,
   which suppresses the browser default, so without this keyboard users lose
   the focus indicator entirely. */
:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 2px;
  border-radius: var(--radius);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--white);
  padding: 0.75rem 1.1rem;
  font-weight: 700;
}

.skip-link:focus {
  left: 0;
  color: var(--white);
}

/* ------------------------------------------------------------------ header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--ink);
  color: var(--white);
  border-bottom: 3px solid var(--gold);
}

.nav-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0.7rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  justify-content: space-between;
}

.brand {
  display: flex;
  align-items: center;
  /* The wordmark is a link home, and at the reduced mobile font size it came to
     35px. min-height rather than padding: it holds regardless of font metrics. */
  min-height: 44px;
  gap: 0.7rem;
  color: var(--white);
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 1.75rem;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

.brand:hover {
  color: var(--gold);
}

.brand img {
  width: 42px;
  height: 42px;
  object-fit: contain;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.3rem;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a {
  position: relative;
  /* The public site's primary navigation, and the only nav on a phone once the
     disclosure menu is open — 0.45rem of padding left these at 35px. */
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.45rem 0.6rem;
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Gold underline grows from the left on hover — the one bit of motion in the
   nav, and it is a transform so it costs nothing to animate. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0.6rem;
  right: 0.6rem;
  bottom: 0.15rem;
  height: 2px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.18s ease;
}

.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--white);
}

.nav-links a:hover::after,
.nav-links a[aria-current="page"]::after {
  transform: scaleX(1);
}

.nav-cta {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
}

/* Disclosure-based mobile menu. A <details> element rather than JS: it works
   before Alpine loads, keyboard-operates for free, and cannot get stuck open
   if a script fails. */
.nav-toggle {
  display: none;
}

.nav-toggle > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.5rem 0.7rem;
  border: 2px solid rgba(255, 255, 255, 0.35);
  color: var(--white);
  font-weight: 700;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.nav-toggle > summary::-webkit-details-marker {
  display: none;
}

.nav-toggle[open] > summary {
  border-color: var(--gold);
  color: var(--gold);
}

/* ----------------------------------------------------------------- buttons */

/* The element selectors are wrapped in :where() so they add no specificity.
   Written plainly as `.btn, button.btn, input[type=submit].btn`, the
   element-qualified branches score (0,1,1) and beat every (0,1,0) modifier
   that follows — so on a <button>, `.btn-secondary`'s border and `.btn-sm`'s
   padding silently lost to this rule's `border: 0` and full-size padding.
   Every RSVP Yes/Maybe/No control rendered as bare text.

   They are not needed to beat the user-agent stylesheet either: any author
   rule wins over UA regardless of specificity. They are here only to say
   which elements this is for. */
:where(button, input[type="submit"]).btn,
.btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.72rem 1.2rem;
  border-radius: var(--radius);
  font: inherit;
  font-weight: 800;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-decoration: none;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn-primary {
  background: var(--purple);
  color: var(--white);
}

.btn-primary:hover {
  background: var(--purple-deep);
  color: var(--white);
}

.btn-secondary {
  background: transparent;
  color: var(--ink);
  border: 2px solid var(--ink);
}

.btn-secondary:hover {
  background: var(--ink);
  color: var(--white);
}

/* On the dark header and hero the outline button needs to invert. */
.on-dark .btn-secondary {
  color: var(--white);
  border-color: rgba(255, 255, 255, 0.6);
}

.on-dark .btn-secondary:hover {
  background: var(--white);
  color: var(--ink);
}

.btn-accent {
  background: var(--gold);
  color: var(--ink);
}

.btn-accent:hover {
  background: var(--gold-deep);
  color: var(--ink);
}

.btn-ghost {
  background: transparent;
  color: inherit;
}

.btn-ghost:hover {
  color: var(--gold);
}

.btn-sm {
  padding: 0.42rem 0.75rem;
  font-size: 0.78rem;
}

.btn-danger {
  background: var(--danger);
  color: var(--white);
}

/* ---------------------------------------------------------------- messages */

.messages {
  max-width: var(--max);
  margin: 1rem auto 0;
  padding: 0 1.25rem;
  display: grid;
  gap: 0.5rem;
}

.flash {
  padding: 0.85rem 1rem;
  border-left: 4px solid var(--gold);
  background: var(--paper-alt);
  box-shadow: var(--shadow);
}

.flash-error {
  border-left-color: var(--danger);
}

.flash-success {
  border-left-color: var(--success);
}

.site-main {
  min-height: 60vh;
}

/* ------------------------------------------------------------------ footer */

.site-footer {
  margin-top: 4rem;
  background: var(--ink);
  color: rgba(255, 255, 255, 0.85);
  border-top: 3px solid var(--gold);
}

.footer-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 2.75rem 1.25rem;
  display: grid;
  gap: 1.75rem;
  grid-template-columns: 1.4fr 1fr;
}

.footer-brand {
  font-family: var(--font-display);
  font-size: 2rem;
  letter-spacing: 0.05em;
  margin: 0 0 0.5rem;
  color: var(--white);
}

.site-footer a {
  color: var(--gold);
}

.site-footer a:hover {
  color: var(--white);
}

.footer-social {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.8rem;
}

.footer-legal {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.85rem;
  margin: 0;
}

/* The legal row sits below the two footer columns and spans the full width:
   the privacy policy has to be reachable from every page for Google Ads, so
   it gets its own band rather than being buried in a column. */
.footer-legal-bar {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1rem 1.25rem 2rem;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
}

.footer-legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.85rem;
}

/* --------------------------------------------------------- consent banner */

/* Fixed to the bottom rather than shown as a modal: a cookie wall makes the
   landing page's content unreachable, which Google Ads treats as a
   destination problem in its own right. */
.cookie-banner {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 60;
  background: var(--ink);
  color: rgba(255, 255, 255, 0.9);
  border-top: 3px solid var(--gold);
  box-shadow: 0 -12px 30px rgba(16, 16, 20, 0.25);
}

.cookie-banner[hidden] {
  display: none;
}

.cookie-banner-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1.1rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  align-items: center;
  justify-content: space-between;
}

.cookie-banner-title {
  font-family: var(--font-display);
  font-size: 1.35rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--white);
  margin: 0 0 0.15rem;
}

.cookie-banner-text {
  margin: 0;
  font-size: 0.9rem;
  max-width: 52ch;
}

.cookie-banner a {
  color: var(--gold);
}

/* Both answers are the same size and sit side by side. Refusing has to be as
   easy as accepting — a de-emphasised "Reject" is the classic dark pattern
   the Irish DPC enforces against, not a style preference. */
.cookie-banner-actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.cookie-banner-actions .btn {
  min-width: 7.5rem;
}

/* ------------------------------------------------------------- breadcrumbs */

.breadcrumbs {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1rem 1.25rem 0;
  font-size: 0.85rem;
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* A pseudo-element, not a character in the markup: "Home slash News slash"
   is noise in a screen reader, and ::before content is not announced. */
.breadcrumbs li + li::before {
  content: "/";
  color: var(--muted);
  margin-right: 0.5rem;
}

.breadcrumbs [aria-current="page"] {
  color: var(--muted);
}

/* -------------------------------------------------------------- match band */

/* The one strip a visitor sees without scrolling past the hero. Purple to
   black with the gold rule — both accent colours on the highest-traffic
   element on the site. */
.match-band {
  background: linear-gradient(100deg, var(--purple-deep), var(--ink) 65%);
  color: var(--white);
  border-bottom: 4px solid var(--gold);
}

.match-band-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1.75rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem 2.5rem;
}

.match-card {
  flex: 1 1 18rem;
}

.match-teams {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.6vw, 2rem);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin: 0.15rem 0 0.3rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.match-meta {
  margin: 0;
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.92rem;
}

/* -------------------------------------------------------------------- hero */

.hero {
  position: relative;
  min-height: min(88vh, 760px);
  display: grid;
  align-items: end;
  color: var(--white);
  overflow: hidden;
  background: var(--ink);
}

/* The banner is an <img> underneath rather than a CSS background, so it can
   be the preloadable LCP element and carry alt text. */
.hero-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Purple wash over the photo: keeps white text legible on any image and is
   where most of the county colour on the page comes from. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    115deg,
    rgba(16, 16, 20, 0.92) 0%,
    rgba(55, 25, 79, 0.82) 45%,
    rgba(91, 42, 134, 0.4) 100%
  );
}

.hero-content {
  position: relative;
  z-index: 1;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  padding: 4.5rem 1.25rem 3.5rem;
  animation: rise 0.8s ease both;
}

.hero-brand {
  font-family: var(--font-display);
  font-size: clamp(3.4rem, 10vw, 7rem);
  letter-spacing: 0.05em;
  line-height: 0.9;
  margin: 0 0 1rem;
  text-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

/* Gold rule under the hero eyebrow — the club's signature mark, reused on
   section titles. */
.hero-content .eyebrow::before,
.section-title::before {
  content: "";
  display: block;
  width: 3rem;
  height: 4px;
  background: var(--gold);
  margin-bottom: 0.75rem;
}

.hero-lead {
  max-width: 36rem;
  font-size: 1.15rem;
  margin: 0 0 1.5rem;
  color: rgba(255, 255, 255, 0.94);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* ---------------------------------------------------------------- sections */

.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: 3.5rem 1.25rem;
}

/* Long-form prose (policies, club info). Capped by line length rather than by
   the layout grid — 70ch is about where a paragraph stops being comfortable
   to read, and a full-width privacy policy is a wall. */
.section-prose {
  max-width: 70ch;
}

.section-dark {
  max-width: none;
  background: var(--ink);
  color: var(--white);
}

.section-dark .section-title,
.section-dark .section-lead {
  color: var(--white);
}

.section-inner {
  max-width: var(--max);
  margin: 0 auto;
}

.section-title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 3rem);
  letter-spacing: 0.04em;
  margin: 0 0 0.5rem;
  color: var(--ink);
  text-transform: uppercase;
}

.section-lead {
  color: var(--muted);
  max-width: 40rem;
  margin: 0 0 2rem;
}

.grid-3 {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* ------------------------------------------------------------------- cards */

.media-tile {
  position: relative;
  overflow: hidden;
  background: var(--white);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.media-tile:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
  border-color: var(--purple-light);
}

.media-tile img {
  aspect-ratio: 16 / 10;
  width: 100%;
  object-fit: cover;
}

.media-tile-body {
  padding: 1.1rem 1.2rem 1.3rem;
}

.card-link {
  text-decoration: none;
  color: inherit;
  display: block;
}

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--purple);
  margin: 0 0 0.35rem;
}

.section-dark .eyebrow,
.hero .eyebrow {
  color: var(--gold);
}

.stack {
  display: grid;
  gap: 1rem;
}

.panel {
  background: var(--white);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 1.25rem 1.4rem;
}

.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.75rem;
}

.panel-head a {
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.plain-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

/* --------------------------------------------------------- portal dashboard */

/* Two columns: what the member has to do on the left, how the team is doing
   on the right. Collapses to one at 900px — see the responsive block. */
.portal-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1.6fr 1fr;
  align-items: start;
}

/* The action strip. Only rendered when there is something in it, so it can
   afford to be loud — gold left rule, purple counter. */
.action-strip {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.action-card {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.85rem 1rem;
  background: var(--paper-alt);
  border-left: 4px solid var(--gold);
}

.action-card-urgent {
  border-left-color: var(--danger);
}

.action-card > span:last-child {
  display: grid;
  gap: 0.1rem;
  font-size: 0.9rem;
}

.action-count {
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1;
  color: var(--purple);
  white-space: nowrap;
}

.action-card-urgent .action-count {
  color: var(--danger);
}

/* Next up — the one card on the page that is doing the job the member came
   here for, so it gets the purple wash and the full-size RSVP buttons. */
.next-up {
  background: linear-gradient(135deg, var(--purple-deep), var(--ink) 70%);
  color: var(--white);
  border-bottom: 4px solid var(--gold);
  padding: 1.5rem 1.6rem;
}

.next-up-title {
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 3.5vw, 2.6rem);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin: 0.2rem 0 0.5rem;
  color: var(--white);
}

.next-up-meta {
  margin: 0 0 0.35rem;
  color: rgba(255, 255, 255, 0.82);
}

.next-up .muted {
  color: rgba(255, 255, 255, 0.72);
}

.next-up-rsvp {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}

.form-guide {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.form-guide li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.form-guide-meta {
  display: grid;
  font-size: 0.9rem;
  line-height: 1.3;
}

/* ------------------------------------------------------------- roll call */

/* A roll-call row has no date block, so it must not inherit .event-row's
   5.5rem first column — the member's name was being wrapped into it.
   Compounded with .event-row on purpose: .event-row redeclares
   grid-template-columns twice further down the file, and at an equal (0,1,0)
   this rule lost to both on source order — it never applied anywhere. */
.event-row.rollcall-row {
  grid-template-columns: 1fr auto;
}

/* Segmented control over real radios. The radio itself is visually hidden
   and the label is the whole button, so the tap target is ~44px rather than
   the 13px dot a coach was expected to hit on a pitch in the rain. */
.rollcall-choice {
  display: flex;
  border: 2px solid var(--ink);
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}

.rollcall-choice label {
  padding: 0.6rem 0.9rem;
  min-width: 4.5rem;
  text-align: center;
  cursor: pointer;
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-right: 1px solid var(--line);
  user-select: none;
}

.rollcall-choice label:last-child {
  border-right: 0;
}

.rollcall-choice input:checked + label {
  background: var(--purple);
  color: var(--white);
}

/* The hidden input keeps the focus ring — it just has to be drawn on the
   label, which is the thing actually on screen. */
.rollcall-choice input:focus-visible + label {
  outline: 3px solid var(--gold);
  outline-offset: -3px;
}

/* Off-screen but still in the accessibility tree: `display:none` would take
   the radio out of the tab order and out of the form entirely. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* -------------------------------------------------------------- event rows */

.event-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.event-row {
  display: grid;
  grid-template-columns: 5.5rem 1fr auto;
  gap: 1rem;
  align-items: center;
  padding: 1rem 0;
  border-bottom: 1px solid var(--line);
}

.event-row:last-child {
  border-bottom: 0;
}

/* Most rows are not date-led — an invoice month, a player name, a group or a
   resource label leads instead, and the fixed 5.5rem track wrapped it to four
   lines. Compounded with .event-row so (0,2,0) beats the ≤760px override too:
   media queries add no specificity, so a bare .data-row would lose there. */
.event-row.data-row {
  grid-template-columns: 1fr auto;
}

/* An email is one unbreakable word and a grid item's min-content contribution
   is its longest word, so a row falling back to a member's email forced the
   track — and the page — wider than the screen. `anywhere` rather than
   `break-word`: only `anywhere` reduces that min-content contribution. */
.event-row > * {
  overflow-wrap: anywhere;
}

.event-title {
  font-weight: 700;
  font-size: 1.05rem;
}

.date-block {
  text-align: center;
  background: var(--ink);
  color: var(--white);
  padding: 0.55rem 0.4rem;
  border-bottom: 3px solid var(--gold);
}

.date-block .day {
  font-family: var(--font-display);
  font-size: 1.9rem;
  line-height: 1;
  color: var(--white);
}

.date-block .month {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--gold);
  font-weight: 700;
}

.muted {
  color: var(--muted);
}

/* ------------------------------------------------------------------ badges */

.badge {
  display: inline-block;
  padding: 0.22rem 0.6rem;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: var(--paper-alt);
  color: var(--ink);
  border: 1px solid var(--line);
}

.badge-game {
  background: var(--purple);
  color: var(--white);
  border-color: var(--purple);
}

.badge-pending {
  background: #fff6d6;
  color: #7a5600;
  border-color: #f0dc9a;
}

.badge-active {
  background: #e7f6ec;
  color: #14612f;
  border-color: #b9e2c6;
}

/* Cancelled and "not yet due" are not warnings and not achievements — without
   their own variant they fell back to bare .badge and read identically to each
   other. */
.badge-muted {
  background: var(--paper-alt);
  color: #5b5b5b;
  border-color: var(--line);
}

/* Overdue shared .badge-pending's amber with merely-unpaid, so the one row a
   member needs to act on looked like every other row. */
.badge-overdue {
  background: #fdeaea;
  color: #8a1c1c;
  border-color: #f2c2c2;
}

/* -------------------------------------------------------------- scorelines */

.season-record {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0 0 2rem;
  padding: 0.9rem 1.1rem;
  background: var(--ink);
  color: var(--white);
  border-left: 4px solid var(--gold);
}

.season-record strong {
  font-family: var(--font-display);
  font-size: 1.9rem;
  letter-spacing: 0.06em;
  line-height: 1;
  color: var(--gold);
}

.season-record .muted {
  color: rgba(255, 255, 255, 0.68);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.scoreline {
  display: inline-flex;
  align-items: stretch;
  white-space: nowrap;
}

/* The W/L/D chip is the only colour-coded element on the page, so it also
   carries the letter — colour alone would leave the outcome unreadable to
   anyone who cannot distinguish the two. */
.scoreline-result {
  display: grid;
  place-items: center;
  min-width: 2rem;
  padding: 0.25rem 0.5rem;
  font-weight: 800;
  font-size: 0.85rem;
  color: var(--white);
  background: var(--muted);
}

.scoreline-win .scoreline-result {
  background: var(--success);
}

.scoreline-loss .scoreline-result {
  background: var(--danger);
}

.scoreline-draw .scoreline-result {
  background: var(--purple);
}

.scoreline-score {
  display: grid;
  place-items: center;
  padding: 0.25rem 0.7rem;
  font-family: var(--font-display);
  font-size: 1.35rem;
  letter-spacing: 0.04em;
  background: var(--ink);
  color: var(--white);
}

.rsvp-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* The partial hardcodes .btn-sm, which is ~33px — under the 44px minimum, and
   three of them sit side by side, so a mis-tap files the wrong answer. */
.rsvp-group .btn {
  min-height: 44px;
}

.rsvp-group .btn.is-active {
  outline: 3px solid var(--gold);
  outline-offset: 1px;
}

/* ------------------------------------------------------------------- forms */

.form-grid {
  display: grid;
  gap: 1rem;
  max-width: 32rem;
}

.form-grid label {
  display: grid;
  gap: 0.35rem;
  font-weight: 600;
}

.form-grid input,
.form-grid select,
.form-grid textarea {
  width: 100%;
  padding: 0.7rem 0.8rem;
  border: 1px solid #c3c3cc;
  border-radius: var(--radius);
  font: inherit;
  background: var(--white);
  color: var(--ink);
}

.form-grid input:focus,
.form-grid select:focus,
.form-grid textarea:focus {
  border-color: var(--purple);
}

.form-grid .errorlist {
  color: var(--danger);
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
  font-size: 0.9rem;
}

/* The only control for choosing which months to pay, at the browser default of
   ~13px. The label is the tap target, not the box: a 44px checkbox is a huge
   graphic, whereas a 44px label lets the badge and the box share the target. */
.pay-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  padding: 0 0.25rem;
  cursor: pointer;
}

.pay-checkbox {
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
}

/* Pinned to the bottom of the viewport: a coach marking a 25-name roster had to
   scroll the whole list to reach Save. Each row also saves itself over HTMX, so
   this is no longer the only thing standing between the marks and the database
   — but the button still has to be reachable with one thumb. */
.rollcall-actions {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 1rem;
  padding: 0.75rem 0;
  background: var(--paper);
  border-top: 1px solid var(--line);
}

/* -------------------------------------------------------------- portal nav */

.portal-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 1.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 2px solid var(--line);
}

.portal-nav a {
  text-decoration: none;
  /* inline-flex + min-height rather than more padding: padding alone landed
     these at 42px, and guessing at font metrics to clear 44 is how it drifts
     back. These are the portal's primary navigation on a phone. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--ink);
  padding: 0.6rem 0.8rem;
  border: 1px solid transparent;
}

.portal-nav a:hover {
  border-color: var(--line);
  background: var(--paper-alt);
  color: var(--purple);
}

.portal-nav a[aria-current="page"] {
  background: var(--ink);
  color: var(--white);
}

/* Grouped secondary links. The disclosure sits inline with the primary
   links and drops its panel below, so opening it never reflows the page
   content underneath. */
.portal-nav-more {
  position: relative;
}

.portal-nav-more > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.4rem 0.7rem;
  font-weight: 700;
  font-size: 0.85rem;
  border: 1px solid var(--line);
  user-select: none;
}

.portal-nav-more > summary::-webkit-details-marker {
  display: none;
}

.portal-nav-more > summary::after {
  content: " ▾";
  color: var(--purple);
}

.portal-nav-more[open] > summary {
  background: var(--paper-alt);
  border-color: var(--purple);
}

.portal-nav-group {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.25rem);
  left: 0;
  min-width: 12rem;
  display: grid;
  background: var(--white);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 0.25rem;
}

.portal-nav .portal-nav-coach > summary {
  color: var(--purple);
  border-color: var(--purple-light);
}

.hof-grid,
.gallery-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.quote-block {
  margin: 1.5rem 0;
  padding: 1.25rem 1.5rem;
  border-left: 4px solid var(--gold);
  background: var(--paper-alt);
  font-size: 1.15rem;
}

.stream-block {
  margin: 1.25rem 0;
}

/* Editor-authored rich text. This class was referenced by the templates but
   never defined, so everything written in the CMS rendered with raw browser
   defaults inside an otherwise designed page. */
.rich-text {
  font-size: 1.05rem;
  overflow-wrap: anywhere;
}

.rich-text > * + * {
  margin-top: 1rem;
}

.rich-text h2,
.rich-text h3,
.rich-text h4 {
  font-family: var(--font-display);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin-top: 2rem;
  margin-bottom: 0.5rem;
  color: var(--ink);
}

.rich-text h2 {
  font-size: 1.9rem;
}

.rich-text h3 {
  font-size: 1.5rem;
}

.rich-text h4 {
  font-size: 1.25rem;
}

.rich-text ul,
.rich-text ol {
  padding-left: 1.4rem;
}

.rich-text li + li {
  margin-top: 0.35rem;
}

.rich-text a {
  color: var(--purple);
  font-weight: 600;
}

.rich-text blockquote {
  margin: 1.5rem 0;
  padding-left: 1.1rem;
  border-left: 4px solid var(--gold);
  font-size: 1.15rem;
  color: var(--ink-soft);
}

.rich-text hr {
  border: 0;
  border-top: 2px solid var(--line);
  margin: 2rem 0;
}

/* ------------------------------------------------------------------ tables */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

.data-table th,
.data-table td {
  text-align: left;
  padding: 0.65rem 0.75rem;
  border-bottom: 1px solid var(--line);
}

.data-table th {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}

/* Wide tables scroll inside their own box rather than pushing the page
   sideways on a phone. */
.table-scroll {
  overflow-x: auto;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .nav-toggle {
    display: block;
  }

  /* Inside the disclosure the links stack full width with real tap targets,
     instead of wrapping into a dense pile of text. */
  .nav-toggle .nav-links {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--ink);
    border-bottom: 3px solid var(--gold);
    padding: 0.5rem 1.25rem 1rem;
  }

  .nav-toggle .nav-links a {
    padding: 0.85rem 0.2rem;
    font-size: 0.95rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }

  .site-header {
    position: relative;
  }

  .nav-inner > .nav-links {
    display: none;
  }

  /* Brand + Menu + Portal + Sign out do not fit on a 390px screen, and the
     nav was not allowed to wrap — so the header pushed the document to
     ~500px and every page scrolled sideways. Horizontal scroll on a phone is
     a mobile-usability failure in Search Console and a landing-page
     experience hit in Ads, not just an eyesore. */
  .nav-inner {
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
  }

  .brand {
    font-size: 1.4rem;
  }

  .nav-cta .btn {
    min-height: 44px;
  }
}

@media (max-width: 900px) {
  .portal-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 760px) {
  .footer-inner {
    grid-template-columns: 1fr;
  }

  /* The roll-call control spans the row on a phone rather than being squeezed
     into the third grid column at four characters wide. */
  .rollcall-choice {
    grid-column: 1 / -1;
  }

  /* An absolutely-positioned 12rem panel anchored to a disclosure sitting
     near the right edge runs off a 390px screen and scrolls the page
     sideways. On a phone it drops into flow on its own line instead. */
  .portal-nav-more[open] {
    width: 100%;
  }

  .portal-nav-group {
    position: static;
    min-width: 0;
    margin-top: 0.25rem;
  }

  .rollcall-choice label {
    flex: 1;
  }

  .event-row {
    grid-template-columns: 4.5rem 1fr;
  }

  /* .btn-sm exists to be compact, which is right for a mouse and wrong for a
     thumb — and on a phone these are the coach's actual controls (Roll call,
     RSVPs, Volunteers, Edit, Cancel). Raised only at this width so desktop
     keeps the dense row. */
  .btn-sm {
    min-height: 44px;
  }

  /* Whatever the third child is, not just the RSVP group: .scoreline is
     `nowrap` at ~115px and the coach row's five-button cluster is wider still,
     so auto-placing either into the 4.5rem date column scrolled the page
     sideways. */
  .event-row > :nth-child(3) {
    grid-column: 1 / -1;
    justify-self: start;
  }

  .hero {
    min-height: min(70vh, 560px);
  }
}

/* Respect the OS-level request for less motion: the hero animation, the
   smooth scroll and every hover transform are decorative. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .btn:hover,
  .media-tile:hover {
    transform: none;
  }
}

/* ------------------------------------------------------------ announcements */

/* Notices from a coach or the committee, on the dashboard and on their own
   page. The level drives a left rule and the eyebrow only — never the body
   text, which has to keep its contrast whatever the notice is about. */
.announcement-list {
  display: grid;
  gap: 0.9rem;
  margin-bottom: 1.5rem;
}

.announcement {
  background: var(--paper-alt);
  border-left: 4px solid var(--purple);
  padding: 0.9rem 1.1rem;
}

.announcement-important {
  border-left-color: var(--gold-deep);
}

.announcement-urgent {
  border-left-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 6%, var(--paper-alt));
}

.announcement-urgent .eyebrow {
  color: var(--danger);
}

.announcement-important .eyebrow {
  color: var(--gold-deep);
}

.announcement-title {
  font-family: var(--font-display);
  font-size: 1.45rem;
  letter-spacing: 0.01em;
  margin: 0 0 0.35rem;
}

.announcement-body > :first-child {
  margin-top: 0;
}

.announcement-body > :last-child {
  margin-bottom: 0;
}

.announcement-audience {
  font-size: 0.82rem;
  margin: 0.55rem 0 0;
}

/* On the dashboard the notices sit above a dense two-column grid, so they are
   trimmed to a quieter weight than on their own page. */
.announcement-list-compact .announcement-title {
  font-size: 1.2rem;
}

.announcement-more {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* -------------------------------------------------------------- pagination */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.25rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
  font-size: 0.9rem;
}

/* Newer/Older are the only way to page schedule, RSVP history and attendance
   on a phone, and as bare text they were ~20px tall. */
.pager-link {
  font-weight: 700;
  white-space: nowrap;
  padding: 0.65rem 0.5rem;
}

/* Kept in the flow rather than removed at either end of the range, so the
   status text stays centred and the two live links do not jump sideways
   between pages. */
.pager-link.is-disabled {
  color: var(--muted);
  opacity: 0.5;
}

.pager-status {
  text-align: center;
}

/* ------------------------------------------------- post-registration welcome */

/* The one page in the sign-up flow that a new member sees before the portal.
   Full purple wash and a gold rule — it is a moment worth marking, and it is
   also the landing page an ad click ends on, so it has to look like the club
   it just sold them. */
.welcome-card {
  background: linear-gradient(135deg, var(--purple-deep), var(--ink) 72%);
  color: var(--white);
  border-bottom: 4px solid var(--gold);
  padding: clamp(1.75rem, 5vw, 3rem);
  text-align: center;
}

.welcome-title {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 8vw, 4.5rem);
  line-height: 0.95;
  margin: 0 0 0.75rem;
}

.welcome-lead {
  max-width: 46ch;
  margin: 0 auto 1.5rem;
  font-size: 1.05rem;
}

/* Numbered, because these are in order — join a group, then answer an RSVP,
   then pay. An unordered list would say they are interchangeable. */
.welcome-steps {
  list-style: none;
  counter-reset: none;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.welcome-steps > li {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  background: var(--paper-alt);
  border-top: 3px solid var(--gold);
  padding: 1.1rem 1.2rem;
}

.welcome-steps h2 {
  font-family: var(--font-display);
  font-size: 1.35rem;
  margin: 0 0 0.35rem;
}

.welcome-steps p {
  margin: 0 0 0.6rem;
  font-size: 0.92rem;
}

.welcome-step-number {
  font-family: var(--font-display);
  font-size: 2.2rem;
  line-height: 1;
  color: var(--purple);
}
