/* =============================================================================
   Golden Nest Global School — Global Design System
   DAIS-inspired: restrained navy/gold palette, editorial serif display type,
   generous whitespace, soft elevation. Mobile-first throughout.

   Breakpoints used across this file:
     sm: 480px   md: 768px   lg: 1024px   xl: 1280px
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,500;0,9..144,600;0,9..144,700;1,9..144,500&family=Inter:wght@400;500;600;700;800&display=swap");

/* -----------------------------------------------------------------------
   1. Design Tokens
   ----------------------------------------------------------------------- */
:root {
  /* Color — core palette
     --color-primary is the single source of truth for the school's brand
     color. --color-primary-dark/-light are auto-derived from it via
     color-mix() so changing this one value re-themes hover/dark states
     everywhere without touching any other rule. */
  --color-primary: #743089;
  --color-primary-dark: color-mix(in srgb, var(--color-primary) 80%, black);
  --color-primary-light: color-mix(in srgb, var(--color-primary) 75%, white);
  --color-accent: #c89b3c;
  /* Derived from --color-accent (same pattern as --color-primary above) so
     changing THEME.colors.accent in config.json re-themes these too. The
     70% mix is deliberate: it keeps --color-accent-dark at >=4.5:1 against
     white, which .eyebrow text needs to meet WCAG AA. */
  --color-accent-dark: color-mix(in srgb, var(--color-accent) 70%, black);
  --color-accent-light: color-mix(in srgb, var(--color-accent) 55%, white);
  --color-secondary: #7a1f2b;
  /* Text sitting ON the gold accent (buttons, ticker label). The old
     --color-primary-dark measured 4.17:1 there — just under AA. */
  --color-on-accent: #12060f;

  /* Color — surfaces & text (dark cinematic)
     The whole site runs dark now. --color-heading exists because headings
     used to be brand-purple, which is unreadable on a near-black canvas. */
  --color-bg: #1a1028;
  --color-bg-alt: #241834;
  --color-surface: rgba(255, 255, 255, 0.045);
  --color-surface-solid: #180f1f;
  --color-border: rgba(255, 255, 255, 0.12);
  --color-heading: #ffffff;
  --color-text: #efeaf3;
  --color-text-muted: #a99db4;
  --color-text-inverse: #ffffff;
  --color-text-inverse-muted: rgba(255, 255, 255, 0.72);

  /* Color — semantic */
  --color-success: #2e7d4f;
  --color-error: #b3261e;
  --color-warning: #b7791f;

  /* Color — event category accents.
     Lightened for the dark canvas: the original deep maroon/navy/forest
     versions measured under 2:1 against a near-black background. */
  --color-cultural: #f08fa4;
  --color-sports: #7cc4ec;
  --color-academic: #7fd3a3;
  --color-excursion: #e8c877;

  /* Typography */
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: clamp(1.5rem, 1.35rem + 0.75vw, 1.875rem);
  --text-3xl: clamp(1.875rem, 1.6rem + 1.4vw, 2.5rem);
  --text-4xl: clamp(2.25rem, 1.85rem + 2vw, 3.25rem);
  --text-5xl: clamp(2.75rem, 2.1rem + 3vw, 4.25rem);

  --leading-tight: 1.15;
  --leading-snug: 1.35;
  --leading-normal: 1.6;

  /* Spacing (4px base) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-8: 3rem;
  --space-10: 4rem;
  --space-12: 5rem;
  --space-16: 7rem;

  /* Radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
  --radius-full: 999px;

  /* Elevation */
  /* Deeper and near-black — light-tinted shadows go muddy on a dark canvas. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 6px 20px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.5);
  --shadow-xl: 0 28px 70px rgba(0, 0, 0, 0.6);

  /* Motion */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 400ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Layering */
  --z-header: 100;
  --z-dropdown: 200;
  --z-overlay: 900;
  --z-modal: 1000;
  /* Above the lightbox and the fixed header, so nothing can overlap it */
  --z-popup: 1100;
  /* Above the notice popup too: while the page is still assembling, the
     pre-loader is the only thing that should be visible. */
  --z-preloader: 1200;

  /* Layout */
  --container-max: 1200px;
  --container-max-narrow: 800px;
  --container-padding: 1.25rem;
  --header-height: 76px;

  /* Real rendered height of the fixed header (navbar + announcement ticker),
     written by syncHeaderHeight() in main.js on load and on every resize.
     The --header-height fallback only applies for the instant before JS
     runs. Anything that must clear the fixed header — anchor jumps, the
     mobile off-canvas menu — measures against this, never a hardcoded
     value, because the ticker's height changes with viewport width. */
  --header-offset: var(--header-height);
}

/* Anchor targets (nav dropdown links, "Learn More" buttons, the skip link,
   and the events page's scrollIntoView calls) would otherwise land flush at
   y=0, i.e. underneath the fixed header. */
main[id],
section[id],
#eventGrid,
#eventGalleryPanel {
  scroll-margin-top: calc(var(--header-offset) + var(--space-4));
}

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

* {
  margin: 0;
}

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

body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  min-height: 100vh;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: none;
}

a {
  color: inherit;
  text-decoration: none;
}

ul,
ol {
  list-style: none;
  padding: 0;
}

table {
  border-collapse: collapse;
  width: 100%;
}

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

@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;
  }
}

.skip-link {
  position: absolute;
  top: -48px;
  left: var(--space-4);
  z-index: var(--z-modal);
  background: var(--color-primary);
  color: var(--color-text-inverse);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-sm);
  transition: top var(--transition-fast);
}

.skip-link:focus {
  top: var(--space-4);
}

/* -----------------------------------------------------------------------
   3. Typography
   ----------------------------------------------------------------------- */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--leading-tight);
  color: var(--color-heading);
  letter-spacing: -0.01em;
}

h1 {
  font-size: var(--text-5xl);
}
h2 {
  font-size: var(--text-4xl);
}
h3 {
  font-size: var(--text-3xl);
}
h4 {
  font-size: var(--text-2xl);
}
h5 {
  font-size: var(--text-xl);
  font-weight: 700;
}
h6 {
  font-size: var(--text-lg);
  font-weight: 700;
}

p {
  color: var(--color-text);
}

p + p {
  margin-top: var(--space-4);
}

.lead {
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  color: var(--color-text-muted);
}

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

.text-inverse {
  color: var(--color-text-inverse);
}

.text-accent {
  color: var(--color-accent);
}

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

.text-sm {
  font-size: var(--text-sm);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.eyebrow::before {
  content: "";
  width: 24px;
  height: 2px;
  background: var(--color-accent);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* -----------------------------------------------------------------------
   4. Layout Primitives
   ----------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-padding);
}

.container--narrow {
  max-width: var(--container-max-narrow);
}

.section {
  padding-block: var(--space-12);
}

.section--sm {
  padding-block: var(--space-8);
}

.section--alt {
  background: var(--color-bg-alt);
}

.section--primary {
  background: var(--color-primary);
  color: var(--color-text-inverse);
}

.section--primary h1,
.section--primary h2,
.section--primary h3,
.section--primary h4 {
  color: var(--color-text-inverse);
}

.section__header {
  max-width: 640px;
  margin-bottom: var(--space-10);
}

.section__header.text-center {
  margin-inline: auto;
}

.section__header > .eyebrow {
  margin-bottom: var(--space-3);
}

.section__header > h2 {
  margin-bottom: var(--space-4);
}

.grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
  .grid--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .grid--4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.flex {
  display: flex;
}

.flex--center {
  align-items: center;
  justify-content: center;
}

.flex--between {
  align-items: center;
  justify-content: space-between;
}

.flex--wrap {
  flex-wrap: wrap;
}

.flex--gap-sm {
  gap: var(--space-3);
}

.flex--gap-md {
  gap: var(--space-5);
}

/* -----------------------------------------------------------------------
   5. Buttons
   ----------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.75rem 1.5rem;
  font-size: var(--text-base);
  font-weight: 600;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast), transform var(--transition-fast),
    box-shadow var(--transition-fast);
  white-space: nowrap;
}

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

.btn--primary {
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.btn--primary:hover {
  background: var(--color-accent-dark);
  box-shadow: var(--shadow-md);
}

.btn--secondary {
  background: var(--color-primary);
  color: var(--color-text-inverse);
}

.btn--secondary:hover {
  background: var(--color-primary-light);
  box-shadow: var(--shadow-md);
}

.btn--outline {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}

.btn--outline:hover {
  background: rgba(255, 255, 255, 0.1);
}

.section:not(.section--primary) .btn--outline {
  border-color: var(--color-heading);
  color: var(--color-heading);
}

.section:not(.section--primary) .btn--outline:hover {
  background: var(--color-primary);
  color: var(--color-text-inverse);
}

.btn--ghost {
  background: transparent;
  color: var(--color-heading);
  padding-inline: var(--space-2);
}

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

.btn--sm {
  padding: 0.5rem 1rem;
  font-size: var(--text-sm);
}

.btn--lg {
  padding: 1rem 2rem;
  font-size: var(--text-lg);
}

.btn--block {
  width: 100%;
}

/* -----------------------------------------------------------------------
   6. Forms
   ----------------------------------------------------------------------- */
.form-group {
  margin-bottom: var(--space-5);
}

.form-label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-heading);
}

.form-input,
.form-textarea,
.form-select {
  width: 100%;
  padding: 0.75rem 1rem;
  font-size: var(--text-base);
  color: var(--color-text);
  /* Slightly recessed rather than the translucent --color-surface, so a
     field still looks like an input on a dark page. */
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--color-text-muted);
}

/* The native dropdown list renders with the OS palette; forcing both keeps
   the options readable instead of white-on-white in some browsers. */
.form-select option {
  background: #17101d;
  color: var(--color-text);
}

.form-textarea {
  resize: vertical;
  min-height: 140px;
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(200, 155, 60, 0.2);
}

.form-hint {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.form-error {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-error);
}

/* -----------------------------------------------------------------------
   7. Badges / Tags
   ----------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.3rem 0.75rem;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: var(--radius-full);
  background: var(--color-bg-alt);
  color: var(--color-heading);
}

/* Tints raised from ~10% to ~16% so the pill still reads as a pill against a
   dark card rather than dissolving into it. */
.badge--cultural {
  background: rgba(240, 143, 164, 0.16);
  color: var(--color-cultural);
}

.badge--sports {
  background: rgba(124, 196, 236, 0.16);
  color: var(--color-sports);
}

.badge--academic {
  background: rgba(127, 211, 163, 0.16);
  color: var(--color-academic);
}

.badge--excursion {
  background: rgba(232, 200, 119, 0.16);
  color: var(--color-excursion);
}

/* -----------------------------------------------------------------------
   8. Site Header & Navbar
   ----------------------------------------------------------------------- */
/* Dark glass at all times. It used to swap to solid white once scrolled,
   which broke the cinematic look the moment the page moved; scrolling now
   only deepens the tint and adds a shadow. */
/* ⚠️ THE FROSTED GLASS EFFECT IS ON ::before, NOT ON .site-header ITSELF.
   This looks like a pointless indirection. It is not — do not "simplify" it.

   `backdrop-filter` (like `filter`, `transform` and `perspective`) makes an
   element the CONTAINING BLOCK for any `position: fixed` descendant. The mobile
   navigation menu lives inside this header and is `position: fixed` with
   `inset: var(--header-offset) 0 0 0`, meaning "fill the screen below the
   header". With the filter on .site-header, that resolved against the HEADER's
   115px box instead of the viewport: the menu rendered 96px tall and showed
   1 of its 6 links, with the rest clipped away. On a site where ~70% of traffic
   is mobile, that made the site effectively un-navigable on a phone.

   Moving the filter to a pseudo-element keeps the identical visual result while
   leaving .site-header filter-free, so `position: fixed` children resolve
   against the viewport as intended. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  /* Background lives on ::before. Keep this transparent. */
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: box-shadow var(--transition-base), border-color var(--transition-base);
}

.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(12, 7, 16, 0.55);
  -webkit-backdrop-filter: blur(16px) saturate(130%);
  backdrop-filter: blur(16px) saturate(130%);
  transition: background var(--transition-base);
}

/* The header's real content must paint above the blur layer. `position:
   relative` is enough — both are positioned with `z-index: auto`, so paint
   order follows DOM order and ::before comes first. */
.site-header > * {
  position: relative;
}

.site-header.is-scrolled {
  box-shadow: var(--shadow-md);
  border-bottom-color: var(--color-border);
}

.site-header.is-scrolled::before {
  background: rgba(10, 6, 14, 0.88);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header::before {
    background: rgba(12, 7, 16, 0.94);
  }
}

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--header-height);
  gap: var(--space-4);
}

.navbar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

.navbar__logo {
  width: 44px;
  height: 44px;
  object-fit: contain;
}

.navbar__name {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-heading);
  line-height: var(--leading-tight);
}

.navbar__toggle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
}

.navbar__toggle-bar {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--color-heading);
  border-radius: var(--radius-full);
  transition: transform var(--transition-base), opacity var(--transition-base);
}

.navbar__toggle[aria-expanded="true"] .navbar__toggle-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.navbar__toggle[aria-expanded="true"] .navbar__toggle-bar:nth-child(2) {
  opacity: 0;
}

.navbar__toggle[aria-expanded="true"] .navbar__toggle-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.navbar__menu {
  position: fixed;
  /* Must clear the FULL header (navbar + ticker), not just --header-height,
     or the menu's top ~39px scrolls underneath the header. */
  inset: var(--header-offset) 0 0 0;
  background: var(--color-bg);
  padding: var(--space-6) var(--container-padding) var(--space-10);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform var(--transition-slow);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.navbar__menu.is-open {
  transform: translateX(0);
}

.navbar__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.navbar__item {
  border-bottom: 1px solid var(--color-border);
}

.navbar__link,
.navbar__dropdown-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--space-4) var(--space-1);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-heading);
  text-align: left;
}

.navbar__dropdown-toggle::after {
  content: "";
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--transition-fast);
}

.navbar__item--dropdown.is-open .navbar__dropdown-toggle::after {
  transform: rotate(-135deg);
}

.navbar__dropdown-menu {
  display: none;
  flex-direction: column;
  gap: var(--space-1);
  padding: 0 0 var(--space-3) var(--space-3);
}

.navbar__item--dropdown.is-open .navbar__dropdown-menu {
  display: flex;
}

.navbar__dropdown-link {
  display: block;
  padding: var(--space-2) var(--space-1);
  color: var(--color-text-muted);
  font-weight: 500;
}

.navbar__dropdown-link:hover {
  color: var(--color-accent);
}

.navbar__cta {
  align-self: flex-start;
}

.navbar__link.is-active,
.navbar__dropdown-toggle.is-active {
  color: var(--color-accent);
}

/* ⚠️ 900px, NOT the 1024px used elsewhere in this file. Deliberate.
   Android's "Request desktop site" renders at a ~980px viewport. At a 1024px
   breakpoint that is still below the cutoff, so a visitor who explicitly asked
   for the desktop site was handed the hamburger menu anyway and reasonably
   concluded the option was broken. 900px puts 980px comfortably on the desktop
   side. It is also where the six nav links + logo + CTA stop being cramped —
   verified for horizontal overflow at 900, 980 and 1024.
   If you add another nav item, re-check that this still fits. */
@media (min-width: 900px) {
  .navbar__toggle {
    display: none;
  }

  .navbar__menu {
    position: static;
    inset: auto;
    transform: none;
    flex-direction: row;
    align-items: center;
    padding: 0;
    background: transparent;
    overflow: visible;
    gap: var(--space-8);
  }

  .navbar__list {
    flex-direction: row;
    align-items: center;
    gap: var(--space-6);
  }

  .navbar__item {
    position: relative;
    border-bottom: none;
  }

  .navbar__link,
  .navbar__dropdown-toggle {
    width: auto;
    padding: var(--space-2) 0;
    position: relative;
  }

  .navbar__link::before,
  .navbar__dropdown-toggle::before {
    content: "";
    position: absolute;
    left: 0;
    bottom: -2px;
    width: 100%;
    height: 2px;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition-fast);
  }

  .navbar__link:hover::before,
  .navbar__dropdown-toggle:hover::before,
  .navbar__link.is-active::before,
  .navbar__dropdown-toggle.is-active::before {
    transform: scaleX(1);
  }

  .navbar__dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    /* Solid, not the translucent --color-surface: a see-through dropdown
       over moving hero imagery is unreadable. */
    background: #17101d;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-3);
    z-index: var(--z-dropdown);
    display: none;
  }

  .navbar__item--dropdown:hover .navbar__dropdown-menu,
  .navbar__item--dropdown.is-open .navbar__dropdown-menu {
    display: flex;
  }

  .navbar__dropdown-link {
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
  }

  .navbar__dropdown-link:hover {
    background: var(--color-bg-alt);
  }

  .navbar__cta {
    align-self: center;
  }
}

/* -----------------------------------------------------------------------
   9. Announcement Ticker
   ----------------------------------------------------------------------- */
.announcement-ticker {
  display: flex;
  align-items: center;
  background: var(--color-primary-dark);
  color: var(--color-text-inverse);
  font-size: var(--text-sm);
}

.announcement-ticker__label {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.announcement-ticker__viewport {
  flex: 1;
  overflow: hidden;
  padding-block: var(--space-2);
}

.announcement-ticker__track {
  display: flex;
  gap: var(--space-10);
  width: max-content;
  animation: ticker-scroll 28s linear infinite;
}

.announcement-ticker:hover .announcement-ticker__track {
  animation-play-state: paused;
}

.announcement-ticker__item {
  white-space: nowrap;
  color: var(--color-text-inverse-muted);
}

@keyframes ticker-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* -----------------------------------------------------------------------
   10. Hero
   ----------------------------------------------------------------------- */
.hero {
  background: linear-gradient(180deg, var(--color-bg-alt) 0%, var(--color-bg) 100%);
  padding-block: var(--space-12);
}

.hero__inner {
  display: grid;
  gap: var(--space-8);
  align-items: center;
}

.hero__content {
  max-width: 560px;
}

.hero__eyebrow {
  margin-bottom: var(--space-4);
}

.hero__title {
  margin-bottom: var(--space-5);
}

.hero__subtitle {
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  margin-bottom: var(--space-6);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.hero__media {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-xl);
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 1024px) {
  .hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* -----------------------------------------------------------------------
   11. Feature Grid / Cards
   ----------------------------------------------------------------------- */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

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

/* The aspect-ratio is on the LINK, not just the image inside it.
   The image carries the same ratio, but `aspect-ratio` needs a resolvable
   width to produce a height — and when a cover photo is missing (which is the
   normal state until the school supplies event photos) the <img> has no
   intrinsic size, so the whole link collapsed to 2px. That left a row of
   squashed, unclickable event cards, most visibly on a phone.
   Putting the ratio on the container means the card holds its shape whether
   the photo has arrived, is still loading, or failed outright. */
.event-card__media {
  display: block;
  /* width: 100% is REQUIRED, not redundant with display: block.
     On the homepage this element is an <a>, but on the Events page events.js
     renders it as a <button> — and a button shrinks to fit its content even
     when set to display: block. With a missing cover photo it collapsed to
     12px wide, which gave aspect-ratio almost no width to work from and
     produced a 9px-tall unclickable card. */
  width: 100%;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--color-surface);
  border: 0;
  padding: 0;
}

.event-card__media img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
  height: 100%;
  transition: transform var(--transition-base);
}

.card:hover .event-card__media img {
  transform: scale(1.04);
}

.event-card__body {
  padding: var(--space-5);
}

.feature-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
  .feature-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .feature-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- balanced variant ---------------------------------------------------
   Centres an INCOMPLETE final row instead of leaving an empty cell on the
   right. With 4 or 5 cards in a 3-up grid the default parks the leftovers
   hard left with a hole beside them, which reads as a card that failed to
   load; centring them gives the intended inverted-trapezium shape.

   This switches to flex because CSS Grid genuinely cannot do it: the track
   count is fixed by grid-template-columns and those tracks already fill the
   row, so `justify-content` has nothing left to distribute. Flex items sized
   to the same width as a grid column, then centred, achieve it — and it
   holds for any number of cards at any breakpoint, so adding or removing a
   card later needs no CSS change.

   Must stay AFTER the .feature-grid rules above: same specificity, so source
   order is what lets `display: flex` win. -------------------------------- */
.feature-grid--balanced {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* gap is inherited from .feature-grid and applies to flex too */
}

/* flex-grow stays 0 deliberately. If the leftovers were allowed to grow they
   would stretch to fill the row, making the last row's cards visibly wider
   than the row above — which looks more broken than the gap did. */
.feature-grid--balanced > * {
  flex: 0 1 100%;
}

@media (min-width: 768px) {
  /* 2 columns: one gap to subtract */
  .feature-grid--balanced > * {
    flex-basis: calc((100% - var(--space-6)) / 2);
  }
}

@media (min-width: 1024px) {
  /* 3 columns: two gaps to subtract */
  .feature-grid--balanced > * {
    flex-basis: calc((100% - 2 * var(--space-6)) / 3);
  }
}

.feature-card {
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.feature-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

.feature-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  margin-bottom: var(--space-5);
  border-radius: var(--radius-md);
  background: rgba(200, 155, 60, 0.14);
  color: var(--color-accent);
}

.feature-card__title {
  font-size: var(--text-xl);
  margin-bottom: var(--space-3);
}

.feature-card__text {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* -----------------------------------------------------------------------
   12. Stats Counters
   ----------------------------------------------------------------------- */
.stats {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  padding-block: var(--space-10);
}

.stats__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  text-align: center;
}

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

.stats__number {
  display: block;
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 700;
  color: var(--color-accent-light);
}

.stats__label {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-inverse-muted);
}

/* -----------------------------------------------------------------------
   13. Footer
   ----------------------------------------------------------------------- */
.site-footer {
  background: var(--color-primary-dark);
  color: var(--color-text-inverse-muted);
  padding-top: var(--space-12);
}

.footer__grid {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: minmax(0, 1fr);
  padding-bottom: var(--space-10);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

@media (min-width: 768px) {
  .footer__grid {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
  }
}

.footer__col--brand {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.footer__brand-name {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  color: var(--color-text-inverse);
}

.footer__heading {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-inverse);
  margin-bottom: var(--space-4);
}

.footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.footer__link:hover {
  color: var(--color-accent-light);
}

.footer__social {
  display: flex;
  gap: var(--space-3);
}

.footer__social-link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.08);
  transition: background var(--transition-fast);
}

.footer__social-link:hover {
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.footer__bottom {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-block: var(--space-6);
  font-size: var(--text-xs);
}

/* Registered institution name — present for traceability, deliberately quiet
   so it doesn't compete with the brand name above it. */
.footer__legal {
  margin-top: var(--space-1);
  opacity: 0.7;
}

@media (min-width: 768px) {
  .footer__bottom {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

/* -----------------------------------------------------------------------
   14. Interior Page Components (About / Academics / Admissions / Contact)
   ----------------------------------------------------------------------- */
.stat-figure {
  display: block;
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--color-accent);
}

.avatar-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 88px;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  background: var(--color-primary);
  color: var(--color-accent-light);
  margin-bottom: var(--space-4);
}

/* The founder's message is the featured one on the About page, so its monogram
   should not be the smallest on it. The committee cards scale theirs from 128px
   on mobile to 160px on desktop purely to fill a tall narrow card — at the base
   88px the founder read as subordinate to the people below him. 128px keeps him
   at least level with them at every viewport, while staying clear of the
   quotation that actually carries the section. */
#founder-message .avatar-circle {
  width: 128px;
  height: 128px;
  font-size: var(--text-3xl);
}

.feature-card__icon--number {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
}

.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.table-wrapper {
  overflow-x: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.table {
  width: 100%;
  min-width: 420px;
}

.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

.table th {
  background: var(--color-bg-alt);
  color: var(--color-heading);
  font-weight: 700;
}

.table tr:last-child td {
  border-bottom: none;
}

.faq-item {
  border-bottom: 1px solid var(--color-border);
  padding-block: var(--space-4);
}

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  cursor: pointer;
  font-weight: 600;
  color: var(--color-heading);
  list-style: none;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "";
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  transform: rotate(45deg);
  transition: transform var(--transition-fast);
}

.faq-item[open] summary::after {
  transform: rotate(-135deg);
}

.faq-item__answer {
  margin-top: var(--space-3);
  color: var(--color-text-muted);
}

.form-status {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  display: none;
}

.form-status.is-visible {
  display: block;
}

/* Lightened for the dark canvas — the solid green/red were ~2:1 on near-black. */
.form-status--success {
  background: rgba(127, 211, 163, 0.14);
  color: #8fe0b4;
}

.form-status--error {
  background: rgba(255, 138, 128, 0.14);
  color: #ff9c93;
}

.map-embed {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.map-embed iframe {
  width: 100%;
  height: 340px;
  border: 0;
  display: block;
}

/* One card per location; auto-fits however many config.json defines. */
.map-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
  .map-grid {
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  }
}

.map-card__label {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}

.map-card__address {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin-bottom: var(--space-4);
}

.map-card .map-embed iframe {
  height: 260px;
}

.map-card__directions {
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-heading);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.map-card__directions:hover {
  color: var(--color-accent);
}

/* -----------------------------------------------------------------------
   14b. Horizontal card scroller (About page — management team)
   Keeps all members on ONE row regardless of how many there are, scrolling
   sideways instead of wrapping to a second row.
   ----------------------------------------------------------------------- */
.card-scroller {
  display: flex;
  gap: var(--space-6);
  overflow-x: auto;
  /* Two deliberate omissions here:
     1. No `scroll-snap-type` — mandatory snapping fights programmatic scrolling
        in Chromium, which stopped the arrow buttons from working at all. The
        arrows land exactly on a card boundary themselves, so snap bought us
        nothing and cost us the controls.
     2. No `scroll-behavior: smooth` — that would force smooth animation onto
        the user's own swipe and scrollbar drag too. about.html animates the
        arrow scrolls itself (see initTeamScroller). */
  /* top/bottom padding leaves room for the cards' hover-lift and shadow,
     which an overflow container would otherwise clip */
  padding-block: var(--space-2) var(--space-4);
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}

/* Card width is expressed as "how many fit across", so the row always shows a
   whole number of cards plus a sliver of the next — that peek is the cue that
   the row scrolls. The .12 in each divisor is what creates the sliver. */
.card-scroller > * {
  flex: 0 0 min(78vw, 300px);
  min-height: 380px;
  display: flex;
  flex-direction: column;
  /* Top-aligned, NOT centred. All cards stretch to the tallest one, so centring
     made each card's content float by half its own shortfall — with messages of
     slightly different lengths the monograms and names sat at five different
     heights across the row, which read as sloppy. Aligning to the top lines the
     monograms and names up exactly and leaves the ragged edge at the bottom,
     where nobody notices it. */
  justify-content: flex-start;
  padding: var(--space-5);
}

/* Qualifications sit on their own line rather than after a separator. At this
   card width "Management Committee · B.Com" wrapped anyway, which left the
   separator dangling at the end of a line; forcing the break makes it
   deliberate, and guarantees every card's role block is the same height so the
   messages below stay aligned even if a shorter qualification would have fit. */
.team-card__qualification {
  display: block;
  color: var(--color-text-muted);
}

/* The avatar is inline-flex and normally centres via text-align; once the card
   becomes a flex column it needs centring explicitly. It's also scaled up here
   to occupy the space the taller card would otherwise leave empty. */
.card-scroller > * .avatar-circle {
  margin-inline: auto;
  width: 128px;
  height: 128px;
  font-size: var(--text-3xl);
}

@media (min-width: 1024px) {
  .card-scroller > * .avatar-circle {
    width: 160px;
    height: 160px;
    font-size: var(--text-4xl);
  }
}

@media (min-width: 640px) {
  .card-scroller > * {
    flex-basis: calc((100% - var(--space-6)) / 2.15);
  }
}

@media (min-width: 1024px) {
  .card-scroller > * {
    flex-basis: calc((100% - 3 * var(--space-6)) / 4.2);
    min-height: 440px;
  }
}

.card-scroller::-webkit-scrollbar {
  height: 8px;
}

.card-scroller::-webkit-scrollbar-track {
  background: var(--color-bg-alt);
  border-radius: var(--radius-full);
}

.card-scroller::-webkit-scrollbar-thumb {
  background: var(--color-border);
  border-radius: var(--radius-full);
}

.card-scroller::-webkit-scrollbar-thumb:hover {
  background: var(--color-accent);
}

.scroller-controls {
  display: flex;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* `display: flex` above outranks the UA stylesheet's `[hidden] { display: none }`,
   so hiding the controls needs this explicit rule. */
.scroller-controls[hidden] {
  display: none;
}

.scroller-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-heading);
  box-shadow: var(--shadow-sm);
  transition: background var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast), opacity var(--transition-fast);
}

.scroller-nav:hover:not(:disabled) {
  background: var(--color-primary);
  border-color: var(--color-heading);
  color: var(--color-text-inverse);
}

.scroller-nav:disabled {
  opacity: 0.35;
  cursor: default;
}

/* -----------------------------------------------------------------------
   14c. Notice / promotional pop-up (homepage)
   The frame shrink-wraps the image, so the pop-up is whatever shape the
   notice is — portrait, landscape or square — without ever exceeding the
   viewport or distorting the artwork.
   ----------------------------------------------------------------------- */
.notice-popup {
  position: fixed;
  inset: 0;
  z-index: var(--z-popup);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: rgba(0, 0, 0, 0.7);
  animation: notice-popup-fade var(--transition-base) ease both;
}

.notice-popup__frame {
  position: relative;
  /* fit-content is what makes the frame adapt to the image rather than the
     image stretch to fill the frame */
  width: fit-content;
  max-width: 90vw;
  max-height: 90vh;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xl);
  animation: notice-popup-zoom var(--transition-slow) both;
}

.notice-popup__image {
  display: block;
  /* width/height auto + max constraints = never upscaled, never distorted,
     never cropped, never larger than the viewport */
  width: auto;
  height: auto;
  max-width: 90vw;
  max-height: 90vh;
  border-radius: var(--radius-md);
}

.notice-popup__close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  /* Sits inside the image corner rather than outside it, so it can never be
     clipped off-screen on a narrow viewport. The dark disc keeps it legible
     over light and dark artwork alike. */
  background: rgba(7, 7, 7, 0.6);
  color: #fff;
  -webkit-backdrop-filter: blur(2px); /* Safari still needs the prefix */
  backdrop-filter: blur(2px);
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.notice-popup__close:hover {
  background: rgba(7, 7, 7, 0.85);
  transform: scale(1.06);
}

.notice-popup__close:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

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

@keyframes notice-popup-zoom {
  from {
    opacity: 0;
    transform: scale(0.94) translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .notice-popup,
  .notice-popup__frame {
    animation: none;
  }
}

/* -----------------------------------------------------------------------
   14d. Cinematic hero + liquid-glass surfaces  (prototype)
   Opt-in: only applies to pages carrying <body data-hero="cinematic">, so
   the existing homepage is untouched. Reuses the standard slider markup and
   JS — only the presentation changes.
   ----------------------------------------------------------------------- */
/* Doubled class on purpose: the base `.hero-slider { height: min(640px, 82vh) }`
   lives further down the file, so at equal specificity it would win on order. */
.hero-slider.hero-slider--cinematic {
  /* svh accounts for mobile browser chrome; the vh line is the fallback for
     browsers without it, and must come first. */
  height: 100vh;
  height: 100svh;
  max-height: none;
  min-height: 560px;
}

/* ⚠️ THE DOUBLED CLASS HERE IS REQUIRED, not a typo.
   The generic mobile rule further down this file is `.hero-slider` — one class,
   specificity 0,1,0 — which LOSES to `.hero-slider.hero-slider--cinematic`
   above (0,2,0) no matter where it sits or that it is inside a media query.
   Media queries add no specificity. Matching the two classes is what lets this
   actually apply on a phone.

   WHY A SHORTER HERO ON MOBILE: the banner photographs are 16:9 landscape. In
   a 390x844 portrait viewport a full-height hero with `object-fit: cover` had
   to scale the photo until it filled 844px of height, which left only ~26% of
   the picture's width on screen — a narrow vertical slice through the middle,
   with the actual subject cropped out either side. Reducing the height widens
   that slice to ~45%. Combined with the per-slide focal point (see the `focus`
   field in config.json) that is enough to keep the subject in frame.
   Do not put this back to 100svh without solving the crop some other way. */
@media (max-width: 767px) {
  .hero-slider.hero-slider--cinematic {
    height: 62vh;
    height: 62svh;
    min-height: 380px;
    max-height: 560px;
  }
}

/* Darker, taller gradient than the standard slider: text sits over photos
   here, so legibility has to hold regardless of what the photo looks like. */
.hero-slider--cinematic .hero-slider__overlay {
  background: linear-gradient(
    105deg,
    rgba(7, 7, 12, 0.88) 0%,
    rgba(7, 7, 12, 0.62) 42%,
    rgba(7, 7, 12, 0.28) 72%,
    rgba(7, 7, 12, 0.5) 100%
  );
}

/* The caption IS the hero card now: a compact frosted panel sitting low-left,
   roughly where the original slider's caption sat. It lives inside each slide,
   so its content changes with the image. Deliberately small — the previous
   centred panel covered about half the artwork. */
.hero-slider--cinematic .hero-slider__caption {
  /* Sits low-left. It can go close to the bottom edge because the arrows and
     dots are clustered bottom-RIGHT, so there's nothing to collide with. */
  inset: auto auto calc(var(--space-6) + 20px) 0;
  left: max(var(--container-padding), calc((100% - var(--container-max)) / 2));
  right: auto;
  width: min(430px, calc(100% - 2 * var(--container-padding)));
  max-width: none;
  padding: var(--space-5);
  border-radius: var(--radius-md);
  text-align: left;
  margin: 0;
}

.hero-slider--cinematic .hero-slider__eyebrow {
  font-size: var(--text-xs);
  color: var(--color-accent-light);
}

.hero-slider--cinematic .hero-slider__title {
  font-size: clamp(1.5rem, 1.05rem + 1.5vw, 2.1rem);
  font-weight: 600;
  margin-top: var(--space-2);
}

.hero-slider--cinematic .hero-slider__subtitle {
  display: block;
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.82);
  margin-top: var(--space-2);
}

/* On desktop the glass panel is left-aligned, so the usual left/right arrows
   would sit on top of it. Both controls move into one cluster bottom-right,
   alongside the dots. */
.hero-slider--cinematic .hero-slider__arrow {
  top: auto;
  bottom: var(--space-6);
  transform: none;
  width: 42px;
  height: 42px;
}

.hero-slider--cinematic .hero-slider__arrow--prev {
  left: auto;
  right: calc(var(--container-padding) + 52px);
}

.hero-slider--cinematic .hero-slider__arrow--next {
  right: var(--container-padding);
}

.hero-slider--cinematic .hero-slider__dots {
  left: auto;
  right: calc(var(--container-padding) + 116px);
  bottom: calc(var(--space-6) + 16px);
  transform: none;
}

/* (The former centred `.hero-cinematic__panel` lived here — it carried a
   static headline and covered roughly half the artwork. Replaced by the
   compact per-slide caption card above.) */

/* Reusable frosted surface. Kept to a couple of elements on purpose —
   backdrop-filter is genuinely expensive to composite on low-end phones. */
.glass {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: blur(18px) saturate(140%); /* Safari needs the prefix */
  backdrop-filter: blur(18px) saturate(140%);
  box-shadow: 0 24px 60px rgba(7, 7, 12, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

/* Devices that can't composite a backdrop blur get a solid tint instead of a
   washed-out unreadable panel. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass {
    background: rgba(20, 8, 26, 0.82);
  }
}

/* --- scroll cue -------------------------------------------------------- */
/* This is a <button>, so it needs the browser's default button chrome stripped
   off — background, border, padding — and `font: inherit`, because buttons do
   NOT inherit the page font family the way other elements do. Without that line
   it renders in the browser's default UI font and looks wrong. */
.hero-scroll-cue {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: var(--space-5);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  color: rgba(255, 255, 255, 0.7);
  font: inherit;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  background: none;
  border: 0;
  /* Padding rather than a bare text hit area: it makes the whole cue,
     including the animated line beneath it, comfortably clickable. */
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  transition: color var(--transition-fast);
}

.hero-scroll-cue:hover {
  color: #ffffff;
}

/* Keyboard focus needs a visible ring. :focus-visible rather than :focus so it
   appears for keyboard users without drawing a box around every mouse click. */
.hero-scroll-cue:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
  color: #ffffff;
}

.hero-scroll-cue__line {
  width: 1px;
  height: 46px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0.7), transparent);
  animation: scroll-cue 2.2s ease-in-out infinite;
}

/* A phone hero has room for the panel and the controls, and not much else. */
@media (max-width: 767px) {
  .hero-scroll-cue {
    display: none;
  }

  /* On a phone the card spans the width, so here it DOES have to clear the
     control cluster rather than sitting alongside it. */
  .hero-slider--cinematic .hero-slider__caption {
    left: var(--container-padding);
    right: var(--container-padding);
    width: auto;
    bottom: calc(var(--space-6) + 60px);
    padding: var(--space-4);
  }

  .hero-slider--cinematic .hero-slider__subtitle {
    font-size: var(--text-xs);
  }
}

@keyframes scroll-cue {
  0%, 100% { opacity: 0.25; transform: scaleY(0.6); transform-origin: top; }
  50%      { opacity: 1;    transform: scaleY(1);   transform-origin: top; }
}

/* (The former `[data-hero] .site-header:not(.is-scrolled)` glass overrides
   lived here. They're gone — the header is dark glass site-wide now, defined
   once in section 8, so it no longer flips to white on scroll.) */

/* The fixed header deliberately floats over the hero on every page now, so the
   body padding main.js adds for it is cancelled and each hero reserves its own
   top space instead. !important because main.js writes it as an inline style. */
body[data-hero="cinematic"] {
  padding-top: 0 !important;
}

/* --- interior page hero -------------------------------------------------
   Interior pages have no photography, so the drama comes from an aurora
   gradient rather than an image. Same cinematic language, zero asset cost. */
.page-hero {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding-block: calc(var(--header-offset) + var(--space-10)) var(--space-12);
  background:
    radial-gradient(70% 130% at 12% -10%, rgba(116, 48, 137, 0.55), transparent 62%),
    radial-gradient(55% 110% at 88% 10%, rgba(200, 155, 60, 0.16), transparent 62%),
    var(--color-bg);
}

/* Soft horizon line so the hero resolves into the page instead of stopping dead. */
.page-hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 140px;
  background: linear-gradient(to bottom, transparent, var(--color-bg));
  pointer-events: none;
}

.page-hero > * {
  position: relative;
  z-index: 1;
}

.page-hero h1 {
  font-size: clamp(2.25rem, 1.3rem + 3.4vw, 3.75rem);
}

.page-hero .lead {
  color: rgba(255, 255, 255, 0.78);
}

/* --- section rhythm ----------------------------------------------------
   A faint top glow on alternating sections keeps a long dark page from
   reading as one flat slab. */
.section--alt {
  position: relative;
}

.section--alt::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    rgba(255, 255, 255, 0.14),
    transparent
  );
}

/* Cards read as lifted glass rather than paper. No backdrop blur here on
   purpose — there can be a dozen cards on screen and blurring each one is
   expensive on low-end phones. */
.card:hover,
.feature-card:hover {
  border-color: rgba(255, 255, 255, 0.22);
}

.stats {
  background:
    radial-gradient(80% 160% at 50% 0%, var(--color-primary-light), transparent 70%),
    var(--color-primary);
}

/* --- scroll reveal ----------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 700ms cubic-bezier(0.2, 0.7, 0.3, 1), transform 700ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .hero-scroll-cue__line {
    animation: none;
  }
}

/* -----------------------------------------------------------------------
   15. Utilities
   ----------------------------------------------------------------------- */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mt-8 { margin-top: var(--space-8); }

.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }
.mb-8 { margin-bottom: var(--space-8); }

.u-hidden {
  display: none !important;
}

@media (min-width: 1024px) {
  .u-hidden-desktop {
    display: none !important;
  }
}

@media (max-width: 1023px) {
  .u-hidden-mobile {
    display: none !important;
  }
}

.u-rounded {
  border-radius: var(--radius-md);
}

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

/* -----------------------------------------------------------------------
   16. Events Gallery (Year Tabs, Category Filters, Tier 3 Panel)
   ----------------------------------------------------------------------- */
.year-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
  margin-bottom: var(--space-5);
}

.year-tab {
  padding: 0.6rem 1.5rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-heading);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.year-tab:hover {
  border-color: var(--color-accent);
}

.year-tab.is-active {
  background: var(--color-primary);
  border-color: var(--color-heading);
  color: var(--color-text-inverse);
}

.category-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
  margin-bottom: var(--space-8);
}

.category-filter {
  padding: 0.4rem 1rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: 600;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.category-filter:hover {
  border-color: var(--color-accent);
}

.category-filter.is-active {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}

.event-gallery-panel {
  margin-top: var(--space-10);
  padding-top: var(--space-10);
  border-top: 1px solid var(--color-border);
}

.event-gallery-panel__header {
  margin-bottom: var(--space-6);
  max-width: 720px;
}

.gallery-section-heading {
  margin: var(--space-8) 0 var(--space-4);
}

.gallery-media-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 768px) {
  .gallery-media-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.gallery-media-item {
  position: relative;
  display: block;
  width: 100%;
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--color-border);
  padding: 0;
  background: var(--color-bg-alt);
}

.gallery-media-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-base);
}

.gallery-media-item:hover img {
  transform: scale(1.06);
}

.gallery-media-item__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(11, 37, 69, 0.35);
  color: var(--color-text-inverse);
}

/* -----------------------------------------------------------------------
   17. Lightbox
   ----------------------------------------------------------------------- */
.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: rgba(7, 26, 51, 0.92);
}

.lightbox.is-open {
  display: flex;
}

.lightbox__stage {
  width: 100%;
  max-width: min(90vw, 1000px);
  max-height: 78vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox__stage img,
.lightbox__stage video {
  max-width: 100%;
  max-height: 78vh;
  border-radius: var(--radius-md);
}

.lightbox__stage iframe {
  width: min(90vw, 960px);
  aspect-ratio: 16 / 9;
  height: auto;
  border-radius: var(--radius-md);
  border: 0;
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.1);
  color: var(--color-text-inverse);
  transition: background var(--transition-fast);
}

.lightbox__close:hover,
.lightbox__nav:hover {
  background: rgba(255, 255, 255, 0.2);
}

.lightbox__close {
  top: var(--space-5);
  right: var(--space-5);
}

.lightbox__nav--prev {
  left: var(--space-5);
  top: 50%;
  transform: translateY(-50%);
}

.lightbox__nav--next {
  right: var(--space-5);
  top: 50%;
  transform: translateY(-50%);
}

.lightbox__caption {
  position: absolute;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%);
  max-width: 80vw;
  text-align: center;
  color: var(--color-text-inverse-muted);
  font-size: var(--text-sm);
}

/* -----------------------------------------------------------------------
   18. Hero Slider (homepage, above the existing static hero)
   ----------------------------------------------------------------------- */
.hero-slider {
  position: relative;
  height: min(640px, 82vh);
  min-height: 420px;
  overflow: hidden;
  background: var(--color-primary-dark);
}

.hero-slider__track {
  position: relative;
  width: 100%;
  height: 100%;
}

.hero-slider__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-slow);
}

.hero-slider__slide.is-active {
  opacity: 1;
  visibility: visible;
}

.hero-slider__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-slider__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(7, 26, 51, 0.15) 0%, rgba(7, 26, 51, 0.75) 100%);
}

.hero-slider__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--space-12);
  padding-inline: var(--container-padding);
  max-width: var(--container-max);
  margin-inline: auto;
}

.hero-slider__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent-light);
}

.hero-slider__eyebrow::before {
  content: "";
  width: 24px;
  height: 2px;
  background: var(--color-accent-light);
}

.hero-slider__title {
  color: var(--color-text-inverse);
  margin-top: var(--space-3);
}

.hero-slider__subtitle {
  color: var(--color-text-inverse-muted);
  max-width: 560px;
  margin-top: var(--space-3);
  font-size: var(--text-lg);
}

/* The call-to-action buttons on the banner card (the "above the fold" CTA).
   Rendered by heroCtaMarkup() in landing.js from HERO_SLIDER.cta in config.json;
   switch them off there, not here.

   `flex-wrap` matters: on a narrow phone the two buttons cannot sit side by side,
   and without it the second one would be pushed off the edge of the screen
   rather than dropping onto its own line. */
.hero-slider__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

@media (max-width: 767px) {
  /* The mobile banner is much shorter (62svh), so the card has far less room.
     Full-width stacked buttons are also the easier target for a thumb. The
     doubled class is needed to outrank the desktop `.btn` rules, which are two
     classes deep (e.g. `.btn.btn--primary`) — a single class here would lose on
     specificity no matter that this is inside a media query. */
  .hero-slider__actions .btn.btn {
    flex: 1 1 100%;
    justify-content: center;
    padding-block: var(--space-3);
  }

  .hero-slider__actions {
    margin-top: var(--space-4);
    gap: var(--space-2);
  }
}

.hero-slider__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.15);
  color: var(--color-text-inverse);
  transition: background var(--transition-fast);
}

.hero-slider__arrow:hover {
  background: rgba(255, 255, 255, 0.3);
}

.hero-slider__arrow--prev {
  left: var(--space-5);
}

.hero-slider__arrow--next {
  right: var(--space-5);
}

.hero-slider__dots {
  position: absolute;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  gap: var(--space-3);
}

.hero-slider__dot {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.4);
  padding: 0;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.hero-slider__dot.is-active {
  background: var(--color-text-inverse);
  transform: scale(1.2);
}

@media (max-width: 767px) {
  .hero-slider {
    height: min(480px, 70vh);
  }

  .hero-slider__arrow {
    width: 40px;
    height: 40px;
  }

  .hero-slider__caption {
    bottom: var(--space-8);
  }
}

/* -----------------------------------------------------------------------
   26. Page Pre-loader
   -----------------------------------------------------------------------
   Driven by /js/preloader.js. The backdrop deliberately reuses the same
   aurora gradient as .page-hero and resolves to --color-bg, so it is
   visually identical to the page's own empty state — that is what lets the
   overlay appear instantly while the crest fades in a beat later. A fast
   connection finishes inside that beat and the visitor sees no loading
   screen at all.

   Everything here must survive the overlay being removed at any moment, and
   nothing here may depend on JS having run: see the SAFETY note in
   preloader.js. ---------------------------------------------------------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: var(--z-preloader);
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(70% 120% at 15% -10%, rgba(116, 48, 137, 0.5), transparent 62%),
    radial-gradient(60% 110% at 85% 8%, rgba(200, 155, 60, 0.14), transparent 62%),
    var(--color-bg);
}

.preloader.is-leaving {
  opacity: 0;
  pointer-events: none;
  transition: opacity 420ms ease;
}

.preloader__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  text-align: center;
  /* Held invisible for --preloader-logo-delay (written by preloader.js from
     PRELOADER.logoDelayMs, so the CSS and the JS cannot drift apart). */
  opacity: 0;
  animation: preloader-in 380ms ease var(--preloader-logo-delay, 220ms) forwards;
}

.preloader__crest {
  width: 76px;
  height: 76px;
  filter: drop-shadow(0 10px 28px rgba(0, 0, 0, 0.55));
  animation: preloader-breathe 2.6s ease-in-out infinite;
}

.preloader__shield {
  fill: var(--color-primary-dark);
  stroke: var(--color-accent);
  stroke-width: 2;
}

.preloader__star {
  fill: var(--color-accent-light);
}

.preloader__monogram {
  fill: var(--color-accent);
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
}

.preloader__wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-sm);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-accent-light);
}

.preloader__bar {
  position: relative;
  width: 132px;
  height: 2px;
  overflow: hidden;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.14);
}

/* Indeterminate on purpose — there is no meaningful percentage to report
   when what we are waiting on is a chain of fetches of unknown size. */
.preloader__bar-fill {
  position: absolute;
  inset: 0;
  width: 40%;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, var(--color-accent), transparent);
  animation: preloader-sweep 1.3s ease-in-out infinite;
}

@keyframes preloader-in {
  to {
    opacity: 1;
  }
}

@keyframes preloader-breathe {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.9;
  }
  50% {
    transform: scale(1.045);
    opacity: 1;
  }
}

@keyframes preloader-sweep {
  0% {
    transform: translateX(-110%);
  }
  100% {
    transform: translateX(310%);
  }
}

/* No motion, but still a branded hold — the crest appears immediately and
   sits still, rather than animating or being suppressed entirely. */
.preloader[data-reduce-motion="true"] .preloader__inner {
  opacity: 1;
  animation: none;
}

.preloader[data-reduce-motion="true"] .preloader__crest,
.preloader[data-reduce-motion="true"] .preloader__bar-fill {
  animation: none;
}

.preloader[data-reduce-motion="true"] .preloader__bar-fill {
  width: 100%;
  background: var(--color-accent);
  opacity: 0.5;
}

@media (prefers-reduced-motion: reduce) {
  .preloader__inner,
  .preloader__crest,
  .preloader__bar-fill {
    animation: none;
  }

  .preloader__inner {
    opacity: 1;
  }
}

/* -----------------------------------------------------------------------
   27. Error Page (404)
   -----------------------------------------------------------------------
   Full-height centred treatment rather than the standard .page-hero, so a
   dead end still feels like part of the site instead of a server default.
   The oversized numeral is decorative and aria-hidden in the markup; the
   real heading is the <h1>. ---------------------------------------------- */
.error-page {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Clears the fixed header, whose real height only JS knows. */
  min-height: calc(100vh - var(--header-offset));
  padding-block: calc(var(--header-offset) + var(--space-8)) var(--space-10);
  background:
    radial-gradient(65% 120% at 18% -10%, rgba(116, 48, 137, 0.6), transparent 62%),
    radial-gradient(55% 105% at 84% 6%, rgba(200, 155, 60, 0.18), transparent 62%),
    var(--color-bg);
}

.error-page::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 160px;
  background: linear-gradient(to bottom, transparent, var(--color-bg));
  pointer-events: none;
}

.error-page__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* The numeral sits behind the card and bleeds off the top of it, which is
   what stops the composition reading as a plain centred text block. */
.error-page__numeral {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(7rem, 4rem + 16vw, 14rem);
  font-weight: 700;
  line-height: 0.82;
  letter-spacing: -0.03em;
  background: linear-gradient(
    170deg,
    var(--color-accent-light) 0%,
    var(--color-accent) 42%,
    color-mix(in srgb, var(--color-primary) 70%, var(--color-accent)) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 18px 44px rgba(0, 0, 0, 0.5));
  user-select: none;
}

/* Safari before 16.2 has no color-mix(); without this the gradient's last
   stop is invalid and the whole numeral loses its fill and goes invisible. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .error-page__numeral {
    background: linear-gradient(170deg, var(--color-accent-light) 0%, var(--color-accent) 60%, var(--color-primary) 100%);
    -webkit-background-clip: text;
    background-clip: text;
  }
}

.error-page__card {
  /* Pulled up into the numeral so the two overlap rather than stack. */
  margin-top: calc(-1 * var(--space-5));
  /* The spacing scale skips 7 — 6 (2rem) then 8 (3rem). */
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  max-width: 560px;
}

.error-page__card h1 {
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem);
  margin-bottom: var(--space-3);
}

.error-page__card .lead {
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: 0;
}

.error-page__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
  margin-top: var(--space-6);
}

/* Secondary "try one of these instead" row. A dead end should offer the
   handful of pages someone was most likely aiming for. */
.error-page__suggestions {
  margin-top: var(--space-6);
  width: 100%;
}

.error-page__suggestions-label {
  font-size: var(--text-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

.error-page__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.error-page__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.05);
  color: var(--color-text);
  font-size: var(--text-sm);
  text-decoration: none;
  transition: border-color var(--transition-fast), background var(--transition-fast),
    transform var(--transition-fast);
}

.error-page__link:hover,
.error-page__link:focus-visible {
  border-color: var(--color-accent);
  background: rgba(200, 155, 60, 0.14);
  color: var(--color-heading);
  transform: translateY(-2px);
}

@media (max-width: 767px) {
  .error-page {
    min-height: auto;
  }

  .error-page__card {
    padding: var(--space-6) var(--space-5);
  }

  .error-page__links {
    gap: var(--space-2);
  }

  /* 40px at the desktop padding, which is under the 44px minimum touch
     target — the extra vertical padding only matters where taps happen. */
  .error-page__link {
    padding: var(--space-3) var(--space-4);
  }
}

@media (prefers-reduced-motion: reduce) {
  .error-page__link:hover,
  .error-page__link:focus-visible {
    transform: none;
  }
}

/* -----------------------------------------------------------------------
   28. Junior College highlight (Academics page)
   -----------------------------------------------------------------------
   A single emphasised block for the IT/Computer Science offering, which the
   school flagged as its main differentiator against nearby junior colleges.
   Deliberately NOT another .feature-card — it has to read as a step up from
   the three stream cards above it, not as a fourth one. ------------------ */
.jc-highlight {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-6);
  align-items: center;
  padding: var(--space-6);
  border-radius: var(--radius-lg);
}

.jc-highlight__body h3 {
  font-size: clamp(1.375rem, 1.1rem + 1.1vw, 1.875rem);
  margin: 0;
}

.jc-highlight__body p {
  margin-bottom: 0;
}

/* The gold used for `strong` here is the accent at full strength — on the
   glass surface it measures well clear of AA, and it is what draws the eye to
   the two subject names, which is the whole point of the block. */
.jc-highlight__body strong {
  color: var(--color-accent-light);
}

.jc-highlight__tags {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.jc-highlight__tags li {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.05);
  font-size: var(--text-sm);
  white-space: nowrap;
  text-align: center;
}

/* The first two tags are the differentiator, so they carry the accent. */
.jc-highlight__tags li:nth-child(-n + 2) {
  border-color: var(--color-accent);
  background: rgba(200, 155, 60, 0.16);
  color: var(--color-heading);
  font-weight: 600;
}

@media (max-width: 899px) {
  /* Below this the two columns squeeze the paragraph into a narrow ribbon, so
     the tags move underneath and run as a wrapping row instead. */
  .jc-highlight {
    grid-template-columns: minmax(0, 1fr);
  }

  .jc-highlight__tags {
    flex-direction: row;
    flex-wrap: wrap;
  }
}

/* -----------------------------------------------------------------------
   30. Mobile touch targets
   -----------------------------------------------------------------------
   Roughly 70% of this site's visitors are on a phone, so tap targets are a
   primary concern rather than an afterthought.

   Both Apple and Google put the minimum comfortable target at ~44px. Several
   controls were well under: footer links rendered 20px tall (text height with
   no padding) and the hero slider dots were 10px. A 10px dot is essentially
   un-tappable with a thumb — you hit the photo behind it instead.

   These rules only apply below the desktop breakpoint. On a mouse-driven
   desktop the tighter spacing is fine and looks better, so nothing changes
   there. ---------------------------------------------------------------- */
@media (max-width: 899px) {
  /* Footer link lists. Padding rather than min-height so the whole row is
     tappable, not just the words. */
  .footer__links a,
  .footer__link {
    display: inline-block;
    padding-block: var(--space-2);
    min-height: 44px;
    line-height: 1.6;
  }

  /* The slider dots keep their small VISUAL size — a chunky dot would look
     wrong — but gain an invisible padded hit area around them. This is the
     standard way to enlarge a target without changing the design. */
  .hero-slider__dot {
    position: relative;
  }

  .hero-slider__dot::after {
    content: "";
    position: absolute;
    /* -17px around a 10px dot gives a 44x44 target. */
    inset: -17px;
  }

  /* Wider gap so the enlarged hit areas don't overlap each other. */
  .hero-slider__dots {
    gap: var(--space-4);
  }

  /* Slider arrows: bump from 40px to a comfortable 48px. */
  .hero-slider--cinematic .hero-slider__arrow {
    width: 48px;
    height: 48px;
  }

  /* Events page: the year tabs and category filter chips. Standalone controls
     that a visitor taps repeatedly while browsing the gallery. */
  .year-tab,
  .category-filter {
    min-height: 44px;
    padding-block: var(--space-2);
  }

  /* Contact page: "Get Directions" under each map. On a phone this is the
     single most likely thing a parent taps on the whole site — it opens their
     maps app to navigate to the campus. 22px was far too small for that. */
  .map-card__directions {
    min-height: 44px;
    align-items: center;
    padding-block: var(--space-2);
  }

  /* The notice pop-up's close button. It is the ONLY way to dismiss a
     fullscreen overlay on a phone (no Escape key, and the backdrop is mostly
     covered by the image), so it must be comfortably hittable. */
  .notice-popup__close {
    width: 44px;
    height: 44px;
  }

  /* Inline links inside body copy get a little vertical breathing room, but
     are deliberately NOT pushed to 44px. The 44px guideline is for standalone
     controls; forcing it on a link inside a sentence would make consecutive
     lines' tap areas overlap each other, which is worse than a small target.
     Anything standalone — nav, buttons, footer lists, dots — IS sized above. */
  .feature-card__text a,
  .faq-item__answer a {
    padding-block: 2px;
  }
}

/* =============================================================================
   SECTION 31 — STICKY MOBILE CALL-TO-ACTION BAR
   =============================================================================
   The bar pinned to the bottom of the screen on phones. Built by initMobileCta()
   in main.js from the MOBILE_CTA block in config.json — to change the labels,
   the link, or to switch the bar off, edit config.json, not this file.

   PHONES ONLY, BY DESIGN. It is display:none from 768px up, where the full
   navigation is already visible in the header and a pinned bar would just take
   up room. Everything below therefore lives inside the media query.
   ========================================================================== */

/* Hidden by default; the media query below switches it on. Written this way
   round so that any browser that fails to match the query shows nothing at all
   rather than a bar floating in the middle of a desktop page. */
.mobile-cta {
  display: none;
}

@media (max-width: 767px) {
  .mobile-cta {
    display: flex;
    gap: var(--space-2);
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60; /* above page content, below the notice pop-up overlay */
    padding: var(--space-3);
    /* env(safe-area-inset-bottom) clears the home indicator on iPhones with no
       home button. It resolves to 0px everywhere else, so it is safe to always
       include. Without it the buttons sit under the system gesture bar and are
       genuinely hard to tap. */
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: rgba(12, 7, 16, 0.92);
    backdrop-filter: blur(14px) saturate(130%);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
  }

  .mobile-cta__btn {
    flex: 1 1 0;
    /* flex-basis 0 with minmax-style shrinking: `min-width: 0` stops a long
       label from refusing to shrink and forcing the two buttons uneven. */
    min-width: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 48px;
    padding-inline: var(--space-3);
    border-radius: var(--radius-md);
    font-family: inherit; /* buttons/links do not inherit font by default */
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.01em;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--transition-fast), color var(--transition-fast);
  }

  /* "Call Us" — outlined, so the application button stays the louder of the two.
     Gold border to match the crest. */
  .mobile-cta__btn--call {
    color: var(--color-accent);
    border: 1px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
    background: color-mix(in srgb, var(--color-accent) 10%, transparent);
  }

  .mobile-cta__btn--call:active {
    background: color-mix(in srgb, var(--color-accent) 22%, transparent);
  }

  /* "Apply Now" — solid, the primary action. */
  .mobile-cta__btn--action {
    color: var(--color-text-inverse, #fff);
    background: var(--color-primary);
    border: 1px solid var(--color-primary);
  }

  .mobile-cta__btn--action:active {
    background: var(--color-primary-dark, var(--color-primary));
  }

  /* Reserve room at the foot of the page so the bar never covers the last of
     the footer. The class is added to <html> by initMobileCta() only when the
     bar actually mounted, so a page with the bar switched off keeps its normal
     spacing. 76px is the bar's height plus a little breathing room. */
  .has-mobile-cta body {
    padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  }
}

/* =============================================================================
   SECTION 32 — BREADCRUMBS
   =============================================================================
   The "Home > Academics" trail at the top of every interior page (not the
   homepage, which is the root and has nothing to trace back to).

   The markup is static, in each page's .page-hero — see the comment there. This
   only styles it. The separator is drawn in CSS with ::before rather than typed
   into the HTML, so screen readers read "Home, Academics" instead of
   "Home greater-than Academics".
   ========================================================================== */

.breadcrumb {
  margin-bottom: var(--space-5);
  font-size: var(--text-sm);
}

.breadcrumb__list {
  display: flex;
  flex-wrap: wrap;           /* a long page name wraps instead of overflowing */
  align-items: center;
  justify-content: center;   /* .page-hero is centre-aligned */
  gap: var(--space-2);
  list-style: none;          /* it is an <ol>, so the markers must be cleared */
  margin: 0;
  padding: 0;
}

.breadcrumb__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text-muted);
}

/* Decorative separator before every item except the first. aria-hidden is not
   needed: CSS-generated content is not part of the accessibility tree. */
.breadcrumb__item + .breadcrumb__item::before {
  content: "/";
  color: color-mix(in srgb, var(--color-text-muted) 60%, transparent);
}

.breadcrumb__item a {
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color var(--transition-fast);
}

.breadcrumb__item a:hover,
.breadcrumb__item a:focus-visible {
  color: var(--color-accent);
  text-decoration: underline;
}

/* The current page. Not a link, and slightly brighter so the trail reads as
   "you came from here, you are now here". */
.breadcrumb__item[aria-current="page"] {
  color: var(--color-text);
  font-weight: 600;
}
