/* ==========================================================================
   Component library
   Build these once. Every page composes from here — no ad-hoc markup.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--touch);
  padding: var(--space-3) var(--space-5);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn--primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; }

.btn--secondary {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--ink);
}
.btn--secondary:hover { border-color: var(--ink-muted); color: var(--ink); }

.btn--blue {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
.btn--blue:hover { background: var(--primary-deep); border-color: var(--primary-deep); color: #fff; }

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--primary);
  padding-inline: var(--space-3);
}
.btn--ghost:hover { background: var(--primary-tint); color: var(--primary-deep); }

.btn--danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn--danger:hover { background: #8C1216; border-color: #8C1216; color: #fff; }

.btn--lg { font-size: var(--step-1); padding: var(--space-4) var(--space-6); }
.btn--sm { min-height: 36px; padding: var(--space-2) var(--space-3); font-size: var(--step--1); }
.btn--block { width: 100%; }

/* On a touch device every target returns to the 44px minimum. A 36px
   button is fine under a mouse cursor and appropriate for dense table
   rows; it is not fine under a thumb. */
@media (pointer: coarse) {
  .btn--sm { min-height: var(--touch); }
}

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

.btn__icon { width: 1.1em; height: 1.1em; flex: none; }

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */

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

.field__label {
  display: block;
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0.01em;
  margin-bottom: var(--space-2);
  color: var(--ink);
}
.field__req { color: var(--accent); margin-left: 2px; }

.field__hint {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-top: var(--space-2);
}

.field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--danger);
  margin-top: var(--space-2);
}

.input, .select, .textarea {
  width: 100%;
  min-height: var(--touch);
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: var(--step-0);
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--ink-faint); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-muted); }
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--primary-bright);
  box-shadow: 0 0 0 3px rgba(24, 120, 196, 0.18);
  outline: none;
}
.textarea { min-height: 120px; resize: vertical; }

.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill='%235B6472' d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  padding-right: var(--space-7);
}

.field--invalid .input,
.field--invalid .select,
.field--invalid .textarea {
  border-color: var(--danger);
  background: var(--danger-tint);
}
.field--invalid .input:focus { box-shadow: 0 0 0 3px rgba(168, 24, 30, 0.18); }

.field--valid .input { border-color: var(--success); }

.input[disabled], .select[disabled] {
  background: var(--surface-sunk);
  color: var(--ink-faint);
  cursor: not-allowed;
}

/* Checkbox & radio — 44px hit area via padded label */
.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: var(--touch);
  padding: var(--space-2) 0;
  cursor: pointer;
}
.choice input { width: 20px; height: 20px; margin: 2px 0 0; flex: none; accent-color: var(--primary); }
.choice__text { font-size: var(--step-0); }
.choice__note { display: block; font-size: var(--step--1); color: var(--ink-muted); }

/* Card-style radio, for pathway / option pickers */
.choice-card {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.choice-card:hover { border-color: var(--primary-bright); background: var(--primary-tint); }
.choice-card:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-tint);
  box-shadow: inset 0 0 0 1px var(--primary);
}

/* Typeahead — the school directory lookup with free-text fallback */
.typeahead { position: relative; }
.typeahead__menu {
  position: absolute; inset-inline: 0; top: calc(100% + 4px);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  max-height: 260px; overflow-y: auto;
  z-index: 50;
  padding: var(--space-2);
}
.typeahead__item {
  display: block; width: 100%; text-align: left;
  padding: var(--space-3);
  border: 0; background: transparent;
  border-radius: var(--radius-sm);
  font: inherit; color: var(--ink); cursor: pointer;
}
.typeahead__item:hover, .typeahead__item[aria-selected="true"] { background: var(--primary-tint); }
.typeahead__item small { display: block; color: var(--ink-muted); font-size: var(--step--1); }
.typeahead__add {
  border-top: 1px solid var(--line);
  margin-top: var(--space-2); padding-top: var(--space-2);
}

/* --------------------------------------------------------------------------
   Stepper — the multi-step registration indicator
   -------------------------------------------------------------------------- */

.stepper { margin-bottom: var(--space-7); }

.stepper__list {
  display: flex; list-style: none; margin: 0; padding: 0;
  gap: var(--space-2); overflow-x: auto; scrollbar-width: none;
}
.stepper__list::-webkit-scrollbar { display: none; }

.stepper__item { flex: 1 1 0; min-width: 92px; }

.stepper__link {
  display: block; text-decoration: none; color: inherit;
  padding-top: var(--space-3);
  border-top: 3px solid var(--line);
  transition: border-color var(--dur-fast) var(--ease);
}
.stepper__num {
  display: block; font-size: var(--step--2); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  /* --ink-faint would read as 4.39:1 on the warm surface — under AA at
     this size. Upcoming steps are de-emphasised by the grey top border
     and label weight instead of by pushing the text lighter. */
  color: var(--ink-muted); margin-bottom: 2px;
}
.stepper__label { font-size: var(--step--1); font-weight: 600; color: var(--ink-muted); }

.stepper__item--done .stepper__link  { border-top-color: var(--success); }
.stepper__item--done .stepper__num   { color: var(--success); }
.stepper__item--done .stepper__label { color: var(--ink); }

.stepper__item--current .stepper__link  { border-top-color: var(--accent); }
.stepper__item--current .stepper__num   { color: var(--accent); }
.stepper__item--current .stepper__label { color: var(--ink); font-weight: 700; }

/* Steps that are links (the profile wizard). Hover and focus say "you can go here" without
   changing the done/current colours that carry the actual progress. */
a.stepper__link { cursor: pointer; border-radius: var(--radius-sm); }
a.stepper__link:hover .stepper__label,
a.stepper__link:focus-visible .stepper__label { color: var(--primary); text-decoration: underline; text-underline-offset: 3px; }
a.stepper__link:hover { border-top-color: var(--primary); }

/* Below 560px five labelled steps cannot fit. Rather than scroll the
   strip sideways — which can push the current step off-screen and defeat
   the whole purpose of a progress indicator — collapse the others to
   coloured segments and label only where the user actually is. */
@media (max-width: 559px) {
  .stepper__item { min-width: 0; flex: 1 1 0; }
  .stepper__item .stepper__num,
  .stepper__item .stepper__label { display: none; }
  .stepper__item--current { flex: 3 1 auto; }
  .stepper__item--current .stepper__num,
  .stepper__item--current .stepper__label { display: block; }
  .stepper__link { padding-top: var(--space-2); }
}

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.card--pad-lg { padding: var(--space-6); }
.card--flush  { padding: 0; overflow: hidden; }

.card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}
.card__title { font-family: var(--font-body); font-size: var(--step-1); font-weight: 650; margin: 0; letter-spacing: 0; }

.card--link { display: block; text-decoration: none; color: inherit; transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease); }
.card--link:hover { border-color: var(--primary-bright); transform: translateY(-2px); color: inherit; }

/* Topic card — the 14 exam content areas */
.topic {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 600;
}
.topic__icon {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--primary-tint);
  color: var(--primary);
}

/* --------------------------------------------------------------------------
   Badges & pills
   -------------------------------------------------------------------------- */

.badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--step--1);
  font-weight: 650;
  line-height: 1.5;
  white-space: nowrap;
}
.badge--neutral { background: var(--surface-sunk); color: var(--ink-muted); }
.badge--info    { background: var(--primary-tint); color: var(--primary-deep); }
.badge--success { background: var(--success-tint); color: var(--success); }
.badge--warning { background: var(--warning-tint); color: var(--warning); }
.badge--danger  { background: var(--danger-tint);  color: var(--danger); }
.badge--gold    { background: var(--gold-tint);    color: var(--gold); }

/* --------------------------------------------------------------------------
   Alerts
   -------------------------------------------------------------------------- */

.alert {
  display: flex; gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid;
  border-radius: var(--radius);
  font-size: var(--step-0);
}
.alert__icon { flex: none; width: 20px; height: 20px; margin-top: 2px; }
.alert__title { font-weight: 700; margin: 0 0 var(--space-1); }
.alert p:last-child { margin-bottom: 0; }

.alert--info    { background: var(--primary-tint); border-color: #BCD8ED; color: var(--primary-deep); }
.alert--success { background: var(--success-tint); border-color: #B4DEC7; color: #145C38; }
.alert--warning { background: var(--warning-tint); border-color: #EBCF9F; color: #7A4800; }
.alert--danger  { background: var(--danger-tint);  border-color: #EDBDBF; color: #8C1216; }

/* --------------------------------------------------------------------------
   Tables — stack into cards below 720px
   -------------------------------------------------------------------------- */

.table-wrap { overflow-x: auto; }

.table { width: 100%; border-collapse: collapse; font-size: var(--step--1); }
.table th, .table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.table th {
  background: var(--surface-sunk);
  font-weight: 700;
  font-size: var(--step--2);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
  white-space: nowrap;
}
.table tbody tr:hover { background: var(--surface-warm); }
.table td.numeric, .table th.numeric { text-align: right; font-variant-numeric: tabular-nums; }

@media (max-width: 719px) {
  .table--stack thead { display: none; }
  .table--stack tr {
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-bottom: var(--space-3);
    padding: var(--space-2);
  }
  .table--stack td {
    display: flex; justify-content: space-between; gap: var(--space-4);
    border: 0; padding: var(--space-2) var(--space-3);
    text-align: right;
  }
  .table--stack td::before {
    content: attr(data-label);
    font-weight: 700; font-size: var(--step--2);
    letter-spacing: var(--tracking-wide); text-transform: uppercase;
    color: var(--ink-muted); text-align: left;
  }
}

/* --------------------------------------------------------------------------
   Stats band
   -------------------------------------------------------------------------- */

.stats {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 768px) { .stats { grid-template-columns: repeat(4, 1fr); } }

.stat__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--display-sm);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}
.stat__label {
  display: block;
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-top: var(--space-2);
}
.stats--on-dark .stat__value { color: var(--gold-light); }
.stats--on-dark .stat__label { color: rgba(255,255,255,0.78); }

/* --------------------------------------------------------------------------
   Partner logos — the wall on /home/partners and the ticker on the home page.
   The list itself is Models/PartnerLogos.cs; the tile is _PartnerLogoTile.cshtml.
   -------------------------------------------------------------------------- */

.logo-wall {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 560px) { .logo-wall { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .logo-wall { grid-template-columns: repeat(4, 1fr); } }

/* A logo is never cropped and never stretched: these arrive anywhere from 5:1 to
   square, and a uniform tile is what makes them sit together.

   The height is fixed rather than an aspect-ratio. With aspect-ratio the box has
   no definite height, so the image's own max-height:100% cannot resolve against
   it — a near-square logo then sizes itself from its width and pushes the tile
   50% taller than its 3:2 neighbours. Measured, not guessed. A definite height
   also reserves the space, so nothing shifts as the logos load. */
.logo-tile {
  width: 100%; height: 132px;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
@media (min-width: 560px) { .logo-tile { height: 148px; } }
/* The image fills the tile and object-fit letterboxes the artwork inside it.
   The obvious alternative — auto size with max-width/max-height:100% — does not
   hold: a percentage max-height does not resolve against a centred grid item, so
   the near-square logos sized themselves from their width and spilled 90px out
   of the tile. Measured, not guessed. contain never crops and never stretches. */
.logo-tile img {
  width: 100%; height: 100%;
  object-fit: contain; display: block;
}
.logo-tile--dark { background: var(--ink); border-color: var(--ink); }

/* When a logo fails to load (app.js retries once, then adds --failed), the tile shows
   the partner's initials in a gold ring and their name instead of a broken-image icon.
   A failed dark tile goes light, since the fallback is ink on a warm ground. */
.logo-tile__fallback { display: none; }
.logo-tile--failed img { display: none; }
.logo-tile--failed,
.logo-tile--failed.logo-tile--dark {
  background: var(--surface-warm); border-color: var(--line);
}
.logo-tile--failed .logo-tile__fallback {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2); height: 100%; text-align: center;
}
.logo-tile__initials {
  display: grid; place-items: center; flex: none;
  width: 48px; height: 48px; border-radius: 50%;
  border: 2px solid var(--gold); background: var(--surface);
  font-family: var(--font-display); font-weight: 700; font-size: var(--step-0);
  color: var(--primary-deep); letter-spacing: 0.02em;
}
.logo-tile__name {
  font-size: var(--step--1); font-weight: 650; line-height: 1.25; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The ticker tile is only 84px tall: initials and name side by side. */
.logo-tile--ticker.logo-tile--failed .logo-tile__fallback {
  flex-direction: row; justify-content: flex-start; text-align: left; gap: var(--space-3);
}
.logo-tile--ticker .logo-tile__initials { width: 40px; height: 40px; font-size: var(--step--1); }

/* Presenting Partners: one or more lead tiles side by side, centred above the wall. */
.logo-lead-row {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-5);
}
.logo-lead-row > * { flex: 1 1 280px; max-width: 400px; }

/* One partner leads the wall, on its own centred row above it. Position and width
   carry the emphasis; the tile keeps the wall's height and the same contain rule,
   so the logo is larger without being cropped or stretched.

   Width is the lever deliberately. This logo is a 5:1 wordmark, the widest of the
   set, so in a wall tile it already fills the width and uses a third of the
   height — more height would only add an empty band. Widening it is what actually
   makes it bigger. Change max-width alone to tune the size. */
.logo-tile--lead { max-width: 400px; margin-inline: auto; }
.logo-tile__label {
  display: block; text-align: center; margin-top: var(--space-2);
  font-size: var(--step--1); font-weight: 650;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--gold);
}

/* The home-page ticker: one row of tiles sliding left forever. The track holds
   the list TWICE and the animation moves it exactly one copy (-50%), so the loop
   is seamless. Tiles are spaced with margin-right, not gap: with gap the track is
   16 tiles + 15 gaps wide, so half of it is one gap short of a copy and the loop
   visibly jumps. With a margin on every tile, half the track is exactly one copy.
   The second copy is aria-hidden in the markup, so a screen reader hears each
   partner once. */
.logo-ticker {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.logo-ticker__track {
  display: flex; width: max-content;
  animation: logo-ticker 32s linear infinite;
}
.logo-ticker__track:hover,
.logo-ticker__track:focus-within { animation-play-state: paused; }
.logo-tile--ticker {
  width: 180px; height: 84px; flex: none;
  margin-right: var(--space-5);
  padding: var(--space-3) var(--space-4);
}
@keyframes logo-ticker { to { transform: translateX(-50%); } }

/* tokens.css already shortens every animation under reduced motion, but that alone
   would leave this strip frozen with logos clipped at both edges. Turn it into a
   plain wrapped wall instead, and drop the duplicate copy that only existed for
   the loop. */
@media (prefers-reduced-motion: reduce) {
  .logo-ticker { -webkit-mask-image: none; mask-image: none; }
  .logo-ticker__track {
    animation: none; width: auto;
    flex-wrap: wrap; justify-content: center; row-gap: var(--space-4);
  }
  .logo-tile--dup { display: none; }
}

/* --------------------------------------------------------------------------
   Important dates
   -------------------------------------------------------------------------- */

.dates { list-style: none; margin: 0; padding: 0; }
.dates li {
  display: flex; gap: var(--space-4); align-items: baseline;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line);
  margin: 0;
}
.dates li:last-child { border-bottom: 0; }
.dates__when {
  flex: none; width: 8.5rem;
  font-weight: 700; color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.dates__what { font-size: var(--step-0); }
.dates__note { display: block; font-size: var(--step--1); color: var(--ink-muted); }

/* --------------------------------------------------------------------------
   Accordion (FAQs)
   -------------------------------------------------------------------------- */

.accordion { border-top: 1px solid var(--line); }
.accordion details { border-bottom: 1px solid var(--line); }
.accordion summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  min-height: var(--touch);
  padding: var(--space-4) 0;
  cursor: pointer;
  font-weight: 650;
  list-style: none;
}
.accordion summary::-webkit-details-marker { display: none; }
.accordion summary::after {
  content: "";
  flex: none; width: 12px; height: 12px;
  border-right: 2px solid var(--primary); border-bottom: 2px solid var(--primary);
  transform: rotate(45deg); transform-origin: center;
  transition: transform var(--dur) var(--ease);
  margin-right: 4px;
}
.accordion details[open] summary::after { transform: rotate(-135deg); }
.accordion__body { padding-bottom: var(--space-5); color: var(--ink-muted); max-width: var(--prose); }

/* --------------------------------------------------------------------------
   Modal
   -------------------------------------------------------------------------- */

.modal {
  border: 0; padding: 0;
  border-radius: var(--radius-lg);
  max-width: 460px; width: calc(100% - var(--space-6));
  box-shadow: var(--shadow-lg);
}
.modal::backdrop { background: rgba(17, 24, 39, 0.55); }
.modal__inner { padding: var(--space-6); }
.modal__title { font-size: var(--step-2); margin-bottom: var(--space-3); }
.modal__actions {
  display: flex; gap: var(--space-3); justify-content: flex-end;
  margin-top: var(--space-6); flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   Empty state & skeleton
   -------------------------------------------------------------------------- */

.empty {
  text-align: center;
  padding: var(--space-8) var(--space-5);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface-warm);
}
.empty__icon { width: 44px; height: 44px; margin: 0 auto var(--space-4); color: var(--ink-faint); }
.empty__title { font-family: var(--font-body); font-size: var(--step-1); font-weight: 650; margin-bottom: var(--space-2); letter-spacing: 0; }
.empty p { color: var(--ink-muted); max-width: 42ch; margin-inline: auto; }

/* Progress bar — profile completion on the wizard and the dashboard. */
.progress {
  height: 6px;
  background: var(--surface-sunk);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.progress__bar {
  height: 100%;
  background: var(--success);
  border-radius: var(--radius-pill);
  transition: width var(--dur) var(--ease);
}

.skeleton {
  background: linear-gradient(90deg, var(--surface-sunk) 25%, #EDE8DE 37%, var(--surface-sunk) 63%);
  background-size: 400% 100%;
  animation: skeleton 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
.skeleton--text  { height: 0.85em; margin-bottom: var(--space-2); }
.skeleton--title { height: 1.6em; width: 55%; margin-bottom: var(--space-4); }
.skeleton--block { height: 120px; }
@keyframes skeleton { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

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

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255,255,255,0.94);
  backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--line);
}

.site-header__bar {
  display: flex; align-items: center; gap: var(--space-4);
  min-height: 76px;
}

.brand { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; color: inherit; margin-right: auto; }
.brand__seal { width: 46px; height: 46px; flex: none; }
.brand__text { display: flex; flex-direction: column; line-height: 1.15; }
.brand__name {
  font-family: var(--font-display); font-weight: 700;
  font-size: var(--step-0); letter-spacing: var(--tracking-tight);
}
.brand__sub { font-size: var(--step--2); color: var(--ink-muted); letter-spacing: 0.04em; text-transform: uppercase; }

/* The desktop nav appears at 1100px, not 1000px. Signed-in staff carry three extra
   things in this bar — an "Admin panel" link, the account chip and "Log out" — and
   below 1100 there is no slack left for them. A hamburger there beats a squeezed row. */
.nav { display: none; }
@media (min-width: 1100px) {
  .nav { display: flex; align-items: center; gap: var(--space-2); }
}
.nav__link {
  display: inline-flex; align-items: center;
  min-height: 40px; padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--step--1); font-weight: 600;
  color: var(--ink); text-decoration: none;
  /* One line, always. Without this a two-word label like "Co-Chairs' Message"
     breaks in half when the row gets tight and grows taller than its neighbours,
     which reads as a broken menu rather than a full one. */
  white-space: nowrap;
}
.nav__link:hover { background: var(--primary-tint); color: var(--primary-deep); }
.nav__link[aria-current="page"] { color: var(--primary); box-shadow: inset 0 -2px 0 var(--accent); border-radius: 0; }

.lang-toggle {
  display: inline-flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.lang-toggle button {
  min-height: 36px; min-width: 44px; padding: 0 var(--space-3);
  border: 0; background: transparent;
  font: inherit; font-size: var(--step--1); font-weight: 650;
  color: var(--ink-muted); cursor: pointer;
}
@media (pointer: coarse) { .lang-toggle button { min-height: var(--touch); } }
.lang-toggle button[aria-pressed="true"] { background: var(--primary); color: #fff; }

.menu-btn {
  display: inline-grid; place-items: center;
  width: var(--touch); height: var(--touch);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); cursor: pointer; color: var(--ink);
}
/* Must match the .nav breakpoint exactly. Out of step, the header shows either both
   the nav and the hamburger, or neither. */
@media (min-width: 1100px) { .menu-btn { display: none; } }

.mobile-nav {
  display: none;
  padding: var(--space-4) 0 var(--space-5);
  border-top: 1px solid var(--line);
}
.mobile-nav[data-open="true"] { display: block; }
.mobile-nav a {
  display: block; min-height: var(--touch);
  padding: var(--space-3) 0;
  font-weight: 600; color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--line);
}

/* Who is signed in, and the way out. Hidden on narrow screens, where the same
   controls live in the mobile drawer instead. */
.site-user { display: none; }
@media (min-width: 1100px) {
  .site-user { display: inline-flex; align-items: center; gap: var(--space-2); }
}
/* Same size, weight and colour as .nav__link, so the name reads as part of the bar
   rather than a grey footnote beside it. The ellipsis is the guard for long names, not
   the expected state — the bar has been given room for it (no Home item, short
   message labels). */
.site-user__name {
  max-width: 14ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--step--1); font-weight: 600; color: var(--ink);
}

/* Below 1280 the chip is the first thing to go. Ellipsised to 12ch it renders as
   "superad…", which names nobody while costing about 90px the nav actually needs.
   "Log out" stays, and the full address is still on the parent's title attribute. */
@media (max-width: 1279px) { .site-user__name { display: none; } }
.site-user__out { display: inline-flex; margin: 0; }
/* "लॉग आउट" has a space in it; when the bar is squeezed the button broke onto two lines. */
.site-user__out .btn { white-space: nowrap; }

.mobile-nav__who {
  padding: var(--space-3) 0;
  font-size: var(--step--2); color: var(--ink-muted);
  border-bottom: 1px solid var(--line);
}
.mobile-nav form { margin: var(--space-3) 0 0; }

/* Group headings inside the drawer — the desktop dropdowns, flattened. Plain text,
   not controls: on a phone every link is one tap away, and a collapsible group would
   only add a tap. */
.mobile-nav__heading {
  margin: 0; padding: var(--space-4) 0 var(--space-1);
  font-size: var(--step--2); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------------
   Utility strip — the dark line above the header: helpline, exam date, quick
   links. Outside .site-header on purpose, so it scrolls away and the sticky bar
   keeps its 76px.
   -------------------------------------------------------------------------- */

.util-strip {
  background: var(--primary-deep); color: #fff;
  font-size: var(--step--1); font-weight: 500;
}
.util-strip__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); min-height: 40px;
}
.util-strip a { color: #fff; text-decoration: none; }
.util-strip a:hover { text-decoration: underline; color: #fff; }
.util-strip__items { display: flex; align-items: center; gap: var(--space-5); }
.util-strip__item { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.util-strip__item svg { width: 16px; height: 16px; flex: none; stroke: var(--gold-light); }
/* The WhatsApp glyph is a filled shape, not a stroked outline like the others, so it
   takes the brand green as its fill and no stroke. */
.util-strip__item .contact-line__icon--whatsapp { stroke: none; color: #25D366; }
.util-strip__links { display: flex; gap: var(--space-5); }
/* Narrow screens keep only what a phone user acts on: the helpline. The quick links
   are all in the drawer already, and WhatsApp has the floating button. */
@media (max-width: 899.98px) { .util-strip__links { display: none; } }
@media (max-width: 599.98px) { .util-strip__item:not(.util-strip__item--phone) { display: none; } }

/* --------------------------------------------------------------------------
   Desktop nav dropdowns. A .nav__toggle button opens the .nav__menu panel it
   aria-controls (app.js); a fine pointer also gets it on hover. Buttons rather
   than links so touch and keyboard users reach the panel — the section's landing
   page is the first item inside it.
   -------------------------------------------------------------------------- */

.nav__item { position: relative; }
.nav__toggle {
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: var(--step--1); font-weight: 600;
  gap: var(--space-1);
}
.nav__toggle svg { width: 12px; height: 12px; stroke: var(--ink-muted); transition: transform var(--dur-fast) var(--ease); }
.nav__toggle[aria-expanded="true"] { background: var(--primary-tint); color: var(--primary-deep); }
.nav__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.nav__menu {
  display: none; position: absolute; top: 100%; left: 0; z-index: 101;
  min-width: 260px; padding: var(--space-2); margin-top: var(--space-1);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.nav__menu[data-open="true"] { display: block; }
.nav__menu a {
  display: block; padding: var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--step--1); font-weight: 600;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.nav__menu a:hover, .nav__menu a:focus-visible { background: var(--primary-tint); color: var(--primary-deep); }
@media (hover: hover) and (pointer: fine) {
  .nav__item:hover .nav__menu,
  .nav__item:focus-within .nav__menu { display: block; }
}

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

.site-footer {
  background: var(--ink);
  color: rgba(255,255,255,0.76);
  padding-block: var(--space-8) var(--space-6);
  font-size: var(--step--1);
}
.site-footer h4 { color: #fff; font-size: var(--step--1); letter-spacing: var(--tracking-wide); text-transform: uppercase; margin-bottom: var(--space-4); }
.site-footer a { color: rgba(255,255,255,0.76); text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li + li { margin-top: var(--space-2); }
.site-footer__base {
  margin-top: var(--space-7); padding-top: var(--space-5);
  border-top: 1px solid rgba(255,255,255,0.14);
  display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between;
  color: rgba(255,255,255,0.55);
  /* The contact FAB is fixed 16px from the bottom-right and is 56px tall, so it
     covers the last 72px of the viewport — exactly where the legal links land once
     you scroll to the end of the page. With the footer's own 32px this clears it. */
  padding-bottom: var(--space-7);
}

.site-footer__social { margin-top: var(--space-7); }
.site-footer__social ul {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  list-style: none; padding: 0; margin: 0;
}
/* li + li margin-top from .site-footer would push every wrapped icon out of line. */
.site-footer__social li + li { margin-top: 0; }
.site-footer__social a {
  display: grid; place-items: center;
  width: var(--touch); height: var(--touch);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 50%;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.site-footer__social a:hover {
  color: #fff;
  border-color: rgba(255,255,255,0.4);
  background: rgba(255,255,255,0.08);
  /* .site-footer a:hover underlines by default, which on an icon-only link draws a
     stray rule under the glyph. */
  text-decoration: none;
}

/* --------------------------------------------------------------------------
   Admin / dashboard shell
   -------------------------------------------------------------------------- */

.shell { display: grid; grid-template-columns: 1fr; }
@media (min-width: 960px) { .shell { grid-template-columns: 264px 1fr; } }

.shell__aside {
  background: var(--surface-warm);
  border-right: 1px solid var(--line);
  padding: var(--space-5);
}
.shell__main { padding: var(--space-6) var(--space-5) var(--space-9); min-width: 0; }
@media (min-width: 960px) { .shell__main { padding-inline: var(--space-7); } }

.side-nav { list-style: none; margin: 0 0 var(--space-6); padding: 0; }
.side-nav li { margin: 0; }
.side-nav a {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: var(--touch); padding: 0 var(--space-3);
  border-radius: var(--radius);
  color: var(--ink); text-decoration: none;
  font-size: var(--step--1); font-weight: 600;
}
.side-nav a:hover { background: var(--surface-sunk); }
.side-nav a[aria-current="page"] { background: var(--primary); color: #fff; }
.side-nav__icon { width: 18px; height: 18px; flex: none; }

.side-nav__heading {
  font-size: var(--step--2); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  /* --ink-muted, not --ink-faint: this sits on --surface-warm, where
     faint drops to 4.39:1. Faint is only safe on plain white. */
  color: var(--ink-muted);
  margin: var(--space-5) var(--space-3) var(--space-2);
}

.page-head {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  align-items: flex-end; justify-content: space-between;
  margin-bottom: var(--space-6);
}
.page-head h1 { font-size: var(--display-sm); margin: 0; }

/* --------------------------------------------------------------------------
   Floating contact button
   --------------------------------------------------------------------------
   WhatsApp is how most families will actually reach us, so it gets a
   persistent, thumb-reachable place rather than living only in the footer.

   Bottom-right and above the fold on every screen size. z-index 60 clears the
   sticky admin topbar (100 is admin-only, and this widget never renders there)
   while sitting above ordinary page content (50).
   -------------------------------------------------------------------------- */

.contact-fab {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}

.contact-fab__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}

.contact-fab__actions[hidden] { display: none; }

.contact-fab__action {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  /* The row is the target, not just the circle — a 48px circle alone is a hard
     tap on a cheap touchscreen. */
  border-radius: var(--radius-pill);
}

.contact-fab__label {
  background: var(--surface);
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow);
  white-space: nowrap;
}

.contact-fab__icon {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  flex: none;
  border-radius: 50%;
  color: #fff;
  box-shadow: var(--shadow-lg);
}

/* WhatsApp's own green. Brand recognition is the whole point of this button —
   in our palette it would read as a generic success chip and be missed. */
.contact-fab__action--whatsapp .contact-fab__icon { background: #1FA855; }

/* --------------------------------------------------------------------------
   Contact lines — the helpline and WhatsApp numbers wherever they are offered
   as contact details rather than mentioned in a sentence: the Contact page, the
   footer, the dashboard help card and the legal contact block. Icons come from
   _IconPhone / _IconWhatsApp so the paths live in one place; the numbers come
   from ContactOptions, never from the markup.
   -------------------------------------------------------------------------- */

.contact-line { display: flex; align-items: center; gap: var(--space-2); }
.contact-line + .contact-line { margin-top: var(--space-2); }
.contact-line__icon { width: 18px; height: 18px; flex: none; }
/* Brand green, and a lighter one on the near-black footer where the darker
   green loses too much contrast against the background. */
.contact-line__icon--whatsapp { color: #1FA855; }
.site-footer .contact-line__icon--whatsapp { color: #25D366; }
.contact-fab__action--call     .contact-fab__icon { background: var(--primary); }

.contact-fab__toggle {
  width: 56px; height: 56px;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  position: relative;
}

.contact-fab__toggle:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 3px;
}

/* Both icons occupy the same cell; only one is visible, so the button never
   changes size as it toggles. */
.contact-fab__toggle-icon {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  transition: opacity 120ms ease, transform 120ms ease;
}

.contact-fab__toggle-icon--close { opacity: 0; transform: rotate(-45deg); }

.contact-fab__toggle[aria-expanded="true"] .contact-fab__toggle-icon--open  { opacity: 0; transform: rotate(45deg); }
.contact-fab__toggle[aria-expanded="true"] .contact-fab__toggle-icon--close { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .contact-fab__toggle-icon { transition: none; }
}

/* A print-out of a page has no use for a floating button, and it would land on
   top of the form content. */
@media print { .contact-fab { display: none !important; } }

/* --------------------------------------------------------------------------
   Execution team
   Photo tiles that reveal the member's one-line role on hover and open the full
   profile in a dialog on click. Hover is an enhancement, never the only route:
   most of this audience is on a phone, where hover does not exist, so the role
   line is printed under the name instead (see the (hover: none) block below).
   -------------------------------------------------------------------------- */

.team-grid {
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 640px) { .team-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .team-grid { grid-template-columns: repeat(4, 1fr); } }

/* The whole tile is the button, and the name sits inside it, so the control has
   a real accessible name instead of announcing as an unlabelled link. */
.team-card__trigger {
  display: flex; flex-direction: column; gap: var(--space-3);
  width: 100%; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}

.team-card__photo {
  position: relative; display: block;
  aspect-ratio: 1; overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
}
.team-card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Placeholder until a photograph is dropped into wwwroot/img/team/. */
.team-card__initials {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 7vw, 2.75rem); font-weight: 700;
  letter-spacing: var(--tracking-wide);
  color: var(--primary-deep);
  background: linear-gradient(160deg, var(--primary-tint), var(--gold-tint));
}

.team-card__veil {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-4);
  background: linear-gradient(to top, rgba(10, 66, 113, 0.94), rgba(10, 66, 113, 0.72) 55%, rgba(10, 66, 113, 0.25));
  color: #fff;
  opacity: 0; transition: opacity 220ms ease-in-out;
}
.team-card__role { font-size: var(--step--1); line-height: var(--leading-snug); }
.team-card__cue {
  font-size: var(--step--2); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--gold-light);
}

.team-card__name {
  font-weight: 650; font-size: var(--step--1); line-height: var(--leading-snug);
  color: var(--ink);
}
.team-card__role-static { display: none; }

/* Hover devices get the reveal. Focus-visible gets it too, so a keyboard user
   sees the same thing rather than tabbing past a tile that says nothing. */
@media (hover: hover) {
  .team-card__trigger:hover .team-card__veil,
  .team-card__trigger:focus-visible .team-card__veil { opacity: 1; }
  .team-card__trigger:hover .team-card__name { color: var(--primary); }
}

/* No hover: the veil can never be summoned, so the role is printed instead and
   the tile stays tappable for the full profile. */
@media (hover: none) {
  .team-card__veil { display: none; }
  .team-card__role-static {
    display: block; margin-top: var(--space-1);
    font-size: var(--step--2); line-height: var(--leading-snug); color: var(--ink-muted);
  }
}

@media (prefers-reduced-motion: reduce) { .team-card__veil { transition: none; } }

/* Team profile dialog. The shared .modal is sized for a confirm prompt; a profile is
   a portrait and 250 words of prose, so it gets its own shell: a tinted header band
   carrying the photo, a scrolling body, and a footer that stays put while it scrolls. */
.modal--profile { max-width: 620px; }
.modal--profile .modal__inner {
  padding: 0;
  display: flex; flex-direction: column;
  max-height: min(86vh, 780px);
}

.profile__head {
  position: relative;
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-6);
  padding-right: var(--space-8);           /* clear of the close button */
  background: linear-gradient(135deg, var(--primary-tint) 0%, var(--gold-tint) 100%);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.profile__avatar {
  flex: 0 0 auto;
  width: 84px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-pill);
  border: 3px solid var(--surface);
  box-shadow: var(--shadow);
  background: var(--surface-sunk);
}
.profile__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.profile__avatar > span {
  font-family: var(--font-display); font-weight: 700;
  font-size: var(--step-2); color: var(--primary-deep);
  letter-spacing: var(--tracking-wide);
}

.profile__ident { min-width: 0; }
.profile__name {
  margin: 0;
  font-size: var(--step-2); line-height: var(--leading-tight);
}
.profile__role {
  margin: var(--space-2) 0 0;
  font-size: var(--step--1); line-height: var(--leading-snug); color: var(--ink-muted);
}

.profile__close {
  position: absolute; top: var(--space-4); right: var(--space-4);
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.75);
  color: var(--ink-muted); font-size: 1.25rem; line-height: 1; cursor: pointer;
}
.profile__close:hover { background: var(--surface); color: var(--ink); }

.profile__body {
  flex: 1 1 auto; overflow-y: auto;
  padding: var(--space-6);
  color: var(--ink-muted); line-height: var(--leading-normal);
}
.profile__body p + p { margin-top: var(--space-4); }
/* The source profiles carry their own emphasis — bold for roles and awards, italic
   for titles of works. Keep it legible rather than shouty. */
.profile__body strong { color: var(--ink); font-weight: 650; }
.profile__body em { font-style: italic; }
.profile__body sup { font-size: 0.68em; line-height: 0; vertical-align: super; }

.profile__foot {
  flex: 0 0 auto;
  display: flex; justify-content: flex-end;
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--line);
  background: var(--surface-warm);
}

/* On a phone the portrait costs room the name needs, so the header stacks. */
@media (max-width: 479px) {
  .profile__head { flex-direction: column; align-items: flex-start; gap: var(--space-3); padding: var(--space-5); padding-right: var(--space-7); }
  .profile__avatar { width: 64px; height: 64px; }
  .profile__body { padding: var(--space-5); }
  .profile__foot { padding: var(--space-4) var(--space-5); }
}

/* ---- Registration-closed notice (_RegistrationClosedNotice) --------------
   Tinted panel with a rule down its side, not a filled block: at CTA size and
   CTA colour people click it, and there is nothing here to click. The rule and
   the icon carry the emphasis instead. Used by the home hero and the About band. */
.reg-notice {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  max-width: 34rem;
  padding: var(--space-4) var(--space-5);
  background: var(--accent-tint);
  border-inline-start: 4px solid var(--accent);
  border-radius: var(--radius);
}
.reg-notice__icon { flex: none; width: 1.6rem; height: 1.6rem; color: var(--accent); margin-top: 0.1rem; }
.reg-notice__title {
  display: block;
  font-size: var(--step-1);
  font-weight: 700;
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--accent-deep);
}
.reg-notice__body { display: block; margin-top: var(--space-1); color: var(--ink-muted); }
