/* ==========================================================================
   components.css — Reusable, self-contained UI components.

   Sections (in order of appearance below):
     1. Buttons
     2. Cards / Grid
     3. Navbar (brand, nav menu, hamburger)
     4. Footer
     5. Auth (login card, form fields)
     6. Sections (hero, about, why, provide, highlights)

   The admin dashboard shell + panels live in the page-scoped dashboard.css
   (linked only from dashboard.html), not here.
   ========================================================================== */


/* ──────────────────────────────────────────────────────────────────────
   1. Buttons
   ────────────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  min-height: 44px;
  padding: var(--spacing-sm) var(--spacing-lg);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  background: transparent;
  color: var(--color-text);
  transition: background var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast),
              box-shadow var(--transition-fast),
              transform var(--transition-fast);
}

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

.btn:disabled,
.btn.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

.btn.is-loading {
  opacity: 0.7;
  cursor: wait;
  pointer-events: none;
}

.btn--sm {
  min-height: 36px;
  padding: var(--spacing-xs) var(--spacing-md);
  font-size: var(--font-size-sm);
}

.btn--primary {
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-md);
}

.btn--primary:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-1px);
}

.btn--primary:active {
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}

.btn--secondary {
  background: var(--color-surface);
  color: var(--color-primary-dark);
  border-color: var(--color-border-strong);
}

.btn--secondary:hover {
  background: var(--color-surface-glass-strong);
  border-color: var(--color-primary);
  color: var(--color-primary-dark);
}

.btn--ghost {
  background: transparent;
  color: var(--color-text);
}

.btn--ghost:hover {
  background: var(--color-surface-glass);
}


/* ──────────────────────────────────────────────────────────────────────
   2. Cards / Grid
   ────────────────────────────────────────────────────────────────────── */

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-lg);
}

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

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

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

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

.card__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  margin-bottom: var(--spacing-sm);
  color: var(--color-text);
}

.card__body {
  color: var(--color-text-light);
}


/* ──────────────────────────────────────────────────────────────────────
   3. Navbar (brand, nav menu, hamburger)
   ────────────────────────────────────────────────────────────────────── */

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
  text-decoration: none;
  color: var(--color-text);
}

.brand:visited {
  color: var(--color-text);
}

.brand__logo {
  display: block;
  width: auto;
  height: 40px;
}

.nav {
  display: flex;
  align-items: center;
}

/* Hamburger toggle — visible on mobile only.
   Raw px below are intrinsic glyph geometry (44px touch target, 10px inset,
   22px / 5px bar metrics, 7px = bar + gap for the X-rotation pivot), not the
   spacing scale — same convention as .highlight-card__badge { width: 90px }. */
.nav__toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 10px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.nav__toggle:hover {
  background: var(--color-surface-glass-strong);
  border-color: var(--color-border);
}

.nav__toggle:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

.nav__toggle-bar {
  display: block;
  width: 22px;
  height: var(--width-accent-line-thin);
  background: var(--color-text);
  border-radius: var(--radius-pill);
  transition: transform var(--transition-base), opacity var(--transition-base);
}

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

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

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

/* Mobile-first: drawer panel hidden by default, slides down when open */
.nav__menu {
  position: absolute;
  inset: 100% var(--spacing-md) auto var(--spacing-md);
  display: none;
  flex-direction: column;
  gap: var(--spacing-xs);
  padding: var(--spacing-md);
  background: var(--color-surface-glass-strong);
  backdrop-filter: var(--blur-md);
  -webkit-backdrop-filter: var(--blur-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

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

.nav__link {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.nav__link:visited {
  color: var(--color-text);
}

.nav__link:hover {
  background: var(--color-surface-glass);
  color: var(--color-primary-dark);
}

.nav__link:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

/* Restore the 44px touch target the .btn--sm variant trims to 36px — the CTA
   keeps btn--sm's compact padding/type but stays a full-size tap target. */
.nav__cta {
  min-height: 44px;
  margin-top: var(--spacing-xs);
}

@media (min-width: 768px) {
  .nav__toggle {
    display: none;
  }

  .nav__menu {
    position: static;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--spacing-xs);
    padding: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  .nav__cta {
    margin-top: 0;
    margin-left: var(--spacing-sm);
  }

  /* Desktop nav links drop the drawer's block-tint hover — the colour shift
     plus the animated brand underline below carry hover/focus feedback. */
  .nav__link:hover {
    background: transparent;
  }

  /* Animated brand underline — desktop only (the mobile drawer keeps its
     block-tint hover). transform-based, so the global reduced-motion rule
     auto-collapses the transition. */
  .nav__link::after {
    content: "";
    position: absolute;
    left: var(--spacing-md);
    right: var(--spacing-md);
    bottom: var(--spacing-xs);
    height: var(--width-accent-line-thin);
    background: var(--gradient-primary);
    border-radius: var(--radius-pill);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition-base);
  }

  .nav__link:hover::after,
  .nav__link:focus-visible::after {
    transform: scaleX(1);
  }
}


/* ──────────────────────────────────────────────────────────────────────
   4. Footer (internals)
   ────────────────────────────────────────────────────────────────────── */

.site-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  padding-block: var(--spacing-2xl) var(--spacing-lg);
}

.site-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1.4fr;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}

@media (max-width: 720px) {
  .site-footer__grid {
    grid-template-columns: 1fr;
    gap: var(--spacing-xl);
  }
}

.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  max-width: 18rem;
}

.site-footer__logo {
  display: block;
  width: auto;
  height: 44px;
}

.site-footer__tagline {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  margin: 0;
}

.site-footer__byline {
  font-size: var(--font-size-xs);
  margin: 0;
}

.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.site-footer__heading {
  font-size: 0.8rem;
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-light);
  margin: 0 0 var(--spacing-xs);
}

.site-footer__list,
.site-footer__contact-list,
.site-footer__social,
.site-footer__legal {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-footer__list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}

.site-footer__list a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 1px;
  transition: background-size var(--transition-base), color var(--transition-fast);
}

.site-footer__list a:hover,
.site-footer__list a:focus-visible {
  background-size: 100% 1px;
  color: var(--color-primary-dark);
}

.site-footer__list a:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 4px;
}

.site-footer__contact {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
}

.site-footer__contact-list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2xs, 0.25rem);
  font-size: var(--font-size-sm);
  color: var(--color-text-light);
}

.site-footer__contact-list li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.4rem 0.55rem;
  border-radius: var(--radius-sm, 6px);
  min-height: 32px;
}

.site-footer__contact-list a {
  color: inherit;
  text-decoration: none;
  font-weight: var(--font-weight-medium);
}

.site-footer__contact-list a:hover,
.site-footer__contact-list a:focus-visible {
  color: var(--color-primary-dark);
}

.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-xs);
}

.site-footer__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  color: var(--color-text-light);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

.site-footer__social a:hover,
.site-footer__social a:focus-visible {
  color: var(--color-primary);
  background-color: var(--color-surface-subtle);
}

.site-footer__social a:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

.site-footer__social svg {
  width: 20px;
  height: 20px;
}

.site-footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
  padding-block: 1.25rem;
  border-top: 1px solid var(--color-border);
}

.site-footer__copy {
  font-size: var(--font-size-xs);
  margin: 0;
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-md);
}

.site-footer__legal a {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-light);
  text-decoration: none;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  transition: color var(--transition-fast);
}

.site-footer__legal a:hover,
.site-footer__legal a:focus-visible {
  color: var(--color-primary-dark);
}

.site-footer__legal a:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 4px;
}

@media (max-width: 720px) {
  .site-footer__bar {
    flex-direction: column;
    align-items: flex-start;
  }
}


/* ──────────────────────────────────────────────────────────────────────
   5. Auth (login card, form fields)
   ────────────────────────────────────────────────────────────────────── */

.auth {
  min-height: calc(100vh - var(--height-header));
  display: flex;
  align-items: center;
  padding-block: var(--spacing-3xl);
}

.auth__layout {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-xl);
}

/* Breeze, the STEM mascot — above the card on phones, beside it (card left,
   Breeze right) at >=1024px. Decorative: aria-hidden + alt="" in the markup.
   The teal glow is filter: drop-shadow(--shadow-mascot); the soft wash behind
   him is the ::before radial. */
.auth__mascot {
  position: relative;   /* anchors the ::before glow (and `filter` below adds the stacking context so its z-index:-1 stays contained) */
  width: 120px;
  filter: drop-shadow(var(--shadow-mascot));
  animation: hero-float 5s ease-in-out infinite;
}

.auth__mascot::before {
  content: "";
  position: absolute;
  inset: -25%;
  z-index: -1;
  border-radius: var(--radius-full);
  background: radial-gradient(circle, var(--color-secondary-alpha-20) 0%, transparent 70%);
}

.auth__mascot img {
  width: 100%;
  height: auto;
}

@media (min-width: 1024px) {
  .auth__layout {
    flex-direction: row-reverse;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-2xl);
  }

  .auth__mascot {
    width: 280px;
  }
}

.auth-card {
  position: relative;
  overflow: hidden;   /* clips the ::before accent to the rounded top corners */
  width: 100%;
  max-width: 440px;
  padding: var(--spacing-2xl);
  background: var(--color-surface-glass-strong);
  backdrop-filter: var(--blur-md);
  -webkit-backdrop-filter: var(--blur-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-xl);
}

/* Brand-gradient top accent — echoes the site-footer treatment so the card
   and the footer frame the page the same way. */
.auth-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--width-accent-line-thin);
  background: var(--gradient-primary);
}

.auth-card__brand {
  display: flex;
  justify-content: center;
  margin-bottom: var(--spacing-lg);
}

.auth-card__brand img {
  width: auto;
  height: 50px;
}

.auth-card__title {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-extrabold);
  color: var(--color-text);
  text-align: center;
  margin-bottom: var(--spacing-xs);
}

.auth-card__subtitle {
  text-align: center;
  margin-bottom: var(--spacing-xl);
}

.auth-card__footer {
  text-align: center;
  margin-top: var(--spacing-lg);
  font-size: var(--font-size-sm);
}

/* Form */
.auth-form {
  display: grid;
  gap: var(--spacing-md);
}

.auth-form__field {
  display: grid;
  gap: var(--spacing-xs);
}

.auth-form__status {
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  color: var(--color-text-light);
}

.auth-form__submit {
  margin-top: var(--spacing-sm);
  width: 100%;
}

.auth-form__meta {
  text-align: right;
  font-size: var(--font-size-sm);
  margin-top: var(--spacing-xs);
}

.auth-form__link {
  color: var(--color-link);
  text-decoration: none;
  transition: color var(--transition-fast);
}

.auth-form__link:hover {
  color: var(--color-primary-dark);
  text-decoration: underline;
}

.auth-form__link:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

/* Inputs */
.label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

.input {
  width: 100%;
  min-height: 44px;
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  color: var(--color-text);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.input:hover {
  border-color: var(--color-primary-light);
}

.input:focus-visible {
  outline: none;
  border-color: var(--color-primary-dark);
  box-shadow: 0 0 0 3px var(--color-primary-alpha-20);
}

.input[aria-invalid="true"] {
  border-color: var(--color-error);
}

.input[aria-invalid="true"]:focus-visible {
  box-shadow: 0 0 0 3px var(--color-error-alpha-20);
}

.input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.form-error {
  display: block;
  font-size: var(--font-size-sm);
  color: var(--color-error);
}

@media (prefers-reduced-motion: reduce) {
  .auth__mascot {
    animation: none !important;
  }
}


/* ──────────────────────────────────────────────────────────────────────
   6. Sections (hero, about, why, provide, highlights)
   ────────────────────────────────────────────────────────────────────── */


/* ── Hero ── */

.hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: min(100vh, 760px);
  margin: var(--spacing-md);
  padding: var(--spacing-2xl) var(--spacing-md);
  overflow: hidden;
  isolation: isolate;
  contain: layout paint style;
  border-radius: var(--radius-xl);
  background:
    radial-gradient(circle at 20% 80%, rgba(72, 208, 212, 0.30) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(62, 147, 235, 0.30) 0%, transparent 50%),
    conic-gradient(from 45deg at 50% 50%, var(--color-primary), var(--color-secondary), var(--color-primary)),
    url("https://rtpark-assets-dev.s3.us-east-1.amazonaws.com/stem_enrichment_1.jpg") center/cover no-repeat;
  animation: hero-aurora 15s ease-in-out infinite;
}

.hero::before,
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.hero::before {
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
  opacity: 0.45;
}

.hero::after {
  background: repeating-linear-gradient(45deg,
    transparent,
    transparent 2px,
    rgba(255, 255, 255, 0.02) 2px,
    rgba(255, 255, 255, 0.02) 4px);
}

.hero__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-2xl);
  align-items: center;
  position: relative;
  z-index: var(--z-base);
  width: 100%;
  text-align: center;
}

@media (min-width: 768px) {
  .hero__inner {
    grid-template-columns: 1fr 1fr;
    gap: var(--spacing-3xl);   /* 4rem to match original .rtp-wrap gap */
    text-align: left;
  }
}

.hero__card {
  position: relative;
  padding: var(--spacing-xl);   /* 2rem-ish — matches original .rtp-card 2.5rem closer than the previous 3rem */
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: saturate(120%) blur(18px);
  -webkit-backdrop-filter: saturate(120%) blur(18px);
  border-radius: var(--radius-lg);
  box-shadow:
    0 20px 40px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.25);
  transition: transform var(--transition-base),
              box-shadow var(--transition-base);
}

.hero__card:hover {
  transform: translateY(-2px);
  box-shadow:
    0 28px 56px rgba(0, 0, 0, 0.14),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

@media (min-width: 768px) {
  .hero__card { padding: calc(var(--spacing-xl) + var(--spacing-sm)); /* 40px = original 2.5rem */ }
}

.hero__card::before {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: calc(var(--radius-lg) + 2px);
  background: linear-gradient(45deg,
    var(--color-primary),
    var(--color-secondary),
    var(--color-primary),
    var(--color-secondary));
  background-size: 300% 300%;
  animation: hero-border 6s ease-in-out infinite;
}

.hero__title {
  font-size: clamp(2.125rem, 4.5vw, 3.5rem);
  font-weight: var(--font-weight-extrabold);
  line-height: var(--line-height-tight);
  letter-spacing: -0.01em;
  background: linear-gradient(45deg, var(--color-text-inverse), rgba(255, 255, 255, 0.92), var(--color-text-inverse));
  background-size: 200% 200%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  animation: hero-text 6s ease-in-out infinite;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero__title {
    color: var(--color-text-inverse);
    background: none;
  }
}

/* ── Shared gradient-text section title (Phase 8) ──
   Standardizes the brand gradient-clip used by light-background section H2s
   onto one class so the look is driven by `--gradient-brand`. Falls back to a
   solid primary color where `background-clip: text` is unsupported. Sections
   on dark surfaces (e.g. the Why card) keep their own solid color for
   contrast and do NOT take this class. */
.section-title--gradient {
  background: var(--gradient-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .section-title--gradient {
    color: var(--color-primary);
    background: none;
  }
}

.hero__sub {
  font-size: clamp(1.125rem, 2.5vw, 1.5rem);
  color: rgba(255, 255, 255, 0.95);
  margin-top: calc(var(--spacing-sm) + var(--spacing-xs));   /* 12px = 0.75rem (canonical) */
}

.hero__tag {
  font-size: var(--font-size-base);
  font-style: italic;
  color: rgba(255, 255, 255, 0.85);
  margin-top: var(--spacing-xs);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  margin-top: var(--spacing-lg);
  justify-content: center;
}

@media (min-width: 768px) {
  .hero__actions { justify-content: flex-start; }
}

/* Ghost variant lives on the dark hero gradient — repaint border and
   text in inverse-friendly tones. */
.hero .btn--ghost {
  color: var(--color-text-inverse);
  border-color: rgba(255, 255, 255, 0.4);
}

.hero .btn--ghost:hover {
  background: rgba(255, 255, 255, 0.15);
  border-color: rgba(255, 255, 255, 0.6);
}

.hero__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-md) var(--spacing-lg);
  list-style: none;
  margin: var(--spacing-xl) 0 0;
  padding: var(--spacing-md) 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  justify-content: center;
}

@media (min-width: 768px) {
  .hero__stats { justify-content: flex-start; }
}

.hero__stat {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: rgba(255, 255, 255, 0.9);
  letter-spacing: 0.02em;
}

.hero__stat strong {
  font-weight: var(--font-weight-bold);
  color: var(--color-text-inverse);
}

.hero__visual {
  position: relative;
  display: grid;
  place-items: center;
}

/* Circular sticker frame around the mascot */
.hero__mascot {
  position: relative;
  width: 280px;
  height: 280px;
  padding: 12px;
  margin: 0;
  background: var(--color-surface);
  border-radius: var(--radius-full);
  box-shadow:
    0 20px 40px rgba(0, 0, 0, 0.20),
    0 0 0 4px rgba(255, 255, 255, 0.8);
  animation: hero-mascot-bob 4s ease-in-out infinite;
  /* Hover tilts via individual `scale` + `rotate` props so they compose
     additively with the bob animation's `transform` shorthand. */
  transition: scale var(--transition-base),
              rotate var(--transition-base);
  /* Parallax: hero-effects.js writes --parallax-mascot on .hero__visual. */
  translate: 0 var(--parallax-mascot, 0);
  /* No z-index — DOM order handles stacking; blobs render above mascot
     but their corner positions don't visually overlap the centered mascot. */
}

.hero__mascot:hover {
  scale: 1.04;
  rotate: -2deg;
}

.hero__mascot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: var(--radius-full);
  background: linear-gradient(45deg, var(--color-secondary), var(--color-primary));
}

.hero__chip {
  position: absolute;
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-xl);   /* 28px — closer to original 25px than full pill */
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-inverse);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  animation: hero-float 3s ease-in-out infinite;
  /* Parallax: hero-effects.js writes --parallax-chips on .hero__visual.
     Lives on the `translate` property so the `transform`-based float
     animation composes additively. */
  translate: 0 var(--parallax-chips, 0);
  /* No z-index — DOM order stacks blobs ABOVE chips (matches canonical),
     letting the 0.45-opacity blobs cast a tint over the chips for the
     meshed/blended look. */
}

/* Spotlight cycle: each chip pulses brighter + scales briefly on a 9s
   stagger, layered on top of the float animation. Spotlight uses the
   individual `scale` property so it composes additively with the
   shorthand `transform: translateY(...)` used by hero-float — no inner
   wrapper needed. */
.hero__chip--build {
  top: 8%;
  left: -8%;
  background: var(--color-primary);
  animation: hero-float 3s ease-in-out infinite,
             hero-chip-spotlight 9s ease-in-out infinite 0s;
}

.hero__chip--code {
  top: 62%;
  right: 0;
  background: var(--color-secondary);
  color: var(--color-primary);   /* Brand blue text on teal — matches the live site even though `home-page-block.css` shows white */
  animation: hero-float 3s ease-in-out infinite 0.8s,
             hero-chip-spotlight 9s ease-in-out infinite 3s;
}

.hero__chip--create {
  bottom: 18%;
  left: -4%;
  background: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
  animation: hero-float 3s ease-in-out infinite 1.6s,
             hero-chip-spotlight 9s ease-in-out infinite 6s;
}

/* Organic-shape blobs */
.hero__blob {
  position: absolute;
  opacity: 0.45;
  pointer-events: none;
  animation: hero-blob 10s ease-in-out infinite;
  /* Parallax: hero-effects.js writes --parallax-blobs on .hero__visual. */
  translate: 0 var(--parallax-blobs, 0);
}

.hero__blob--1 {
  top: -10%;
  left: -12%;
  width: 120px;
  height: 120px;
  background: conic-gradient(from 45deg, var(--color-secondary), var(--color-primary), var(--color-secondary));
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}

.hero__blob--2 {
  bottom: -18%;
  right: -10%;
  width: 90px;
  height: 90px;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.35), var(--color-secondary));
  border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
  animation-direction: reverse;
}

/* Wave is preserved as a structural element from the original design but
   left visually empty — the original SCSS had its `background: #fff` line
   commented out. Setting display:none so it doesn't reserve space either. */
.hero__wave {
  display: none;
}

.hero__scroll-indicator {
  position: absolute;
  bottom: var(--spacing-lg);
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: var(--blur-sm);
  -webkit-backdrop-filter: var(--blur-sm);
  border: 1px solid rgba(255, 255, 255, 0.2);
  transition: background var(--transition-fast),
              color var(--transition-fast);
  animation: hero-scroll-bounce 2s ease-in-out infinite;
}

.hero__scroll-indicator:hover,
.hero__scroll-indicator:focus-visible {
  background: rgba(255, 255, 255, 0.18);
  color: var(--color-text-inverse);
}

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

.hero__scroll-indicator svg { width: 20px; height: 20px; }

@keyframes hero-aurora {
  0%, 100% { background-position: 0% 0%, 0% 0%, 0% 0%, center; }
  50%      { background-position: 100% 100%, 100% 100%, 100% 100%, center; }
}

@keyframes hero-border {
  0%, 100% { background-position: 0% 50%; opacity: 0.85; }
  50%      { background-position: 100% 50%; opacity: 1; }
}

@keyframes hero-text {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

@keyframes hero-mascot-bob {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50%      { transform: translateY(-6px) rotate(1deg); }
}

@keyframes hero-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}

@keyframes hero-blob {
  0%, 100% { transform: rotate(0) scale(1); }
  50%      { transform: rotate(180deg) scale(1.1); }
}

@keyframes hero-scroll-bounce {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 8px); }
}

/* Animates the individual `scale` property (not the shorthand) so the
   `transform: translateY(...)` from hero-float keeps composing. */
@keyframes hero-chip-spotlight {
  0%, 22%, 100% {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    scale: 1;
  }
  11% {
    box-shadow: 0 8px 24px rgba(255, 255, 255, 0.4),
                0 4px 12px rgba(0, 0, 0, 0.20);
    scale: 1.08;
  }
}

@media (max-width: 767px) {
  .hero__mascot {
    width: 220px;
    height: 220px;
  }

  .hero__chip {
    font-size: var(--font-size-xs);
    padding: var(--spacing-xs) var(--spacing-sm);
  }

  /* Tighter positions hugging the mascot edges instead of crowding it. */
  .hero__chip--build  { top: 6%; left: 0; }
  .hero__chip--code   { top: 50%; right: -2%; }
  .hero__chip--create { bottom: 6%; left: 4%; }
}

@media (max-width: 480px) {
  /* Three chips can't breathe around a 220px circle on phones. Hide
     `Code` and keep the brand beat (Build / Create). */
  .hero__chip--code { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero,
  .hero__card::before,
  .hero__title,
  .hero__mascot,
  .hero__chip,
  .hero__blob,
  .hero__scroll-indicator {
    animation: none !important;
  }
}

/* ── Phase 4 polish: reveal delay modifiers (hero copy cascade) ──
   Modifier classes set --reveal-delay; the rule below applies it directly,
   independent of the [style*="--stagger-i"] gate in base.css. */
.reveal--d0 { --reveal-delay: 0ms; }
.reveal--d1 { --reveal-delay: 80ms; }
.reveal--d2 { --reveal-delay: 160ms; }
.reveal--d3 { --reveal-delay: 240ms; }
.reveal--d4 { --reveal-delay: 320ms; }
.js .reveal--d0,
.js .reveal--d1,
.js .reveal--d2,
.js .reveal--d3,
.js .reveal--d4 { transition-delay: var(--reveal-delay, 0ms); }

/* Promos card cascade — explicit nth-child delay because .promos__grid
   is not registered with scroll-stagger.js, and the legacy nth-child
   rule in base.css targets .reveal (not .reveal--up-sm). */
.promos__grid > .story-card:nth-child(2) { transition-delay: 120ms; }

/* Hero chip hover/focus spotlight — spring easing on box-shadow only
   (animation property on .hero__chip is owned by hero-float + spotlight). */
.hero__chip {
  transition: box-shadow 280ms cubic-bezier(0.34, 1.2, 0.64, 1);
}
.hero__chip:hover,
.hero__chip:focus-visible {
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.35),
    0 10px 28px rgba(255, 255, 255, 0.45),
    0 6px 16px rgba(0, 0, 0, 0.22);
}

/* Scroll-indicator pulse — replaces the prior hero-scroll-bounce via
   cascade order. translateX(-50%) is preserved inside the keyframe so the
   centering origin doesn't drift. */
@keyframes hero-scroll-pulse {
  0%, 100% { transform: translate(-50%, -4px); opacity: 0.6; }
  50%      { transform: translate(-50%,  4px); opacity: 1; }
}
.hero__scroll-indicator {
  animation: hero-scroll-pulse 2.4s ease-in-out infinite;
}

/* Promos header divider — a single brand "signal scan" that sweeps in once
   when the section reveals, then rests as a centered gradient line. BEM
   modifier so it doesn't collide with the base .promos__divider. */
.promos__divider--shimmer {
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--color-secondary) 35%,
    var(--color-primary) 50%,
    var(--color-secondary) 65%,
    transparent 100%
  );
  background-size: 300% 100%;
  background-position: 50% 0;
}
.promos__divider--shimmer.is-visible {
  animation: promos-divider-scan 1.3s ease-out 0.15s 1 both;
}
@keyframes promos-divider-scan {
  0%   { background-position: 150% 0; }
  100% { background-position: 50% 0; }
}

/* Mobile aurora dial-down — softer blobs at small viewports. */
@media (max-width: 720px) {
  .hero__blob { opacity: 0.3; filter: blur(4px); }
}

/* Reduced-motion no-op for all Phase 4 additions. */
@media (prefers-reduced-motion: reduce) {
  .hero__chip { transition: none; }
  .hero__scroll-indicator { animation: none !important; }
  .promos__divider--shimmer,
  .promos__divider--shimmer.is-visible {
    animation: none !important;
    background-position: 50% 0;
  }
}


/* ── About ── */

.about {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block: var(--section-py);   /* Phase 8: standardized section rhythm */
  background:
    radial-gradient(circle at 25% 75%, var(--color-secondary-alpha-06) 0%, transparent 50%),
    radial-gradient(circle at 75% 25%, var(--color-primary-alpha-06) 0%, transparent 50%),
    var(--color-surface);
}

.about__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-2xl);
  align-items: center;
  position: relative;
}

/* Phase 5.2: registered custom property so the rotor-blade mask
   `conic-gradient` can interpolate `var(--wipe)` smoothly inside
   @keyframes. Without @property the value would jump between keyframe
   stops (still functional as a fast cut, but not the smooth sweep). */
@property --wipe {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@media (min-width: 768px) {
  .about__inner {
    grid-template-columns: 2fr 1fr;
    gap: var(--spacing-3xl);
  }
}

@media (min-width: 1024px) {
  .about__inner {
    grid-template-columns: 1fr 400px;
  }
}

/* Glass content card around the kicker + pill + body copy */
.about__content {
  position: relative;
  padding: var(--spacing-2xl);
  background: var(--color-surface-glass-stronger);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--color-overlay-white-border);
  border-radius: var(--radius-xl);
  box-shadow:
    var(--shadow-card-glass),
    inset 0 1px 0 var(--color-surface-glass-max);
}

.about__content::before {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: calc(var(--radius-xl) + 2px);
  opacity: 0.15;
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary), var(--color-primary));
  background-size: 200% 200%;
  animation: about-border-glow 6s ease-in-out infinite;
}

.about__head {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  align-items: flex-start;
  margin-bottom: var(--spacing-xl);
}

@media (min-width: 1262px) {
  /* canonical: flex-direction: row; margin-bottom: 2.5rem at 1262+ */
  .about__head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    margin-bottom: calc(var(--spacing-xl) + var(--spacing-sm));   /* 40px = 2.5rem */
  }
}

/* Big animated kicker (was a tiny label before) */
.about__kicker {
  position: relative;
  font-size: clamp(2.5rem, 5vw, 3.5rem);
  font-weight: var(--font-weight-extrabold);
  line-height: var(--line-height-extra-tight);   /* 1.1 (canonical) */
  letter-spacing: 0.02em;
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 4px var(--color-primary-alpha-20));
  margin: 0;
}

.about__kicker::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -8px;
  width: 80px;
  height: 6px;
  background: var(--color-secondary);
  border-radius: 3px;
  box-shadow: 0 2px 8px var(--color-secondary-alpha-30);
  /* Phase 5.1: drawn-in underline on section settle. */
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 450ms cubic-bezier(0.16, 1, 0.3, 1) 300ms;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .about__kicker { color: var(--color-primary); background: none; }
}

/* Filled gradient pill with shimmer sweep */
.about__pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  overflow: hidden;
  padding: 0.875rem 2.25rem;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  border-radius: var(--radius-pill);
  font-size: clamp(0.875rem, 2vw, 1rem);
  font-weight: var(--font-weight-bold);
  line-height: 1;   /* canonical uses /1 in font-shorthand — without this, body's 1.6 line-height makes the pill taller than spec */
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-inverse);
  box-shadow:
    0 6px 20px var(--color-primary-alpha-25),
    inset 0 1px 0 var(--color-overlay-white-soft);
}

.about__pill::before {
  content: "";
  position: absolute;
  inset: 0;
  left: -100%;
  background: linear-gradient(90deg, transparent, var(--color-overlay-white-medium), transparent);
  animation: about-pill-shine 4s ease-in-out infinite;
}

.about__text {
  font-size: clamp(1rem, 2.5vw, 1.125rem);
  line-height: var(--line-height-base);
  color: var(--color-text);
  max-width: 65ch;
}

/* Inline highlight for the "Program" / "program" terms (mimics the glossary
   auto-link color treatment from the live Moodle site without re-introducing
   the underlying anchor + tooltip). */
.about__keyword {
  position: relative;
  color: var(--color-primary);
  font-weight: inherit;
  text-decoration: none; /* the gradient ::after underline is the affordance */
}

.about__keyword:hover,
.about__keyword:focus-visible {
  color: var(--color-primary-dark);
}

/* Phase 5.1: gradient underline draws in on section settle, staggered
   across the two keywords so the eye walks the paragraph. */
.about__keyword::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
  border-radius: 1px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Section CTA — reuses .btn .btn--ghost; only spacing belongs here. */
.about__cta {
  margin-block-start: var(--spacing-lg);
}

/* Phase 5.1: arrow gets its own span so we can nudge it independently of
   the CTA's hover transform. The nudge plays once when the section settles
   to hint at interactivity. */
.about__cta-arrow {
  display: inline-block;
  margin-inline-start: 0.35em;
  will-change: transform;
}

/* Slideshow image (visible at all breakpoints; mobile gets a shorter frame) */
.about__image {
  position: relative;
  display: block;
  margin: 0;
}

/* Tilted-card backdrop: rotates 360° over 8s, pokes its corners out from
   behind the image at 5-15° angles for the offset/layered look. Inset
   matches the canonical SCSS (-4px), but the visible "tilted card" effect
   comes from the corners protruding during rotation.

   Phase 5.1: ambient rotation is now gated on section settle so the rim
   genuinely "spins up" when the user arrives (windmill / turbine metaphor)
   instead of running unseen above the fold. Pre-settle the diamond is a
   static frame. */
.about__image::before {
  content: "";
  position: absolute;
  inset: -4px;
  z-index: -1;
  border-radius: var(--radius-xl);   /* 28px = 24 (slides) + 4 (inset) — canonical match */
  opacity: 0;   /* GCO-97 candidate: hidden until the fly-in entrance fires on settle */
  background: conic-gradient(from 45deg, var(--color-primary), var(--color-secondary), var(--color-primary));
}

/* Phase 5.1: outer counter-rotating ring. Larger, fainter, opposite
   direction, 1/3 the speed — reads as a turbine/gyroscope ring around
   the inner diamond. Fades in on settle so it never spins unseen. */
.about__rim--outer {
  position: absolute;
  inset: -18px;
  z-index: -1;
  pointer-events: none;
  border-radius: calc(var(--radius-xl) + 14px);
  opacity: 0;
  background: conic-gradient(
    from 225deg,
    transparent 0deg,
    var(--color-secondary) 80deg,
    transparent 160deg,
    var(--color-primary) 260deg,
    transparent 340deg
  );
  transition: opacity 700ms ease-out 350ms;
}

/* Phase 5.1: four corner ticks — radar/sensor pings on arrival.
   Single-shot, staggered, then quiet. */
.about__tick {
  position: absolute;
  width: 10px;
  height: 10px;
  z-index: 1;
  pointer-events: none;
  border-radius: var(--radius-full);
  background: var(--color-secondary);
  box-shadow: 0 0 12px var(--color-secondary-alpha-30);
  opacity: 0;
}
.about__tick--tl { top: -6px;    left: -6px;   }
.about__tick--tr { top: -6px;    right: -6px;  }
.about__tick--bl { bottom: -6px; left: -6px;   }
.about__tick--br { bottom: -6px; right: -6px;  }

.about__image::after {
  content: "";
  position: absolute;
  top: 10%;
  left: 10%;
  width: 30%;
  height: 20%;
  border-radius: var(--radius-full);
  background: linear-gradient(135deg, var(--color-overlay-white-shine), transparent);
  filter: blur(8px);
  animation: about-shine-drift 6s ease-in-out infinite;
}

.about__slides {
  position: relative;
  width: 100%;
  height: 220px;
  border-radius: var(--radius-2xl);   /* 24px (canonical) */
  overflow: hidden;
  box-shadow: var(--shadow-slide-stack);
}

@media (min-width: 480px)  { .about__slides { height: 280px; } }
@media (min-width: 768px)  { .about__slides { height: 360px; } }
@media (min-width: 1024px) { .about__slides { height: 400px; } }

/* Animation runs by default (no-JS path keeps cycling); when the section
   is off-screen, JS adds .is-paused and the animations halt to save paint. */
.about__slides.is-paused .about__slide {
  animation-play-state: paused;
}

.about__slide {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  /* Phase 5.2: rotor-blade wipe transition. Slides stay fully painted;
     visibility is controlled by a conic-gradient mask whose wedge angle
     (`--wipe`) sweeps 0deg → 360deg clockwise from 12 o'clock. The
     sweep direction matches `about-rim-rotate`, so the whole figure
     reads as one machine. The z-index dance below ensures the wiping-in
     slide paints OVER the just-finished slide (z: 10 active, 9 previous,
     1 idle), so the blade sweep reveals the new image over the old. */
  opacity: 1;
  z-index: 1;
  --wipe: 0deg;
  -webkit-mask-image: conic-gradient(
    from -90deg at 50% 50%,
    black 0deg,
    black var(--wipe),
    transparent var(--wipe),
    transparent 360deg
  );
  mask-image: conic-gradient(
    from -90deg at 50% 50%,
    black 0deg,
    black var(--wipe),
    transparent var(--wipe),
    transparent 360deg
  );
  animation: about-rotor-cycle 36s infinite linear;
}

/* Phase 5.2: negative delays so at t=0 slide 7 is already the "previous"
   background (frame 14.3% — z-index 9, fully painted) and slide 1 is
   beginning its wipe-in (frame 0% — z-index 10, mask closed). All other
   slides sit at z-index 1 fully painted but covered by the active pair. */
.about__slide--1 { background-image: url("../images/gallery/students-stem-01.webp"); animation-delay:   0s;     }
.about__slide--2 { background-image: url("../images/gallery/students-stem-02.webp"); animation-delay: -30.86s; }
.about__slide--3 { background-image: url("../images/gallery/students-stem-04.webp"); animation-delay: -25.71s; }
.about__slide--4 { background-image: url("../images/gallery/students-stem-05.webp"); animation-delay: -20.57s; }
.about__slide--5 { background-image: url("../images/gallery/students-stem-06.webp"); animation-delay: -15.43s; }
.about__slide--6 { background-image: url("../images/gallery/students-stem-15.webp"); animation-delay: -10.29s; }
.about__slide--7 { background-image: url("../images/gallery/students-stem-17.webp"); animation-delay:  -5.14s; }

/* Phase 5.1: 7-segment carousel progress bar under the photo. Each dot
   tracks the matching slide via the same 36s cycle and animation-delay
   keypoints, so the fill scrubs left-to-right then resets in lockstep
   with the crossfade. Paused alongside the slides via :has(). */
.about__progress {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 14px;
}

.about__progress-dot {
  position: relative;
  width: 24px;
  height: 3px;
  border-radius: var(--radius-sm);
  background: var(--color-overlay-white-medium);
  overflow: hidden;
}

.about__progress-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
  border-radius: inherit;
  transform: scaleX(0);
  transform-origin: left center;
  animation: about-progress-fill 36s infinite linear;
}

.about__image:has(.about__slides.is-paused) .about__progress-dot::after {
  animation-play-state: paused;
}

/* Per-dot delays mirror the slide animation-delay keypoints (36 / 7). */
.about__progress-dot--1::after { animation-delay: 0s;     }
.about__progress-dot--2::after { animation-delay: 5.14s;  }
.about__progress-dot--3::after { animation-delay: 10.29s; }
.about__progress-dot--4::after { animation-delay: 15.43s; }
.about__progress-dot--5::after { animation-delay: 20.57s; }
.about__progress-dot--6::after { animation-delay: 25.71s; }
.about__progress-dot--7::after { animation-delay: 30.86s; }

/* Animated bottom edge accent */
.about__divider {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--color-primary), var(--color-secondary), var(--color-primary));
  background-size: 200% 100%;
  animation: about-divider-flow 5s ease-in-out infinite;
}

/* Phase 5.2: rotor-blade cycle. One iteration = one slide's full life:
   wipe-in (0–4%, ~1.44s sweep CW), in-hold-on-top (4–14.286%, the 1/7
   slot at z-10), revealed-underneath-while-next-wipes-in (14.30–28.57%,
   z-9 so the incoming wedge unveils us), then idle background for the
   rest of the cycle (z-1) until our delay loops us back to 0%.

   The `--wipe` value stays at 360deg from 4% onward so we stay fully
   painted as the lower-tier background. The 100%→0% snap-back to
   `--wipe: 0deg` happens while we're at z-1 covered by every other
   active slide, so it never shows. */
@keyframes about-rotor-cycle {
  0%      { --wipe:   0deg; z-index: 10; }
  4%      { --wipe: 360deg; z-index: 10; }
  14.286% { --wipe: 360deg; z-index: 10; }
  14.29%  {                  z-index: 9;  }
  28.572% { --wipe: 360deg; z-index: 9;  }
  28.58%  {                  z-index: 1;  }
  100%    { --wipe: 360deg; z-index: 1;  }
}

@keyframes about-border-glow {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

@keyframes about-pill-shine {
  0%        { left: -100%; }
  50%, 100% { left: 100%; }
}

@keyframes about-rim-rotate {
  from { transform: rotate(0); }
  to   { transform: rotate(360deg); }
}

/* Phase 5.1: spin-up burst played once on settle. Two full revolutions in
   1.4s lands back at 0deg, so chaining the ambient about-rim-rotate after
   it produces no visible snap. */
@keyframes about-rim-spinup {
  from { transform: rotate(0); }
  to   { transform: rotate(720deg); }
}

/* GCO-97 candidate: rim flies in from off-left while spinning two full turns
   and fading in; ends at translateX(0)/rotate(0) so the ambient rotate chains
   with no snap. Spring easing gives the "snap into place" dock. */
@keyframes about-rim-flyin {
  from { opacity: 0;    transform: translateX(-130%) rotate(-720deg); }
  to   { opacity: 0.35; transform: translateX(0) rotate(0); }
}

/* Phase 5.1: counter-rotating outer ring runs the opposite direction at
   1/3 the inner speed (24s vs 8s) for the gyroscope/turbine feel. */
@keyframes about-rim-counter-rotate {
  from { transform: rotate(0); }
  to   { transform: rotate(-360deg); }
}

/* Phase 5.1: pill micro-bounce on arrival — overshoot then settle. */
@keyframes about-pill-pop {
  0%   { transform: scale(0.92); }
  60%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}

/* Phase 5.1: CTA arrow nudge — single forward-then-back hint. */
@keyframes about-arrow-nudge {
  0%, 100% { transform: translateX(0); }
  40%      { transform: translateX(6px); }
  70%      { transform: translateX(2px); }
}

/* Phase 5.1: corner tick ping — expand-and-fade like a sensor sweep. */
@keyframes about-tick-ping {
  0%   { opacity: 0; transform: scale(0.4); }
  20%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(2.4); }
}

/* Phase 5.1: carousel progress fill — scrubs during slide hold (0-14%),
   stays full through the crossfade (14-16%), then resets instantly so the
   next dot's delay picks up cleanly. */
@keyframes about-progress-fill {
  0%      { transform: scaleX(0); }
  14%     { transform: scaleX(1); }
  16%     { transform: scaleX(1); }
  16.01%, 100% { transform: scaleX(0); }
}

@keyframes about-shine-drift {
  /* Phase 5: bumped shine opacity floor/peak +0.10 to pair with rim 0.35 bump. */
  0%, 100% { transform: translate(0, 0); opacity: 0.4; }
  50%      { transform: translate(20px, -10px); opacity: 0.7; }
}

@keyframes about-divider-flow {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .about__slide,
  .about__content::before,
  .about__pill::before,
  .about__image::before,
  .about__image::after,
  .about__divider,
  .about__rim--outer,
  .about__tick,
  .about__cta-arrow,
  .about__progress-dot::after {
    animation: none !important;
  }
  /* Phase 5.2: with the rotor mask, the default mask is fully closed.
     For reduced motion we need slide 1 fully revealed AND on top, with
     all other slides masked away so they don't paint stacked. */
  .about__slide   { --wipe: 0deg;   z-index: 1; }
  .about__slide--1 { --wipe: 360deg; z-index: 2; opacity: 1; }
  /* Static end-states for the settle flourishes so the layout doesn't
     look half-built when motion is off. */
  .about__kicker::after,
  .about__keyword::after { transform: scaleX(1); transition: none; }
  .about__rim--outer { opacity: 0.20; }
  .about__image::before { opacity: 0.35; }   /* GCO-97: static rim when motion off */
  .about__tick { opacity: 0; }
  .about__progress-dot--1::after { transform: scaleX(1); }
}

/* Phase 5 — About card hover lift.
   Subtle 2px elevation on hover/focus-within for the content card.
   Wrapped in reduced-motion no-op below. */
.about__content {
  transition: transform 250ms ease, box-shadow 250ms ease;
}
.about__content:hover,
.about__content:focus-within {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}

@media (prefers-reduced-motion: reduce) {
  .about__content {
    transition: none;
  }
  .about__content:hover,
  .about__content:focus-within {
    transform: none;
    box-shadow: none;
  }
}

/* ============================================================ */
/* Phase 5.1 — About arrival layer.                             */
/* Triggered by `.about.is-settled` (added by initSectionSettle  */
/* in main.js when the section is ~40% visible AND past the     */
/* 15% rootMargin line). Everything below is additive and fires */
/* once; reduced-motion users get the static end-states above.  */
/* ============================================================ */

/* Kicker underline draws from left to right on arrival. */
.about.is-settled .about__kicker::after {
  transform: scaleX(1);
}

/* Keyword underlines draw after the paragraph fade settles, staggered
   so the eye walks left-to-right through the body copy. */
.about.is-settled .about__keyword:nth-of-type(1)::after {
  transform: scaleX(1);
  transition-delay: 700ms;
}
.about.is-settled .about__keyword:nth-of-type(2)::after {
  transform: scaleX(1);
  transition-delay: 900ms;
}

/* Pill micro-bounce — one-shot overshoot just after the kicker lands. */
.about.is-settled .about__pill {
  animation: about-pill-pop 500ms cubic-bezier(0.34, 1.56, 0.64, 1) 450ms 1 backwards;
}

/* CTA arrow nudge — plays once after the cascade completes. */
.about.is-settled .about__cta-arrow {
  animation: about-arrow-nudge 900ms ease-in-out 1200ms 1;
}

/* Rim spin-up: 720° in 1.4s (windmill startup), then ambient 8s rotation
   forever. The 720° endpoint is congruent to 0° so the handoff to the
   ambient keyframe is seamless. */
.about__image.rim-flown::before {
  /* GCO-97 candidate: fly in from the left while spinning, overshoot-dock,
     then hand off to the perpetual rotate. Triggered when the image itself
     is well in view (initSectionSettle rim-flown @ 0.55), and lengthened so
     the spin reads smoothly. */
  animation:
    about-rim-flyin 1.6s cubic-bezier(0.34, 1.2, 0.64, 1) 0s 1 forwards,
    about-rim-rotate 8s linear 1.6s infinite;
}

/* Outer ring fades in then counter-rotates. */
.about.is-settled .about__rim--outer {
  opacity: 0.20;
  animation: about-rim-counter-rotate 24s linear 400ms infinite;
}

/* Tick pings fire clockwise from the top-left corner. */
.about.is-settled .about__tick {
  animation: about-tick-ping 1.4s cubic-bezier(0.2, 0.7, 0.2, 1) 1 forwards;
}
.about.is-settled .about__tick--tr { animation-delay: 120ms; }
.about.is-settled .about__tick--br { animation-delay: 240ms; }
.about.is-settled .about__tick--bl { animation-delay: 360ms; }


/* ── Why ── */

.why {
  position: relative;
  overflow: hidden;
  padding-block: var(--section-py);   /* Phase 8: standardized section rhythm */
  background: var(--color-surface);
}

/* The inner wrap IS the gradient card. Image + mascot are positioned
   relative to it (mobile: bottom-right inside; desktop: hanging off the
   right edge for visual punch). */
.why__inner {
  position: relative;
  z-index: var(--z-base);
  overflow: visible;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-xl);
  align-items: center;
  padding: var(--spacing-3xl) var(--spacing-2xl);   /* 64×48 (canonical 4rem 3rem) */
  background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-primary) 100%);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card-brand);
  min-height: clamp(420px, 48vw, 620px);
}

/* "STEM" watermark behind the content — matches canonical positioning
   approach (centered absolute with translate, full-width text-align center). */
.why__inner::before {
  content: "STEM";
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  width: 100%;
  text-align: center;
  font-size: clamp(8rem, 15vw, 12rem);
  font-weight: 900;
  letter-spacing: 0.1em;
  /* Phase 5: bumped watermark alpha to 0.22 (was --color-overlay-white-faint = 0.08)
     for a touch more presence behind the copy. Parallax var supplied by
     hero-effects.js (HeroParallax.registerWhy), with a 0px fallback. */
  color: rgba(255, 255, 255, 0.22);
  pointer-events: none;
  transform: translate(-50%, -50%) translate3d(0, var(--parallax-why, 0px), 0) rotate(-5deg);
}

/* Soft halo accent in the upper-right of the card */
.why__inner::after {
  content: "";
  position: absolute;
  top: 20%;
  right: 15%;
  width: 120px;
  height: 120px;
  border-radius: var(--radius-full);
  background: radial-gradient(circle, var(--color-overlay-white-light) 0%, transparent 70%);
  z-index: 1;
  pointer-events: none;
}

@media (min-width: 768px) {
  /* canonical: padding: 5rem 4rem (80×64); grid 3fr 2fr; gap 4rem */
  .why__inner {
    grid-template-columns: 3fr 2fr;
    gap: var(--spacing-3xl);
    padding: calc(var(--spacing-3xl) + var(--spacing-md)) var(--spacing-3xl);   /* 80×64 */
  }
}

@media (min-width: 1024px) {
  /* canonical: padding: 6rem 5rem (96×80) */
  .why__inner { padding: var(--spacing-4xl) calc(var(--spacing-3xl) + var(--spacing-md)); }   /* 96×80 */
}

.why__text {
  position: relative;
  z-index: 3;
  color: var(--color-text-inverse);
}

.why__title {
  position: relative;
  font-size: clamp(2rem, 5vw, 3.5rem);
  font-weight: var(--font-weight-extrabold);
  line-height: var(--line-height-tight);
  letter-spacing: 0.02em;
  color: var(--color-text-inverse);
  margin-bottom: var(--spacing-lg);   /* 24px = 1.5rem (canonical) */
  background: none;
  -webkit-background-clip: initial;
  background-clip: initial;
}

.why__title::after {
  content: "";
  position: absolute;
  bottom: -8px;
  left: 0;
  width: 120px;
  height: 4px;
  border-radius: 2px;
  background: var(--color-overlay-white-strong);
  animation: why-title-shine 3s ease-in-out infinite;
}

.why__intro {
  font-size: clamp(1.125rem, 2.5vw, 1.25rem);
  line-height: var(--line-height-base);
  color: var(--color-text-inverse);
  opacity: 0.95;
  margin-bottom: var(--spacing-xl);
}

/* Inline highlight for "program" — same role as .about__keyword but tuned
   for the dark blue/teal gradient bg of the Why card. Uses brand gold
   (--color-accent) for high contrast against the gradient — light blue
   shades blend in too much; gold stays clearly readable. */
.why__keyword {
  color: var(--color-accent);
  font-weight: var(--font-weight-semibold);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.why__sub {
  font-size: clamp(1.25rem, 3vw, 1.5rem);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-inverse);
  margin-bottom: var(--spacing-lg);   /* 24px = 1.5rem (canonical) */
}

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

.why__list li {
  position: relative;
  padding-left: var(--spacing-xl);
  margin-bottom: calc(var(--spacing-md) + var(--spacing-xs));
  font-size: clamp(1rem, 2.5vw, 1.125rem);
  line-height: var(--line-height-base);
  color: var(--color-text-inverse);
}

.why__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  background: var(--color-overlay-white-bold);
  box-shadow:
    0 0 8px var(--color-overlay-white-shine),
    0 2px 4px var(--color-overlay-black-light);
  transform: translateY(-50%);
}

/* Student illustration — hangs off the bottom-right of the card on
   mobile (small), centered in its grid cell on tablet, and absolutely
   positioned hanging off the card on desktop */
.why__image {
  position: absolute;
  right: -1.5rem;
  bottom: -2rem;
  margin: 0;
  z-index: 2;
  display: none;
}

.why__image img {
  width: clamp(360px, 32vw, 400px);
  height: auto;
  object-fit: contain;
  object-position: bottom right;
  filter: drop-shadow(var(--shadow-image-heavy));
  animation: why-float 4s ease-in-out infinite;
}

@media (min-width: 768px) {
  .why__image {
    position: relative;
    right: auto;
    bottom: auto;
    display: flex;
    justify-content: center;
    margin-top: var(--spacing-lg);   /* 24px = 1.5rem (canonical) */
    z-index: 2;
  }
  .why__image img { width: clamp(260px, 46vw, 380px); }
}

@media (min-width: 1024px) {
  .why__image {
    position: absolute;
    right: -3rem;
    bottom: -3rem;
    display: block;
    margin-top: 0;
  }
  .why__image img { width: clamp(360px, 32vw, 400px); }
}

@media (min-width: 1400px) {
  .why__image { right: -2rem; bottom: clamp(-4rem, -7vw, -8rem); }
}

/* Breeze mascot — bottom-left of the card, bouncing */
.why__mascot {
  position: absolute;
  bottom: -2rem;
  left: var(--spacing-xl);
  margin: 0;
  z-index: 4;
}

.why__mascot img {
  width: clamp(120px, 15vw, 200px);
  height: auto;
  object-fit: contain;
  filter: drop-shadow(var(--shadow-image-soft));
  animation: why-mascot-bounce 3s ease-in-out infinite;
}

@media (min-width: 768px) {
  .why__mascot { bottom: -5rem; left: var(--spacing-2xl); }
  .why__mascot img { width: clamp(140px, 18vw, 220px); }
}

@media (min-width: 1024px) {
  .why__mascot { bottom: -2.5rem; left: calc(var(--spacing-2xl) + var(--spacing-sm)); }
  .why__mascot img { width: clamp(160px, 16vw, 240px); }
}

@media (min-width: 1400px) {
  /* canonical: bottom: -3.25rem; left: 3.75rem */
  .why__mascot { bottom: -3.25rem; left: calc(var(--spacing-2xl) + var(--spacing-md) - var(--spacing-xs)); }   /* 48 + 16 − 4 = 60px = 3.75rem */
  .why__mascot img { width: clamp(180px, 14vw, 260px); }
}

/* GCO-97 candidate: Breeze + the student converge into the card on settle.
   Hidden until the section settles (JS on); each arrives with spring easing.
   The per-image float/bounce lives on the child <img>, so it composes with
   the figure's slide-in rather than fighting it. */
.js .why__image,
.js .why__mascot {
  opacity: 0;
}
.why.is-settled .why__image {
  animation: why-image-arrive 0.9s cubic-bezier(0.34, 1.2, 0.64, 1) 0.1s both;
}
.why.is-settled .why__mascot {
  animation: why-mascot-arrive 0.9s cubic-bezier(0.34, 1.2, 0.64, 1) 0.25s both;
}
@keyframes why-image-arrive {
  from { opacity: 0; transform: translateX(70px) rotate(3deg); }
  to   { opacity: 1; transform: translateX(0) rotate(0); }
}
@keyframes why-mascot-arrive {
  from { opacity: 0; transform: translateX(-60px) translateY(22px) rotate(-8deg); }
  to   { opacity: 1; transform: translateX(0) translateY(0) rotate(0); }
}

/* Animated bottom edge accent on the section (not the card) */
.why__divider {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--color-secondary), var(--color-primary), var(--color-secondary));
  background-size: 200% 100%;
  animation: why-divider-flow 4s ease-in-out infinite;
}

@keyframes why-title-shine {
  0%, 100% { background: var(--color-overlay-white-strong); box-shadow: 0 0 8px var(--color-overlay-white-border); }
  50%      { background: var(--color-overlay-white-full);   box-shadow: 0 0 12px var(--color-overlay-white-half); }
}

@keyframes why-float {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50%      { transform: translateY(-8px) rotate(1deg); }
}

@keyframes why-mascot-bounce {
  0%, 100% { transform: translateY(0) rotate(2deg); }
  50%      { transform: translateY(-6px) rotate(-2deg); }
}

@keyframes why-divider-flow {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

@media (max-width: 767px) {
  .why__image { display: none; }
}

.why.is-paused .why__title::after,
.why.is-paused .why__image img,
.why.is-paused .why__mascot img,
.why.is-paused .why__divider {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .why__title::after,
  .why__image img,
  .why__mascot img,
  .why__divider {
    animation: none !important;
  }
  /* GCO-97 candidate: show the converging figures statically when motion off. */
  .js .why__image,
  .js .why__mascot {
    opacity: 1;
    animation: none !important;
  }
}


/* ── Provide ── */

.provide {
  position: relative;
  padding-block: var(--section-py);   /* Phase 8: standardized section rhythm */
  background:
    radial-gradient(circle at 25% 25%, var(--color-secondary-alpha-04) 0%, transparent 50%),
    radial-gradient(circle at 75% 75%, var(--color-primary-alpha-04) 0%, transparent 50%),
    var(--color-surface-subtle);   /* P8.5: alternating surface rhythm */
}

/* IO-gated pause: provide-animations.js toggles .is-paused on .provide
   when the section is off-screen, halting the three infinite animations
   below to save paint cycles on long pages. */
.provide.is-paused .provide__column::before,
.provide.is-paused .provide__title::after,
.provide.is-paused .provide__divider::before {
  animation-play-state: paused;
}

.provide__inner {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-2xl);
}

@media (min-width: 768px) {
  /* No align-items: start — let the default (stretch) keep both columns
     at equal height, matching the live original. The canonical SCSS sets
     align-items: start, but the live site renders both cards equal-height
     (live CSS wins over the SCSS in the repo). */
  .provide__inner {
    grid-template-columns: 1fr 1fr;
    gap: var(--spacing-3xl);
  }
}

/* Glass column with animated gradient ring + lift on hover */
.provide__column {
  position: relative;
  /* canonical mobile: 3rem 2.5rem */
  padding: var(--spacing-2xl) calc(var(--spacing-xl) + var(--spacing-sm));   /* 48×40 */
  background: var(--color-surface-glass-max);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--color-overlay-white-mild);
  border-radius: var(--radius-2xl);   /* 24px (canonical) */
  box-shadow:
    0 12px 24px var(--color-overlay-black-faint),
    0 4px 8px var(--color-overlay-black-soft),
    inset 0 1px 0 var(--color-overlay-white-strong);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

@media (min-width: 768px) {
  /* canonical tablet: 3.5rem 3rem */
  .provide__column { padding: calc(var(--spacing-2xl) + var(--spacing-sm)) var(--spacing-2xl); }   /* 56×48 */
}

@media (min-width: 1024px) {
  /* canonical desktop: 4rem 3.5rem */
  .provide__column { padding: var(--spacing-3xl) calc(var(--spacing-2xl) + var(--spacing-sm)); }   /* 64×56 */
}

.provide__column::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: calc(var(--radius-2xl) + 1px);   /* 25px = 24 (column) + 1 (inset) — canonical */
  opacity: 0.10;
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
  background-size: 200% 200%;
  animation: provide-border-glow 8s ease-in-out infinite;
}

.provide__column:hover {
  transform: translateY(calc(-1 * var(--spacing-xs)));
  box-shadow:
    0 20px 40px var(--color-overlay-black-medium),
    0 8px 16px var(--color-overlay-black-faint),
    inset 0 1px 0 var(--color-overlay-white-bold);
}

.provide__title {
  position: relative;
  font-size: clamp(1.75rem, 4vw, 2.25rem);
  font-weight: var(--font-weight-extrabold);
  letter-spacing: 0.02em;
  margin-bottom: var(--spacing-xl);
}

.provide__title::after {
  content: "";
  position: absolute;
  bottom: calc(-1 * var(--spacing-sm));
  left: 0;
  width: var(--width-accent-line);
  height: var(--spacing-xs);
  border-radius: var(--radius-xs);
  background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
  animation: provide-title-shine 4s ease-in-out infinite;
}

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

/* List-item styles — bullets on BOTH columns (matching the live original;
   the canonical SCSS scopes padding-left + ::before to .provide-list only,
   but the rendered live site shows teal dots on both lists). */
.provide__list li {
  position: relative;
  margin-bottom: var(--spacing-lg);   /* 24px = 1.5rem (canonical) */
  padding-left: var(--spacing-xl);   /* 32px = 2rem */
  font-size: clamp(1rem, 2.5vw, 1.125rem);
  line-height: var(--line-height-base);
  color: var(--color-text);
  /* Phase 6: entrance is owned by `.reveal--up-sm` + initScrollStagger
     (`.provide__list` is registered in main.js). The legacy per-li
     `provide-fade-in` keyframe + nth-child delays were retired so the
     two mechanisms no longer fight over opacity/transform. */
}

.provide__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: var(--spacing-sm);
  height: var(--spacing-sm);
  border-radius: var(--radius-full);
  background: var(--color-secondary);
  box-shadow:
    0 0 8px var(--color-secondary-alpha-40),
    0 2px 4px var(--color-overlay-black-light);
  transform: translateY(-50%);
}

/* A1 (scientific upgrade): each marker fires a single sensor "ping" the
   instant its bullet reveals — the same instrument language as the About
   corner ticks (reuses `about-tick-ping`). Centred on the dot via offsets
   (no translate) so the keyframe's scale() expands cleanly from centre. */
.provide__list li::after {
  content: "";
  position: absolute;
  left: 0;
  top: calc(0.7em - (var(--spacing-sm) / 2));
  width: var(--spacing-sm);
  height: var(--spacing-sm);
  border-radius: var(--radius-full);
  background: var(--color-secondary);
  transform-origin: center;
  opacity: 0;
  pointer-events: none;
}

.provide__list .reveal--up-sm.is-visible::after {
  animation: about-tick-ping 1.1s ease-out forwards;
}

/* A2: bullets settle with a gentle spring overshoot instead of a linear
   glide (same curve Phase 7 reserves for the CTA button). */
.provide__list .reveal--up-sm {
  transition-timing-function: cubic-bezier(0.34, 1.2, 0.64, 1);
}

/* Structure list: bold dates colored brand blue */
.provide__list--structure li strong {
  font-weight: var(--font-weight-bold);
  color: var(--color-primary-dark);
}

/* Vertical center divider (desktop only) — decorative */
.provide__divider {
  display: none;
}

@media (min-width: 768px) {
  /* SCI (scientific upgrade): the center divider IS a DNA double helix —
     two brand-coloured strands + base-pair rungs that drift slowly
     downward. Replaces the flat glow line; the A3 "energy flow" idea is
     folded into the helix drift. overflow:hidden clips the over-scanned
     SVG so the loop is seamless. */
  .provide__divider {
    display: block;
    position: absolute;
    left: 50%;
    top: 8%;
    bottom: 8%;
    width: 2.25rem;
    transform: translateX(-50%);
    overflow: hidden;
  }

  .provide-helix {
    display: block;
    width: 100%;
    height: 100%;
  }

  .provide-helix__drift {
    animation: provide-helix-drift 14s linear infinite;
  }

  .provide-helix__strand {
    fill: none;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.6;
  }

  .provide-helix__strand--a { stroke: var(--color-primary); }
  .provide-helix__strand--b { stroke: var(--color-secondary); }

  .provide-helix__rungs {
    stroke: var(--color-secondary);
    stroke-width: 1.4;
    stroke-linecap: round;
    opacity: 0.22;
  }

  /* Focal "origin" node at the helix centre (kept from the prior divider). */
  .provide__divider::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--bullet-size-md);
    height: var(--bullet-size-md);
    transform: translate(-50%, -50%);
    background: var(--color-secondary);
    border-radius: var(--radius-full);
    box-shadow:
      0 0 12px var(--color-secondary-alpha-50),
      0 2px 4px var(--color-overlay-black-light);
    animation: provide-divider-pulse 5s ease-in-out infinite;
  }
}

@keyframes provide-border-glow {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

@keyframes provide-title-shine {
  0%, 100% { background: linear-gradient(90deg, var(--color-primary), var(--color-secondary)); box-shadow: 0 0 8px var(--color-primary-alpha-30); }
  50%      { background: linear-gradient(90deg, var(--color-secondary), var(--color-primary)); box-shadow: 0 0 12px var(--color-secondary-alpha-40); }
}

@keyframes provide-divider-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1);   box-shadow: 0 0 16px var(--color-secondary-alpha-50), 0 2px 4px var(--color-overlay-black-light); }
  50%      { transform: translate(-50%, -50%) scale(1.2); box-shadow: 0 0 22px var(--color-secondary-alpha-70), 0 2px 6px var(--color-overlay-black-mild); }
}

/* SCI: helix drifts one full rung period (220 user units = 2 strand
   periods) so both strands and the base-pair pattern loop seamlessly. */
@keyframes provide-helix-drift {
  from { transform: translateY(0); }
  to   { transform: translateY(-220px); }
}

@media (prefers-reduced-motion: reduce) {
  .provide__column,
  .provide__column::before,
  .provide__title::after,
  .provide__divider::before,
  .provide-helix__drift,
  .provide__list li::after {
    animation: none !important;
  }
  .provide__column:hover { transform: none; }
}


/* ── Highlights ── */

.highlights {
  position: relative;
  overflow: hidden;
  padding-block: var(--section-py);   /* Phase 8: standardized section rhythm */
  background: var(--color-surface);
}

/* Spotlight light-pools — two very soft brand glows drift slowly behind
   the cards so "highlight" reads as light catching the glass. Replaces
   the Three.js particle canvas, which moved to the Featured Lesson
   (Design Process) section. */
.highlights::before,
.highlights::after {
  content: "";
  position: absolute;
  z-index: 0;
  border-radius: var(--radius-full);
  pointer-events: none;
  filter: blur(28px);
  opacity: 0.9;
}

.highlights::before {
  width: 52vw;
  height: 52vw;
  max-width: 680px;
  max-height: 680px;
  top: -16%;
  left: -10%;
  background: radial-gradient(circle, var(--color-primary-alpha-40), transparent 70%);
  animation: highlights-pool-a 22s ease-in-out infinite alternate;
}

.highlights::after {
  width: 58vw;
  height: 58vw;
  max-width: 720px;
  max-height: 720px;
  bottom: -20%;
  right: -12%;
  background: radial-gradient(circle, var(--color-secondary-alpha-40), transparent 70%);
  animation: highlights-pool-b 27s ease-in-out infinite alternate;
}

/* IO-gated pause: highlights-animations.js toggles .is-paused on .highlights
   when the section is off-screen, halting the infinite CSS animations to save
   paint cycles on long pages. (The Three.js canvas pauses its own RAF loop
   separately via the IntersectionObserver in featured-bg.js.) */
.highlights.is-paused .highlights__divider::before,
.highlights.is-paused .highlight-card__badge,
.highlights.is-paused::before,
.highlights.is-paused::after {
  animation-play-state: paused;
}

.highlights__inner {
  position: relative;
  z-index: 2;
  text-align: center;
}

.highlights__header {
  margin-bottom: var(--spacing-3xl);   /* 64px = 4rem (canonical) */
}

.highlights__title {
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  font-weight: var(--font-weight-extrabold);
  margin-bottom: var(--spacing-sm);
}

.highlights__subtitle {
  font-size: clamp(1rem, 2.5vw, 1.25rem);
  color: var(--color-text-light);
  font-weight: var(--font-weight-medium);
  margin-bottom: var(--spacing-lg);   /* 24px = 1.5rem (canonical) */
}

/* Animated underline divider with shimmer sweep */
.highlights__divider {
  position: relative;
  width: 120px;
  height: var(--width-accent-line-thin);
  margin: 0 auto;
  overflow: hidden;
  background: linear-gradient(90deg, transparent, var(--color-secondary), var(--color-primary), var(--color-secondary), transparent);
}

.highlights__divider::before {
  content: "";
  position: absolute;
  inset: 0;
  left: -100%;
  background: linear-gradient(90deg, transparent, var(--color-overlay-white-strong), transparent);
  animation: highlights-shimmer 3s ease-in-out infinite;
}

.highlights__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-xl);
  margin-top: var(--spacing-xl);
}

@media (min-width: 768px) {
  .highlights__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--spacing-2xl);
  }
}

@media (min-width: 1024px) {
  /* canonical: gap: 2.5rem at 1024+ */
  .highlights__grid {
    grid-template-columns: repeat(4, 1fr);
    gap: calc(var(--spacing-xl) + var(--spacing-sm));   /* 40px = 2.5rem */
  }
}

/* Glass cards over the canvas. Entry animation handled by .reveal--scale
   on the elements (triggered by IntersectionObserver in main.js when the
   card scrolls into view) rather than firing on page load. */
.highlight-card {
  position: relative;
  overflow: hidden;
  /* canonical mobile: 3rem 2rem */
  padding: var(--spacing-2xl) var(--spacing-xl);   /* 48×32 */
  background: var(--color-overlay-white-mild);
  backdrop-filter: var(--blur-xl) saturate(180%);
  -webkit-backdrop-filter: var(--blur-xl) saturate(180%);
  border: 1px solid var(--color-overlay-white-mild);
  border-radius: var(--radius-2xl);   /* 24px (canonical) */
  box-shadow:
    0 8px 32px var(--color-overlay-black-light),
    inset 0 1px 0 var(--color-overlay-white-border);
  /* Phase 6: lift + ring snap at 250ms; the soft tint keeps its slower fade. */
  transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1),
              background 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

@media (min-width: 768px) {
  /* canonical tablet: 3.5rem 2.5rem */
  .highlight-card { padding: calc(var(--spacing-2xl) + var(--spacing-sm)) calc(var(--spacing-xl) + var(--spacing-sm)); }   /* 56×40 */
}

@media (min-width: 1024px) {
  /* canonical desktop: back to 3rem 2rem */
  .highlight-card { padding: var(--spacing-2xl) var(--spacing-xl); }   /* 48×32 */
}

/* Stagger handled by base.css `.stagger > .reveal:nth-child(N)` rules. */

/* Hover sweep — gradient sheen slides across */
.highlight-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--color-secondary-alpha-10), transparent);
  transition: left 0.6s ease;
}

/* Phase 6: subtler lift via --hc-lift (composed with scroll-scale below)
   plus a teal hover ring + brand drop. Was a -12px jump with a diffuse
   glow; now a tighter, more deliberate hover. */
.highlight-card:hover {
  --hc-lift: -3px;
  background: var(--color-overlay-white-soft);
  box-shadow:
    0 0 0 1px var(--color-secondary-alpha-40),
    0 12px 32px var(--color-primary-alpha-20),
    inset 0 1px 0 var(--color-overlay-white-shine);
}

.highlight-card:hover::before {
  left: 100%;
}

/* Circular gradient badge — number centered */
.highlight-card__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 90px;
  height: 90px;
  margin: 0 auto var(--spacing-md);
  border-radius: var(--radius-full);
  background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
  color: var(--color-text-inverse);
  box-shadow:
    0 8px 24px var(--color-primary-alpha-40),
    inset 0 2px 0 var(--color-overlay-white-border);
  animation: highlights-badge-pulse 4s ease-in-out infinite;
  transition: transform 0.3s ease;
}

/* Phase 6: stagger the idle pulse so the four badges no longer breathe in
   unison (delays 0 / 0.6 / 1.2 / 1.8s across the grid). */
.highlights__grid .highlight-card:nth-child(1) .highlight-card__badge { animation-delay: 0s; }
.highlights__grid .highlight-card:nth-child(2) .highlight-card__badge { animation-delay: 0.6s; }
.highlights__grid .highlight-card:nth-child(3) .highlight-card__badge { animation-delay: 1.2s; }
.highlights__grid .highlight-card:nth-child(4) .highlight-card__badge { animation-delay: 1.8s; }

/* GCO-97 candidate: card text rises in AFTER the card scales in, cascaded
   left -> right. Delayed past the card's ~0.7s scale so it reads as a
   distinct second beat (the words land once the card is in place). */
.highlights__grid .highlight-card:nth-child(1) .highlight-card__text { transition-delay: 0.45s; }
.highlights__grid .highlight-card:nth-child(2) .highlight-card__text { transition-delay: 0.55s; }
.highlights__grid .highlight-card:nth-child(3) .highlight-card__text { transition-delay: 0.65s; }
.highlights__grid .highlight-card:nth-child(4) .highlight-card__text { transition-delay: 0.75s; }

/* Phase 6: badge ring is brand blue/teal only. The shared `.fx-grad-border`
   conic cycles primary -> secondary -> accent -> primary; the accent stop
   reads as an out-of-place colour on the badge, so override just the
   gradient here (rotation, mask, and reduced-motion no-op stay inherited
   from the textures.css base rule — scoped, no other consumers affected). */
.highlight-card__badge.fx-grad-border::before {
  background: conic-gradient(
    from var(--grad-rotation),
    var(--color-primary),
    var(--color-secondary),
    var(--color-primary)
  );
}

.highlight-card:hover .highlight-card__badge {
  animation: highlights-badge-wobble 0.6s ease-in-out;
}

.highlight-card__num {
  font-size: 2.5rem;
  font-weight: var(--font-weight-extrabold);
  line-height: 1;
}

.highlight-card__text {
  max-width: 220px;
  margin: 0 auto;
  font-size: clamp(1.125rem, 2.5vw, 1.25rem);
  line-height: 1.5;   /* canonical — between snug (1.3) and base (1.6) */
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

/* B3: card content micro-stagger. Badge and text each reveal via
   `.reveal--fade`; delaying the text ~110ms after the badge makes each
   card "assemble" instead of popping as one block. (Reduced motion:
   base.css forces `.reveal--fade` visible, so this is inert there.) */
.highlight-card .highlight-card__text.reveal--fade {
  transition-delay: 110ms;
}

/* B1: one-shot scale pop on the number when the count-up starts, so even
   single-digit badges read as "activating" (class toggled by
   highlights-animations.js; never added on the reduced-motion path). */
.highlight-card__num.is-counted {
  animation: hc-num-pop 600ms cubic-bezier(0.34, 1.2, 0.64, 1);
}

@keyframes hc-num-pop {
  0%   { transform: scale(0.55); opacity: 0.4; }
  60%  { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); }
}

@keyframes highlights-shimmer {
  0%   { left: -100%; }
  50%  { left: 100%; }
  100% { left: 100%; }
}

@keyframes highlights-badge-pulse {
  0%, 100% { transform: scale(1);    box-shadow: 0 8px 24px var(--color-primary-alpha-40),  inset 0 2px 0 var(--color-overlay-white-border); }
  50%      { transform: scale(1.05); box-shadow: 0 12px 32px var(--color-primary-alpha-50), inset 0 2px 0 var(--color-overlay-white-shine); }
}

@keyframes highlights-badge-wobble {
  0%, 100% { transform: rotate(0) scale(1); }
  25%      { transform: rotate(-3deg) scale(1.05); }
  75%      { transform: rotate(3deg) scale(1.05); }
}

/* Spotlight pools drift + breathe on slow, offset paths so the light is
   clearly alive without ever competing with the cards. */
@keyframes highlights-pool-a {
  from { transform: translate3d(0, 0, 0) scale(1);          opacity: 0.6; }
  to   { transform: translate3d(120px, 80px, 0) scale(1.2); opacity: 0.95; }
}

@keyframes highlights-pool-b {
  from { transform: translate3d(0, 0, 0) scale(1.15);         opacity: 0.95; }
  to   { transform: translate3d(-110px, -70px, 0) scale(1);   opacity: 0.6; }
}

/* (Fireflies layer + per-dot `--firefly-i` indexer moved to stories.css
   under .featured-lesson__fireflies when the particle background relocated
   to the Featured Lesson section.) */

/* ── Phase 6: card scroll-scale + composed hover lift ──
   --hc-scale is animated by a view() timeline (scroll-driven). --hc-lift
   is flipped on :hover. Composing both into one transform here keeps the
   continuous scrub and the hover translate from clobbering each other, and
   takes transform ownership from `.reveal--scale` (which still owns the
   one-shot opacity fade-in). @property makes --hc-scale interpolate
   smoothly instead of snapping between keyframes. */
@property --hc-scale {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

.highlights__grid .highlight-card {
  --hc-scale: 1;
  transform: translateY(var(--hc-lift, 0px)) scale(var(--hc-scale));
}

@supports (animation-timeline: view()) {
  .highlights__grid .highlight-card {
    animation: hc-scroll-scale linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 32%;
  }

  @keyframes hc-scroll-scale {
    from { --hc-scale: 0.92; }
    to   { --hc-scale: 1; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .highlights::before,
  .highlights::after,
  .highlights__divider::before,
  .highlight-card,
  .highlight-card__badge,
  .highlights__grid .highlight-card,
  .highlight-card__num.is-counted {
    animation: none !important;
  }
  .highlight-card { opacity: 1; }
  .highlights__grid .highlight-card { --hc-scale: 1; }
  .highlight-card:hover { --hc-lift: 0px; }
  .highlight-card:hover .highlight-card__badge { animation: none; }
}


/* ── 404 ── */

.not-found,
.locked-page {
  min-height: calc(100vh - var(--height-header));
  display: flex;
  align-items: center;
  padding-block: var(--spacing-3xl);
}

.not-found__card,
.locked-page__card {
  width: 100%;
  max-width: 540px;
  margin-inline: auto;
  padding: var(--spacing-2xl);
  text-align: center;
  background: var(--color-surface-glass-strong);
  backdrop-filter: var(--blur-md);
  -webkit-backdrop-filter: var(--blur-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}

.not-found__mascot {
  width: 200px;
  height: 200px;
  margin: 0 auto var(--spacing-md);
  filter: drop-shadow(var(--shadow-mascot));
  animation: hero-float 5s ease-in-out infinite;
}

.not-found__code,
.locked-page__eyebrow {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.2em;
  color: var(--color-primary-dark);
  margin-bottom: var(--spacing-sm);
}

.not-found__title,
.locked-page__title {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-extrabold);
  color: var(--color-text);
  margin-bottom: var(--spacing-sm);
}

.not-found__body,
.locked-page__body {
  color: var(--color-text-light);
  margin-bottom: var(--spacing-xl);
}

.locked-page__body:last-of-type {
  margin-bottom: var(--spacing-2xl);
}

/* Button row on the locked-page explainer (Sign in / Design Process / Home). */
.locked-page__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
  .not-found__mascot {
    animation: none !important;
  }
}


/* ── Demo-account banner (Phase 5 grant-launch) ──
   Sticky banner shown on every auth-gated page when the signed-in Firebase
   user has the custom claim role=demo. JS lives in assets/js/demo-banner.js;
   wiring lives in dashboard.js / courses.js / course-viewer.js.

   .is-demo-readonly on <body> visually disables destructive UI affordances
   (primary + destructive buttons + data-action save/delete handles) so the
   reviewer doesn't bump into actions that the backend would 403 anyway. The
   real gate is server-side (_DEMO_BLOCKED_ROUTES + DEMO_ROLE_READONLY). */

.demo-banner {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  flex-wrap: wrap;
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--color-warning-surface);
  color: var(--color-warning-text);
  border-bottom: 1px solid var(--color-accent-dark);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  text-align: center;
  box-shadow: var(--shadow-sm);
}

.demo-banner__pill {
  display: inline-block;
  padding: 2px var(--spacing-sm);
  background: var(--color-warning-text);
  color: var(--color-warning-surface);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.demo-banner__msg {
  flex: 0 1 auto;
}

.demo-banner__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--spacing-sm);
  color: var(--color-warning-text);
  font-weight: var(--font-weight-bold);
  text-decoration: underline;
}

.demo-banner__link:hover,
.demo-banner__link:focus-visible {
  text-decoration: none;
}

.demo-banner__btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--spacing-md);
  background: var(--color-warning-text);
  color: var(--color-warning-surface);
  border: 0;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}

.demo-banner__btn:hover,
.demo-banner__btn:focus-visible {
  filter: brightness(1.08);
}

/* Demo mode: visually disable every destructive / write control. The
   backend _DEMO_BLOCKED_ROUTES 403s these for real, and api.js short-
   circuits the write wrappers - this is the matching affordance. Scoped to
   genuinely destructive selectors only (check-in / uncheck-in / delete /
   bulk delete / save), so read-only browse + report generation (which now
   return synthetic fixtures) stay fully usable for reviewers. Any control
   that must stay live in demo can opt out with .is-demo-allowed. */
.is-demo-readonly .btn--destructive:not(.is-demo-allowed),
.is-demo-readonly .dash-btn--danger:not(.is-demo-allowed),
.is-demo-readonly .reg-checkin-btn:not(.is-demo-allowed),
.is-demo-readonly .reg-delete-row-btn:not(.is-demo-allowed),
.is-demo-readonly [data-action="save"]:not(.is-demo-allowed),
.is-demo-readonly [data-action="delete"]:not(.is-demo-allowed) {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
  filter: grayscale(0.4);
}


/* ── Login hint card (Phase 5 grant-launch reviewer creds) ──
   Shown below the sign-in form when APP_CONFIG.FEATURES.SHOW_DEMO_HINT is
   true. The card is default-hidden (the `hidden` attribute) so a JS failure
   doesn't leak the demo credentials onto every login page; login.js flips
   `hidden = false` only when the flag is on. */

.login-hint {
  margin-top: var(--spacing-lg);
  padding: var(--spacing-md);
  background: var(--color-surface-glass-strong);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  text-align: left;
}

.login-hint__lead {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-primary-dark);
  margin: 0 0 var(--spacing-sm);
}

.login-hint__creds {
  font-size: var(--font-size-sm);
  color: var(--color-text);
  margin: 0 0 var(--spacing-sm);
  line-height: 1.6;
}

.login-hint__creds code {
  background: var(--color-warning-surface);
  color: var(--color-warning-text);
  padding: 1px var(--spacing-xs);
  border-radius: var(--radius-xs);
  font-family: var(--font-family-mono);
  font-size: 0.95em;
}

.login-hint__note {
  font-size: var(--font-size-xs);
  color: var(--color-text-light);
  margin: 0;
}
