/* ============================================================
   ECM Design Tokens & Base Utilities
   WSS School — warm dark, dense, accessible
   ============================================================ */

/* ----------------------------------------------------------
   0. Alpine.js x-cloak (hide before init)
   ---------------------------------------------------------- */
[x-cloak] {
  display: none !important;
}

/* Size utilities that the compiled Tailwind build does not contain.
   The build is content-scanned, so a class is only present if it is spelled
   literally somewhere in templates/. Anything sized in markup that the scan
   misses is a no-op and falls back to the element's intrinsic size. The worst
   case was the nav chevron: `w-3.5 h-3.5` missing meant a 14px icon rendered
   at 71px, which stretched the header row to 113px and pushed the "My Clubs"
   trigger off the top of the page.

   Re-audited against static/css/tailwind.css (447 classes): w-3/h-3, w-3.5/h-3.5,
   w-6/h-6, w-7/h-7, w-10/h-10, w-16/h-16, mt-0.5 and ml-0.5 ARE all in the build
   now and are kept below only as a fallback for pages that ship an older
   cached tailwind.css. `-mt-0.5` (the "My Clubs" chevron nudge, _nav.html:28) is
   genuinely still missing, which is why only that one is declared here. */
.w-3  { width: 0.75rem; }
.h-3  { height: 0.75rem; }
.w-3\.5  { width: 0.875rem; }
.h-3\.5  { height: 0.875rem; }
.w-6  { width: 1.5rem; }
.h-6  { height: 1.5rem; }
.w-10 { width: 2.5rem; }
.h-10 { height: 2.5rem; }
.w-16 { width: 4rem; }
.h-16 { height: 4rem; }
.-mt-0\.5 { margin-top: -0.125rem; }

/* Visually hidden but available to screen readers.
   Tailwind's own .sr-only is NOT in the compiled build (the compiled
   tailwind.css only contains the utilities the old scan picked up, and
   pages without the Play CDN get nothing), so it is defined here
   instead. Without it, `class="sr-only"` renders as visible text. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* ----------------------------------------------------------
   1. Custom Properties
   ---------------------------------------------------------- */
:root {
  /* Colour palette — warm dark (stone-900 base) */
  --color-primary: #F97316;        /* orange-500, brightened for dark bg */
  --color-primary-hover: #FB923C;  /* orange-400 */
  --color-primary-light: rgba(249, 115, 22, 0.15);
  --color-secondary: #5EEAD4;      /* teal-300 */
  --color-secondary-hover: #99F6E4;
  --color-secondary-light: rgba(94, 234, 212, 0.15);
  --color-accent: #FBBF24;         /* amber-400 */
  --color-accent-light: rgba(251, 191, 36, 0.15);

  --color-bg: #1C1917;             /* stone-900 */
  --color-bg-alt: #292524;         /* stone-800 */
  --color-text: #F5F5F4;           /* stone-100 */
  --color-text-muted: #A8A29E;     /* stone-400 */
  --color-border: rgba(255, 255, 255, 0.10);

  --color-success: #4ADE80;
  --color-success-light: rgba(74, 222, 128, 0.15);
  --color-danger: #F87171;
  --color-danger-light: rgba(248, 113, 113, 0.15);

  /* Palette V3 — semantic aliases */
  --elevated: #35302B;
  --brand: #FBBF24;
  --brand2: #FB923C;
  --teal: #5EEAD4;
  --boys: #60A5FA;
  --girls: #F472B6;
  --success: #4ADE80;
  --danger: #F87171;

  /* Gradients */
  --gradient-warm: linear-gradient(135deg, #292524 0%, #1C1917 50%, #292524 100%);

  /* Typography */
  --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;

  --leading-tight: 1.25;
  --leading-normal: 1.5;

  /* Spacing & radii */
  --radius-sm: 0.375rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;
  --radius-full: 9999px;

  /* Shadows — deeper for dark bg */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.45), 0 2px 4px rgba(0, 0, 0, 0.35);

  /* Transitions */
  --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Focus ring — sharp 2px amber */
  --focus-ring: 0 0 0 3px rgba(251, 191, 36, 0.35);
}

/* ----------------------------------------------------------
   2. Base Typography & Reset Additions
   ---------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background-color: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  tab-size: 4;
}

body {
  min-height: 100vh;
  margin: 0;
  background-color: var(--color-bg);
  /* Subtle warm radial glow (top) + dark dot texture */
  background-image:
    radial-gradient(ellipse 90% 45% at 50% -5%, rgba(249, 115, 22, 0.07), transparent 70%),
    radial-gradient(circle, rgba(255, 255, 255, 0.035) 1px, transparent 1px);
  background-size: auto, 24px 24px;
  background-attachment: fixed;
}

img,
svg {
  display: block;
  max-width: 100%;
}

/* Long club and event names are single unbreakable tokens, and every heading
   sits inside a flex/grid track, so the token set the track's min-content
   width and pushed the page into horizontal scroll — measured 20-157px of
   overflow on /clubs/, /clubs/<pk>/, /events/<pk>/, the ECM staff page and
   the contact page. `anywhere` (not `break-word`) is deliberate: only
   `anywhere` also lowers the min-content contribution, which is what actually
   lets the grid track shrink. */
h1,
h2,
h3,
h4 {
  overflow-wrap: anywhere;
}

/* ----------------------------------------------------------
   3. Card
   ---------------------------------------------------------- */
.card {
  background-color: var(--color-bg-alt); /* stone-800 */
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.5rem;
  transition: box-shadow var(--transition-normal);
}

.card:hover {
  box-shadow: var(--shadow-md);
}

/* ----------------------------------------------------------
   4. Buttons
   ---------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: var(--leading-tight);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  background-color: rgba(255, 255, 255, 0.06);
  color: var(--color-text);
  transition: background-color var(--transition-fast),
    color var(--transition-fast),
    box-shadow var(--transition-fast);
}

.btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Primary — amber CTA (v3) */
.btn-primary {
  background-color: var(--brand);
  color: #1C1917;
  border-color: var(--brand);
}

.btn-primary:hover:not(:disabled) {
  background-color: #FCD34D;
  border-color: #FCD34D;
}

/* Secondary */
.btn-secondary {
  background-color: var(--color-secondary);
  color: #1C1917;
  border-color: var(--color-secondary);
}

.btn-secondary:hover:not(:disabled) {
  background-color: var(--color-secondary-hover);
  border-color: var(--color-secondary-hover);
}

/* Danger */
.btn-danger {
  background-color: #EF4444;
  color: #fff;
  border-color: #EF4444;
}

.btn-danger:hover:not(:disabled) {
  background-color: #DC2626;
  border-color: #DC2626;
}

/* Ghost: a low-emphasis action that still needs to read as a control.
   Used for reversible toggles (hide/show) sitting next to a committing action
   (approve/reject), so the two are not weighted the same. */
.btn-ghost {
  background-color: transparent;
  color: #475569;
  border-color: #CBD5E1;
}

.btn-ghost:hover:not(:disabled) {
  background-color: #F1F5F9;
  color: #0F172A;
  border-color: #94A3B8;
}

/* The "Join" CTA on every event card, and the identical button that
   _event_modal_js.html rebuilds inside the details modal. The markup still
   carries the pre-teal Tailwind soup -- bg-indigo-600 / hover:bg-indigo-700 /
   active:bg-indigo-800 / shadow-indigo-500/25 / rounded-xl (12px) -- and
   light.css:3 states the palette has no purple. Those are all @layer
   utilities, so this unlayered rule outranks them and the button becomes the
   same teal CTA as .btn-primary (white on #0F766E measures 5.50:1).
   Only background/colour/radius/shadow are restyled: adding a border would
   grow the button's box by 2px, because the markup sets no border.
   .btn-event's other half is the modal's "Joined" state, which reuses the
   class with a bg-emerald-100 branch; excluding that branch here stops the
   two states from being flattened into one. */
.btn-event:not(.bg-emerald-100) {
  background-color: #0F766E;
  color: #fff;
  border-radius: 0.375rem;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.16);
}

.btn-event:not(.bg-emerald-100):hover:not(:disabled) {
  background-color: #115E59;
}

.btn-event:not(.bg-emerald-100):active:not(:disabled) {
  background-color: #0B3F38;
  box-shadow: none;
}

.btn-event.bg-emerald-100 {
  background-color: #D1FAE5;
  color: #065F46;
  border-radius: 0.375rem;
}

.btn-event.bg-emerald-100:hover:not(:disabled) {
  background-color: #BBF7D0;
}

/* Signed-in user's name in the top bar. It had no width budget at all, so a
   long username shoved the chevron and the whole topbar past the viewport.
   min-width:0 is what actually lets it shrink as a flex item. */
.nav-username {
  max-width: 9rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A4 poster hit area on the event card (opens the lightbox). The div carries
   an onclick and the markup only says cursor-pointer, which misdescribes a
   zoom affordance. */
.event-poster {
  cursor: zoom-in;
}

/* The card's club-name / audience-pill row. Below ~420px the two no longer
   fit side by side and the name lost more than half its width to a single
   8-character pill: measured at 320px on /clubs/34/ the name box was 137.2px
   against a 191px scrollWidth, i.e. 54px of "ROBOTICS SOCIETY" replaced by an
   ellipsis, because the pill is `shrink-0` and the name carried
   `max-w-[60%]` on top of that. Wrapping is the fix that does not need a
   shorter club name: the pill drops to its own line and the name gets the
   full row. 820px and up is unchanged -- there the row already fits on one
   line, measured 495.6px of name inside a 696px row. */
@media (max-width: 420px) {
  .event-card-toprow {
    flex-wrap: wrap;
  }
}

/* ----------------------------------------------------------
   5. Chip / Badge / Pill -- ONE base, five historical names
   ---------------------------------------------------------- */
/* The same pill was implemented six times (.chip, .badge, .status-pill,
   .d-chip, .seat-open-badge and profile.html's .tag) across 2 radii, 5
   paddings, 3 sizes and 2 weights. This block is the single base; the other
   names are aliases so every existing class="..." keeps working and the
   per-template copies can be deleted. Radius is 6px for all of them (house
   rule: 6px buttons and chips), which is also what light.css already pinned
   for .chip. */
.chip,
.badge,
.d-chip,
.status-pill,
.seat-open-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.625rem;
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1;
  border-radius: 0.375rem;
  white-space: nowrap;
  vertical-align: middle;
}

/* Borders are per-variant, not on the shared base: .badge and .d-chip were
   previously borderless, and forcing a 1px transparent border onto them would
   grow every one by 2px in both directions. */
.chip {
  background: rgba(255, 255, 255, 0.05); /* white/5 */
  border: 1px solid rgba(255, 255, 255, 0.10); /* white/10 */
  color: var(--color-text-muted);
}

/* .badge keeps no fill and no border of its own: it is a slot for a
   .badge-* modifier, and call sites pair it with an inline colour or with
   .seat-open-badge. */
.badge {
  background: transparent;
  border: 0;
}

.badge-success {
  background-color: var(--color-success-light);
  color: var(--color-success);
}

.badge-danger {
  background-color: var(--color-danger-light);
  color: var(--color-danger);
}

.badge-primary {
  background-color: var(--color-primary-light);
  color: #FB923C;
}

.badge-secondary {
  background-color: var(--color-secondary-light);
  color: var(--color-secondary);
}

/* Dashboard table pills. -orange/-amber/-stone already lived here; -teal/-red/
   -green were only defined inside three templates' own <style> blocks, so
   anywhere else the pill had no fill and inherited its colour. */
.d-chip {
  background: rgba(255, 255, 255, 0.06);
  border: 0;
  color: var(--color-text-muted);
}

.d-chip-teal { background: rgba(94, 234, 212, 0.12); color: #5EEAD4; }
.d-chip-red { background: rgba(248, 113, 113, 0.12); color: #F87171; }
.d-chip-green { background: rgba(34, 197, 94, 0.14); color: #86EFAC; }
.d-chip-orange { background: rgba(251, 146, 60, 0.12); color: #FB923C; }
.d-chip-amber { background: rgba(251, 191, 36, 0.12); color: #FBBF24; }
.d-chip-stone { background: rgba(168, 162, 158, 0.12); color: #A8A29E; }

/* ECM dashboard / media review segmented filter. It is an interactive control,
   so it is a step larger than a read-only pill and carries its own states. */
.status-pill {
  padding: 0.375rem 0.875rem;
  font-size: 0.8125rem;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--color-text-muted);
  cursor: pointer;
  text-decoration: none;
}

.status-pill:hover {
  color: var(--color-text);
  border-color: rgba(255, 255, 255, 0.20);
  text-decoration: none;
}

.status-pill.active {
  background: var(--brand);
  border-color: var(--brand);
  color: #1C1917;
}

.status-pill .pill-n {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  opacity: 0.8;
}

/* Seat availability on the club staff page: the quietest pill in the set. */
.seat-open-badge {
  padding: 0.125rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--color-text-muted);
}

.chip-teal {
  background: rgba(94, 234, 212, 0.12);
  color: #5EEAD4;
}

.chip-amber {
  background: rgba(251, 191, 36, 0.12);
  color: #FBBF24;
}

.chip-red {
  background: rgba(248, 113, 113, 0.12);
  color: #F87171;
}

.chip-stone {
  background: rgba(255, 255, 255, 0.06);
  color: #A8A29E;
}

.chip-success {
  background: rgba(74, 222, 128, 0.12);
  color: #4ADE80;
}

.chip-primary {
  background: rgba(249, 115, 22, 0.12);
  color: #FB923C;
}

.chip-danger {
  background: rgba(248, 113, 113, 0.12);
  color: #F87171;
}

.chip-secondary {
  background: rgba(94, 234, 212, 0.12);
  color: #5EEAD4;
}

.chip-green {
  background: rgba(34, 197, 94, 0.14);
  color: #86EFAC;
}

.chip-orange {
  background: rgba(234, 88, 12, 0.14);
  color: #FDBA74;
}

/* ----------------------------------------------------------
   6. Focus-visible (global accessibility) — amber rings
   ---------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

button:focus:not(:focus-visible),
a:focus:not(:focus-visible),
input:focus:not(:focus-visible) {
  outline: none;
}

/* ----------------------------------------------------------
   7. Selection colour
   ---------------------------------------------------------- */
::selection {
  background-color: rgba(251, 191, 36, 0.25);
  color: var(--color-text);
}

/* ----------------------------------------------------------
   9. Skeleton loading states (HTMX hx-indicator)
   ---------------------------------------------------------- */
.skeleton {
  position: relative;
  overflow: hidden;
  background-color: #292524;
  border-radius: var(--radius-md);
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.08),
    transparent
  );
  animation: skeleton-shimmer 1.5s infinite;
}

@keyframes skeleton-shimmer {
  100% {
    transform: translateX(100%);
  }
}

.htmx-request .skeleton {
  display: block;
}

/* htmx ships no stylesheet, so an element carrying `htmx-indicator` has no
   opinion about its own visibility and simply sits there at rest — which is
   why the timetable drew a permanent grey skeleton over every FREE/Busy cell
   and nothing ever removed it. This is the pair from the htmx docs: hidden at
   rest, shown while the element (or an ancestor) is in the request state.
   `.htmx-request` is applied by htmx at runtime, so it never appears in a
   template -- same reason the two rules further down survive. */
.htmx-indicator {
  opacity: 0;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* ----------------------------------------------------------
   10. Motion utilities & HTMX states (v1 — kept intact)
   Palette: primary #F97316, secondary #5EEAD4, accent #FBBF24,
   bg #1C1917, text #F5F5F4 (stone-900 dark). No gradients, no emojis.
   ---------------------------------------------------------- */
/* The single definition of the page-load entrance animation. Three templates
   used to carry their own copy: homepage.html (@keyframes fadeUp, 12px/0.4s)
   and club_detail.html + club_list.html (@keyframes fadeUp, 8px/0.4s), each
   re-declaring `.anim-fade-up` too. Because their <style> blocks are injected
   after this file, whichever page you were on decided the duration and the
   travel distance. One definition, one duration, here. */
@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes pop {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes htmx-pulse {
  0%,
  100% {
    opacity: 0.6;
  }
  50% {
    opacity: 0.35;
  }
}

/* Page-load entrance: fade + 12px rise, .4s ease-out backwards */
.anim-fade-up {
  animation: fade-up 0.4s ease-out backwards;
}

/* Simple fade-in, .4s */
.anim-fade-in {
  animation: fade-in 0.4s ease-out backwards;
}

/* Small pop: scale .96 -> 1 + fade, .25s */
.anim-pop {
  animation: pop 0.25s ease-out backwards;
}

/* Gentle opacity pulse (live indicators, attention cues) */
.pulse-soft {
  animation: htmx-pulse 2s ease-in-out infinite;
}

/* Staggered children: each runs fade-up with incremental delay */
.stagger > * {
  animation: fade-up 0.4s ease-out backwards;
}

.stagger > *:nth-child(1) { animation-delay: 0ms; }
.stagger > *:nth-child(2) { animation-delay: 60ms; }
.stagger > *:nth-child(3) { animation-delay: 120ms; }
.stagger > *:nth-child(4) { animation-delay: 180ms; }
.stagger > *:nth-child(5) { animation-delay: 240ms; }
.stagger > *:nth-child(6) { animation-delay: 300ms; }
.stagger > *:nth-child(7) { animation-delay: 360ms; }
.stagger > *:nth-child(8) { animation-delay: 420ms; }
.stagger > *:nth-child(9) { animation-delay: 480ms; }
.stagger > *:nth-child(10) { animation-delay: 540ms; }
.stagger > *:nth-child(11) { animation-delay: 600ms; }
.stagger > *:nth-child(12) { animation-delay: 660ms; }

/* Card hover: lift 3px + shadow-lg */
.hover-lift {
  transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}

.hover-lift:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}

/* Button press feedback */
.btn-press:active {
  transform: scale(0.95);
}

/* HTMX request state: dim + pulse on the triggering element */
.htmx-request {
  opacity: 0.6;
  animation: htmx-pulse 1s ease-in-out infinite;
}

/* HTMX swap state: fade */
.htmx-swapping {
  opacity: 0;
  transition: opacity 0.2s ease-out;
}

/* Focus rings in amber: see §6 (:focus-visible). */

/* Respect users who prefer reduced motion: kill all animation */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* ============================================================
   11. Rich Desktop System — v2 (dark)
   Palette: #F97316 / #5EEAD4 / #FBBF24 / #1C1917 / #F5F5F4
   ============================================================ */

/* ----------------------------------------------------------
   11.1  Container
   ---------------------------------------------------------- */
.container-page {
  max-width: 80rem;
  margin-inline: auto;
  padding-inline: 1.5rem;
}

/* ----------------------------------------------------------
   11.3  Stat band — 4-up KPI grid (desktop)
   ---------------------------------------------------------- */
.stat-band {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 768px) {
  .stat-band {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ----------------------------------------------------------
   11.4  KPI number
   ---------------------------------------------------------- */
.kpi-num {
  font-size: 2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-tight);
}

/* ----------------------------------------------------------
   11.6  Aside card
   ---------------------------------------------------------- */
.aside-card {
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  box-shadow: var(--shadow-sm);
}

.aside-card + .aside-card {
  margin-top: 0.75rem;
}

/* ----------------------------------------------------------
   11.7  Table — rich, dense data table
   ---------------------------------------------------------- */
.table-rich {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.table-rich thead {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--color-bg-alt); /* stone-800 */
}

.table-rich th {
  text-align: left;
  padding: 0.625rem 0.75rem;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted); /* stone-400 */
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.table-rich td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--color-border);
}

/* Zebra striping — white/[0.02] */
.table-rich tbody tr:nth-child(even) {
  background-color: rgba(255, 255, 255, 0.02);
}

/* Hover highlight — white/5 */
.table-rich tbody tr:hover {
  background-color: rgba(255, 255, 255, 0.05);
}

/* Right-align numeric columns */
.table-rich .num,
.table-rich td.num,
.table-rich th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------
   11.12  Tab indicator — animated underline
   ---------------------------------------------------------- */
.tab-indicator {
  position: relative;
  display: inline-block;
  padding-bottom: 0.375rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tab-indicator:hover {
  color: var(--color-text);
}

.tab-indicator.is-active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

/* ----------------------------------------------------------
   11.13  Cover banner — rich header band (dark)
   ---------------------------------------------------------- */
.cover-banner {
  position: relative;
  padding: 3rem 2rem 2.5rem;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, #1C1917 0%, #292524 60%, #44403C 100%);
  color: #FAFAF9;
  overflow: hidden;
}

.cover-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at 80% 20%,
    rgba(251, 191, 36, 0.18) 0%,
    transparent 60%
  );
  pointer-events: none;
}

.cover-banner > * {
  position: relative;
  z-index: 1;
}

/* ----------------------------------------------------------
   11.14  Info card — label/value rows
   ---------------------------------------------------------- */
.info-card {
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  box-shadow: var(--shadow-sm);
}

.info-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0.5rem 0;
  gap: 1rem;
}

.info-row + .info-row {
  border-top: 1px solid var(--color-border);
}

.info-row .label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text-muted);
}

.info-row .value {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
  text-align: right;
}

/* ----------------------------------------------------------
   11.15  Avatar — initial tiles (sm / md / lg)
   ---------------------------------------------------------- */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: #1C1917;
  font-weight: 600;
  line-height: 1;
  user-select: none;
  flex-shrink: 0;
}

.avatar-sm {
  width: 2rem;
  height: 2rem;
  font-size: var(--text-xs);
}

/* ----------------------------------------------------------
   11.16  Empty state — centered, generous padding
   ---------------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 4rem 2rem;
  color: var(--color-text-muted);
}

.empty-state .empty-icon {
  margin-bottom: 1rem;
  opacity: 0.45;
}

.empty-state .empty-title {
  font-size: var(--text-lg);
  font-weight: 600;
  /* --color-text is the DARK theme's ink (#F5F5F4). At (0,2,0) this out-specified
     light.css's `body .empty-title` at (0,1,1), so on the light pages the title
     rendered #F5F5F4 on #F1F5F9 -- 1.00:1, i.e. invisible. --ink is the light
     theme's token and resolves correctly in both themes via light.css. */
  color: var(--ink);
  margin-bottom: 0.375rem;
}

.empty-state .empty-desc {
  font-size: var(--text-sm);
  max-width: 24rem;
  line-height: var(--leading-normal);
}

/* ============================================================
   14. Dark chrome — remap light Tailwind utilities used by
   shared templates (nav / sidebar / footer / page cards).
   custom.css loads after tailwind.css, so these win on equal
   specificity while still losing to inline styles.
   ============================================================ */

/* Surfaces
   .bg-stone-200 and .border-stone-100 / .text-stone-800 were removed here:
   the compiled build scans templates/ and no template has used any of the
   three for months, so both layers were carrying a second definition that
   nothing could reach. Add them back (and to light.css) if a page grows a
   use. */
.bg-white { background-color: #292524; }        /* stone-800 */
.bg-stone-50 { background-color: #1C1917; }     /* stone-900 */
.bg-stone-100 { background-color: #292524; }    /* stone-800 */

/* Text */
.text-stone-300 { color: #D6D3D1; }
.text-stone-400 { color: #A8A29E; }
.text-stone-500 { color: #A8A29E; }
.text-stone-600 { color: #A8A29E; }
.text-stone-700 { color: #E7E5E4; }
.text-stone-900 { color: #F5F5F4; }

/* Borders */
.border-stone-200 { border-color: rgba(255, 255, 255, 0.10); }
.border-stone-300 { border-color: rgba(255, 255, 255, 0.12); }

/* Amber accents (staff/club-head sections).
   .text-amber-600/700/800 have no reference in any template; only the
   bg-* washes and .hover\:bg-amber-50 are in use. */
.bg-amber-50 { background-color: rgba(251, 191, 36, 0.12); }
.bg-amber-100 { background-color: rgba(251, 191, 36, 0.18); }

.hover\:bg-amber-50:hover { background-color: rgba(251, 191, 36, 0.10); }

/* ============================================================
   15. Toast layer — dark surfaces (inline styles need !important)
   ============================================================ */
[aria-live="polite"] [role="status"] {
  background-color: #292524 !important;
  color: #F5F5F4 !important;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45), 0 2px 4px rgba(0, 0, 0, 0.35) !important;
}

[aria-live="polite"] [role="status"] button {
  color: #A8A29E !important;
}

/* ============================================================
   16. globals.css dark overrides — globals loads after custom.css,
   so these use higher-specificity selectors (body-prefixed) to win
   on element rules while still losing to Tailwind utilities.
   ============================================================ */

/* Headings (globals sets #1C1917) */
body h1,
body h2,
body h3,
body h4 {
  color: var(--color-text);
}

/* Links (globals sets #C2410C) */
body a {
  color: var(--color-primary);
}

/* Inputs (globals sets white bg / stone-300 border) */
body input[type="text"],
body input[type="email"],
body input[type="password"],
body input[type="number"],
body input[type="url"],
body input[type="date"],
body input[type="search"],
body textarea,
body select {
  background-color: #1C1917; /* stone-900 */
  border-color: rgba(255, 255, 255, 0.10);
  color: #F5F5F4;
}

body input[type="text"]:focus,
body input[type="email"]:focus,
body input[type="password"]:focus,
body input[type="number"]:focus,
body input[type="url"]:focus,
body input[type="date"]:focus,
body input[type="search"]:focus,
body textarea:focus,
body select:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.2);
}

/* Plain tables (globals sets light header / hover) */
body thead th {
  background-color: #292524;
  color: var(--color-text-muted);
}

body tbody tr:hover {
  background-color: rgba(255, 255, 255, 0.05);
}

/* Scrollbars (globals sets light track/thumb) */
::-webkit-scrollbar-track {
  background: #1C1917 !important;
}

::-webkit-scrollbar-thumb {
  background: #44403C !important;
  border-radius: var(--radius-full);
}

::-webkit-scrollbar-thumb:hover {
  background: #57534E !important;
}

/* Firefox */
* {
  scrollbar-width: thin;
  scrollbar-color: #44403C #1C1917 !important;
}

/* ============================================================
   17. Consolidated shell helpers — extracted from per-template
   <style> blocks that repeat across 3+ templates.
   Follow-up agent strips the per-template duplicates.
   Palette: stone-900 #1C1917, stone-800 #292524,
   stone-100/400 text, orange #F97316, amber #FBBF24, teal #5EEAD4.
   ============================================================ */

/* ----------------------------------------------------------
   17.1  Page shells
   ---------------------------------------------------------- */
.page-shell {
  max-width: 68rem;
  margin-left: auto;
  margin-right: auto;
  padding: 2rem 1.5rem;
}

.page-dark {
  min-height: 100vh;
  background: #1C1917;
  padding: 2.5rem 1.5rem;
}

/* The one owner of the page-level h1.
   h1 carried six sizes, two weights and seventeen class vocabularies across
   the app (page-title, form-title, d-title, err-title, tt-title, media-title,
   respond-title, resp-title, contact-title, builder-title, profile-name,
   login-headline, reg-title, hero-title, and a bare `text-2xl font-bold
   text-stone-100` seven times), and every page shipped its own size in its own
   <style>. This rule is the single source: 24px, weight 600, matching
   light.css's `body h1` (Fraunces / 600 / -0.01em) so the two cannot disagree.
   Templates that still set `.page-title` themselves must drop their copy --
   see the report. */
.page-title {
  font-size: 1.5rem;
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-text);
}

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.25rem;
}

.page-sub {
  font-size: 0.875rem;
  color: #A8A29E;
  margin-bottom: 1.5rem;
}

/* ----------------------------------------------------------
   17.2  Eyebrow / label helpers
   ---------------------------------------------------------- */
.eyebrow {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #FBBF24;
  margin-bottom: 0.25rem;
}

.d-eyebrow {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #FB923C;
}

/* ----------------------------------------------------------
   17.3  Typography helpers
   ---------------------------------------------------------- */
.text-strong { color: #F5F5F4; }
.text-muted  { color: #A8A29E; }
.text-faint  { color: #78716C; }
.text-orange { color: #FB923C; }
.text-dark   { color: #1C1917; }

.d-title { font-size: 1.5rem; font-weight: 700; color: #F5F5F4; letter-spacing: -0.02em; }
.d-sub   { font-size: 0.875rem; color: #A8A29E; }
.d-label { font-size: 0.875rem; font-weight: 500; color: #F5F5F4; }
.d-muted { font-size: 0.75rem; color: #78716C; }
.d-h2    { font-size: 1.125rem; font-weight: 600; color: #F5F5F4; letter-spacing: -0.01em; }
.d-orange { color: #FB923C; }
.d-amber  { color: #FBBF24; }

/* ----------------------------------------------------------
   17.4  KPI / stat tiles
   ---------------------------------------------------------- */
.kpi-card {
  background: #292524;
  border: 1px solid #44403C;
  border-radius: 0.75rem;
  padding: 1rem 1.25rem;
  border-top: 3px solid #EA580C;
}

.kpi-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: #F5F5F4;
  margin-top: 0.25rem;
}

.kpi-sub {
  font-size: 0.75rem;
  color: #A8A29E;
  margin-top: 0.125rem;
}

.stat-tile {
  background: #292524;
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 0.75rem;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  padding: 1rem 1.25rem;
}

.stat-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: #F5F5F4;
  margin-top: 0.25rem;
}

.stat-sub {
  font-size: 0.75rem;
  color: #A8A29E;
  margin-top: 0.125rem;
}

.d-kpi     { padding: 1.25rem; }
.d-kpi-num { font-size: 2rem; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

/* 17.5 (chip variants) and 11.8 (chip base) both lived here as a second copy
   of the rules now consolidated in section 5. Removed: they were the second
   radius (9999px on .d-chip) and the second padding in the pill family. */

/* ----------------------------------------------------------
   17.6  Cards & containers
   ---------------------------------------------------------- */
.form-card {
  width: 100%;
  max-width: 28rem;
  background: #292524;
  border: 1px solid #44403C;
  border-radius: 0.75rem;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  padding: 2rem;
}

.table-card {
  background: #292524;
  border: 1px solid #44403C;
  border-radius: 0.75rem;
  overflow-x: auto;
}

.d-card {
  background: #292524;
  border: 1px solid #44403C;
  border-radius: 0.75rem;
}

/* ----------------------------------------------------------
   17.7  Forms
   ---------------------------------------------------------- */
.form-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: #F5F5F4;
  letter-spacing: -0.02em;
  margin-bottom: 0.5rem;
}

.form-sub {
  font-size: 0.875rem;
  color: #A8A29E;
  margin-bottom: 1.5rem;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 0.875rem;
}

.form-back {
  margin-top: 1rem;
  text-align: center;
  font-size: 0.875rem;
  color: #A8A29E;
}

.form-foot {
  margin-top: 1.25rem;
  text-align: center;
  font-size: 0.875rem;
  color: #A8A29E;
}

.dark-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.field {
  margin-bottom: 0.875rem;
}

.field-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: #D6D3D1;
  margin-bottom: 0.375rem;
}

.field-input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid #57534E;
  border-radius: 0.5rem;
  background: #1C1917;
  color: #F5F5F4;
  font-size: 0.875rem;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}

.field-input:focus {
  outline: none;
  border-color: #FBBF24;
  box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.2);
}

.check-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
}

/* WCAG 2.2 SC 2.5.8 (Target Size Minimum, AA) wants 24x24 CSS px.
   The club leader contact rows are a stack of inline mailto: links measured at
   129-143 x 19 at 390px, listed ~17px apart -- so the "spacing" exception does
   not rescue them, and they are the only way to contact a club president from
   a phone. inline-block plus block padding gives each a ~24px hit area that
   overlaps its neighbour's, which satisfies the criterion outright.
   Deliberately written here rather than in club_detail.html's inline <style>
   so the two stylesheets cannot disagree about specificity: this is
   (0,1,2) and loads before the page's unlayered (0,1,1), so it wins. */
body .head-detail a[href^="mailto:"] {
  display: inline-block;
  padding-block: 0.2rem;
  min-height: 24px;
}

.check-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  color: #D6D3D1;
  padding: 0.375rem 0.5rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: background-color 150ms ease;
}

.check-label:hover {
  background: rgba(255, 255, 255, 0.05);
}

.submit-btn {
  width: 100%;
  padding: 0.75rem 1rem;
  background: #0F766E;
  color: #fff;
  border: 1px solid #0F766E;
  border-radius: 0.5rem;
  font-weight: 600;
  font-size: 0.9375rem;
  cursor: pointer;
  transition: background-color 150ms ease;
}

.submit-btn:hover {
  background: #0D9488;
}

.btn-block {
  width: 100%;
}

/* ----------------------------------------------------------
   17.8  Dashboard table
   ---------------------------------------------------------- */
.d-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

/* ----------------------------------------------------------
   17.9  Tab button
   ---------------------------------------------------------- */
.tab-btn {
  position: relative;
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  transition: color 0.15s ease;
}

/* ----------------------------------------------------------
   17.10  Message / alert banner
   ---------------------------------------------------------- */
.msg {
  background: #292524;
  border: 1px solid #44403C;
  border-left: 3px solid #22C55E;
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  color: #D6D3D1;
  margin-bottom: 0.75rem;
}

/* ----------------------------------------------------------
   17.11  Empty state (compact)
   ---------------------------------------------------------- */
.empty {
  padding: 3rem 1.5rem;
  text-align: center;
}

/* ----------------------------------------------------------
   17.12  Action link
   ---------------------------------------------------------- */
.action-link {
  font-size: 0.75rem;
  color: #FBBF24;
  background: none;
  border: none;
  cursor: pointer;
  /* WCAG 2.2 SC 2.5.8 (Target Size Minimum, AA) wants 24x24 CSS px. Measured at
     390px: "Acknowledge", "Activate" and "Deactivate" were 77x18 -- and each of
     those is a one-tap WRITE sitting at the far right of a table that needs up
     to 1029px of horizontal panning to reach. Block padding takes the height to
     26px without changing the visual weight. */
  padding: 0.25rem 0;
  font-family: inherit;
  min-height: 24px;
}

/* ----------------------------------------------------------
   17.13  Avatar tile
   ---------------------------------------------------------- */
.avatar-tile {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1rem;
  flex-shrink: 0;
}

/* ============================================================
    19. Palette V3 — extended utilities
    ============================================================ */

/* ----------------------------------------------------------
    19.3  Table scroll + elevated surface
    ---------------------------------------------------------- */
.table-scroll {
  overflow-x: auto;
}

/* ----------------------------------------------------------
    19.4  Live dot — pulsing indicator
    ---------------------------------------------------------- */
@keyframes live-pulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.45;
    transform: scale(0.85);
  }
}

.live-dot {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
  background: #4ADE80;
  animation: live-pulse 1.8s ease-in-out infinite;
}

/* ----------------------------------------------------------
    19.6  Entrance — anim-rise (stagger-ready, v3)
    ---------------------------------------------------------- */
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Stale-compiler polyfills. Re-audited against the current build (447
   classes): all five of these ARE in static/css/tailwind.css now, so they are
   only here to cover a browser holding a cached tailwind.css from before the
   last rebuild. Harmless either way -- these are unlayered, so they only ever
   restate what the build already says. */
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
.px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
.flex-1 { flex: 1 1 0%; }
.min-w-0 { min-width: 0; }
.whitespace-nowrap { white-space: nowrap; }

/* The Details and A4 lightbox overlays carry BOTH `hidden` and `flex` so they
   centre themselves when shown, and the JS toggles visibility with
   classList.remove("hidden") / .add("hidden"). Two display utilities in one
   @layer means the winner is decided by build order, and `flex` was winning:
   the overlays stayed display:flex at opacity 0, covering the viewport and
   swallowing every click on the page below them. (The Play CDN used to mask
   this by injecting its utilities unlayered, at higher priority.)

   Unlayered so it beats @layer utilities, and scoped by id so it cannot break
   the legitimate `hidden md:flex` nav row in _nav.html. */
#details-modal.hidden,
#poster-modal.hidden {
  display: none;
}

/* Deliberately unlayered.
   The `body select { width: 100% }` reset below is also unlayered, and
   Tailwind's utilities sit in @layer utilities, so no utility class can
   override it. As a flex item that width becomes the flex-basis, which let
   the "side" picker claim 1182px and squeezed its flex-1 sibling to 26px.
   This keeps the reset for stacked fields while letting flexbox size this
   one. */
.select-fixed { width: 12rem; }

/* Long identifiers (route names, emails, slugs) are unbreakable tokens and
   pushed pages into horizontal scroll on narrow screens. */
code, kbd, samp {
  overflow-wrap: anywhere;
}

/* Overflow guards: pills never escape their boxes.
   The guard was already here but inert: every pill also sets
   `white-space: nowrap`, which wins for wrapping and let a long value push
   ~370px past the box. Truncate instead of overflowing -- audit_log.html
   already does this with .d-truncate. `.status-pill` is deliberately NOT
   listed: it is a filter control, so clipping its label would be worse than
   letting the row wrap. */
.chip,
.tag,
.badge,
.d-chip,
.seat-open-badge {
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}