@import "./tokens.css";
@import "./chrome.css";
@import "./sections.css";
/* Interaction states and feedback. Last, so its :hover / :focus / [data-*]
   rules land after the resting styles they build on. */
@import "./motion.css";

/* ==========================================================================
   Inter v4 variable (rsms build — carries the `opsz` optical-size axis).
   SIL Open Font License; see /fonts/Inter-LICENSE.txt.
   ========================================================================== */
@font-face {
  font-family: "InterVariable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/InterVariable.woff2") format("woff2");
}

/* ==========================================================================
   Reset
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans);
  font-optical-sizing: auto; /* Inter v4 Display forms at large sizes */
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color var(--duration) var(--ease),
    color var(--duration) var(--ease);
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: none;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 800;
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-heading);
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Visible only to assistive tech until focused. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -100px;
  z-index: 100;
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: var(--radius-pill);
  font-weight: 700;
  transition: top var(--duration) var(--ease);
}
.skip-link:focus {
  top: var(--space-4);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-6);
}

.section {
  padding-block: clamp(var(--space-12), 7vw, var(--space-20));
}
.section--tight {
  padding-block: clamp(var(--space-10), 5vw, var(--space-16));
}

/* The header floats over the page rather than sitting in the flow, so the
   first band on every page is what reserves its height. Written as a floor,
   not an addition, so bands that are already taller than the navbar keep the
   spacing the design gives them. */
main > :first-child {
  padding-block-start: max(
    var(--section-pad-start, clamp(var(--space-12), 7vw, var(--space-20))),
    var(--header-offset)
  );
}
main > .section--tight:first-child {
  --section-pad-start: clamp(var(--space-10), 5vw, var(--space-16));
}

/* In-page anchors (#faq, #videos, #contact) must clear the floating pill. */
main [id] {
  scroll-margin-top: calc(var(--nav-block) + var(--space-4));
}

/* Section background variants map 1:1 to the bands in the Figma frames. */
.section--plain {
  background: var(--bg);
}
.section--subtle {
  background: var(--bg-subtle);
}
.section--tint {
  background: var(--bg-tint);
}
.section--soft {
  background: var(--bg-brand-soft);
  color: var(--fg-on-soft);
}
.section--brand {
  background: var(--bg-brand);
  color: var(--fg-on-brand);
}
.section--hero {
  background: var(--bg-hero);
  color: var(--fg-on-brand);
}
/* A small navy banner used in place of the tall light hero on the three
   legal pages (Privacy, Terms, Refund). Dark-blue surface, light copy,
   compact vertical rhythm — a header strip, not a full-viewport hero. */
.section--legal-banner {
  background: var(--bg-brand);
  color: var(--fg-on-brand);
  padding-block-end: clamp(var(--space-8), 4vw, var(--space-12));
}
main > .section--legal-banner:first-child {
  --section-pad-start: clamp(var(--space-8), 4vw, var(--space-12));
}
.section--legal-banner .overline {
  color: var(--fg-on-brand);
  opacity: 0.72;
}
.section--legal-banner .lead {
  color: var(--fg-on-brand);
  opacity: 0.85;
}

/* The header pill is transparent before the reader scrolls, so on the legal
   pages it floats over the navy legal banner. Dark nav text is unreadable
   there, so lift it to white while un-scrolled; once scrolled the pill turns
   to opaque clay again and the default dark text takes back over. Mirrors the
   experts-category / team / programs-register treatment for dark banners. */
body[data-page="legal"] .header:not([data-scrolled='true']) .header__link,
body[data-page="legal"] .header:not([data-scrolled='true']) .header__login,
body[data-page="legal"] .header:not([data-scrolled='true']) .header__menu-btn,
body[data-page="legal"] .header:not([data-scrolled='true']) .header__link:hover,
body[data-page="legal"] .header:not([data-scrolled='true']) .header__nav:not(:hover) .header__link[aria-current='page'] {
  color: #fff;
}
body[data-page="legal"] .header:not([data-scrolled='true']) .header__login {
  border-color: rgba(255, 255, 255, 0.5);
}

.stack {
  display: flex;
  flex-direction: column;
}

.grid {
  display: grid;
  gap: var(--space-6);
}
.grid--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* Split layouts: copy on one side, media or a form on the other. */
.split {
  display: grid;
  gap: clamp(var(--space-8), 5vw, var(--space-16));
  align-items: start;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.split--copy-wide {
  grid-template-columns: 1.15fr 1fr;
}
.split--form {
  grid-template-columns: 1fr 1.02fr;
  align-items: start;
}
/* Alternating direction: the media swaps to the other side so consecutive
   image+text bands read as a left/right rhythm rather than the same layout
   stacked twice. On a stacked single column it has no effect (source order
   already puts the copy first). */
@media (min-width: 1081px) {
  .split--reverse > *:first-child { order: 2; }
  .split--reverse > *:last-child { order: 1; }
  /* Give the inquiry form more room so its two-column layout has space. */
  .split--inquiry { grid-template-columns: 0.82fr 1.18fr; }
  /* Thin image column on the left, wider feature copy on the right. */
  .split--advantage { grid-template-columns: 0.82fr 1.18fr; }
}
/* Career Experts / Student Experts (app/experts): the photo swaps sides
   between the two panels so the page reads as a conversation moving back
   and forth rather than the same layout repeated twice down the page. */
.expert-panel--reverse > *:first-child {
  order: 2;
}
.expert-panel--reverse > *:last-child {
  order: 1;
}

/* ==========================================================================
   Typography
   ========================================================================== */
.overline {
  font-size: var(--text-overline);
  font-weight: 700;
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  color: var(--accent);
  line-height: 1.4;
}
.section--brand .overline,
.section--hero .overline {
  color: var(--ls-blue-400);
}
.section--soft .overline {
  color: var(--fg-on-soft);
}

/* Sizes, weights, leading and tracking below are the values on the Figma
   text layers; only the fluid lower bound is added for small screens. */
.display {
  font-size: clamp(2.375rem, 1rem + 4.9vw, var(--text-5xl));
  font-weight: 800;
  letter-spacing: var(--tracking-display);
  line-height: 1.06;
  /* Greedy wrapping, matching how the Figma text layer breaks.
     Must be `wrap`, not `normal` — `normal` is not a valid text-wrap value
     and would silently leave the reset's `balance` in effect. */
  text-wrap: wrap;
}

/* The phrase inside a display heading that carries the promise. Set in the
   light brand blue, which is the one colour on the navy hero that is already
   doing emphasis (the eyebrow, the arrow glyphs) — so it reads as the same
   voice rather than as a fourth colour introduced for one line.

   <em> without the italic: the emphasis is real and worth marking up, but
   these are Display letterforms at 60px and an oblique cut would fight the
   headline's own weight. */
.heading__mark {
  font-style: normal;
  /* The emphasis word each headline is about takes a blue shade — light blue
     where it sits over a dark band, the accent blue on light bands (below).
     The warm coral now lives on secondary accents (ratings, the "Most chosen"
     ribbon) rather than the headline word. */
  color: var(--ls-blue-400);
}
/* On a light band the light blue would disappear, so there it takes the
   accent blue instead — same role, legible ink. */
.section:not(.section--hero):not(.section--brand) .heading__mark {
  color: var(--accent);
}

.h1 {
  font-size: clamp(2.125rem, 1.1rem + 3.6vw, var(--text-4xl));
  font-weight: 700;
  letter-spacing: var(--tracking-display);
  line-height: 1.1;
}

.h2 {
  font-size: clamp(1.875rem, 1.15rem + 2.7vw, var(--text-3xl));
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
}

.h3 {
  font-size: clamp(1.25rem, 1.05rem + 0.8vw, var(--text-xl));
  font-weight: 700;
  letter-spacing: var(--tracking-heading);
  line-height: 1.25;
}

.h4 {
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: -0.004em;
  line-height: 1.4;
}

.lead {
  font-size: var(--text-md);
  line-height: 1.7;
  color: var(--fg-muted);
}
.section--brand .lead,
.section--hero .lead {
  color: var(--fg-on-brand-muted);
}

.body {
  color: var(--fg-muted);
  line-height: var(--leading-relaxed);
}
.section--brand .body,
.section--hero .body {
  color: var(--fg-on-brand-muted);
}

.small {
  font-size: var(--text-sm);
  color: var(--fg-muted);
}

.measure {
  max-width: 62ch;
}
.measure--narrow {
  max-width: 46ch;
}

.text-center {
  text-align: center;
}
.mx-auto {
  margin-inline: auto;
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.9375rem var(--space-7);
  border-radius: var(--radius-pill);
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: -0.005em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background-color var(--duration) var(--ease),
    border-color var(--duration) var(--ease),
    color var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* The press: the whole control compresses toward its own centre. Scale, not
   a nudge, so the arrow and the label move together as one object. */
.btn:active {
  transform: scale(0.965);
}

/* --- Rolling label ------------------------------------------------------
   On hover the resting label leaves through the top and its replacement
   follows it in from below in a second colour. Both copies live in the same
   grid cell, so the button's width never changes and nothing reflows.

   The clip box is padded a sliver taller than the line box and pulled back
   by the same amount: without that, `overflow: hidden` would slice the
   descenders off letters like g and y at rest.
   ---------------------------------------------------------------------- */
.roll {
  display: inline-grid;
  overflow: hidden;
  padding-block: 0.12em;
  margin-block: -0.12em;
  vertical-align: middle;
  /* `overflow: hidden` also zeroes this element's automatic minimum size, so
     as a flex item in a button narrower than its label it would silently
     shrink and slice the last few characters off ("Free Assessmen"). The
     clip box exists to hide the label above and below the line, never to
     trim it — pinning the minimum to the label's own width keeps the
     vertical clipping and drops the horizontal. */
  min-width: max-content;
}
.roll__in,
.roll__out {
  grid-area: 1 / 1;
  display: block;
  transition: transform 340ms var(--ease);
}
.roll__out {
  color: var(--roll-fg, currentColor);
  transform: translateY(115%);
}
.btn:hover .roll__in,
.btn:focus-visible .roll__in {
  transform: translateY(-115%);
}
.btn:hover .roll__out,
.btn:focus-visible .roll__out {
  transform: translateY(0);
}

.btn--primary {
  background: var(--accent);
  color: var(--accent-fg);
  --roll-fg: var(--ls-blue-200);
}
.btn--primary:hover {
  background: var(--accent-hover);
}

.btn--navy {
  background: var(--ls-navy-800);
  color: var(--ls-white);
  --roll-fg: var(--ls-blue-400);
}
.btn--navy:hover {
  background: var(--ls-navy-700);
}

.btn--outline {
  background: transparent;
  color: var(--fg-on-soft);
  border-color: var(--ls-navy-800);
  --roll-fg: var(--accent);
}
.btn--outline:hover {
  background: rgba(18, 48, 94, 0.06);
}

/* The outline button is drawn in navy for light bands. On the hero and the
   dark bands that is navy on navy — a button with no visible edge and no
   visible label. Same control, inverted ink. */
.section--hero .btn--outline,
.section--brand .btn--outline {
  color: var(--fg-on-brand);
  border-color: rgba(255, 255, 255, 0.45);
  --roll-fg: var(--ls-blue-400);
}
.section--hero .btn--outline:hover,
.section--brand .btn--outline:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.85);
}

.btn--ghost {
  background: var(--surface);
  color: var(--fg);
  border-color: var(--border);
  --roll-fg: var(--accent);
}
.btn--ghost:hover {
  border-color: var(--border-strong);
}

.btn--block {
  width: 100%;
}

.btn--sm {
  padding: 0.6875rem var(--space-5);
  font-size: var(--text-sm);
}

/* Text link with a trailing arrow — used for "Discover the Lodestar way" etc. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--accent);
  transition: gap var(--duration) var(--ease);
}
.link-arrow:hover {
  gap: 0.625rem;
}
.section--soft .link-arrow {
  color: var(--fg-on-soft);
}

/* ==========================================================================
   Cards
   ========================================================================== */
/* ==========================================================================
   Cards
   One anatomy, used everywhere: media or icon, eyebrow, title, body, then a
   ruled footer with quiet metadata on the left and the deciding value on the
   right. Spacing is declared per part rather than as a blanket gap, because
   the whole point is that the parts are NOT equally spaced — the title owns
   the space above it, and the footer is pushed to the bottom edge.
   ========================================================================== */
.card {
  position: relative;
  border-radius: var(--radius-xl);
  padding: clamp(var(--space-6), 2.2vw, var(--space-8));
  background: var(--surface-subtle);
  /* Re-assert the foreground: inside a dark section the card would
     otherwise inherit white text onto its own light surface. */
  color: var(--fg);
  display: flex;
  flex-direction: column;
  height: 100%;
  /* Clips the full-bleed media header to the card's radius. */
  overflow: hidden;
}

/* Spacing is a rhythm between neighbours, not one flat gap: the pairs that
   belong together (eyebrow→title, title→body) sit closer than the ones that
   separate groups. Each rule below is more specific than the default, so
   order does not matter and nothing needs a negative margin. */
.card > * + * {
  margin-top: var(--space-4);
}
.card > .card__media + *,
.card > .card__icon + *,
.card > .card__header + * {
  margin-top: var(--space-5);
}
.card > .card__eyebrow + .card__title {
  margin-top: var(--space-2);
}
.card > .card__title + .card__body {
  margin-top: var(--space-3);
}

/* --- Icon and headline on one line -------------------------------------- */
/* The tile and the words that belong to it, read as one object. The text
   column is min-width 0 so a long title wraps within its own column rather
   than widening the grid and shouldering the tile off the card. */
.card__header {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  align-items: center;
}
.card__header-copy {
  min-width: 0;
}
/* Tighter than the stacked pairing: at this size the eyebrow is reading as a
   line of the title block, not as a separate part. */
.card__header .card__eyebrow + .card__title {
  margin-top: var(--space-1);
}

/* --- The visual anchor -------------------------------------------------- */
.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.875rem;
  height: 2.875rem;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--ls-navy-800);
}
.card__icon svg {
  width: 1.375rem;
  height: 1.375rem;
}
.card__index {
  font-size: var(--text-md);
  font-weight: 800;
  letter-spacing: -0.02em;
}
.card--navy .card__icon,
.card--on-brand .card__icon {
  background: rgba(255, 255, 255, 0.14);
  color: var(--ls-white);
}
.card--soft .card__icon {
  background: var(--ls-white);
  color: var(--accent);
}

/* --- Media header ------------------------------------------------------- */
/* Full-bleed to the card's edges: the padding belongs to the copy, not the
   picture. The slot a real photograph drops into. */
.card__media {
  position: relative;
  /* Bleeds to the top and side edges only — the bottom keeps its margin, or
     the copy underneath would ride up over the tag chip. */
  margin: calc(clamp(var(--space-6), 2.2vw, var(--space-8)) * -1)
    calc(clamp(var(--space-6), 2.2vw, var(--space-8)) * -1) 0;
  /* A fixed aspect ratio, not a min-height: every thumbnail in a row is then
     exactly the same height regardless of the photo's own dimensions, which
     is what stops the misaligned/ragged card grids. The image fills this box
     via object-fit below, so it is cropped to fit rather than setting the
     height itself. */
  aspect-ratio: 16 / 10;
  display: flex;
  align-items: flex-end;
  padding: var(--space-4);
  background: var(--ls-gradient-media);
}
.card__media--tall {
  aspect-ratio: 4 / 3;
}
/* A real photo instead of the gradient placeholder — same slot, same tag
   chip on top, but the chip needs its own stacking context and a scrim
   under it once there is a photo behind it to stay legible on any image. */
.card__media--photo {
  background: var(--ls-navy-900);
}
.card__media-img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.card__media--photo::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(11, 22, 38, 0) 45%, rgba(11, 22, 38, 0.6) 100%);
  pointer-events: none;
}
.card__media--photo .card__tag {
  position: relative;
  z-index: 2;
}
/* The category rides on the image instead of costing the card a line. */
.card__tag {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.3125rem 0.6875rem;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  color: var(--ls-navy-900);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
}
.card__tag svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--accent);
}

/* --- Category / tag badges --------------------------------------------- */
/* A scannable pill that sits in the card body (below the media), distinct
   from .card__tag which rides on the image. Spacing to its neighbours is left
   to the card's owl rhythm below, so the badge element carries no margins. */
.card__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.3125rem 0.6875rem;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
  background: var(--ls-blue-50);
  color: var(--ls-navy-800);
  border: 1px solid var(--ls-gray-200);
}
.badge svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--accent);
}
/* The primary category — the accent pill that reads first. */
.badge--accent {
  background: var(--accent-soft);
  color: var(--ls-navy-900);
  border-color: transparent;
}
.card--navy .badge,
.card--on-brand .badge {
  background: rgba(255, 255, 255, 0.12);
  color: var(--ls-white);
  border-color: var(--border-on-brand);
}
/* Slightly larger in the program cards so the category keeps pace with the
   enlarged grade chip and title. */
.program .badge {
  font-size: var(--text-sm);
  padding: 0.375rem 0.8125rem;
}
.card > .card__badges + .card__title,
.card > .card__badges + * {
  margin-top: var(--space-3);
}

/* --- Copy --------------------------------------------------------------- */
.card__eyebrow {
  font-size: var(--text-overline);
  font-weight: 700;
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  color: var(--accent);
}
.card--navy .card__eyebrow,
.card--on-brand .card__eyebrow {
  color: var(--ls-blue-400);
}
.card--soft .card__eyebrow {
  color: var(--ls-navy-800);
}

/* The card's whole reason for existing — so it is the biggest, heaviest
   thing in it, and it sits at the top. */
.card__title {
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-snug);
  text-wrap: balance;
}
.card__title--lg {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
}
.card__title a {
  color: inherit;
  transition: color var(--duration) var(--ease);
}
.card__title a:hover {
  color: var(--accent);
}
.card--navy .card__title a:hover,
.card--on-brand .card__title a:hover {
  color: var(--ls-blue-400);
}

.card__body {
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--fg-muted);
}
.card--navy .card__body,
.card--on-brand .card__body {
  color: rgba(255, 255, 255, 0.76);
}
.card--soft .card__body {
  color: var(--fg-on-soft-muted);
}

/* --- Footer ------------------------------------------------------------- */
/* `margin-top: auto` is what lines the footers of a row of cards up with
   each other however much copy each one carries. */
.card > .card__meta {
  margin-top: auto;
}
.card__meta {
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.card--navy .card__meta,
.card--on-brand .card__meta {
  border-top-color: var(--border-on-brand);
}
.card--soft .card__meta {
  border-top-color: rgba(18, 48, 94, 0.14);
}

/* A meta row that sits where it is written instead of being pinned to the
   bottom — for cards where the value IS the headline (a price), not a
   footnote. The rule moves below it so it still closes a group. */
.card > .card__meta--inline {
  margin-top: var(--space-5);
}
.card__meta--inline {
  padding-top: 0;
  padding-bottom: var(--space-5);
  border-top: 0;
  border-bottom: 1px solid var(--border);
}
.card--navy .card__meta--inline,
.card--on-brand .card__meta--inline {
  border-bottom-color: var(--border-on-brand);
}
.card--soft .card__meta--inline {
  border-bottom-color: rgba(18, 48, 94, 0.14);
}

.card__meta-item {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  font-size: var(--text-sm);
  color: var(--fg-muted);
  min-width: 0;
}
.card__meta-item svg {
  width: 0.9375rem;
  height: 0.9375rem;
  flex: none;
  opacity: 0.8;
}
.card--navy .card__meta-item,
.card--on-brand .card__meta-item {
  color: rgba(255, 255, 255, 0.7);
}
.card--soft .card__meta-item {
  color: var(--fg-on-soft-muted);
}

/* Right-aligned by rule, not by choice at the call site — so the deciding
   value lands in the same place on every card in a grid. */
.card__value {
  margin-left: auto;
  display: inline-flex;
  align-items: baseline;
  gap: 0.25rem;
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--accent);
  white-space: nowrap;
}
.card__value--lg {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-display);
}
.card__value-unit {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--fg-muted);
}
.card--navy .card__value,
.card--on-brand .card__value {
  color: var(--ls-blue-400);
}
.card--navy .card__value-unit,
.card--on-brand .card__value-unit {
  color: rgba(255, 255, 255, 0.7);
}
.card--soft .card__value {
  color: var(--ls-navy-800);
}
.card--soft .card__value-unit {
  color: var(--fg-on-soft-muted);
}

/* A CTA occupying the value slot — same position and weight as a price, so a
   grid mixing the two still has one thing to look at on the right. */
.card__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: inherit;
  text-decoration: none;
  transition: gap var(--duration) var(--ease);
}
.card__cta:hover {
  gap: 0.625rem;
}
.card__cta svg {
  width: 0.875rem;
  height: 0.875rem;
}
/* On a clickable card the CTA has to read as a link AT REST, not only on
   hover — that persistent accent-coloured "Explore →" is the standing signal
   that the whole card is a link. (Previously it inherited the muted meta
   colour and only lit up on hover, so the cards looked inert.) */
.card--link .card__cta {
  color: var(--accent);
  font-weight: 700;
}
/* A URL is reference, not reading matter: it truncates rather than wrapping
   to three lines and pushing the card's footer out of alignment. */
.card__url {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card__value svg {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* --- Quote cards --------------------------------------------------------
   The quote is the headline of its card, so it gets title treatment and the
   attribution drops to metadata below the rule.
   ---------------------------------------------------------------------- */
/* The mark is decoration, so it is sized like an ornament and set in the
   soft accent — loud enough to anchor the card, quiet enough that the eye
   still lands on the sentence first. Its line box is collapsed so it does
   not push the quote down a full 40px. */
.card__quote-mark {
  display: block;
  font-size: 2.75rem;
  font-weight: 800;
  line-height: 0.6;
  height: 1.25rem;
  color: var(--accent-soft);
  user-select: none;
}
.card--navy .card__quote-mark,
.card--on-brand .card__quote-mark {
  color: rgba(255, 255, 255, 0.28);
}

.card__quote {
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1.6;
  letter-spacing: -0.006em;
  color: var(--fg);
  text-wrap: pretty;
}
.card__byline {
  gap: var(--space-4);
}
/* The stars are a glyph row, not text, so they centre on the byline rather
   than sitting on its baseline. */
.card__byline .card__value {
  align-items: center;
}
.card__byline-copy {
  display: flex;
  flex-direction: column;
  gap: 0.0625rem;
  min-width: 0;
}
.card__byline-copy strong {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--fg);
}
.card__byline-copy span {
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.card--navy .card__quote,
.card--on-brand .card__quote {
  color: var(--ls-white);
}
.card--navy .card__byline-copy strong,
.card--on-brand .card__byline-copy strong {
  color: var(--ls-white);
}
.card--navy .card__byline-copy span,
.card--on-brand .card__byline-copy span {
  color: rgba(255, 255, 255, 0.7);
}
.card--plain {
  background: var(--surface);
  border: 1px solid var(--border);
}
.card--raised {
  background: var(--surface);
  box-shadow: var(--shadow-md);
}
.card--navy {
  background: var(--ls-navy-800);
  color: var(--ls-white);
}
.card--navy .body,
.card--navy .small {
  color: rgba(255, 255, 255, 0.76);
}
.card--soft {
  background: var(--bg-brand-soft);
  color: var(--fg-on-soft);
}
.card--soft .body {
  color: var(--fg-on-soft-muted);
}
.card--on-brand {
  background: var(--surface-on-brand);
  color: var(--ls-white);
}
.card--on-brand .body {
  color: rgba(255, 255, 255, 0.76);
}
/* The 2030 tone: clay, not glass — an opaque puffy surface rather than a
   flat fill. Only the surface changes here — hover lift, the shimmer sweep
   and the icon scale are all the generic `.card` mechanics already defined
   above; the deepened shadow that replaces the neutral hover shadow lives
   in motion.css next to the other tone-specific hover states. */
.card--clay {
  background: var(--clay-surface);
  border: none;
  box-shadow: var(--shadow-2030);
}

/* "Why schools need this" cards: the icon anchors the top-left while a large,
   faded index sits opposite it, then the title and body run full width below.
   Reads as an intentional numbered set rather than a cramped eyebrow number. */
.card--why .card__why-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.card--why .card__num {
  font-size: 2.5rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--accent);
  opacity: 0.24;
  font-variant-numeric: tabular-nums;
}
.card--why .card__title {
  margin-top: var(--space-5);
}
/* A hairline rule separates the heading from the body copy. */
.card--why .card__body {
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Numbered badge on the "how it works" step cards. */
.badge-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--ls-navy-800);
  font-size: var(--text-sm);
  font-weight: 700;
}
.card--navy .badge-num {
  background: var(--accent);
  color: var(--ls-white);
}

/* Small uppercase pill, e.g. "PHASE 01 · CLASS 8". */
.pill {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: 0.4375rem var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-overline);
  font-weight: 700;
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  background: var(--ls-navy-800);
  color: var(--ls-white);
}
.pill--accent {
  background: var(--accent);
  color: var(--accent-fg); /* pairs with the accent in both themes */
}
.pill--soft {
  background: var(--bg-brand-soft);
  color: var(--fg-on-soft);
}
.pill--outline {
  background: transparent;
  border: 1px solid var(--border-on-brand);
  color: var(--fg-on-brand);
}

/* ==========================================================================
   Stats
   ========================================================================== */
.stat__value {
  font-size: clamp(1.875rem, 1.2rem + 2vw, var(--text-3xl));
  font-weight: 800;
  letter-spacing: var(--tracking-display);
  line-height: 1.05;
}
.stat__label {
  font-size: var(--text-base);
  color: var(--fg-muted);
  margin-top: var(--space-2);
}
.section--hero .stat__label,
.section--brand .stat__label {
  color: var(--fg-on-brand-muted);
}
.section--soft .stat__label {
  color: var(--ls-navy-700);
}

/* ==========================================================================
   Media placeholders
   Every "IMG · …" frame in the Figma file is a gradient block with its
   caption. They are reproduced faithfully so real exports can drop in.
   ========================================================================== */
.media {
  position: relative;
  border-radius: var(--radius-xl);
  background: var(--ls-gradient-media);
  overflow: hidden;
  min-height: 200px;
  display: flex;
  align-items: flex-start;
  /* Lifts the standalone content photos off the page for a subtle 3D feel;
     card/video/legal photos carry their own shadow already. */
  box-shadow: var(--shadow-lg);
}
/* Caption type matches the Figma label layers exactly: 10px / 700 / .05em,
   white at 80% opacity. */
.media__caption {
  padding: 0.5625rem 1.125rem;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
}
/* A real photo, not the gradient placeholder. The image sits at z-index 0,
   a bottom-up scrim at 1 so the caption chip stays readable over whatever
   the photo puts behind it, and the caption itself is lifted to 2 — same
   stack, whichever photo lands in the slot. */
.media--photo {
  background: var(--ls-navy-900);
}
.media__img {
  z-index: 0;
  object-fit: cover;
}
.media--photo::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(11, 22, 38, 0) 55%, rgba(11, 22, 38, 0.55) 100%);
  pointer-events: none;
}
.media--photo .media__caption {
  position: relative;
  z-index: 2;
}
/* An explicit aspect-ratio, not just a min-height floor: without one, a
   "tall" box takes its height from min-height alone while its width follows
   the grid column — on a wide `.split` column that renders closer to
   landscape than portrait, so a portrait photo dropped in via `object-fit:
   cover` gets cropped down to a sliver instead of reading as "tall". The
   ratios match the photos actually shipped in public/images (1000×1200 for
   tall, 1400×1000 for wide); min-height stays as the floor for narrow
   columns where the ratio alone would go too short to read as a photo. */
.media--tall {
  aspect-ratio: 5 / 6;
  min-height: 420px;
}
.media--wide {
  aspect-ratio: 7 / 5;
  min-height: 320px;
}
.media__img {
  object-position: center 25%;
}
/* The bare 440×30 caption pill, used where the design shows only a label. */
.media--strip {
  min-height: 30px;
  border-radius: 26px;
  align-items: center;
}
/* Banner strip across the top of a card. */
.media--banner {
  min-height: 30px;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}

/* ==========================================================================
   Shimmer stroke
   A highlight that travels around the edge of a surface. It is a conic
   gradient masked down to a 1px ring, so it only ever paints on the border
   line — never over the fill, never over the content, and never hit-tested.

   The navbar shimmers continuously because it is the one surface always on
   screen. Cards and media sweep once on hover: a shimmer on every card at
   rest would be noise, and would keep a compositor layer alive for each one.
   ========================================================================== */
.header__inner,
.card,
.media,
.office-card {
  position: relative;
}

.header__inner::after,
.card::after,
.media::after,
.office-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  /* A raking band of light. It has to be a linear sweep rather than a conic
     one: on a surface as wide as the navbar, a conic gradient spends nearly
     all of its angular range on the two short edges and crawls a few pixels
     along the long ones. A linear sweep travels at the same rate whatever
     the aspect ratio. */
  background-image: linear-gradient(
    100deg,
    transparent 0%,
    var(--shimmer-tint) 50%,
    transparent 100%
  );
  background-repeat: no-repeat;
  background-size: 55% 100%;
  background-position: -70% 0;
  /* Punch the fill out of the gradient box, leaving only the 1px ring. */
  -webkit-mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 400ms var(--ease);
}

/* Light surfaces need a coloured highlight — white on near-white is nothing. */
.card,
.office-card {
  --shimmer-tint: var(--shimmer-tint-accent);
}
.card--navy,
.card--on-brand {
  --shimmer-tint: rgba(255, 255, 255, 0.85);
}

/* The pill's own surface is near-white on most pages, so a white highlight
   would have nothing to register against — it takes the accent tint. */
.header__inner {
  --shimmer-tint: var(--shimmer-tint-accent);
}
.header__inner::after {
  opacity: 1;
  animation: shimmer-sweep var(--shimmer-lap) linear infinite;
}

.card:hover::after,
.media:hover::after,
.office-card:hover::after {
  opacity: 1;
  animation: shimmer-sweep 1.5s var(--ease);
}

@keyframes shimmer-sweep {
  from {
    background-position: -70% 0;
  }
  to {
    background-position: 170% 0;
  }
}

/* Reduced motion gets no highlight at all. Freezing the animation would leave
   a bright band parked mid-edge, which is worse than none. */
@media (prefers-reduced-motion: reduce) {
  .header__inner::after,
  .card::after,
  .media::after,
  .office-card::after {
    display: none;
  }
}

/* ==========================================================================
   Feature list with the star glyph
   ========================================================================== */
.feature-list {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.feature {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: var(--space-4);
  align-items: start;
}
.feature__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 1.375rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--ls-navy-800);
  margin-top: 0.2rem;
}
.feature__icon svg {
  width: 0.75rem;
  height: 0.75rem;
}

/* Check / dot list used inside program and phase cards. */
.check-list {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.check-list li {
  display: grid;
  grid-template-columns: 1rem 1fr;
  gap: var(--space-3);
  align-items: start;
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}
.check-list svg {
  width: 0.875rem;
  height: 0.875rem;
  margin-top: 0.3rem;
  /* The friendly teal secondary — the affirmation tick reads as its own warm
     accent rather than dimmed body ink. Bright enough on both light bands and
     the navy hero the check-list also sits on. */
  color: var(--secondary);
  opacity: 1;
}
/* On the soft brand band the mid-teal loses contrast against the pale blue —
   drop to the deeper ink step there. */
.section--soft .check-list svg {
  color: var(--secondary-ink);
}

.dot-list {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.dot-list li {
  display: grid;
  grid-template-columns: 1.125rem 1fr;
  gap: var(--space-4);
  align-items: start;
  font-weight: 500;
}
.dot-list li::before {
  content: "";
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--accent-soft);
  margin-top: 0.25rem;
}

/* Perk tiles: the schools-inquiry copy column presents its four selling
   points as a 2×2 grid of small icon cards rather than a flat bullet list,
   so the left side carries visual weight opposite the glass form. */
.perk-grid {
  list-style: none;
  padding: 0;
  margin-top: var(--space-8);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}
.perk {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease),
    border-color var(--duration) var(--ease);
}
.perk:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}
.perk__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--ls-navy-800);
}
.perk__icon svg {
  width: 1.375rem;
  height: 1.375rem;
}
.perk__text {
  font-weight: 600;
  line-height: var(--leading-snug);
  color: var(--fg);
}
@media (max-width: 560px) {
  .perk-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Arrow rows inside the "session" cards on How It Works. */
.arrow-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--bg-tint);
  font-size: var(--text-base);
  color: var(--fg);
}
.arrow-row svg {
  width: 0.875rem;
  height: 0.875rem;
  flex: none;
  color: var(--accent);
}
/* The dark variant, for a `.section--brand` context. */
.section--brand .arrow-row {
  background: rgba(255, 255, 255, 0.07);
}
.section--brand .arrow-row svg {
  color: var(--ls-blue-400);
}

/* ==========================================================================
   Forms
   ========================================================================== */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.field__label {
  font-size: var(--text-sm);
  font-weight: 600;
}
.input,
.textarea {
  width: 100%;
  padding: 0.9375rem var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--fg);
  font-size: var(--text-base);
  transition: border-color var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease);
}
.input::placeholder,
.textarea::placeholder {
  color: var(--fg-muted);
}
.input:focus,
.textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.textarea {
  min-height: 5.5rem;
  resize: vertical;
}

/* Grade / city chooser rendered as radio chips. */
.chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.chip {
  position: relative;
}
.chip input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.chip span {
  display: block;
  color: var(--fg);
  padding: 0.6875rem var(--space-5);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: var(--text-base);
  font-weight: 600;
  text-align: center;
  transition: background-color var(--duration) var(--ease),
    border-color var(--duration) var(--ease), color var(--duration) var(--ease);
}
.chip input:hover + span {
  border-color: var(--border-strong);
}
.chip input:checked + span {
  background: var(--ls-navy-800);
  border-color: var(--ls-navy-800);
  color: var(--ls-white);
}
.chip input:focus-visible + span {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.form-card {
  background: var(--surface);
  color: var(--fg);
  border-radius: var(--radius-xl);
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  box-shadow: var(--shadow-md);
}
.form-card--subtle {
  background: var(--surface-subtle);
  box-shadow: none;
}
.form-card--tint {
  background: var(--bg-tint);
  box-shadow: none;
}
/* Glass variant: translucent, frosted panel that lets the page tint show
   through. Used on the schools inquiry form. */
.form-card--glass {
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
  box-shadow: var(--shadow-lg);
}
/* Two-column field layout so the card reads wider and shorter — the whole
   form stays within one screen. Header, progress, chips, button and note
   span the full width; the text fields pair up two per row. */
.form-card--grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4) var(--space-5);
  align-items: start;
}
.form-card--grid > *:not(.field) {
  grid-column: 1 / -1;
}
.form-card--grid .field--wide {
  grid-column: 1 / -1;
}
/* Grade-level chips: even full-width columns that line up in a tidy grid,
   and no tick on select — the navy fill alone carries the chosen state,
   which reads cleaner here than a checkmark that widens the pill. */
.form-card--grid .chip-group {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}
.form-card--grid .chip span {
  width: 100%;
}
.form-card--grid .chip__check {
  display: none;
}
@media (max-width: 560px) {
  .form-card--grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
.form-note {
  font-size: var(--text-sm);
  color: var(--fg-muted);
  text-align: center;
}

/* --- Form progress ------------------------------------------------------
   How much of the form is filled in. Three states, one bar: it fills as
   required fields become valid, goes indeterminate while the request is in
   flight, and lands full on success.
   ---------------------------------------------------------------------- */
.progress {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  /* Pulls the bar up under the form's lead paragraph, so it reads as part of
     the header rather than as the first field. */
  margin-top: calc(var(--space-3) - var(--space-5));
}

.progress__track {
  position: relative;
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(16, 25, 43, 0.09);
  overflow: hidden;
}

/* Driven by scaleX rather than width: the fill is laid out full-width once
   and only ever transforms, so advancing it costs no layout. --progress is
   the 0–1 fraction, set on the element by FormProgress.
   At 4px tall the pill radius is 2px, so the horizontal squash the scale
   puts on the rounded ends is not resolvable on screen. */
.progress__fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
  transition: transform 420ms var(--ease-out-long),
    background-color var(--duration) var(--ease);
}

/* In flight the count means nothing, so the bar stops reporting a number and
   just shows that something is happening. */
.progress[data-state='submitting'] .progress__fill {
  background-image: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.65) 50%,
    transparent 100%
  );
  background-repeat: no-repeat;
  background-size: 40% 100%;
  animation: shimmer-sweep 1.1s linear infinite;
}

.progress__label {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--fg-muted);
}
.progress[data-state='complete'] .progress__label {
  color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  /* No sweep. The in-flight state is still announced — the label reads
     "Sending…" and the bar is full — it just stops moving. */
  .progress[data-state='submitting'] .progress__fill {
    background-image: none;
  }
}

/* ==========================================================================
   Misc components
   ========================================================================== */

/* Oversized decorative word behind a section ("SINCE 2011"). */
.watermark {
  font-size: clamp(4rem, 12vw, 8.5rem);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 0.85;
  color: var(--watermark);
  user-select: none;
  pointer-events: none;
  overflow: hidden;
  white-space: nowrap;
}

.stars {
  display: flex;
  gap: 0.1875rem;
  color: var(--rating);
}
.stars svg {
  width: 1rem;
  height: 1rem;
}

.avatar {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--accent-soft);
  flex: none;
}
.avatar--sm {
  width: 2rem;
  height: 2rem;
}
.avatar--initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  color: var(--ls-navy-800);
}
.avatar--initial.avatar--sm {
  font-size: var(--text-sm);
}

.quote-mark {
  font-size: 2.5rem;
  line-height: 1;
  font-weight: 800;
  color: var(--ls-blue-400);
}

/* Centred, not `space-between`: with only five or six names, spreading them
   edge to edge pins the first and last flush against the container's own
   boundary — reviewed as the row "touching the screen". Centring keeps the
   same generous gap between names without stretching the row to the full
   container width. */
.logo-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-6) var(--space-8);
  align-items: center;
}
.logo-row span {
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--fg-muted);
}

/* --- School logo chips ---------------------------------------------------
   A placeholder "logo" (the same initials avatar the testimonials use) next
   to each partner name, so the trust row reads as a row of institutions
   rather than a plain list of text — real crests can drop into the avatar
   slot once schools supply them. */
.school-logos {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4) var(--space-5);
}
.school-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5) var(--space-3) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  transition: border-color var(--duration) var(--ease),
    transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}
.school-logo:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}
.school-logo span {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--fg);
}
.school-logo--img {
  padding: var(--space-3) var(--space-5);
  background: transparent;
  border: none;
  border-radius: 0;
}
.school-logo--img img {
  height: 72px;
  width: auto;
  max-width: 200px;
  object-fit: contain;
  display: block;
}
.logo-marquee .logo-marquee__track .school-logo--img,
.logo-marquee .logo-marquee__track .school-logo--img:hover {
  filter: none;
  opacity: 1;
}
.school-logo--img:hover {
  border: none;
  transform: none;
  box-shadow: none;
}

/* --- Logo marquee --------------------------------------------------------
   The partner-school chips scroll in a continuous ring, greyed out at rest.
   The strip is two identical sets side by side; translating the track by one
   set's width (-50%) tiles seamlessly — spacing lives in each chip's trailing
   margin (not flex `gap`) so the two halves line up exactly. Hovering the
   strip pauses the scroll; hovering a chip brings it back to full colour. */
.logo-marquee {
  /* `clip` + a vertical clip-margin lets a chip's hover lift and shadow bleed
     above/below the strip without being cut off, while the horizontal scroll
     stays clipped (and masked) at the sides. Falls back to hidden-like clipping
     where clip-margin is unsupported. */
  overflow: clip;
  overflow-clip-margin: var(--space-4);
  /* Breathing room above (and below) the chips so the strip doesn't sit tight
     against the heading, and the hover lift has clear space. */
  padding-block: var(--space-4);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.logo-marquee__track {
  display: flex;
  width: max-content;
  animation: logo-marquee-scroll 36s linear infinite;
}
.logo-marquee__track .school-logo {
  margin-right: var(--space-5);
  filter: grayscale(1);
  opacity: 0.7;
  transition: filter var(--duration) var(--ease), opacity var(--duration) var(--ease),
    border-color var(--duration) var(--ease), transform var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease);
}
.logo-marquee__track .school-logo:hover {
  filter: grayscale(0);
  opacity: 1;
}
.logo-marquee:hover .logo-marquee__track {
  animation-play-state: paused;
}
@keyframes logo-marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .logo-marquee {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .logo-marquee__track {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-4) var(--space-5);
  }
  .logo-marquee__track .school-logo {
    margin-right: 0;
    filter: none;
    opacity: 1;
  }
  /* The duplicated set is only needed to make the scroll seamless. */
  .logo-marquee__track .school-logo[aria-hidden="true"] {
    display: none;
  }
}

/* --- Photo duo -----------------------------------------------------------
   Two square photos on the Lodestar-advantage row, tilted in opposite
   directions and overlapping at one corner (~15%): the back photo sits
   top-right, the front photo bottom-left, so the front's top-right corner
   covers the back's bottom-left. A surface-coloured ring separates them. */
.photo-duo {
  position: relative;
  width: 100%;
  max-width: 30rem;
  aspect-ratio: 1 / 1;
  margin-inline: auto;
}
.photo-duo__item {
  position: absolute;
  width: 58%;
  aspect-ratio: 1 / 1;
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.photo-duo__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-duo__item--back {
  top: 0;
  right: 0;
  transform: rotate(4deg);
}
.photo-duo__item--front {
  bottom: 0;
  left: 0;
  z-index: 2;
  transform: rotate(-4deg);
  /* ring that lifts the front photo off the one behind it */
  box-shadow: 0 0 0 6px var(--surface), var(--shadow-lg);
}
@media (max-width: 560px) {
  .photo-duo {
    max-width: 22rem;
  }
}

/* --- Advantage list ------------------------------------------------------
   The Lodestar-advantage points as a numbered stack of cards: a gradient
   number chip, a bold title and muted body, each card lifting on hover. */
.advantage-list {
  list-style: none;
  padding: 0;
  margin-top: var(--space-8);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.advantage-item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-5);
  align-items: start;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease), border-color var(--duration) var(--ease);
}
.advantage-item:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}
.advantage-item__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border-radius: var(--radius-md);
  background: linear-gradient(150deg, var(--ls-blue-400), var(--ls-blue-650));
  color: var(--ls-white);
  font-weight: 800;
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
}
.advantage-item__title {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--fg);
}
.advantage-item__body p {
  margin-top: var(--space-2);
  color: var(--fg-muted);
  line-height: var(--leading-normal);
}

/* --- Quote loop ----------------------------------------------------------
   A testimonial carousel that shows three cards at once and rotates in a
   continuous ring, one card at a time (see initQuoteLoop in site.js). The
   track is a plain flex row inside a clipped viewport; the script slides it
   one card left, then silently moves the first card to the end so the loop
   never reaches an edge. Two cards per view on tablet, one on phones. */
.quote-loop {
  --quote-gap: var(--space-6);
}
.quote-loop__viewport {
  overflow: hidden;
}
.quote-loop__track {
  display: flex;
  gap: var(--quote-gap);
  align-items: stretch;
  will-change: transform;
  list-style: none;
  padding: 0;
  margin: 0;
}
.quote-loop__item {
  flex: 0 0 calc((100% - var(--quote-gap) * 2) / 3);
  min-width: 0;
}
.quote-loop__card {
  height: 100%;
}
.quote-loop__card .body {
  margin-top: var(--space-2);
}
@media (max-width: 900px) {
  .quote-loop__item {
    flex-basis: calc((100% - var(--quote-gap)) / 2);
  }
}
@media (max-width: 600px) {
  .quote-loop__item {
    flex-basis: 100%;
  }
}

.divider {
  height: 1px;
  background: var(--border);
  border: 0;
}

.accordion-item {
  background: var(--surface-subtle);
  color: var(--fg);
  border-radius: var(--radius-xl);
  padding: clamp(var(--space-5), 2vw, var(--space-7))
    clamp(var(--space-5), 2vw, var(--space-8));
}
.accordion-item + .accordion-item {
  margin-top: var(--space-5);
}
.accordion-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  cursor: pointer;
  list-style: none;
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: var(--tracking-heading);
}
.accordion-item summary::-webkit-details-marker {
  display: none;
}
/* The marker itself is an icon that rotates a plus into a minus rather than
   two drawn states swapping — see .accordion-item__marker in motion.css. */
.accordion-item p {
  margin-top: var(--space-4);
  color: var(--fg-muted);
  line-height: var(--leading-relaxed);
}


/* ==========================================================================
   Utilities
   ========================================================================== */
.flex {
  display: flex;
}
.items-center {
  align-items: center;
}
.justify-between {
  justify-content: space-between;
}
.flex-wrap {
  flex-wrap: wrap;
}
.gap-2 {
  gap: var(--space-2);
}
.gap-3 {
  gap: var(--space-3);
}
.gap-4 {
  gap: var(--space-4);
}
.gap-5 {
  gap: var(--space-5);
}
.gap-6 {
  gap: var(--space-6);
}
.gap-8 {
  gap: var(--space-8);
}
.mt-2 {
  margin-top: var(--space-2);
}
.mt-3 {
  margin-top: var(--space-3);
}
.mt-4 {
  margin-top: var(--space-4);
}
.mt-5 {
  margin-top: var(--space-5);
}
.mt-6 {
  margin-top: var(--space-6);
}
.mt-8 {
  margin-top: var(--space-8);
}
.mt-10 {
  margin-top: var(--space-10);
}
.mt-12 {
  margin-top: var(--space-12);
}
.mt-16 {
  margin-top: var(--space-16);
}

/* ==========================================================================
   Responsive
   The Figma file only specifies the 1440 desktop frame; tablet and mobile
   behaviour is derived — multi-column grids collapse, split layouts stack,
   and the nav folds into a disclosure menu.
   ========================================================================== */
@media (max-width: 1080px) {
  .grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .split,
  .split--copy-wide,
  .split--form {
    grid-template-columns: minmax(0, 1fr);
  }
  .logo-row {
    gap: var(--space-5) var(--space-8);
  }
}

@media (max-width: 720px) {
  .grid--2,
  .grid--3,
  .grid--4 {
    grid-template-columns: minmax(0, 1fr);
  }
  .container {
    padding-inline: var(--space-5);
  }
  .media--tall {
    min-height: 260px;
  }
  .media--wide {
    min-height: 200px;
  }
  .btn {
    width: 100%;
  }
  .btn--auto {
    width: auto;
  }
}

@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;
  }
}

/* ==========================================================================
   Light surfaces inside dark sections
   Several screens place a white card (form, quote, program) inside a
   .section--brand or .section--hero band. The on-brand text overrides above
   are descendant rules, so they would otherwise bleed onto those light
   surfaces and render white-on-white. Re-assert the normal muted colour.
   These come last so they win the equal-specificity tie.
   ========================================================================== */
.form-card .body,
.form-card .lead,
.card:not(.card--navy):not(.card--on-brand):not(.card--soft) .body,
.card:not(.card--navy):not(.card--on-brand):not(.card--soft) .lead,
.office-card .body,
.post-row .body {
  color: var(--fg-muted);
}

/* Copy on the soft brand band follows the theme, never a raw primitive. */
.section--soft .body,
.section--soft .lead,
.section--soft .small,
.section--soft .stat__label {
  color: var(--fg-on-soft-muted);
}

/* -------------------------------------------------------------------------
   Quick-enquiry mini banner — a compact single-row lead form dropped on a
   navy band (index.html, before "How it works"). Heading on the left, the
   name / phone / email inputs and submit button in one row on the right.
   ---------------------------------------------------------------------- */
.quick-band {
  padding-block: clamp(var(--space-6), 3.5vw, var(--space-10));
}
.quick-band__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6) var(--space-8);
  flex-wrap: wrap;
}
.quick-band__lead {
  flex: 1 1 240px;
  min-width: 220px;
}
.quick-band__title {
  margin-top: var(--space-2);
  font-size: var(--text-xl);
  line-height: 1.25;
  font-weight: 700;
}
.quick-band__fields {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  flex: 2 1 560px;
  flex-wrap: wrap;
}
.quick-band__fields .field {
  flex: 1 1 160px;
  margin-bottom: 0;
}
/* The reCAPTCHA widget (302px) takes its own full-width row inside the band
   so it never squeezes the name/phone/email inputs. */
.quick-band__captcha {
  flex: 1 1 100% !important;
}
.quick-band__submit {
  flex: 0 0 auto;
  white-space: nowrap;
  height: fit-content;
}
@media (max-width: 640px) {
  .quick-band__fields .field {
    flex-basis: 100%;
  }
  .quick-band__submit {
    width: 100%;
  }
}
