/* Pedagogy · Sharjah Expo app
   Palette + font stack carried forward from reference/pedagogy-expo-v2.html
   (per CLAUDE.md "Visual design direction" — keep palette/fonts, redo everything
   else). Layout, hero and all illustration below are new, code-drawn work —
   no images anywhere except the logo (assets/logo/). */

/* Self-hosted display face (Anton, latin subset only — English-only app).
   Local file, no runtime network dependency (see index.html removal of the
   Google Fonts <link>). font-display: swap avoids a blocking hero repaint. */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/Anton-Regular.woff2") format("woff2");
}

:root {
  --bg: #f6f5ef;
  --ink: #192d24;
  --green: #2b614b;
  --green-deep: #1d4a38;
  --muted: #66776b;
  --line: #dce3d7;
  --lime: #d8ee96;
  --purple: #e6def6;
  --purple-ink: #4a3a72;
  /* Focus ring: green (matches the form's primary color), not the old lavender
     which read as unrelated to a green-primary form (item 4c). */
  --focus-ring: #1f7a4c;
  /* Brand inks. Measured-from-source value was #2053AE; softened lighter per
     2026-09-19 live feedback (it read too dark/heavy in the app's UI chrome).
     This is a deliberate UI tweak, not a re-measurement of the client's mark —
     flag alongside the mono variants for Pedagogy's sign-off before the real
     event, same as any other retint of delivered brand colour. The colour logo
     symbols reference these with a hex fallback, so they still render correctly
     even if these custom properties were removed. */
  --pedagogy-blue: #3B68B8;
  --pedagogy-ink: #141414;
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 25px;
  --radius-pill: 999px;
  --font: system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Hero headline only — see index.html's font <link> comment. */
  --font-display: "Anton", "Arial Narrow", var(--font);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
}

button {
  font: inherit;
  cursor: pointer;
  border: 0;
  border-radius: var(--radius-sm);
  padding: 12px 20px;
  min-height: 46px;
  background: #e9eee2;
  color: var(--green);
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), box-shadow .15s ease;
}
button:hover:not(:disabled) { transform: translateY(-2px); }
button:active:not(:disabled) { transform: scale(0.96); transition-duration: .08s; }
button:disabled { opacity: .5; cursor: default; transform: none; }
button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 3px; }
button.is-active,
button.primary { background: var(--green); color: white; font-weight: 700; }
button.quiet { background: transparent; border: 1px solid var(--line); color: var(--ink); }

a { color: var(--green); }

textarea, input[type="text"], input[type="tel"], input[type="date"], input[type="email"], input[type="password"], select {
  font: inherit;
  display: block;
  width: 100%;
  margin-top: 7px;
  background: #fbfcf8;
  border: 1px solid #d8e0d2;
  border-radius: var(--radius-sm);
  padding: 12px;
  color: var(--ink);
  min-height: 48px;
}
textarea:focus-visible, input:focus-visible, select:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}
label { display: block; font-size: 13px; font-weight: 650; margin: 0 0 16px; }

/* ============================= HERO ============================= */
/* Design read: an event-stall landing/prize moment for teenage students and
   parents, premium-but-approachable, code-drawn motion in place of photography
   (no stock imagery anywhere in this app). Asymmetric split (not centered),
   max 4 hero text elements (eyebrow, headline, subtext, CTA), motion kept to
   entrance + slow idle drift + tap feedback — nothing that fights for
   attention against the actual content. */

.hero-screen {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--bg);
  overflow: hidden;
  transition: opacity .28s ease, transform .28s ease;
}
.hero-screen--exit { opacity: 0; transform: translateY(-16px); pointer-events: none; }

.hero-header { padding: 20px 28px 0; }
/* ============================= LOGO ============================= */
/* Real vector sprite (see the inline <symbol> defs at the top of <body>,
   sourced from assets/logo/). Size in CSS only: height is the single lever,
   width follows via aspect-ratio, so there is never a layout shift while the
   sprite resolves. Clear space equal to the emblem's own radius, no border,
   no backing plate, no rounded container — the old raster's white box is gone
   because there is no raster background left to remove it from.

   Responsive collapse (thresholds measured and documented in
   assets/logo/README.md): the full lockup is legible down to about 180px
   wide and starts breaking up around 130px, so it only ever renders at a
   height tall enough to clear that (viewBox 2668:994, so height >= ~67px)
   and is replaced by the mark below that width. Both the hero and the
   persistent app-shell nav use the same lockup-above/mark-below pattern (per
   2026-09-19 feedback: a mark-only nav read as under-branded on the actual
   registration flow) — the nav's lockup just tops out smaller than the
   hero's, since it's secondary chrome, not the landing moment. The mark
   itself is floored at 32px (see logo--nav below) since its counters (the
   P/E/S negative space) fill in below that. */
.logo { display: inline-flex; align-items: center; }
.logo-svg { display: block; width: auto; }
.logo-svg--lockup, .logo-svg--lockup-mono { aspect-ratio: 2668 / 994; }
.logo-svg--mark, .logo-svg--mark-mono { aspect-ratio: 1014 / 994; }

.logo--hero .logo-svg--lockup { height: 40px; display: none; }
.logo--hero .logo-svg--mark { height: 40px; display: block; }
@media (min-width: 760px) {
  .logo--hero .logo-svg--lockup { height: clamp(56px, 6vw, 76px); display: block; }
  .logo--hero .logo-svg--mark { display: none; }
}

.logo--nav .logo-svg--lockup { height: 34px; display: none; }
.logo--nav .logo-svg--mark { height: 34px; min-height: 32px; display: block; }
@media (min-width: 760px) {
  .logo--nav .logo-svg--lockup { height: clamp(48px, 4.4vw, 64px); display: block; }
  .logo--nav .logo-svg--mark { display: none; }
}

/* Footer band uses the full-colour lockup on the page's own cream background
   (see .site-footer below) — not the mono/white treatment on a green band
   from the previous pass, which read as "a green logo" (2026-09-19 feedback:
   keep the logo in its real blue/black ink, no green anywhere). */
.logo--footer .logo-svg--lockup { height: 60px; }

.hero-body {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  align-items: center;
  gap: clamp(24px, 4vw, 64px);
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 56px);
  overflow: hidden;
}

.hero-content { max-width: 660px; z-index: 2; padding-block: 12px; }

.hero-content .eyebrow {
  font-size: 11px;
  letter-spacing: 2px;
  font-weight: 800;
  color: var(--green);
  margin: 0 0 16px;
}

/* WIN AN iPAD is the largest type on the page, by a clear margin — this is a
   noisy expo hall, it has to read from across an aisle (see build brief §2).
   Display face (Anton, see --font-display) instead of a bolded system sans:
   the condensed poster letterforms are what actually make it stand out, not
   just size. Anton's glyphs run narrower than the system font at the same
   point size, so it can go bigger without breaking the line, and its own
   tracking is tight enough that it doesn't need the old negative
   letter-spacing hack (that was compensating for the system font, not this one). */
.hero-title {
  font-family: var(--font-display);
  font-size: clamp(64px, 8.6vw, 132px);
  line-height: 0.88;
  letter-spacing: 0.5px;
  font-weight: 400;
  margin: 0 0 18px;
  color: var(--ink);
  text-transform: uppercase;
}
.hero-title-accent { color: var(--green); }

.hero-subhead {
  font-size: 18px;
  color: var(--muted);
  max-width: 40ch;
  margin: 0 0 22px;
}

.hiw-strip {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin: 0 0 18px;
  padding: 0;
}
.hiw-strip li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
}
.hiw-icon {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--green);
  color: white;
  font-size: 12px;
  font-weight: 800;
  flex-shrink: 0;
}

.hero-time-note { font-size: 13px; color: var(--muted); font-weight: 600; margin: 0 0 20px; }

.hero-cta { font-size: 16px; padding: 15px 30px; }

.hero-fineprint {
  font-size: 12px;
  color: var(--muted);
  max-width: 46ch;
  margin: 16px 0 0;
  line-height: 1.5;
}
.hero-fineprint .link-btn { font-size: 12px; }

/* Staggered entrance: each hero-reveal element carries its own --d index. */
.hero-reveal {
  opacity: 0;
  transform: translateY(16px);
  animation: hero-reveal-in .6s cubic-bezier(.16,1,.3,1) forwards;
  animation-delay: calc(var(--d, 0) * 90ms + 80ms);
}
@keyframes hero-reveal-in { to { opacity: 1; transform: translateY(0); } }

/* Code-drawn iPad silhouette + two floating info cards, replacing v2's
   .orbit/.paper/.ticket illustration entirely (per CLAUDE.md, retired not reused).
   The whole composition drifts with pointer parallax (--px/--py, set from JS,
   transform-only) and the device itself idles on a slow continuous tilt. */
.hero-visual {
  position: relative;
  z-index: 1;
  --px: 0;
  --py: 0;
  min-height: 420px;
}

.hv-ring {
  position: absolute;
  border-radius: 50%;
  border: 1px solid #bcc9b3;
  inset: 6% 10%;
  transform: translate(calc(var(--px) * 6px), calc(var(--py) * 6px));
}
.hv-ring--outer { animation: hv-spin 60s linear infinite; }
.hv-ring--inner { inset: 20% 24%; border-color: #cdb9ea; animation: hv-spin 40s linear infinite reverse; }
@keyframes hv-spin { to { transform: rotate(360deg); } }

.hv-device {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(78%, 420px);
  transform: translate(-50%, -50%) translate(calc(var(--px) * -10px), calc(var(--py) * -10px));
}
.hv-device-svg {
  width: 100%;
  height: auto;
  display: block;
  animation: hv-tilt 7s ease-in-out infinite;
  transform-origin: 50% 90%;
}
@keyframes hv-tilt {
  0%, 100% { transform: perspective(900px) rotateX(4deg) rotateY(-6deg); }
  50% { transform: perspective(900px) rotateX(2deg) rotateY(4deg); }
}
.hv-device-chassis { fill: #1c2b23; }
.hv-device-screen { fill: url(#hv-screen-gradient); }
.hv-device-camera { fill: #46564d; }
.hv-device-shadow {
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: -18px;
  height: 22px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(25,45,36,.30), transparent 70%);
  filter: blur(2px);
}

.hv-card {
  position: absolute;
  background: white;
  border-radius: var(--radius-md);
  padding: 16px 20px;
  box-shadow: 0 18px 40px rgba(25, 45, 36, 0.10);
  animation: hv-float 6s ease-in-out infinite;
}
.hv-card-eyebrow { display: block; font-size: 10px; letter-spacing: 1.5px; font-weight: 800; color: var(--green); margin-bottom: 6px; }
.hv-card-title { display: block; font-size: 18px; letter-spacing: -.3px; }
.hv-card--1 {
  left: -2%; top: 10%;
  background: var(--lime); white-space: nowrap;
  transform: rotate(-4deg) translate(calc(var(--px) * 14px), calc(var(--py) * 14px));
}
.hv-card--2 {
  right: -2%; bottom: 6%;
  background: var(--purple); color: var(--purple-ink); animation-delay: -3s; white-space: nowrap;
  transform: rotate(3deg) translate(calc(var(--px) * -14px), calc(var(--py) * -14px));
}
.hv-card--2 .hv-card-eyebrow { color: var(--purple-ink); }
@keyframes hv-float { 0%, 100% { transform: translateY(0) rotate(var(--r, 0deg)); } 50% { transform: translateY(-10px) rotate(var(--r, 0deg)); } }

/* Bottom "swipe up to enter" banner — a real sticky-sheet affordance. Tap
   always works (motion is a shortcut, never the only way to trigger it, same
   principle both reference prototypes use for their swipe interactions). */
.prize-banner {
  position: relative;
  z-index: 3;
  width: 100%;
  border-radius: 0;
  min-height: 76px;
  padding: 14px 24px calc(14px + env(safe-area-inset-bottom));
  background: linear-gradient(135deg, var(--green), var(--green-deep));
  color: white;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  touch-action: none;
  cursor: grab;
}
.prize-banner-handle {
  width: 44px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.55);
  animation: hv-nudge 2.4s ease-in-out infinite;
}
@keyframes hv-nudge { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
.prize-banner-text { font-weight: 700; font-size: 15px; }

@media (max-width: 980px) {
  .hero-body { grid-template-columns: 1fr; }
  .hero-visual { order: -1; min-height: 260px; opacity: .9; }
  .hero-content { max-width: none; }
  .hero-title { font-size: clamp(44px, 12vw, 64px); }
}

/* ========================= APP SHELL / STEPS ========================= */

.site-header {
  max-width: 1180px;
  margin: auto;
  padding: 22px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 15px;
}

.brand-mark {
  /* Long-press is the staff-view gate (see app.js) — suppress iOS's
     text-selection / callout menu so the press reads as a tap-and-hold, not
     a "copy image" gesture. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

.site-nav { display: flex; gap: 8px; }
.site-nav button { background: transparent; font-size: 13px; }
.site-nav button.is-active { background: var(--green); color: white; }

main {
  max-width: 900px;
  padding: 20px 24px 70px;
  margin: auto;
}

.step-panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 28px;
  background: white;
}
.step-panel .step-progress { font-size: 12px; color: var(--muted); margin: 0 0 16px; }
.step-panel { overflow: hidden; }

/* Direction-aware step transitions: forward slides one way, back the other, so
   the already-existing free back-navigation reads as spatial movement rather
   than a flat swap (motion pass, item 5). app.js toggles these classes. */
#stepContent { animation-duration: .32s; animation-timing-function: cubic-bezier(.16,1,.3,1); animation-fill-mode: both; }
#stepContent.step-anim-forward-in { animation-name: step-slide-in-fwd; }
#stepContent.step-anim-back-in { animation-name: step-slide-in-back; }
@keyframes step-slide-in-fwd { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: translateX(0); } }
@keyframes step-slide-in-back { from { opacity: 0; transform: translateX(-28px); } to { opacity: 1; transform: translateX(0); } }
.eyebrow-small { font-size: 10px; letter-spacing: 1.6px; font-weight: 800; color: var(--green); margin: 0 0 10px; }
.step-heading { font-size: clamp(22px, 3vw, 30px); letter-spacing: -.5px; margin: 0 0 12px; }
.field-hint { color: var(--muted); font-size: 13px; margin: 0 0 18px; }

.step-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }

.field-row.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.contact-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 4px 0 20px; }
.contact-card {
  border-radius: var(--radius-md);
  padding: 18px;
  border: 1px solid var(--line);
}
.contact-card-label { font-size: 13px; font-weight: 700; margin: 0 0 6px; display: flex; align-items: center; gap: 8px; }
.contact-card--required {
  background: #eef6ea;
  border: 1px solid #b9d9a4;
  border-left: 4px solid var(--green);
}
.contact-card--optional {
  background: #fbfcf8;
  border-style: dashed;
}
.phone-input-row { display: flex; gap: 8px; }
.phone-input-row .phone-country-code { width: 84px; flex: 0 0 auto; text-align: center; padding-left: 8px; padding-right: 8px; }
.phone-input-row .phone-number { flex: 1 1 auto; }
.badge { font-size: 10px; letter-spacing: .6px; padding: 3px 8px; border-radius: var(--radius-pill); font-weight: 800; }
.badge--required { background: var(--green); color: white; }
.badge--optional { background: var(--line); color: var(--muted); }

.school-field { position: relative; margin-bottom: 4px; }
.school-input-wrap { position: relative; }
/* The label now lives outside this wrap as its own element (own
   margin-bottom already spaces it from the input below), so the input's
   normal margin-top: 7px (meant to separate it from a label wrapping it
   directly, the pattern every other field in this form still uses) is
   redundant here and was throwing off the Clear button's vertical centering
   below — reset to 0 so the wrap's height exactly equals the input's own
   48px box. */
.school-input-wrap input { margin-top: 0; padding-right: 70px; }
/* Client feedback (2026-09-29): a plain × icon read as broken/unclear —
   changed to a labelled "Clear" pill. Sized and centered against the
   input's own known 48px height (see above) with a fixed height + margin-top
   offset rather than `transform: translateY(-50%)`, deliberately — this
   button is still a <button>, which inherits this file's global
   `button:hover`/`button:active` lift/scale transform (below); centering via
   `transform` here would fight that on every hover/press, which is exactly
   what "should be fixated not animated" flagged. margin-based centering
   doesn't touch the transform property at all, so nothing to conflict with. */
.school-clear-btn {
  position: absolute;
  top: 50%;
  right: 8px;
  height: 32px;
  min-height: 0; /* the generic `button` rule above sets min-height: 46px, which was clamping this to 46px regardless of the explicit height:32px here */
  margin-top: -16px; /* half of the 32px height above */
  padding: 0 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: white;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.school-clear-btn:hover, .school-clear-btn:focus-visible { background: var(--line); color: var(--ink); }
/* Belt-and-braces: even though margin-based centering above doesn't use
   `transform`, explicitly neutralize the global hover-lift/press-scale here
   too, so this small fixed utility control never visibly shifts. */
.school-clear-btn:hover:not(:disabled), .school-clear-btn:active:not(:disabled) { transform: none; }
.school-suggestions {
  list-style: none;
  margin: 4px 0 0;
  padding: 6px;
  position: absolute;
  z-index: 5;
  left: 0; right: 0;
  background: white;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 24px rgba(25, 45, 36, 0.12);
  max-height: 260px;
  overflow-y: auto;
}
.school-suggestions li {
  padding: 10px 12px; border-radius: var(--radius-sm); cursor: pointer; font-size: 14px;
  opacity: 0; transform: translateY(-6px);
  animation: suggestion-in .22s cubic-bezier(.16,1,.3,1) forwards;
  animation-delay: calc(var(--stagger-i, 0) * 35ms);
}
@keyframes suggestion-in { to { opacity: 1; transform: translateY(0); } }
.school-suggestions li:hover, .school-suggestions li:focus-visible { background: #eef6ea; }
.school-suggestions li small { display: block; color: var(--muted); font-size: 11px; margin-top: 2px; }

.link-btn {
  background: none;
  border: 0;
  padding: 0;
  min-height: auto;
  color: var(--green);
  font-weight: 700;
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link-btn:hover { transform: none; }

.curriculum-field { min-width: 0; }
.derived-line {
  font-size: 13px;
  color: var(--muted);
  background: #eef6ea;
  border: 1px solid #b9d9a4;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 7px 0 0;
}
.field-hint--soft { color: var(--muted); font-style: italic; }
/* "Also considering" pills for destinations picked via the "Other" search
   overlay (client feedback, 2026-09-29: needs a visible remove affordance,
   not just re-tapping a chip in the grid above). */
.removable-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: #eef6ea;
  border: 1px solid #b9d9a4;
  border-radius: var(--radius-pill);
  padding: 3px 4px 3px 10px;
  margin: 3px 4px 3px 0;
  font-style: normal;
  font-size: 13px;
}
.removable-pill-x {
  border: none;
  background: transparent;
  color: var(--muted);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.removable-pill-x:hover, .removable-pill-x:focus-visible { background: var(--line); color: var(--ink); }
/* Round E item 5, restyled Round F item 3: real inline field errors,
   replacing alert()/modal-style registration validation. Round F switched
   the color from an ad-hoc #8a3b1f to the app's one existing destructive/
   error red (#c0503a — already used for staff duplicate-flags and "Clear all
   local data"), so this reads as the same "error" language everywhere rather
   than introducing a second reddish tone. Rendered right under/next to the
   field it belongs to; shown only once that field has actually been touched
   or a submit attempt was made (js/registration.js's `touched`/
   `attemptedSubmit` — Round F item 1), never on a completely fresh, untouched
   step. */
.field-error { color: #c0503a; font-size: 12px; font-weight: 700; margin: 6px 0 0; }
/* Round F item 3: the field-error TEXT alone was easy to miss — this puts the
   same red on the actual input/select so the visitor can see which exact box
   is wrong at a glance, not just by reading every line on the page. Applied
   directly to text/date inputs and the two selects; for a phone pair
   (country-code + local-number, two separate boxes) it's applied to their
   shared .phone-input-row wrapper instead, so both boxes in the pair read as
   one invalid unit rather than guessing which of the two is at fault. Same
   live show/hide rule as .field-error (only visible once touched). */
input.field-invalid, select.field-invalid,
.phone-input-row.field-invalid .phone-country-code,
.phone-input-row.field-invalid .phone-number {
  border-color: #c0503a;
  box-shadow: 0 0 0 1px #c0503a;
}
/* Client feedback (2026-09-29): an invalid field that's also focused showed a
   red border AND the global green (--focus-ring) focus-visible outline at
   the same time — read as broken/inconsistent. Once a field is genuinely
   invalid, its focus ring should read red too, not the default green. */
input.field-invalid:focus-visible, select.field-invalid:focus-visible,
.phone-input-row.field-invalid .phone-country-code:focus-visible,
.phone-input-row.field-invalid .phone-number:focus-visible {
  outline-color: #c0503a;
}

.tcs-block { margin: 8px 0 22px; }
/* isolation: own compositing/stacking context, so a neighbouring native <select>
   popup's paint never bleeds a stray line across this text (item 4b). Real
   checkbox input stays in the DOM (keyboard/AT operable) but is visually
   replaced by .check-box, an SVG checkmark that draws itself on check rather
   than popping in (motion pass, item 5). */
.check {
  display: flex; gap: 10px; align-items: flex-start; font-weight: 400; font-size: 14px;
  position: relative; isolation: isolate; cursor: pointer;
}
.check input {
  position: absolute;
  z-index: 1;
  width: 22px; height: 22px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.check-box {
  flex-shrink: 0;
  margin-top: 1px;
  width: 20px; height: 20px;
  border-radius: 5px;
  border: 2px solid #b7c4af;
  background: #fbfcf8;
  display: grid;
  place-items: center;
  transition: background .15s ease, border-color .15s ease, transform .15s cubic-bezier(.34,1.56,.64,1);
}
.check-box svg { width: 13px; height: 13px; overflow: visible; }
.check-box polyline {
  fill: none;
  stroke: white;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  transition: stroke-dashoffset .28s ease .02s;
}
.check input:checked + .check-box {
  background: var(--green);
  border-color: var(--green);
  transform: scale(1.06);
}
.check input:checked + .check-box polyline { stroke-dashoffset: 0; }
.check input:focus-visible + .check-box { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.check:active .check-box { transform: scale(0.92); }

/* Chips — same interaction pattern as reference/pedagogy-expo.html's chips():
   a real checkbox/radio per option, styled as a pill. */
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0 22px; }
.chips label { margin: 0; cursor: pointer; }
.chips input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.chips span {
  display: block;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 600;
  transition: background .2s cubic-bezier(.34,1.56,.64,1), color .15s ease,
    border-color .15s ease, transform .2s cubic-bezier(.34,1.56,.64,1);
}
.chips input:checked + span { background: var(--green); color: white; border-color: var(--green); transform: scale(1.04); }
/* Destination cap (client feedback, 2026-09-29: max 3 countries) — an
   unchecked, disabled chip once the cap is reached, per js/chips.js's `max`
   option. Checked chips are never disabled, so removing one is always
   possible even while at the cap. */
.chips input:disabled + span { opacity: .4; cursor: not-allowed; }
.chips input:focus-visible + span { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.chips label:active span { transform: scale(0.94); transition-duration: .08s; }

.exam-group-label { margin-bottom: 4px; font-weight: 700; color: var(--ink); }

/* Data-driven course suggestion (Phase 3, js/questions.js's getSuggestedCourses):
   a visual mark only — pre-suggest, never pre-select or restrict, per
   curriculum_subjects.json's own app_integration_notes. */
.chips span.chip-suggested { border-color: var(--green); box-shadow: inset 0 0 0 1px var(--green); }
.chips span.chip-suggested::before { content: '\2726'; margin-right: 5px; font-size: 10px; color: var(--green); }
.chips input:checked + span.chip-suggested::before { color: white; }

/* Pooled quiz timer (Phase 2) — only ever rendered inside the quiz step's own
   panel, so it's naturally absent everywhere else (registration, preference
   steps, review) without any extra show/hide logic. */
.quiz-timer {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  color: var(--green);
  background: #eef6ea;
  border: 1px solid #b9d9a4;
  border-radius: var(--radius-pill);
  padding: 6px 14px;
  margin: 0 0 16px;
}
.quiz-timer--low { color: #8a3b1f; background: #fbe9e2; border-color: #eab99f; }
/* Quiz options: one full-width row per option (not a wrapping pill row like
   other chip groups) — answer text length varies too much (a bare "8" next
   to a full sentence) for a shrink-wrapped centered pill to read well; a
   left-aligned row with a fixed-size letter badge keeps every option the
   same shape regardless of its text length. */
.chips--quiz { flex-direction: column; flex-wrap: nowrap; }
.chips--quiz label { width: 100%; }
/* Direct-child combinator: this must only style the option's own outer pill,
   not also `.quiz-option-letter` nested inside it — `.chips span` (a plain
   descendant selector) matches both, and its tag-qualified specificity beats
   a class-only rule, which is exactly what inflated the letter badge into a
   full-width pill the first time this was tried. The badge is a <b>, not a
   <span>, precisely so no span-descendant selector can catch it by accident. */
.chips--quiz label > span {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  text-align: left;
  padding: 14px 18px;
}
.quiz-option-letter {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--line);
  color: var(--ink);
  font-size: 12px;
  font-weight: 800;
  font-style: normal;
}
.chips--quiz input:checked + span .quiz-option-letter { background: rgba(255, 255, 255, 0.25); color: white; }

/* Review screen (Phase 2, session_handoff.md item D) */
.review-section {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  margin: 0 0 16px;
}
.review-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 10px; }
.review-section-head h3 { margin: 0; font-size: 15px; }
.review-fields { display: grid; gap: 10px; margin: 0; }
.review-fields > div { display: flex; justify-content: space-between; gap: 16px; align-items: baseline; }
.review-fields dt { color: var(--muted); font-size: 12px; flex: 0 0 auto; }
.review-fields dd { margin: 0; font-weight: 600; text-align: right; }
/* Each academic question is its own visually separate block (not a run of
   thin dl-rows) so the question, its options, and the pick read as one
   grouped unit at a glance — see js/review.js's renderQuizSection(). */
.review-quiz-item {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin: 0 0 12px;
  background: var(--bg);
}
.review-quiz-item:last-child { margin-bottom: 0; }
.review-quiz-item-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin: 0 0 6px; }
.review-quiz-number { margin: 0; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.review-quiz-question { margin: 0 0 10px; font-weight: 700; color: var(--ink); }
.review-quiz-options { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.review-quiz-options li {
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--muted);
  background: white;
}
.review-quiz-options li.review-quiz-options--selected {
  border-color: var(--green);
  background: #eef6ea;
  color: var(--ink);
  font-weight: 700;
}
.review-quiz-status { margin: 8px 0 0; font-size: 12px; color: var(--muted); font-style: italic; }

/* ============================= STAFF DASHBOARD ============================= */
/* Phase 4. Reuses .review-section/.review-fields/.badge from the review
   screen so a staff record card reads like a familiar denser variant of the
   same summary a visitor already saw, not a separate visual language. The
   one new accent is the duplicate-flag warning color, reusing the same
   reddish tone already established elsewhere in the app for "pay attention"
   states (.quiz-timer--low, .tile--miss) rather than introducing a new hue. */
#view-staff { max-width: 1100px; }

/* Staff sign-in card (Supabase Auth login). Same tokens as the step panel
   (white surface, --line border, --radius-md) so it reads as part of the app;
   fields stack in one column with the label above the input, and the primary
   action is full width. Entrance is a short fade-up, skipped under
   prefers-reduced-motion. */
.staff-login-wrap { display: grid; justify-items: center; padding: 28px 0 12px; }
.staff-login-card {
  width: 100%;
  max-width: 420px;
  background: white;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 32px;
  box-shadow: 0 14px 40px rgba(25, 45, 36, .08);
}
.staff-login-card .field-hint { margin-bottom: 22px; }
.staff-field { margin: 0 0 18px; }
.staff-field label { margin: 0; }
.staff-field input { margin-top: 7px; }
.password-field { position: relative; }
.password-field input { padding-right: 76px; }
.password-toggle {
  position: absolute;
  top: 7px;
  right: 6px;
  min-height: 34px;
  padding: 4px 12px;
  background: transparent;
  color: var(--green);
  font-size: 13px;
  font-weight: 700;
  border-radius: var(--radius-sm);
}
.staff-login-card .field-error { margin: 0 0 14px; }
/* Flat buttons for the header's New visitor button and every secondary button on the
   staff views (Refresh, Back, Sync now, ...). Fill is the logo blue at normal strength
   (--pedagogy-blue #3B68B8, white text ~5.6:1), no 3D/shadow. Scoped to the header +
   staff views so the visitor flow's own Back/Continue buttons are unchanged. */
.site-nav button.quiet,
#view-staff button.quiet {
  background: var(--pedagogy-blue);
  color: #fff;
  border: 1px solid #2f58a0;
  font-weight: 650;
  box-shadow: none;
}
.site-nav button.quiet:hover:not(:disabled),
#view-staff button.quiet:hover:not(:disabled) {
  background: #2f58a0;
  border-color: #264a88;
}
/* Sign-in card's Back button: outline, so the solid green Sign in above it is clearly the primary action. */
#view-staff button.staff-login-back {
  background: transparent;
  color: var(--pedagogy-blue);
  border: 1.5px solid var(--pedagogy-blue);
  font-weight: 650;
  box-shadow: none;
}
#view-staff button.staff-login-back:hover:not(:disabled) {
  background: rgba(59, 104, 184, .08);
  color: #2f58a0;
  border-color: #2f58a0;
}
/* Header Sign out and the destructive "Clear" actions: flat solid red (white on #b8432e is ~5.5:1). */
.site-nav button.signout,
#view-staff button.staff-clear-all {
  background: #b8432e;
  color: #fff;
  border: 1px solid #9c3623;
  font-weight: 700;
  box-shadow: none;
}
.site-nav button.signout:hover:not(:disabled),
#view-staff button.staff-clear-all:hover:not(:disabled) { background: #a13826; }
#view-staff .step-actions + .step-heading { margin-top: 28px; }
#view-staff .step-actions { margin-bottom: 18px; }

.staff-login-submit { width: 100%; }
.staff-login-back { width: 100%; margin-top: 10px; }
@media (prefers-reduced-motion: no-preference) {
  .staff-login-card { animation: staff-login-in .35s cubic-bezier(.16, 1, .3, 1) both; }
}
@keyframes staff-login-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 480px) {
  .staff-login-card { padding: 24px 20px; }
}
/* Client feedback (2026-09-29): "an actual dashboard" — a visible stats bar
   (js/staff.js's renderStatsBar) above the record list, instead of the
   registration count only ever appearing buried inside a step heading. */
.staff-stats { display: flex; gap: 12px; flex-wrap: wrap; margin: 14px 0 22px; }
.staff-stat {
  flex: 1 1 140px;
  background: #eef6ea;
  border: 1px solid #b9d9a4;
  border-radius: var(--radius-md);
  padding: 14px 16px;
}
.staff-stat strong { display: block; font-size: 28px; line-height: 1.1; color: var(--ink); }
.staff-stat span { font-size: 12px; color: var(--muted); font-weight: 650; }
.staff-stat--attention { background: rgba(192, 80, 58, .08); border-color: #c0503a; }
.staff-stat--attention strong { color: #c0503a; }
.staff-record { margin-bottom: 20px; }
.staff-record--flagged { border-left: 4px solid #c0503a; }
.badge--duplicate { background: #c0503a; color: white; }
/* Destructive action (js/staff.js's "Clear all local data") — same warning
   tone as the duplicate-flag styling above, not a new hue. */
.staff-clear-all { border-color: #c0503a; color: #c0503a; }
.staff-clear-all:hover, .staff-clear-all:active { background: #c0503a; color: white; }
.staff-duplicate-actions {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed rgba(192, 80, 58, .35);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.staff-duplicate-actions .field-hint { margin: 0; flex: 1 1 100%; }

.site-footer {
  text-align: center;
  padding: 34px 20px;
  font-size: 12px;
  color: var(--muted);
  background: var(--bg);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.site-footer p { margin: 0; }

/* ============================= SUBMIT CONFIRMATION ============================= */
/* Reworked 2026-09-21 (live feedback): the plain text-only version didn't
   read as a real confirmation. Code-drawn checkmark + confetti burst only
   (no image assets, per CLAUDE.md's visual-design direction), auto-redirect
   note driven by js/review.js's countdown. */
.submitted-confirm { text-align: center; padding: 12px 0 4px; animation: submitted-in .4s ease; }
@keyframes submitted-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.submitted-badge { position: relative; width: 96px; height: 96px; margin: 0 auto 18px; }
.submitted-badge-ring {
  stroke: var(--green);
  stroke-dasharray: 227;
  stroke-dashoffset: 227;
  animation: submitted-ring-draw .6s ease forwards;
}
.submitted-badge-check {
  stroke: var(--green);
  stroke-dasharray: 50;
  stroke-dashoffset: 50;
  animation: submitted-check-draw .35s .55s ease forwards;
}
@keyframes submitted-ring-draw { to { stroke-dashoffset: 0; } }
@keyframes submitted-check-draw { to { stroke-dashoffset: 0; } }
.submitted-confetti { position: absolute; inset: 0; pointer-events: none; }
.confetti-dot {
  position: absolute; top: 50%; left: 50%;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--pedagogy-blue);
  opacity: 0;
  animation: confetti-burst .9s ease-out forwards;
  animation-delay: .55s;
  transform: rotate(calc(var(--i) * 36deg)) translateY(0) scale(1);
}
.confetti-dot:nth-child(odd) { background: var(--green); }
@keyframes confetti-burst {
  0% { opacity: 1; transform: rotate(calc(var(--i) * 36deg)) translateY(0) scale(1); }
  100% { opacity: 0; transform: rotate(calc(var(--i) * 36deg)) translateY(-46px) scale(.4); }
}
.submitted-lead { max-width: 42ch; margin: 0 auto 10px; font-size: 16px; }
.submitted-whatsapp { max-width: 38ch; margin: 0 auto 18px; font-size: 13px; color: var(--muted); }
.submitted-confirm .step-actions { justify-content: center; }
.submitted-redirect-note { margin: 14px 0 0; font-size: 12px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .submitted-confirm, .submitted-badge-ring, .submitted-badge-check { animation: none; }
  .submitted-badge-ring, .submitted-badge-check { stroke-dashoffset: 0; }
}

/* ============================= MODAL ============================= */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(25, 45, 36, 0.42);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal-card {
  background: white;
  border-radius: var(--radius-lg);
  padding: 28px;
  max-width: 480px;
  width: 100%;
  max-height: 84vh;
  overflow-y: auto;
  position: relative;
}
.modal-close {
  position: absolute;
  top: 14px; right: 14px;
  background: transparent;
  min-height: auto;
  padding: 4px 10px;
  font-size: 20px;
  color: var(--muted);
}
.modal-title { font-size: 19px; font-weight: 800; margin: 0 0 12px; padding-right: 24px; }
.modal-body p { font-size: 14px; color: var(--ink); }
.modal-body h4 { font-size: 14px; font-weight: 800; margin: 16px 0 4px; color: var(--ink); }
.modal-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }

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

@media (max-width: 720px) {
  main { padding: 16px 16px 50px; }
  .site-header { padding: 18px; }
  .field-row.two-col,
  .contact-cards { grid-template-columns: 1fr; }
  .hero-content { padding: 0 20px; }
}
