/*!
 * IFMSA-Thailand — official website
 * Built and maintained by Victor (Wachirawit Angkatavanich), VPAO 2026/27
 *
 * ---------------------------------------------------------------------------
 * THIS FILE FOLLOWS THE ORGANISATION'S OWN PR REGULATIONS.
 * Source: "ข้อบังคับการประชาสัมพันธ์ผ่านสื่อต่าง ๆ ของ IFMSA-Thailand",
 * in force 10 October 2024. The rules below are taken from it, not invented.
 *
 *   COLOURS  dark navy #183868 · vivid blue #0d2cf8 · yellow #E5C158 · white
 *   WHITE    at least 60% of a given view should be white space
 *   LIMIT    no more than 3 colors in one view
 *   FONTS    English: Montserrat (the regulation's own recommendation)
 *            Thai:    loopless sans — Kanit / Mitr / Prompt
 *   LOGO     white logo only on dark navy or black; color logo only on
 *            solid white; never color-on-color
 *   FRAMES   no solid rectangular frames or connected-line borders
 *   NAME     always exactly "IFMSA-Thailand" — never IFMSA_THAILAND,
 *            Ifmsa-thailand, or bare IFMSA. LCs are IFMSA-PSU, IFMSA-KKU, …
 *
 * The seven standing committee colors are the official ones, read from the
 * SC Color Code cards in the brand Drive folder.
 *
 * DESIGN LANGUAGE — "kanok geometry". The organization's mark, กนกนาครา
 * (Kanoknagara), is a naga framed by kanok: Thai gold flame-ornament. The
 * skewed gold strokes used as eyebrows, stat rules and dividers here are
 * original forms drawn in that spirit — a nod to the mark's vocabulary,
 * not a copy of it.
 * ---------------------------------------------------------------------------
 */

/* ============================ TOKENS ============================ */
:root {
  --navy:        #183868;
  --navy-deep:   #0f2545;
  /* Dark accent bands (the navy sections, the CTA strip, the record hero,
     the database card). These carry white and gold type, so they must stay
     DARK in both themes. Using --navy here was a real bug: --navy lightens
     to #5b8ad6 in dark mode, which flipped every one of those bands to pale
     blue and dropped gold-on-band to 2.0:1. */
  --band:        #183868;
  --band-ink:    #ffffff;
  --blue:        #0d2cf8;
  --gold:        #E5C158;
  --gold-deep:   #886d2b;   /* 4.9:1 on white, 4.7:1 on the warm ground. The
                               original #b8933a was 2.89 and failed AA. */

  --ink:         #14181f;
  --ink-soft:    #4d5666;
  --ink-faint:   #687183;   /* 4.9:1 on white, 4.7:1 on the warm ground. The
                               original #8b93a3 was 3.09 and failed AA. */
  --paper:       #ffffff;
  --paper-warm:  #fbfaf7;
  --line:        #e7e4dc;

  /* official standing committee colors */
  /* Committee colours: IFMSA's printed HEX codes, Corporate Identity
     Regulations, August 2019, p.15 (copy + summary in the VPAO Drive,
     08_Branding & Assets/IFMSA international - Corporate Identity/).
     Aligned 2026-09-23; they had been one unit off per channel.
     SCOME #100016 and SCOTI #4d1076 are IFMSA-Thailand's own (IFMSA's
     SCOME is white, invisible on a white page; SCOTI post-dates 2019).
     -text / -band rule: the official base where it passes 4.5:1, else
     IFMSA's official darker tone (p.16), else - SCOPH only, whose
     official darker #f15a38 is 3.35:1 - a deeper orange of our own. */
  --scome: #100016;
  --scope: #0181c1;
  --scoph: #f47d3b;
  --scora: #bd202a;
  --score: #2e4a9c;
  --scorp: #5f903f;
  --scoti: #4d1076;

  /* Three of the official committee colours do not clear 4.5:1 as text on
     white: SCOPE 4.27, SCORP 3.78, SCOPH 2.67. The official colour is kept
     for fills, rules and logo chips; text uses these darkened twins so the
     page stays readable without altering the brand. */
  --scome-text: #100016;
  --scope-text: #0e6ea5;
  --scoph-text: #c14b0a;
  --scora-text: #bd202a;
  --score-text: #2e4a9c;
  --scorp-text: #345b22;
  --scoti-text: #4d1076;

  --wrap: 1200px;
  --ease: cubic-bezier(.22,.75,.24,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --navy: #5b8ad6; --navy-deep: #0a1220;
    --band: #101d33;
    --gold: #E5C158; --gold-deep: #f0d891;
    --ink: #eef1f6; --ink-soft: #a9b2c2; --ink-faint: #7b8493;
    --paper: #0b111c; --paper-warm: #111825; --line: #1f2a3b;

    /* Committee colours, lightened so each stays legible on a dark ground.
       SCOME (#100016) and SCOTI (#4d1076) are near-black and vanish outright;
       SCOPE needed a nudge to clear 4.5:1. SCOPH, SCORA, SCORE and SCORP are
       already bright enough, but are restated here so all seven read as one
       set rather than a partial list someone has to audit again. */
    --scome: #b9a5c4; --scope: #028bcf; --scoph: #f47d3b; --scora: #e4636c;
    --score: #7d97e0; --scorp: #93c072; --scoti: #b184d8;

    /* The -text twins in :root are DARKENED for a white page, which is exactly
       backwards here - SCOME's landed at 1.08:1. On a dark ground the
       lightened colours above serve as the text colour too. */
    --scome-text: var(--scome); --scope-text: var(--scope);
    --scoph-text: var(--scoph); --scora-text: var(--scora);
    --score-text: var(--score); --scorp-text: var(--scorp);
    --scoti-text: var(--scoti);
  }
}
:root[data-theme="dark"] {
    --navy: #5b8ad6; --navy-deep: #0a1220;
  --band: #101d33;
  --gold: #E5C158; --gold-deep: #f0d891;
  --ink: #eef1f6; --ink-soft: #a9b2c2; --ink-faint: #7b8493;
  --paper: #0b111c; --paper-warm: #111825; --line: #1f2a3b;

  /* Committee colours, lightened so each stays legible on a dark ground.
     SCOME (#100016) and SCOTI (#4d1076) are near-black and vanish outright;
     SCOPE needed a nudge to clear 4.5:1. SCOPH, SCORA, SCORE and SCORP are
     already bright enough, but are restated here so all seven read as one
     set rather than a partial list someone has to audit again. */
  --scome: #b9a5c4; --scope: #028bcf; --scoph: #f47d3b; --scora: #e4636c;
  --score: #7d97e0; --scorp: #93c072; --scoti: #b184d8;

  /* The -text twins in :root are DARKENED for a white page, which is exactly
     backwards here - SCOME's landed at 1.08:1. On a dark ground the
     lightened colours above serve as the text colour too. */
  --scome-text: var(--scome); --scope-text: var(--scope);
  --scoph-text: var(--scoph); --scora-text: var(--scora);
  --score-text: var(--score); --scorp-text: var(--scorp);
  --scoti-text: var(--scoti);
}

/* ============================ BASE ============================ */
*, *::before, *::after { box-sizing: border-box; }
html {
  scroll-behavior: smooth; -webkit-text-size-adjust: 100%;
  /* The nav is sticky and 76px tall, so an anchor jump would otherwise
     land with the heading hidden underneath it. */
  scroll-padding-top: 96px;
  /* clip rather than hidden: hidden on the root turns it into a scroll
     container and breaks every position:sticky on the page. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Montserrat', 'Montserrat Fallback', 'Montserrat Fallback Android', 'Kanit', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 16.5px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* clip, NOT hidden - the same trap as html above, one rule later. hidden on
     one axis forces the other to auto, which made <body> a scroll container
     that never scrolls (the page scrolls on <html>). Every view() timeline
     resolves to the nearest scroll container, found <body>, and froze: the
     section headings, the committee mark and the hairlines described in
     SCROLL-DRIVEN ANIMATION below had never animated for anyone. Found
     2026-09-23 by reading computed styles at four scroll positions. */
  overflow-x: clip;
}

h1, h2, h3, h4 {
  font-family: 'Montserrat', 'Montserrat Fallback', 'Montserrat Fallback Android', 'Kanit', sans-serif;
  margin: 0; line-height: 1.08; letter-spacing: -.025em;
  text-wrap: balance; font-weight: 800;
}
p { margin: 0; }
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
:lang(th), .th { font-family: 'Kanit', 'Montserrat', sans-serif; letter-spacing: 0; }
code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: .86em;
  background: var(--paper-warm); border-radius: 4px; padding: 2px 7px; color: var(--navy);
}

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 36px; }
@media (max-width: 700px) { .wrap { padding: 0 22px; } }

.skip-link { position: absolute; left: -9999px; top: 0; z-index: 200; background: var(--band); color: #fff; padding: 12px 20px; }
.skip-link:focus { left: 0; }
/* Focus ring: navy on light ground (12:1), white on any band (navy or a
   committee colour), gold in dark mode. Gold on white measured 1.73:1, under
   the 3:1 that WCAG 1.4.11 asks of a focus indicator; that was the old ring. */
:root { --focus: var(--navy); }
/* committee pages set --ct on the root: one token feeds the accents (reading bar, selection) */
[style*="--ct:"] { --accent: var(--ct); }
::selection { background: color-mix(in srgb, var(--accent, var(--gold)) 32%, #fff); color: var(--ink); }
.section-navy, .cta-band, .db-main, .skip-link, .toast { --focus: #fff; }
:root[data-theme="dark"] { --focus: var(--gold); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --focus: var(--gold); } }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

/* Headings balance their lines and body text avoids one-word last lines;
   numbers use equal-width figures so counts and year columns line up.
   Browsers without support ignore all three. */
h1, h2, h3, .hero-title, .page-title, .sc-hero-title { text-wrap: balance; }
p, li, .page-lede, .section-lede { text-wrap: pretty; }
.rec-n, .sc-fact-num, .proj-year, .figure-year, .run-span, .rep-years, [data-count], .lc-region-name span {
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}

/* Thai is never letter-spaced or slanted: tracking pulls vowels and tone marks
   off their consonants, and Kanit has no italic, so the browser fakes one. */
:lang(th) .eyebrow, .eyebrow:lang(th), .eyebrow.th, :lang(th) .footer-h, :lang(th) .lc-region-name,
:lang(th) .step-n, :lang(th) .onpage-label { letter-spacing: 0; text-transform: none; }
:lang(th) em, em:lang(th), :lang(th) i { font-style: normal; font-weight: 600; }

/* kanok-derived skewed gold stroke, the recurring signature of the design */
.eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--navy); margin-bottom: 20px;
}
.eyebrow::before { content: ''; width: 22px; height: 2px; background: var(--gold); transform: skewX(-30deg); flex: none; }

/* ============================ BUTTONS ============================ */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: 'Montserrat', 'Montserrat Fallback', 'Montserrat Fallback Android', sans-serif; font-weight: 700; font-size: 14px;
  padding: 15px 28px; border: 2px solid transparent; cursor: pointer;
  transition: all .3s var(--ease);
}
.btn-primary { background: var(--navy); color: #fff; }
.btn-primary:hover { background: var(--navy-deep); transform: translateY(-2px); }
.btn-ghost { border-color: var(--line); color: var(--ink); }
.btn-ghost:hover { border-color: var(--gold); color: var(--navy); transform: translateY(-2px); }
.btn-gold { background: var(--gold); color: var(--navy-deep); }
.btn-gold:hover { background: var(--gold-deep); color: #fff; transform: translateY(-2px); }
.btn-sm { padding: 10px 20px; font-size: 12.5px; background: var(--navy); color: #fff; }
.btn-sm:hover { background: var(--navy-deep); }
.btn .arrow { transition: transform .3s var(--ease); }
.btn:hover .arrow { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) { .btn:hover { transform: none; } }

/* ============================ NAV ============================ */
.nav {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  transition: box-shadow .35s;
}
.nav.scrolled { box-shadow: 0 1px 0 var(--line), 0 6px 26px -18px rgba(24,56,104,.5); }
.nav-inner { display: flex; align-items: center; gap: 30px; height: 76px; }
.brand { display: flex; align-items: center; gap: 12px; }
/* The mark swaps with the theme. The PR regulation is explicit about this:
   the colour logo goes on solid white only, the white logo on dark navy or
   black - never colour-on-colour. Two images rather than a filter, so the
   gold keeps its actual value in light mode. */
.brand-mark { position: relative; width: 36px; height: 36px; flex: none; display: block; }
.brand-mark img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.brand-mark .mark-dark { opacity: 0; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand-mark .mark-light { opacity: 0; }
  :root:not([data-theme="light"]) .brand-mark .mark-dark  { opacity: 1; }
}
:root[data-theme="dark"] .brand-mark .mark-light { opacity: 0; }
:root[data-theme="dark"] .brand-mark .mark-dark  { opacity: 1; }
.brand-text { font-weight: 800; font-size: 15.5px; letter-spacing: -.01em; }
.brand-text .dash { color: var(--gold); }
.nav-links {
  display: flex; gap: 26px; margin-left: auto;
  font-size: 14px; font-weight: 600; color: var(--ink-soft);
}
/* a nav item must never wrap mid-label; if the set does not fit, the
   whole nav collapses to the menu instead */
.nav-links a { white-space: nowrap; }
.nav-links a { position: relative; padding: 6px 0; transition: color .25s; }
.nav-links a::after { content: ''; position: absolute; left: 0; right: 100%; bottom: 0; height: 2px; background: var(--gold); transition: right .35s var(--ease); }
.nav-links a:hover, .nav-links a[aria-current] { color: var(--navy); }
.nav-links a:hover::after, .nav-links a[aria-current]::after { right: 0; }
/* ---- mobile navigation ----
   This was broken outright: .nav-toggle was display:none and never turned
   back on at any breakpoint, while the 900px query hid .nav-links. Below
   900px the header therefore offered the logo, search and "Verify a
   certificate" and NO route to any other page. On phones - the actual
   audience - the site had no navigation at all.

   tools/audit.js did not catch it because a display:none control has zero
   width and height and was being skipped. That check has been tightened
   too, so this cannot regress silently. */
.nav-toggle {
  display: none; background: none; border: 0; cursor: pointer;
  padding: 10px; margin-left: 4px; flex: none;
}
.nav-toggle span {
  display: block; width: 22px; height: 2px; background: var(--ink);
  transition: transform .3s var(--ease), opacity .2s;
}
.nav-toggle span + span { margin-top: 5px; }
/* the bars become an X while the panel is open */
.nav-toggle[aria-expanded="true"] span:first-child { transform: translateY(3.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child { transform: translateY(-3.5px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .nav-toggle span { transition: none; } }

@media (max-width: 1180px) {
  .nav-toggle { display: block; }

  .nav-links {
    position: fixed; inset: 76px 0 auto 0;
    display: flex; flex-direction: column; gap: 0;
    background: var(--paper);
    border-top: 1px solid var(--line);
    box-shadow: 0 24px 40px -28px rgba(15,37,69,.5);
    padding: 8px 0 16px;
    margin-left: 0;
    max-height: calc(100dvh - 76px); overflow-y: auto;
    /* closed by default; opened by .nav-open on the header */
    visibility: hidden; opacity: 0; transform: translateY(-8px);
    transition: opacity .22s var(--ease), transform .22s var(--ease), visibility .22s;
    z-index: 99;
  }
  .nav.nav-open .nav-links { visibility: visible; opacity: 1; transform: none; }

  .nav-links a {
    padding: 15px 24px; font-size: 16px; color: var(--ink);
    border-bottom: 1px solid var(--line);
  }
  .nav-links a:last-child { border-bottom: 0; }
  .nav-links a::after { display: none; }
  .nav-links a[aria-current] { color: var(--navy); background: var(--paper-warm); box-shadow: inset 3px 0 0 var(--gold); }

  .nav-inner { gap: 12px; }
  .nav-search { margin-left: auto; }
}
@media (prefers-reduced-motion: reduce) { .nav-links { transition: none; } }

/* the search trigger has to stay reachable when the panel is closed */
@media (max-width: 560px) {
  .nav-inner .btn-sm { display: none; }   /* Verify lives in the menu instead */
}

/* ============================ HERO ============================ */
/* Two columns. The right column carries the seven committees as a live
   spectrum — the first build left that half empty, which was the single
   worst thing about it. */
.hero { padding: 92px 0 84px; position: relative; overflow: hidden; }
.hero-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: 72px; align-items: center; }
@media (max-width: 1000px) { .hero-grid { grid-template-columns: 1fr; gap: 56px; } }
.hero-title { font-size: clamp(2.375rem, 0.672rem + 4.76vw, 4.375rem); max-width: 15ch; }
.hero-title .l { display: block; }
.hero-title .gold { color: var(--gold-deep); }
.hero-lede { font-size: clamp(0.9688rem, 0.18rem + 1.275vw, 1.0625rem); color: var(--ink-soft); max-width: 52ch; margin-top: 26px; line-height: 1.7; }
.hero-actions { display: flex; gap: 14px; margin-top: 36px; flex-wrap: wrap; }
/* Official standing under the hero. Quiet by design: small type, no frames
   (PR regulation 6.3), a gold rule like the eyebrows. Authority, not a badge wall. */
.marks { list-style: none; margin: 38px 0 0; padding: 22px 0 0; display: flex; flex-wrap: wrap; gap: 14px 30px; position: relative; }
.marks::before { content: ''; position: absolute; left: 0; top: 0; width: 34px; height: 2px; background: var(--gold); transform: skewX(-30deg); }
.mark { display: flex; flex-direction: column; gap: 3px; max-width: 24ch; }
.mark b { font-size: 12px; font-weight: 800; letter-spacing: .12em; color: var(--navy); }
.mark span { font-size: 12px; line-height: 1.45; color: var(--ink-soft); }
.mark:hover b { color: var(--gold-deep); }

.spectrum { display: flex; flex-direction: column; }
.spec-row {
  display: grid; grid-template-columns: 74px 1fr auto; align-items: center; gap: 16px;
  padding: 14px 16px 14px 0; position: relative;
  transition: transform .4s var(--ease), background .3s;
}
.spec-row::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; background: var(--c); transition: width .4s var(--ease); }
.spec-row:hover { transform: translateX(7px); background: var(--paper-warm); }
.spec-row:hover::before { width: 9px; }
.spec-code { font-weight: 800; font-size: 14.5px; color: var(--ct, var(--c)); padding-left: 18px; }
.spec-name { font-size: 12.5px; color: var(--ink-soft); }
.spec-hex { font-family: ui-monospace, monospace; font-size: 10px; color: var(--ink-faint); letter-spacing: .04em; }
@media (prefers-reduced-motion: reduce) { .spec-row:hover { transform: none; } }

/* ============================ STATS ============================ */
.stats { padding: 0 0 96px; }
.stats-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 24px; }
@media (max-width: 1100px) { .stats-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px) { .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 22px 16px; } }
.stat { padding: 4px 0 4px 20px; position: relative; }
.stat::before { content: ''; position: absolute; left: 0; top: 6px; width: 2px; height: 30px; background: var(--gold); transform: skewX(-30deg); }
.stat-num { font-weight: 800; font-size: clamp(1.875rem, 0.408rem + 2.89vw, 2.625rem); color: var(--navy); letter-spacing: -.035em; font-variant-numeric: tabular-nums; line-height: 1; }
.stat-label { display: block; font-size: 12.5px; color: var(--ink-soft); margin-top: 10px; max-width: 21ch; line-height: 1.5; }
.stat-num { display: block; }
a.stat { transition: transform .34s var(--m-pop); }
a.stat:hover { transform: translateY(-3px) scale(1.015); }
a.stat:active { transform: translateY(0) scale(.99); transition-duration: var(--m-instant); }
a.stat:hover .stat-label { color: var(--ink); text-decoration: underline; text-decoration-color: var(--gold); text-underline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { a.stat, a.stat:hover { transition: none; transform: none; } }

/* ============================ SECTIONS ============================ */
.section { padding: 100px 0; }
/* A phone is not a small laptop. The desktop rhythm (92/100/80px of vertical
   padding) is most of a 844px screen before anything is read: measured
   2026-09-21, the first content block sat 880-1030px down on pages whose
   first screen then held a headline, a paragraph and white space. The
   proportions are kept, the scale is not.
   The 60% white-space rule in the PR regulation still holds - tools/brand_check.js
   measures it on every deploy, and did after this change. */
@media (max-width: 640px) {
  .section { padding: 62px 0; }
  .page-head { padding: 46px 0 30px; }
  .hero { padding: 54px 0 56px; }
}
.section-warm { background: var(--paper-warm); }
.section-navy { background: var(--band); color: var(--band-ink); }
.section-navy .eyebrow { color: var(--gold); }
.section-navy h2 { color: #fff; }
.section-navy .section-lede { color: rgba(255,255,255,.72); }
.section-head { max-width: 60ch; margin-bottom: 60px; }
.section-head h2 { font-size: clamp(1.75rem, 0.432rem + 3.06vw, 2.75rem); }
.section-lede { color: var(--ink-soft); margin-top: 20px; font-size: 15.5px; line-height: 1.7; }

/* ============================ COMMITTEES ============================ */
/* No boxed frames — the regulation forbids solid frames. Each committee is
   identified by its own official color on a top rule. */
.sco-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 38px 30px; }
@media (max-width: 1000px) { .sco-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .sco-grid { grid-template-columns: 1fr; } }
.sco { display: block; padding-top: 20px; border-top: 3px solid var(--c); transition: transform .4s var(--ease); }
.sco:hover { transform: translateY(-6px); }
.sco-code { font-weight: 800; font-size: 21px; color: var(--ct, var(--c)); letter-spacing: -.02em; }
.sco-full { font-size: 12px; color: var(--ink-faint); margin-top: 3px; font-weight: 600; }
.sco-desc { font-size: 13.5px; color: var(--ink-soft); margin-top: 14px; line-height: 1.6; }
.sco-officer { font-size: 10.5px; color: var(--ink-faint); margin-top: 14px; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; }
@media (prefers-reduced-motion: reduce) { .sco:hover { transform: none; } }

/* ============================ STEPS ============================ */
.steps { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 34px; }
@media (max-width: 980px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }
.step { padding-top: 22px; border-top: 2px solid var(--line); transition: border-color .35s; }
.step:hover { border-top-color: var(--gold); }
.step-n { font-size: 11px; font-weight: 800; color: var(--gold-deep); letter-spacing: .16em; }
.step h3 { font-size: 17px; margin: 10px 0 8px; font-weight: 700; }
.step p { font-size: 13.5px; color: var(--ink-soft); line-height: 1.6; }
.section-navy .step { border-top-color: rgba(255,255,255,.18); }
.section-navy .step:hover { border-top-color: var(--gold); }

/* The steps fill in as you reach them: a gold bar runs along each step's
   top rule, one after another, so a process reads as a path you are walking
   rather than four boxes. Scroll-driven and CSS-only; without support, or
   with reduced motion, the rules are simply the plain line above. */
.step { position: relative; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes step-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
    @keyframes step-tick { from { opacity: .35; letter-spacing: .3em; } to { opacity: 1; letter-spacing: .16em; } }
    .step::before {
      content: ""; position: absolute; left: 0; right: 0; top: -2px; height: 2px;
      background: var(--gold); transform-origin: left center;
      animation: step-fill linear both; animation-timeline: view();
      animation-range: entry 60% cover 38%;
    }
    .step:nth-child(2)::before { animation-range: entry 75% cover 44%; }
    .step:nth-child(3)::before { animation-range: entry 90% cover 50%; }
    .step:nth-child(4)::before { animation-range: entry 100% cover 56%; }
    .step-n { animation: step-tick linear both; animation-timeline: view(); animation-range: entry 50% cover 30%; }
  }
}
.section-navy .step p { color: rgba(255,255,255,.7); }
.section-navy .step-n { color: var(--gold); }
.inline-link { font-size: 13.5px; font-weight: 700; color: var(--navy); border-bottom: 2px solid var(--gold); display: inline-block; margin-top: 12px; }
.inline-link:hover { color: var(--gold-deep); }
.section-navy .inline-link { color: #fff; }

/* ============================ EXCHANGE ============================ */
.xc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
@media (max-width: 820px) { .xc-grid { grid-template-columns: 1fr; } }
.xc { padding: 34px 0 0; border-top: 4px solid var(--c); }
.xc-code { font-size: 30px; font-weight: 800; color: var(--c); letter-spacing: -.03em; }
.xc-full { font-size: 12px; color: var(--ink-faint); font-weight: 600; margin-top: 4px; }
.xc p { font-size: 14px; color: var(--ink-soft); margin: 16px 0; line-height: 1.7; }
.lc-label { font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
.lc-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.lc-chips span { font-size: 11.5px; font-weight: 700; color: var(--ink-soft); padding: 5px 11px; background: var(--paper-warm); transition: all .25s; }
.lc-chips span:hover { background: var(--c); color: #fff; }

/* ============================ VERIFY ============================ */
.verify-grid { display: grid; grid-template-columns: 1fr .9fr; gap: 64px; align-items: center; }
@media (max-width: 900px) { .verify-grid { grid-template-columns: 1fr; gap: 40px; } }
.vform { display: flex; flex-direction: column; }
.vform label { font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: rgba(255,255,255,.6); margin-top: 18px; }
.vform input {
  background: transparent; border: 0; border-bottom: 2px solid rgba(255,255,255,.28);
  color: #fff; font-family: inherit; font-size: 17px; font-weight: 600; padding: 12px 2px;
  transition: border-color .3s;
}
.vform input::placeholder { color: rgba(255,255,255,.32); font-weight: 400; }
.vform input:focus { outline: none; border-bottom-color: var(--gold); }
.vform .btn { margin-top: 30px; justify-content: center; }
.vnote { font-size: 11.5px; color: rgba(255,255,255,.45); margin-top: 14px; }

/* ============================ INTERIOR PAGES ============================ */
.page-head { padding: 80px 0 52px; }

/* ---------------------------------------------------------------------------
   ON THIS PAGE  —  the contents, as real links, written by tools/build_onpage.py

   Measured on 2026-09-21 (tools/attention.js): four pages arrived with nothing
   to follow in the first screen - no link, no button, no card - and phones get
   no wayfinding rail at all (.wf-dots is hidden below 760px). This is what a
   phone reader gets instead: what is on this page, before deciding to scroll.

   Chips, not a boxed table of contents: no frame (PR regulation 6.3), the
   gold stroke doing the marking. On a phone the row scrolls sideways rather
   than stacking into a wall of links.
   --------------------------------------------------------------------------- */
.onpage-wrap { padding-bottom: 8px; }
.onpage { display: flex; align-items: baseline; gap: 14px 18px; flex-wrap: wrap; }
.onpage-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); display: inline-flex; align-items: center; gap: 9px; flex: none;
}
.onpage-label::before {
  content: ''; width: 18px; height: 2px; background: var(--gold);
  transform: skewX(-30deg); display: inline-block;
}
.onpage ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px 10px; min-width: 0;
}
.onpage a {
  display: inline-block; padding: 7px 13px; border-radius: 999px;
  background: color-mix(in srgb, var(--navy) 6%, var(--paper));
  font-size: 12.5px; font-weight: 700; color: var(--ink-soft); white-space: nowrap;
  transition: background .25s, color .25s, transform .3s var(--m-pop);
}
.onpage a:hover, .onpage a:focus-visible { background: var(--navy); color: #fff; transform: translateY(-2px); }
.onpage a:active { transform: translateY(0) scale(.97); transition-duration: var(--m-instant); }
@media (max-width: 640px) {
  /* One line that scrolls, so a long page's contents cost one row, not six.
     -webkit-overflow-scrolling and a scroll hint keep it discoverable. */
  .onpage { display: block; padding-bottom: 4px; }
  .onpage-label { margin-bottom: 9px; }
  .onpage ul {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    padding-bottom: 4px; scroll-snap-type: x proximity;
  }
  .onpage ul::-webkit-scrollbar { display: none; }
  .onpage li { scroll-snap-align: start; }
}
@media (prefers-reduced-motion: reduce) { .onpage a { transition: none; } }
/* "You are here" on second-level pages (tools/build_shell.py, crumbs()). */
.crumbs ol { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-wrap: wrap; align-items: center; font-size: 12.5px; font-weight: 700; }
.crumbs li + li::before { content: '/'; margin: 0 9px; color: var(--ink-faint); font-weight: 400; }
.crumbs a { color: var(--ink-soft); text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); text-underline-offset: 3px; }
.crumbs a:hover { color: var(--ink); text-decoration-color: var(--gold); }
.crumbs [aria-current] { color: var(--ink); }
.sc-hero .crumbs a, .sc-hero .crumbs [aria-current], .sc-hero .crumbs li + li::before { color: #fff; }
.page-title { font-size: clamp(2.125rem, 0.6rem + 4.25vw, 3.75rem); max-width: 18ch; }
.page-lede { font-size: clamp(0.9688rem, 0.204rem + 1.445vw, 1.125rem); color: var(--ink-soft); max-width: 58ch; margin-top: 22px; line-height: 1.7; }
.prose-grid { display: grid; grid-template-columns: .8fr 1.2fr; gap: 60px; }
@media (max-width: 880px) { .prose-grid { grid-template-columns: 1fr; gap: 24px; } }
.prose-side h2 { font-size: clamp(1.4375rem, 0.336rem + 2.38vw, 2rem); position: sticky; top: 108px; }
@media (max-width: 880px) { .prose-side h2 { position: static; } }
.prose { display: flex; flex-direction: column; gap: 20px; color: var(--ink-soft); font-size: 15.5px; line-height: 1.75; }
.prose strong { color: var(--ink); font-weight: 700; }
.role-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px 26px; }
@media (max-width: 980px) { .role-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px) { .role-grid { grid-template-columns: 1fr; } }
.role { padding-top: 18px; border-top: 2px solid var(--line); transition: border-color .3s; }
.role:hover { border-top-color: var(--gold); }
.role h3 { font-size: 17px; color: var(--navy); margin-bottom: 8px; font-weight: 800; }
.role p { font-size: 13px; color: var(--ink-soft); line-height: 1.6; }
.role-mail { color: var(--navy); font-weight: 600; word-break: break-word; }
.cta-band { background: var(--band); color: var(--band-ink); padding: 70px 0; }
.cta-inner { display: flex; align-items: center; justify-content: space-between; gap: 32px; flex-wrap: wrap; }
.cta-inner h2 { font-size: clamp(1.5625rem, 0.384rem + 2.72vw, 2.25rem); color: #fff; }
.cta-inner p { color: rgba(255,255,255,.75); margin-top: 12px; max-width: 46ch; font-size: 15px; }

/* ============================ FOOTER ============================ */
.footer { padding: 76px 0 34px; background: var(--paper-warm); }
.footer-grid { display: grid; grid-template-columns: 1.5fr repeat(4, 1fr); gap: 40px; }
@media (max-width: 1100px) { .footer-grid { grid-template-columns: repeat(4, 1fr); } .footer-brand { grid-column: 1 / -1; } }
@media (max-width: 820px) { .footer-grid { grid-template-columns: repeat(2, 1fr); gap: 34px; } }
.footer-brand p { font-size: 13px; color: var(--ink-soft); margin-top: 12px; max-width: 30ch; line-height: 1.6; }
.footer-col h4, .footer-h { font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 14px; }
.footer-col a { display: block; font-size: 13.5px; color: var(--ink-soft); padding: 5px 0; transition: color .25s; }
.footer-col a:hover { color: var(--navy); }
.footer-bottom {
  margin-top: 60px; padding-top: 24px; border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--ink-faint);
}
.footer-bottom .built { display: flex; align-items: center; gap: 9px; }
.footer-bottom .built::before { content: ''; width: 16px; height: 2px; background: var(--gold); transform: skewX(-30deg); }

/* ============================ MOTION ============================ */
/* Reveal: visible unless a script has chosen to hold it back.
 *
 * Until 2026-09-14 every .reveal started at opacity 0 and waited for main.js.
 * With scripts off, or main.js failing to arrive on a bad connection, the
 * page title and nearly everything else stayed invisible: 24 of 27 elements
 * on the homepage, 23 of 25 on SCOPH. And on a slow phone even a working
 * script held the first screen back: the committee intro paragraph painted
 * 0.5-0.9 s after the rest of the page, waiting for script download plus an
 * 850 ms fade.
 *
 * So the default is visible. The first screen gets a CSS entrance that starts
 * at first paint, depends on nothing, and ends visible (no fill-forwards).
 * Siblings step by 80 ms, capped at the fourth.
 * Below the fold, main.js adds .rv-wait as it starts - to elements that are
 * off-screen at that moment, so nothing a reader can see disappears - and
 * the scroll reveal plays as before. If main.js never runs, nothing is
 * hidden. */
/* From .01, not 0. Chrome does not count an element for Largest Contentful
   Paint while its opacity is exactly 0, so a fade from 0 is recorded when it
   ENDS: the homepage lede measured ~3.0 s from 0 and ~2.1 s from .01, same
   conditions, three runs each. .01 is invisible, the fade still starts at
   first paint, and its fast ease-out makes text readable ~0.1-0.2 s in - so
   the earlier number is the one readers experience, not a flattering one. */
@keyframes rv-rise { from { opacity: .01; transform: translateY(18px); } }
.reveal {
  transition: opacity .85s var(--ease-out), transform .85s var(--ease-out);
  animation: rv-rise .7s var(--ease-out) backwards;
}
.reveal:nth-child(2) { animation-delay: .08s; }
.reveal:nth-child(3) { animation-delay: .16s; }
.reveal:nth-child(n+4) { animation-delay: .24s; }
/* No `animation: none` here: .section-head, .page-lede and .sc-hero-mark are
   also driven by the scroll timeline (SCROLL-DRIVEN ANIMATION, below), and
   that must keep winning where the browser supports it. */
.reveal.rv-wait { opacity: 0; transform: translateY(26px); }
.reveal.rv-wait.in { opacity: 1; transform: none; }
.hero-title .l { animation: rv-rise .7s var(--ease-out) backwards; }
.hero-title .l:nth-child(2) { animation-delay: .09s; }
.hero-title .l:nth-child(n+3) { animation-delay: .18s; }
@media (prefers-reduced-motion: reduce) {
  .reveal, .hero-title .l { opacity: 1 !important; transform: none !important; transition: none !important; animation: none !important; }
}

/* ---------------------------------------------------------------------------
 * DARK MODE — committee color safety.
 * The official SC colors are specified for print/social on white. Two of
 * them are near-black (SCOME #100016) or very deep (SCOTI #4d1076) and
 * disappear entirely against a dark background. These lightened variants
 * are used ONLY in dark mode, so the official color is still what renders
 * in the default light view. Same hue, raised lightness — the committee is
 * still recognisable, but readable.
 * ------------------------------------------------------------------------ */
/* (The dark-mode committee colours are declared once, with the rest of the
   dark tokens, near the top of this file. A second partial copy lived here
   and set only five of the seven — the shape of a duplicate that drifts.) */

/* ==================================================================
   COMMITTEE PAGES
   committees.html (the index) and one page per standing committee,
   all generated by tools/build_committees.py from committees.json.

   Each committee page sets --c to its own official colour on <body>,
   so every accent below inherits it without a per-committee rule. The
   rule of no more than three colours in one view is kept by using
   exactly: the committee colour, the paper, and the ink.
   ================================================================== */

/* ---------- index: the seven cards ---------- */
.cc-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px 26px; }
@media (max-width: 1080px) { .cc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .cc-grid { grid-template-columns: 1fr; } }

.cc {
  display: flex; flex-direction: column; min-height: 300px;
  padding: 26px 24px 20px;
  background: var(--paper);
  border-top: 3px solid var(--c, var(--navy));
  box-shadow: 0 1px 0 var(--line), 0 0 0 1px transparent;
  transition: transform .45s var(--ease-out), box-shadow .45s var(--ease-out);
}
.cc:hover { transform: translateY(-7px) scale(1.012); box-shadow: 0 26px 50px -30px rgba(15,37,69,.55); }
.cc:active { transform: translateY(-2px) scale(.994); transition-duration: var(--m-instant); }
.cc-mark {
  height: 46px; margin-bottom: 20px; display: flex; align-items: flex-start;
}
/* the extracted marks are white, so on paper they need the committee colour
   behind them - a small solid chip, which is also how the official colour
   cards present the logo */
.cc-mark img {
  height: 46px; width: auto; padding: 9px 13px;
  background: var(--c, var(--navy));
  object-fit: contain;
}
.cc-body { flex: 1; }
.cc-code { font-size: 25px; font-weight: 800; color: var(--ct, var(--c, var(--navy))); letter-spacing: -.03em; }
.cc-short { font-size: 12px; font-weight: 700; color: var(--ink-faint); margin-top: 4px; letter-spacing: .04em; }
.cc-desc { font-size: 13.5px; color: var(--ink-soft); line-height: 1.65; margin-top: 16px; }
.cc-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.cc-count { color: var(--ink-faint); }
.cc-go { color: var(--ct, var(--c, var(--navy))); display: inline-flex; align-items: center; gap: 7px; }
.cc:hover .cc-go .arrow { transform: translateX(5px); }
.cc-go .arrow { transition: transform .35s var(--ease); }
.cc-help { --c: var(--gold-deep); border-top-style: dashed; }
.cc-help:hover { transform: none; box-shadow: 0 1px 0 var(--line); }

/* ---------- a committee's own page ---------- */
.sc-hero { background: var(--cb, var(--c, var(--navy))); padding: 74px 0 66px; }
.sc-hero-inner { display: flex; align-items: center; gap: 42px; flex-wrap: wrap; }
.sc-hero-mark img { height: 84px; width: auto; object-fit: contain; }
.sc-eyebrow { color: rgba(255,255,255,.72); margin-bottom: 14px; }
.sc-eyebrow::before { background: rgba(255,255,255,.55); }
.sc-hero-title {
  font-size: clamp(2.875rem, 0.96rem + 6.8vw, 6rem); color: #fff;
  letter-spacing: -.045em; line-height: .92;
}
.sc-hero-sub { color: rgba(255,255,255,.8); font-size: clamp(0.875rem, 0.204rem + 1.445vw, 1.0625rem); margin-top: 14px; max-width: 46ch; font-weight: 600; }
@media (max-width: 700px) { .sc-hero-mark img { height: 58px; } .sc-hero-inner { gap: 26px; } }

.sc-lede {
  font-size: clamp(1.125rem, 0.288rem + 2.04vw, 1.625rem); line-height: 1.5; max-width: 34ch;
  font-weight: 600; letter-spacing: -.018em; margin-bottom: 62px;
}

.sc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; }
@media (max-width: 860px) { .sc-cols { grid-template-columns: 1fr; gap: 44px; } }

.sc-h3 {
  font-size: 11.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 22px;
  display: flex; align-items: center; gap: 12px;
}
.sc-h3::before { content: ''; width: 20px; height: 2px; background: var(--c, var(--gold)); transform: skewX(-30deg); flex: none; }

.sc-list { list-style: none; padding: 0; margin: 0; }
.sc-list li {
  position: relative; padding: 13px 0 13px 22px;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px; line-height: 1.6; color: var(--ink-soft);
}
.sc-list li::before {
  content: ''; position: absolute; left: 0; top: 21px;
  width: 8px; height: 2px; background: var(--c, var(--gold)); transform: skewX(-30deg);
}
.sc-list-plain li { padding-left: 22px; }

.sc-officer-band {
  margin-top: 62px; padding: 30px 34px;
  background: var(--paper-warm); border-left: 3px solid var(--c, var(--navy));
}
.sc-officer-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); display: block; margin-bottom: 10px;
}
.sc-officer-band p { font-size: 14.5px; line-height: 1.65; color: var(--ink); max-width: 78ch; }

/* ---------- the project record ---------- */
.proj-grid {
  list-style: none; padding: 0; margin: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  background: var(--line);
  border: 1px solid var(--line);
}
@media (max-width: 940px) { .proj-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .proj-grid { grid-template-columns: 1fr; } }

.proj {
  background: var(--paper); padding: 26px 24px 28px;
  transition: background .3s var(--ease);
  position: relative;
}
.proj::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--c, var(--navy)); opacity: 0; transition: opacity .3s;
}
.proj:hover::after { opacity: 1; }
.proj h3, .proj h4 {
  font-size: 15px; font-weight: 700; line-height: 1.35; letter-spacing: -.012em;
  color: var(--ink);
}
.proj-year {
  display: inline-block; margin-top: 9px;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 10.5px;
  letter-spacing: .06em; color: var(--ct, var(--c, var(--navy))); font-weight: 600;
}
.proj p { font-size: 13px; line-height: 1.62; color: var(--ink-soft); margin-top: 11px; }

.sc-notes { margin-top: 66px; max-width: 76ch; }

/* ---------- shared bits ---------- */
.cta-actions { display: flex; gap: 14px; flex-wrap: wrap; }

.pager { border-top: 1px solid var(--line); background: var(--paper); }
.pager-inner { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 30px 0; }
.pager-link { display: flex; flex-direction: column; gap: 4px; }
.pager-next { text-align: right; }
.pager-dir { font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
.pager-sc { font-size: 21px; font-weight: 800; letter-spacing: -.025em; color: var(--navy); transition: color .25s; }
.pager-link:hover .pager-sc { color: var(--gold-deep); }
.pager-all { font-size: 12px; font-weight: 700; color: var(--ink-soft); border-bottom: 2px solid var(--gold); padding-bottom: 3px; }
@media (max-width: 620px) { .pager-all { display: none; } }

/* the lede and a facts panel share the top of a committee page - the first
   build left the whole right half of this row empty */
.sc-open { display: grid; grid-template-columns: 1.25fr .75fr; gap: 62px; align-items: start; margin-bottom: 72px; }
@media (max-width: 900px) { .sc-open { grid-template-columns: 1fr; gap: 38px; margin-bottom: 54px; } }
.sc-open .sc-lede { margin-bottom: 0; max-width: 30ch; }

.sc-facts { border-top: 2px solid var(--c, var(--navy)); padding-top: 4px; }
.sc-fact {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 13px 0; border-bottom: 1px solid var(--line);
}
.sc-fact dt {
  font-size: 10.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-faint); flex: none;
}
.sc-fact dd { margin: 0; font-size: 13.5px; font-weight: 600; color: var(--ink); text-align: right; }
.sc-fact dd a { color: var(--ct, var(--c, var(--navy))); word-break: break-word; }
.sc-fact dd a:hover { border-bottom: 1px solid currentColor; }
.sc-fact-num {
  font-size: 26px !important; font-weight: 800 !important;
  letter-spacing: -.03em; color: var(--ct, var(--c, var(--navy))) !important; line-height: 1;
}

/* the hero mark is the symbol only - the wordmark version repeats the
   committee code the page already prints in display type */
.sc-hero-mark img { height: 92px; width: 92px; }
@media (max-width: 700px) { .sc-hero-mark img { height: 62px; width: 62px; } }
.cc-mark img { height: 46px; width: 46px; padding: 8px; }

/* the hero spectrum rows on the homepage are links now */
a.spec-row { cursor: pointer; }

/* ==================================================================
   TERM OPENER  -  DATED, MEANT TO COME DOWN
   IFMSA-Thailand's 26th term. It used to read 75, counted from the
   INTERNATIONAL federation's founding in 1951 - the parent's history
   printed over this organisation's name. Corrected 2026-09-21.
   The script (assets/js/anniversary.js) stops firing after the term;
   these rules then style nothing. Safe to delete together, any time.

   Timing note: the whole sequence must land inside the 1.8s the opener is
   on screen. It did not - the old delays ran to 1.82s, so the name arrived
   as the overlay was already leaving. Everything settles by ~1.25s now,
   which leaves a beat of stillness before it lifts.
   ================================================================== */
.anniv-open { overflow: hidden; }

.anniv {
  position: fixed; inset: 0; z-index: 900;
  background: #0f2545;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 1;
  transition: opacity .8s var(--ease-out), transform .9s var(--ease-out);
}
.anniv-out { opacity: 0; transform: translateY(-2.5%); pointer-events: none; }

.anniv-inner { text-align: center; padding: 0 28px; }

/* a kanok-derived gold stroke draws itself across, the same skewed form used
   for eyebrows and dividers throughout the site */
.anniv-rule {
  height: 3px; width: 0; margin: 0 auto 26px;
  background: var(--gold); transform: skewX(-30deg);
  transition: width .5s var(--ease-out) .04s;
}
.anniv-in .anniv-rule { width: 180px; }

/* The digits do not slide, they land: up out of a mask and slightly past
   their size, then settle. The overshoot is what reads as "pop" - a linear
   arrival at the same speed feels like a loading screen. */
.anniv-num { display: flex; justify-content: center; gap: .02em; position: relative; }
.anniv-d { display: block; overflow: hidden; }
.anniv-d b {
  display: block;
  font-size: clamp(6rem, 2.64rem + 18.7vw, 16.25rem); font-weight: 800; line-height: .86;
  letter-spacing: -.06em; color: #fff;
  transform: translateY(105%) scale(.82);
  transition: transform .62s var(--m-pop);
}
.anniv-in .anniv-d:nth-child(1) b { transition-delay: .10s; transform: none; }
.anniv-in .anniv-d:nth-child(2) b { transition-delay: .21s; transform: none; }

/* A gold bloom behind the number, once, as the digits land. Radial and
   short: the kanok gold doing what a spotlight does on a stage. */
.anniv-num::before {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 120%; aspect-ratio: 1; transform: translate(-50%, -50%) scale(.4);
  background: radial-gradient(circle, color-mix(in srgb, var(--gold) 42%, transparent) 0%, transparent 62%);
  opacity: 0; pointer-events: none;
}
.anniv-in .anniv-num::before { animation: anniv-bloom .95s var(--ease-out) .16s both; }
@keyframes anniv-bloom {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.4); }
  38%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.15); }
}

.anniv-years,
.anniv-line,
.anniv-org {
  opacity: 0; transform: translateY(12px) scale(.98);
  transition: opacity .42s var(--ease-out), transform .42s var(--m-pop);
}
.anniv-in .anniv-years { opacity: 1; transform: none; transition-delay: .40s; }
.anniv-in .anniv-line  { opacity: 1; transform: none; transition-delay: .52s; }
.anniv-in .anniv-org   { opacity: 1; transform: none; transition-delay: .64s; }

/* The panel itself arrives, rather than being there already. */
.anniv-inner { transform: scale(.965); transition: transform .7s var(--ease-out); }
.anniv-in .anniv-inner { transform: none; }

.anniv-years {
  margin-top: 26px;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: clamp(0.6875rem, 0.18rem + 1.275vw, 0.875rem); letter-spacing: .3em;
  color: var(--gold);
}
.anniv-line {
  margin-top: 18px; font-size: clamp(0.9375rem, 0.264rem + 1.87vw, 1.375rem); font-weight: 600;
  color: rgba(255,255,255,.82); letter-spacing: -.015em;
}
.anniv-org {
  margin-top: 42px; font-size: 13px; font-weight: 800; letter-spacing: .04em;
  color: rgba(255,255,255,.45);
}
.anniv-org span { color: var(--gold); }

/* Top right, where a dismiss control is looked for, and readable rather than
   hinted at: 50% white on navy was 4.0:1 and 10px of padding made it a small
   target on a phone. A way out of a greeting should never be a hunt. */
.anniv-skip {
  position: absolute; right: 18px; top: 18px;
  background: rgba(255,255,255,.1); border: 0; border-radius: 999px; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.92); padding: 12px 20px; min-height: 44px;
  transition: background .25s, color .25s;
}
.anniv-skip:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }
.anniv-skip:hover { background: rgba(255,255,255,.2); color: #fff; }

@media (prefers-reduced-motion: reduce) { .anniv { display: none; } }

/* ==================================================================
   THE YEAR DIAL
   The federation's real annual cycle, opened on the month it actually
   is. Data in assets/data/calendar.json, behaviour in
   assets/js/year-dial.js. Three colours in view: navy, gold, ink.
   ================================================================== */
.dial-wrap { display: grid; grid-template-columns: minmax(280px, 420px) 1fr; gap: 68px; align-items: center; }
@media (max-width: 900px) { .dial-wrap { grid-template-columns: 1fr; gap: 40px; } }

#year-dial { display: contents; }

.dial-svg { width: 100%; height: auto; overflow: visible; }

.dial-arc {
  fill: none;
  stroke: var(--line);
  stroke-width: 13;
  stroke-linecap: butt;
  cursor: pointer;
  transition: stroke .35s var(--ease), stroke-width .35s var(--ease);
}
.dial-arc:hover { stroke: var(--ink-faint); }
.dial-arc.is-now { stroke: var(--gold); }
.dial-arc.is-active { stroke: var(--navy); stroke-width: 20; }
.dial-arc.is-active.is-now { stroke: var(--gold); }
.dial-arc:focus { outline: none; }
.dial-arc:focus-visible { outline: 3px solid var(--gold); outline-offset: 4px; }

.dial-tick {
  font-family: 'Montserrat', 'Montserrat Fallback', 'Montserrat Fallback Android', sans-serif; font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; fill: var(--ink-faint); cursor: pointer;
  transition: fill .3s var(--ease);
}
.dial-tick.is-active { fill: var(--navy); }
.dial-tick.is-now { fill: var(--gold-deep); }

.dial-hub-month {
  font-family: 'Montserrat', 'Montserrat Fallback', 'Montserrat Fallback Android', sans-serif; font-size: 34px; font-weight: 800;
  letter-spacing: -.03em; fill: var(--ink);
}
.dial-hub-note {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 9.5px;
  letter-spacing: .22em; fill: var(--gold-deep); text-transform: uppercase;
}

.dial-panel { opacity: 0; transform: translateY(10px); }
.dial-panel-in {
  opacity: 1; transform: none;
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .dial-panel { transition: none !important; } }

.dial-eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 11px; font-weight: 800; letter-spacing: .18em; text-transform: uppercase;
  color: var(--gold-deep); margin-bottom: 16px;
}
.dial-eyebrow::before { content: ''; width: 20px; height: 2px; background: var(--gold); transform: skewX(-30deg); }

.dial-headline {
  font-size: clamp(1.5rem, 0.408rem + 2.89vw, 2.375rem); letter-spacing: -.03em; max-width: 18ch;
}
.dial-items { list-style: none; padding: 0; margin: 26px 0 0; }
.dial-items li {
  position: relative; padding: 12px 0 12px 24px;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px; line-height: 1.6; color: var(--ink-soft);
}
.dial-items li::before {
  content: ''; position: absolute; left: 0; top: 20px;
  width: 9px; height: 2px; background: var(--navy); transform: skewX(-30deg);
}

/* ==================================================================
   MODALS  -  the layered-detail system
   The PR regulation's 60%-white-space rule means the page cannot show
   everything at once. So detail opens on top instead of pushing the
   reader to another page and losing their place. See assets/js/modal.js.
   ================================================================== */
.modal-open { overflow: hidden; }

.modal { position: fixed; inset: 0; z-index: 800; display: flex; align-items: center; justify-content: center; padding: 28px; }
.modal-backdrop {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--navy-deep) 62%, transparent);
  backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .32s var(--ease);
}
.modal-in .modal-backdrop { opacity: 1; }

.modal-panel {
  position: relative; z-index: 1;
  width: min(720px, 100%); max-height: min(84vh, 900px); overflow-y: auto;
  background: var(--paper); color: var(--ink);
  padding: 44px 46px 46px;
  box-shadow: 0 40px 90px -40px rgba(10,18,32,.7);
  opacity: 0; transform: translateY(16px) scale(.94);
  transition: opacity .26s var(--ease-out), transform .42s var(--m-pop);
}
.modal-in .modal-panel { opacity: 1; transform: none; }
.modal-panel:focus { outline: none; }
@media (max-width: 620px) { .modal-panel { padding: 34px 24px 36px; max-height: 90vh; } .modal { padding: 14px; } }
@media (prefers-reduced-motion: reduce) {
  .modal-backdrop, .modal-panel { transition: none; }
  .modal-panel { transform: none; }
  /* Everything the pop easing touches, stilled. */
  .cc:hover, .cc:active, .dmap-t:hover, .dmap-t:active,
  a.stat:hover, a.stat:active, .onpage a:hover, .onpage a:active { transform: none; }
  .dmap-t.is-on { animation: none; }
}

.modal-close {
  position: absolute; top: 14px; right: 16px;
  width: 40px; height: 40px; line-height: 1;
  background: none; border: 0; cursor: pointer;
  font-size: 27px; color: var(--ink-faint);
  transition: color .25s, transform .25s var(--ease);
}
.modal-close:hover { color: var(--ink); transform: rotate(90deg); }

.modal-body h2 { font-size: clamp(1.5rem, 0.408rem + 2.89vw, 2.125rem); letter-spacing: -.03em; max-width: 22ch; }
.modal-body h3 { font-size: 19px; margin-top: 26px; }
.modal-body > p { font-size: 15px; line-height: 1.7; color: var(--ink-soft); margin-top: 16px; }
.modal-eyebrow {
  display: inline-flex; align-items: center; gap: 11px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .17em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--navy))); margin-bottom: 15px;
}
.modal-eyebrow::before { content: ''; width: 20px; height: 2px; background: var(--gold); transform: skewX(-30deg); }
.modal-meta {
  display: flex; flex-wrap: wrap; gap: 10px 26px; margin-top: 24px;
  padding-top: 18px; border-top: 1px solid var(--line);
  font-size: 12px; color: var(--ink-faint); font-weight: 600;
}
.modal-actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 30px; }

/* anything that opens a modal reads as pressable */
[data-modal] { cursor: pointer; }

/* ==================================================================
   EXCHANGE PAGE
   These classes were written against an earlier design system and had
   no rules at all, so the two exchange cards rendered unstyled - which
   is what "the font changes weird" looked like from the outside.
   ================================================================== */
.exchange-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 860px) { .exchange-grid { grid-template-columns: 1fr; } }

.exchange-card { background: var(--paper); padding: 40px 38px 42px; position: relative; }
.exchange-card::before { content: ''; position: absolute; left: 0; top: 0; right: 0; height: 3px; background: var(--c, var(--navy)); }
.exchange-card:nth-child(1) { --c: var(--score); }
.exchange-card:nth-child(2) { --c: var(--scope); }
.exchange-card h2, .exchange-card h3 { font-size: clamp(1.75rem, 0.48rem + 3.4vw, 2.5rem); color: var(--ct, var(--c, var(--navy))); letter-spacing: -.035em; }
.exchange-card .exchange-full { font-size: 12.5px; font-weight: 700; color: var(--ink-faint); margin-top: 6px; letter-spacing: .02em; }
.exchange-card p { font-size: 14.5px; line-height: 1.68; color: var(--ink-soft); margin-top: 18px; }

.lc-list { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--line); }
.lc-list h4 {
  font-size: 10.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 12px;
}
.lc-list ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.lc-list li {
  font-size: 12px; font-weight: 600; color: var(--ink-soft);
  padding: 6px 12px; background: var(--paper-warm);
}
.lc-list p { font-size: 13px; margin-top: 14px; }

/* committee pages carry their colour on <body class="sc-page" style="--c:..."> */
.sc-page { --c: var(--navy); }

/* ---------- social channels ---------- */
.soc-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 900px) { .soc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .soc-grid { grid-template-columns: 1fr; } }

.soc {
  background: var(--paper); padding: 24px 22px 22px; position: relative;
  display: flex; flex-direction: column; gap: 5px;
  transition: background .3s var(--ease);
}
.soc::after { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--gold); opacity: 0; transition: opacity .3s; }
.soc:hover::after { opacity: 1; }
.soc-name { font-size: 15px; font-weight: 800; letter-spacing: -.015em; color: var(--navy); }
.soc-handle { font-size: 12.5px; color: var(--ink-soft); word-break: break-word; }
.soc-go { position: absolute; right: 20px; top: 24px; color: var(--ink-faint); transition: transform .3s var(--ease), color .3s; }
.soc:hover .soc-go { transform: translateX(4px); color: var(--gold-deep); }

.footer-social { display: flex; gap: 18px; flex-wrap: wrap; font-size: 12px; font-weight: 600; }
.footer-social a { color: var(--ink-soft); border-bottom: 1px solid transparent; padding-bottom: 2px; transition: color .25s, border-color .25s; }
.footer-social a:hover { color: var(--navy); border-bottom-color: var(--gold); }

/* ==================================================================
   OUR RECORD  (history.html)
   Everything on that page came out of signed letters, certificate
   registers and official reports. The design leans on real figures
   rather than decoration, because the figures are the point.
   ================================================================== */
.hero-record { padding: 86px 0 78px; }
.rec-head { max-width: 62ch; }
.hero-record .eyebrow { color: var(--gold); }
.hero-record .eyebrow::before { background: var(--gold); }
.hero-record h2 { font-size: clamp(1.875rem, 0.552rem + 3.91vw, 3.375rem); color: #fff; letter-spacing: -.035em; max-width: 20ch; }
.rec-lede { color: rgba(255,255,255,.76); font-size: clamp(0.9375rem, 0.216rem + 1.53vw, 1.0938rem); line-height: 1.72; margin-top: 22px; }

.rec-figures {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 34px;
  margin-top: 62px; padding-top: 40px; border-top: 1px solid rgba(255,255,255,.16);
}
@media (max-width: 860px) { .rec-figures { grid-template-columns: repeat(2, 1fr); gap: 30px; } }
.rec-fig { display: flex; flex-direction: column; gap: 10px; }
.rec-n {
  font-size: clamp(2.5rem, 0.72rem + 5.1vw, 4.125rem); font-weight: 800; line-height: .9;
  letter-spacing: -.045em; color: var(--gold);
}
.rec-l { font-size: 12.5px; color: rgba(255,255,255,.62); line-height: 1.5; max-width: 22ch; }
.rec-note { margin-top: 44px; font-size: 14px; color: rgba(255,255,255,.55); max-width: 78ch; line-height: 1.7; }

/* ---------- awards ---------- */
.award-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 760px) { .award-grid { grid-template-columns: 1fr; } }
.award { background: var(--paper); padding: 36px 34px 38px; display: flex; gap: 26px; align-items: flex-start; }
.award-place {
  font-size: 54px; font-weight: 800; line-height: .85; letter-spacing: -.05em;
  color: var(--gold); flex: none;
}
.award-place sup { font-size: .38em; top: -1.1em; }
.award h3 { font-size: 17px; letter-spacing: -.015em; }
.award p { font-size: 13.5px; color: var(--ink-soft); margin-top: 10px; line-height: 1.6; }

/* ---------- representation ---------- */
.rep-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 44px 38px; }
@media (max-width: 1000px) { .rep-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .rep-grid { grid-template-columns: 1fr; } }
.rep { border-top: 2px solid var(--navy); padding-top: 20px; }
.rep h3 { font-size: 16.5px; letter-spacing: -.018em; line-height: 1.32; }
.rep p { font-size: 13.5px; color: var(--ink-soft); line-height: 1.65; margin-top: 13px; }
.rep-years {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 11px !important;
  color: var(--gold-deep) !important; letter-spacing: .06em; margin-top: 16px !important;
}

/* ---------- long-running projects ---------- */
.run-list { list-style: none; padding: 0; margin: 0; }
.run {
  display: grid; grid-template-columns: 150px 1fr; gap: 2px 30px; align-items: baseline;
  padding: 24px 0; border-bottom: 1px solid var(--line);
}
.run > :not(.run-span) { grid-column: 2; }
.run > .run-span { grid-row: 1 / span 2; }
@media (max-width: 700px) { .run { grid-template-columns: 1fr; gap: 8px; } .run > * { grid-column: 1; } .run > .run-span { grid-row: auto; } }
.run-span {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 12px;
  color: var(--gold-deep); letter-spacing: .04em; font-weight: 600;
}
.run h3 { font-size: 18px; letter-spacing: -.02em; }
.run p { font-size: 13.5px; color: var(--ink-soft); margin-top: 7px; line-height: 1.6; }
.run-more { margin-top: 34px; font-size: 14px; color: var(--ink-soft); }

/* ---------- click-to-play video (assets/js/video.js) ----------
   A navy poster, not YouTube's thumbnail: those carry identifiable faces and
   sponsor logos, and fetching them would contact a third party on page load. */
.videos { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 32px 28px; }
.video { display: block; color: inherit; text-decoration: none; }
.video-frame {
  position: relative; display: block; aspect-ratio: 16 / 9; border-radius: 6px; overflow: hidden;
  background: linear-gradient(135deg, var(--navy-deep), #1d4a8a);
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-play {
  position: absolute; left: 50%; top: 50%; width: 56px; height: 56px; margin: -28px 0 0 -28px;
  border-radius: 50%; background: var(--gold); transition: transform .25s var(--m-spring, ease);
}
.video-play::after {
  content: ""; position: absolute; left: 22px; top: 17px;
  border-style: solid; border-width: 11px 0 11px 18px; border-color: transparent transparent transparent var(--navy-deep);
}
.video:hover .video-play, .video:focus-visible .video-play { transform: scale(1.1); }
.video:focus-visible { outline: 2px solid var(--focus, var(--navy)); outline-offset: 4px; border-radius: 6px; }
.video-label {
  display: block; margin-top: 14px; font-size: 12px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--gold-deep);
}
.video-title { display: block; margin-top: 4px; font-size: 15px; line-height: 1.45; color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .video-play { transition: none; } }

/* ---------- values ---------- */
.value-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 860px) { .value-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .value-row { grid-template-columns: 1fr; } }
.value { background: var(--paper); padding: 30px 24px 34px; transition: background .35s var(--ease); }
.value:hover { background: var(--paper-warm); }
.value span {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 11px;
  color: var(--gold-deep); letter-spacing: .12em; display: block; margin-bottom: 14px;
}
.value h3 { font-size: 19px; letter-spacing: -.025em; color: var(--navy); }

/* ==================================================================
   THE EXCHANGE DATABASE
   The single most useful link on the site for an outgoing student,
   plus the filing order and the one trap the national team warns
   every cohort about.
   ================================================================== */
.db-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 860px) { .db-grid { grid-template-columns: 1fr; } }

.db-main {
  position: relative; background: var(--band); color: var(--band-ink);
  padding: 40px 38px 42px; display: flex; flex-direction: column; gap: 9px;
  transition: background .35s var(--ease);
}
.db-main:hover { background: #0f2545; }
.db-eyebrow { font-size: 10.5px; font-weight: 800; letter-spacing: .18em; text-transform: uppercase; color: var(--gold); }
.db-url {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: clamp(1.25rem, 0.36rem + 2.55vw, 1.875rem); font-weight: 600; letter-spacing: -.02em; color: #fff;
  word-break: break-word;
}
.db-desc { font-size: 13.5px; line-height: 1.65; color: rgba(255,255,255,.72); margin-top: 8px; max-width: 40ch; }
.db-go { position: absolute; right: 32px; bottom: 36px; font-size: 22px; color: var(--gold); transition: transform .35s var(--ease); }
.db-main:hover .db-go { transform: translateX(6px); }

.db-warn { background: var(--paper); padding: 36px 34px 38px; border-left: 3px solid var(--gold); }
.db-warn-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold-deep); display: block; margin-bottom: 15px;
}
.db-warn p { font-size: 14px; line-height: 1.7; color: var(--ink-soft); }
.db-warn p + p { margin-top: 15px; }
.db-warn strong { color: var(--ink); }
.db-warn .th { font-size: 13px; color: var(--ink-faint); font-style: normal; }

/* ---------- the filing order ---------- */
.flow { margin-top: 62px; }
.flow-title {
  font-size: 11.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 26px;
  display: flex; align-items: center; gap: 12px;
}
.flow-title::before { content: ''; width: 20px; height: 2px; background: var(--gold); transform: skewX(-30deg); }

.flow-steps { list-style: none; padding: 0; margin: 0; counter-reset: flow; }
.flow-steps li {
  display: grid; grid-template-columns: 70px 210px 1fr; gap: 24px; align-items: baseline;
  padding: 17px 0; border-bottom: 1px solid var(--line);
}
@media (max-width: 780px) {
  .flow-steps li { grid-template-columns: 62px 1fr; gap: 8px 18px; }
  .flow-note { grid-column: 2; }
}
.flow-code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 15px; font-weight: 700;
  color: var(--navy); letter-spacing: .02em;
}
.flow-name { font-size: 15px; font-weight: 700; letter-spacing: -.015em; color: var(--ink); }
.flow-note { font-size: 13.5px; color: var(--ink-soft); line-height: 1.6; }
.flow-end {
  margin-top: 22px; font-size: 13.5px; color: var(--ink-faint);
  display: flex; align-items: center; gap: 12px;
}
.flow-end::before { content: ''; width: 20px; height: 2px; background: var(--gold); transform: skewX(-30deg); }

/* ==================================================================
   GLOSSARY TERMS
   Acronyms in body prose become pressable and open a definition
   anchored to themselves. Deliberately quiet: a dotted gold underline,
   nothing that competes with a real link.
   ================================================================== */
.g-term {
  font: inherit; color: inherit; background: none; border: 0; padding: 0;
  cursor: help;
  border-bottom: 1px dotted var(--gold-deep);
  transition: background .22s var(--ease), border-color .22s;
}
.g-term:hover,
.g-term[aria-expanded="true"] {
  background: color-mix(in srgb, var(--gold) 22%, transparent);
  border-bottom-color: var(--gold-deep);
}
.g-term:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.g-pop {
  position: absolute; z-index: 700; width: min(320px, calc(100vw - 24px));
  background: var(--paper); color: var(--ink);
  padding: 18px 20px 20px;
  border-top: 3px solid var(--gold);
  box-shadow: 0 22px 48px -22px rgba(15,37,69,.55), 0 0 0 1px var(--line);
  opacity: 0; transform: translateY(-5px);
  transition: opacity .2s var(--ease-out), transform .2s var(--ease-out);
}
.g-pop-in { opacity: 1; transform: none; }
.g-pop-term {
  display: block;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 10.5px; letter-spacing: .16em; color: var(--gold-deep); font-weight: 600;
  margin-bottom: 7px;
}
.g-pop-short { display: block; font-size: 14.5px; font-weight: 700; line-height: 1.35; letter-spacing: -.015em; }
.g-pop-long { font-size: 13px; line-height: 1.6; color: var(--ink-soft); margin-top: 10px; }
.g-pop-link {
  display: inline-block; margin-top: 14px;
  font-size: 12px; font-weight: 700; color: var(--navy);
  border-bottom: 2px solid var(--gold); padding-bottom: 2px;
}
@media (prefers-reduced-motion: reduce) { .g-pop { transition: none; } }

/* ---------- a committee's own channels ---------- */
.chan-block { margin-top: 66px; }
.chan-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 820px) { .chan-grid { grid-template-columns: 1fr; } }
.chan {
  position: relative; background: var(--paper); padding: 20px 22px;
  display: flex; flex-direction: column; gap: 4px;
}
.chan::after { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--c, var(--navy)); opacity: 0; transition: opacity .3s; }
.chan:hover::after { opacity: 1; }
.chan-p { font-size: 10.5px; font-weight: 800; letter-spacing: .15em; text-transform: uppercase; color: var(--ink-faint); }
.chan-h { font-size: 14.5px; font-weight: 700; color: var(--ct, var(--c, var(--navy))); word-break: break-word; }
.chan-go { position: absolute; right: 20px; top: 22px; color: var(--ink-faint); transition: transform .3s var(--ease); }
.chan:hover .chan-go { transform: translateX(4px); }
.chan-note { font-size: 13px; color: var(--ink-soft); margin-top: 16px; line-height: 1.6; }

/* ==================================================================
   START HERE  (start.html)
   Four routes in, and the questions people actually ask before they
   join. Deliberately the plainest page on the site: someone reading it
   is new, and does not need to be impressed.
   ================================================================== */
.route-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 900px) { .route-grid { grid-template-columns: 1fr; } }

.route {
  position: relative; background: var(--paper);
  padding: 42px 40px 40px;
  display: flex; flex-direction: column;
}
.route::before { content: ''; position: absolute; left: 0; top: 0; right: 0; height: 3px; background: var(--c, var(--navy)); }
@media (max-width: 560px) { .route { padding: 34px 24px 32px; } }

.route-n {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 11px; letter-spacing: .18em; color: var(--ct, var(--c, var(--navy))); font-weight: 600;
}
.route h2 { font-size: clamp(1.4375rem, 0.36rem + 2.55vw, 1.9375rem); letter-spacing: -.03em; margin-top: 14px; max-width: 17ch; }
.route > p { font-size: 14.5px; line-height: 1.68; color: var(--ink-soft); margin-top: 16px; }

.route-facts { margin: 26px 0 0; }
.route-facts > div {
  display: flex; justify-content: space-between; align-items: baseline; gap: 18px;
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.route-facts dt {
  font-size: 10.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-faint); flex: none;
}
.route-facts dd { margin: 0; font-size: 13px; font-weight: 600; color: var(--ink); text-align: right; }

.route-go { margin-top: auto; padding-top: 28px; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.route-go .inline-link { margin-top: 0; }

/* ---------- questions ---------- */
.faq { max-width: 78ch; border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  list-style: none; cursor: pointer; padding: 22px 44px 22px 0; position: relative;
  font-size: 16.5px; font-weight: 700; letter-spacing: -.018em; color: var(--ink);
  transition: color .25s;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--navy); }
/* a plus that becomes a minus, drawn rather than typed so it stays crisp */
.faq-item summary::before,
.faq-item summary::after {
  content: ''; position: absolute; right: 4px; top: 50%;
  background: var(--gold-deep); transition: transform .3s var(--ease), opacity .3s;
}
.faq-item summary::before { width: 15px; height: 2px; margin-top: -1px; }
.faq-item summary::after  { width: 2px; height: 15px; margin-top: -7.5px; right: 10px; }
.faq-item[open] summary::after { transform: rotate(90deg); opacity: 0; }
.faq-item[open] summary { color: var(--navy); }
.faq-item p { font-size: 14.5px; line-height: 1.72; color: var(--ink-soft); padding: 0 44px 26px 0; max-width: 68ch; }
@media (prefers-reduced-motion: reduce) { .faq-item summary::before, .faq-item summary::after { transition: none; } }


/* A ghost button on a navy band inherited the light-mode ink colour, which
   put near-black text on dark navy at 1.53:1 — effectively unreadable. On any
   dark section it takes the light treatment instead. */
.cta-band .btn-ghost,
.section-navy .btn-ghost,
.hero-record .btn-ghost,
.photo-band .btn-ghost {
  border-color: rgba(255,255,255,.42);
  color: #fff;
}
.cta-band .btn-ghost:hover,
.section-navy .btn-ghost:hover,
.hero-record .btn-ghost:hover,
.photo-band .btn-ghost:hover {
  border-color: var(--gold);
  color: var(--gold);
}

/* the gold dash in the wordmark sat at 1.74:1; --gold-deep carries it now */
.brand-text .dash { color: var(--gold-deep); }
.section-navy .brand-text .dash { color: var(--gold); }


/* ==================================================================
   DARK-MODE CORRECTIONS  (found by tools/audit.js, not by eye)
   ================================================================== */

/* --navy lightens to #5b8ad6 in dark mode, so a solid navy BUTTON carrying
   white text drops to 3.47:1. Dark text on that same fill is 5.40:1.
   The skip link is NOT in this list: it sits on --band, which stays dark in
   both themes, so it keeps white text. An earlier edit collapsed all three
   into one rule and silently undid the button fix. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn-primary,
  :root:not([data-theme="light"]) .btn-sm { color: var(--navy-deep); }
  :root:not([data-theme="light"]) .btn-primary:hover,
  :root:not([data-theme="light"]) .btn-sm:hover { background: var(--gold); color: var(--navy-deep); }
}
:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .btn-sm { color: var(--navy-deep); }
:root[data-theme="dark"] .btn-primary:hover,
:root[data-theme="dark"] .btn-sm:hover { background: var(--gold); color: var(--navy-deep); }




/* A committee hero is a solid field of the committee's colour carrying white
   type. Three official colours are too light for that (SCOPH 2.67:1, SCORP
   3.78, SCOPE 4.27), and in dark mode the LIGHTENED variants make all seven
   too light. So the hero uses --cb: a fixed value, defined once and never
   re-themed, dark enough for white type in either mode. Every rule, chip and
   logo plate elsewhere on the page still uses the official colour exactly. */
:root {
  --scome-band: #100016;
  --scope-band: #0e6ea5;
  --scoph-band: #c14b0a;
  --scora-band: #bd202a;
  --score-band: #2e4a9c;
  --scorp-band: #345b22;
  --scoti-band: #4d1076;
}

/* ---------- 404 ----------
   Cloudflare Pages was answering every unmatched path with 200 and the
   homepage, so a typo'd URL looked like a working page. A real 404.html gets
   served with a real 404 status. */
.nf-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 900px) { .nf-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .nf-grid { grid-template-columns: 1fr; } }
.nf { background: var(--paper); padding: 28px 26px 30px; position: relative; transition: background .3s var(--ease); }
.nf::after { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--gold); opacity: 0; transition: opacity .3s; }
.nf:hover::after { opacity: 1; }
.nf h2 { font-size: 18px; letter-spacing: -.022em; color: var(--navy); }
.nf p { font-size: 13.5px; color: var(--ink-soft); margin-top: 10px; line-height: 1.6; }

/* ==================================================================
   HORIZONTAL RAILS
   A second axis of movement. Measured against how the reference sites
   actually do it (tools/study.js): Stripe runs two ordinary horizontal
   scrollers with scroll-snap, Linear two, Apple none, and not one of
   them hijacks the vertical scroll. So this is a real scroller.
   Behaviour in assets/js/rail.js.
   ================================================================== */
.rail { position: relative; }

/* the rail breaks out of the text column and runs to the screen edge, which
   is what tells you it keeps going */
/* Full-bleed without the scrollbar bug.
   `50vw` counts the scrollbar, so `calc(50% - 50vw)` overshoots the viewport
   by exactly the scrollbar's width — 14px of horizontal page scroll at 320px,
   which is a WCAG 1.4.10 Reflow failure. --sbw is measured once in main.js and
   corrects for it; without JavaScript it falls back to 0 and `overflow-x:clip`
   on the root absorbs the difference. */
.rail-bleed {
  margin-left: calc(50% - 50vw + var(--sbw, 0px) / 2);
  margin-right: calc(50% - 50vw + var(--sbw, 0px) / 2);
  padding-left: calc(50vw - 50% - var(--sbw, 0px) / 2 + 36px);
  padding-right: calc(50vw - 50% - var(--sbw, 0px) / 2 + 36px);
}
@media (max-width: 700px) {
  .rail-bleed { padding-left: 22px; padding-right: 22px; }
}

.rail-track {
  display: flex; gap: 2px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0;
  padding-bottom: 4px;
  cursor: grab;
  /* the scrollbar is replaced by the progress bar below */
  scrollbar-width: none; -ms-overflow-style: none;
}
.rail-track::-webkit-scrollbar { display: none; }
.rail-track.is-dragging { cursor: grabbing; scroll-snap-type: none; }
@supports (mask-image: linear-gradient(#000, #000)) {
  .rail-track { mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 64px), transparent 100%); }
}
.rail-track:focus-visible { outline: 3px solid var(--gold); outline-offset: 4px; }
.rail-track > * { scroll-snap-align: start; flex: 0 0 auto; }

/* edge shadows, shown only when there is genuinely more that way */
.rail::before, .rail::after {
  content: ''; position: absolute; top: 0; bottom: 34px; width: 90px;
  pointer-events: none; z-index: 2; opacity: 1; transition: opacity .3s;
}
.rail::before { left: 0; background: linear-gradient(90deg, var(--paper), transparent); }
.rail::after  { right: 0; background: linear-gradient(270deg, var(--paper), transparent); }
.section-warm .rail::before { background: linear-gradient(90deg, var(--paper-warm), transparent); }
.section-warm .rail::after  { background: linear-gradient(270deg, var(--paper-warm), transparent); }
.rail.at-start::before { opacity: 0; }
.rail.at-end::after { opacity: 0; }

/* ---- controls ---- */
.rail-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; margin-bottom: 26px;
}
.rail-controls { display: flex; align-items: center; gap: 10px; flex: none; }
.rail-btn {
  width: 44px; height: 44px; flex: none; cursor: pointer;
  background: none; border: 2px solid var(--line); color: var(--ink);
  font-size: 15px; line-height: 1;
  transition: border-color .25s, color .25s, opacity .25s, transform .25s var(--ease);
}
.rail-btn:hover:not(:disabled) { border-color: var(--gold); color: var(--navy); }
.rail-btn:active:not(:disabled) { transform: scale(.94); }
.rail-btn:disabled { opacity: .28; cursor: default; }
.rail-count {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 11.5px; letter-spacing: .1em; color: var(--ink-faint);
  min-width: 62px; text-align: right;
}

/* the progress bar replaces the scrollbar: it shows how much is left */
.rail-bar { height: 2px; background: var(--line); margin-top: 26px; overflow: hidden; }
.rail-bar-fill {
  display: block; height: 100%; background: var(--gold-deep);
  transform-origin: left center; transform: scaleX(.04);
  transition: transform .18s var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .rail-bar-fill { transition: none; } }

/* a rail whose contents already fit needs none of its furniture */
.rail-static .rail-controls,
.rail-static .rail-bar { display: none; }
.rail-static .rail-track { scroll-snap-type: none; cursor: default; }

/* ---- the card that rides on a rail ---- */
.rcard {
  width: 320px; background: var(--paper);
  padding: 28px 26px 30px; position: relative;
  display: flex; flex-direction: column;
  border-top: 3px solid var(--c, var(--navy));
  transition: background .3s var(--ease);
}
@media (max-width: 380px) { .rcard { width: 264px; } }
.rcard:hover { background: var(--paper-warm); }
.section-warm .rcard { background: var(--paper-warm); }
.section-warm .rcard:hover { background: var(--paper); }

.rcard-tag {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--navy))); font-weight: 600;
}
.rcard h3 {
  font-size: 17px; line-height: 1.32; letter-spacing: -.02em;
  margin-top: 12px; min-height: 2.6em;
}
.rcard p { font-size: 13px; line-height: 1.6; color: var(--ink-soft); margin-top: 12px; }
.rcard-foot {
  margin-top: auto; padding-top: 20px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.rail-more { margin-top: 30px; }

/* the rail's intro text should not run under the controls */
.rail-head > div:first-child { max-width: 54ch; }
.rail-head .section-lede { margin-top: 14px; }

/* ==================================================================
   SCROLL-DRIVEN ANIMATION, NATIVELY
   Measured on the reference sites first (tools/study.js): Palantir and
   IFMSA's own site both use CSS `animation-timeline`. None of the seven
   loaded GSAP or Lenis. So this needs no library — which matters, because
   the whole site is meant to stay readable by whoever inherits it.

   Everything here is inside @supports. Browsers without it keep the
   IntersectionObserver reveal in main.js, which is the real baseline; this
   only makes the motion continuous rather than one-shot.
   ================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* section headings drift up slightly as they enter */
    @keyframes rise {
      from { opacity: 0; transform: translateY(26px); }
      to   { opacity: 1; transform: none; }
    }
    .section-head,
    .rail-head > div:first-child,
    .page-head .page-lede {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 62%;
    }

    /* the committee hero's mark settles as the page moves under it */
    @keyframes settle {
      from { opacity: 0; transform: translateY(20px) scale(.94); }
      to   { opacity: 1; transform: none; }
    }
    .sc-hero-mark { animation: settle linear both; animation-timeline: view(); animation-range: entry 0% entry 70%; }

    /* a hairline that draws itself across as its section arrives */
    @keyframes draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
    .stats::before {
      content: ''; display: block; height: 2px; background: var(--gold);
      transform-origin: left center;
      animation: draw linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 30%;
    }

    /* a reading-progress bar along the top of the page. It is the honest
       version of a goal gradient: it reports how far you are, it does not
       manufacture urgency. */
    @keyframes progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
    body::after {
      content: ''; position: fixed; left: 0; top: 0; z-index: 300;
      width: 100%; height: 2px; background: var(--accent, var(--gold));
      transform-origin: left center; transform: scaleX(0);
      animation: progress linear both;
      animation-timeline: scroll(root block);
      pointer-events: none;
    }
  }
}

/* ==================================================================
   SEARCH
   15 pages, 81 projects and 35 acronyms is more than anyone should
   have to navigate by guessing. Index built by tools/build_search.py,
   behaviour in assets/js/search.js. Opens on Ctrl/Cmd+K or "/".
   ================================================================== */
.srch-open { overflow: hidden; }

.srch { position: fixed; inset: 0; z-index: 850; display: flex; justify-content: center; padding: 10vh 20px 20px; }
@media (max-width: 640px) { .srch { padding: 0; } }

.srch-backdrop {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--navy-deep) 58%, transparent);
  backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .22s var(--ease);
}
.srch-in .srch-backdrop { opacity: 1; }

.srch-panel {
  position: relative; z-index: 1; width: min(640px, 100%);
  max-height: 74vh; display: flex; flex-direction: column;
  background: var(--paper); color: var(--ink);
  box-shadow: 0 36px 80px -36px rgba(10,18,32,.7), 0 0 0 1px var(--line);
  opacity: 0; transform: translateY(-10px) scale(.99);
  transition: opacity .22s var(--ease-out), transform .22s var(--ease-out);
}
.srch-in .srch-panel { opacity: 1; transform: none; }
@media (max-width: 640px) { .srch-panel { width: 100%; max-height: 100vh; height: 100%; } }
@media (prefers-reduced-motion: reduce) {
  .srch-backdrop, .srch-panel { transition: none; }
  .srch-panel { transform: none; }
}

.srch-field { display: flex; align-items: center; gap: 14px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.srch-icon { width: 19px; height: 19px; flex: none; color: var(--ink-faint); }
.srch-input {
  flex: 1; border: 0; background: none; outline: none;
  font-family: inherit; font-size: 17px; font-weight: 600; color: var(--ink);
  letter-spacing: -.015em; min-width: 0;
}
.srch-input::placeholder { color: var(--ink-faint); font-weight: 400; }
.srch-input::-webkit-search-cancel-button { display: none; }
.srch-esc {
  flex: none; cursor: pointer; background: none; border: 1px solid var(--line);
  color: var(--ink-faint); font-family: inherit; font-size: 10.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; padding: 5px 9px;
  transition: color .2s, border-color .2s;
}
.srch-esc:hover { color: var(--ink); border-color: var(--ink-faint); }

.srch-status { padding: 10px 20px 0; font-size: 11.5px; color: var(--ink-faint); font-weight: 600; letter-spacing: .04em; }
.srch-status:empty { display: none; }

.srch-list { list-style: none; margin: 0; padding: 8px; overflow-y: auto; flex: 1; }
.srch-item { border-left: 3px solid transparent; }
.srch-item.is-active { border-left-color: var(--c, var(--gold)); background: var(--paper-warm); }
.srch-a { display: flex; flex-direction: column; gap: 3px; padding: 11px 14px; }
.srch-kind {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--ink-faint)));
}
.srch-title { font-size: 14.5px; font-weight: 700; letter-spacing: -.015em; line-height: 1.35; }
.srch-desc { font-size: 12.5px; color: var(--ink-soft); line-height: 1.5; }
.srch-list mark { background: color-mix(in srgb, var(--gold) 40%, transparent); color: var(--ink); padding: 0 1px; } /* full ink: inherited dim text on gold was 3.1:1 in dark */

.srch-foot {
  display: flex; gap: 20px; padding: 12px 20px; border-top: 1px solid var(--line);
  font-size: 10.5px; color: var(--ink-faint); font-weight: 600;
}
@media (max-width: 480px) { .srch-foot { display: none; } }
.srch-foot kbd {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 10px;
  border: 1px solid var(--line); padding: 1px 5px; margin-right: 4px; color: var(--ink-soft);
}

/* the trigger in the nav */
.nav-search {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  background: none; border: 1px solid var(--line); color: var(--ink-faint);
  font-family: inherit; font-size: 12.5px; font-weight: 600;
  padding: 8px 12px; transition: border-color .25s, color .25s;
}
.nav-search:hover { border-color: var(--gold); color: var(--ink); }
.nav-search svg { width: 14px; height: 14px; }
.nav-search kbd {
  font-family: ui-monospace, monospace; font-size: 10px; color: var(--ink-faint);
  border: 1px solid var(--line); padding: 0 4px;
}
@media (max-width: 1100px) { .nav-search .nav-search-label, .nav-search kbd { display: none; } .nav-search { padding: 8px; } }


/* Motion the reduced-motion sweep had missed. .sco:hover was covered and
   .cc:hover was not, which was an oversight rather than a policy. */
@media (prefers-reduced-motion: reduce) {
  .cc:hover { transform: none; }
  .cc:hover .cc-go .arrow,
  .soc:hover .soc-go,
  .chan:hover .chan-go,
  .db-main:hover .db-go,
  .cc-go .arrow { transform: none; }
  .rail-btn:active:not(:disabled) { transform: none; }
  .modal-close:hover { transform: none; }
}

/* ---- narrow-viewport corrections (found at 320px by tools/audit.js) ---- */
@media (max-width: 400px) {
  /* the rail's counter plus two buttons did not fit; the counter is the
     least essential of the three and the progress bar already reports
     position, so it goes */
  .rail-count { display: none; }
  .rail-head { flex-direction: column; align-items: flex-start; gap: 18px; }
  .rail-controls { align-self: flex-end; }

  /* route facts are a label/value pair side by side, which cannot hold at
     320px - stack them instead of letting the value push off-screen */
  .route-facts > div { flex-direction: column; align-items: flex-start; gap: 3px; }
  .route-facts dd { text-align: left; }

  /* same pattern on the committee facts panel */
  .sc-fact { flex-direction: column; align-items: flex-start; gap: 3px; }
  .sc-fact dd { text-align: left; }
}

/* ---- certificate verification result ---- */
.vresult {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 22px; padding: 16px 18px;
  border-left: 3px solid var(--gold);
  background: rgba(255,255,255,.07);
}
.vresult-title { font-size: 15px; font-weight: 800; letter-spacing: -.015em; color: #fff; }
.vresult-detail { font-size: 13.5px; line-height: 1.6; color: rgba(255,255,255,.78); }
.vresult-detail a { color: var(--gold); border-bottom: 1px solid currentColor; }
.vresult-ok { border-left-color: #5bbd7a; }
.vresult-no { border-left-color: #e07a5f; }
.vresult-warn { border-left-color: var(--gold); }
.vresult-pending { border-left-color: rgba(255,255,255,.4); }

/* a disabled verify form should look disabled, not broken */
.vform button[disabled] { opacity: .5; cursor: not-allowed; }
.vform input[disabled] { opacity: .55; cursor: not-allowed; }

/* ==================================================================
   EXCHANGE DESTINATIONS
   Every country IFMSA-Thailand holds a contract with this season, from
   the 26/27 quota workbooks. Country-level data only — no personal
   data is read or published. Built by tools/build_destinations_page.py,
   filtered by assets/js/destinations.js.
   ================================================================== */
/* The tile map: one tile per country, placed roughly where it is. Tiles are
   filled, never outlined (PR regulation 6.3: no solid frames). A tile is a
   real button that filters the cards below; the route from Thailand is drawn
   to whichever country is chosen. */
.dmap { margin: 0 0 34px; max-width: 780px; }
.dmap-grid {
  position: relative; display: grid; gap: 4px;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--rows), auto);
}
.dmap-t, .dmap-home {
  position: relative; z-index: 1; aspect-ratio: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  border: 0; border-radius: 6px; padding: 0; font-family: inherit;
  background: color-mix(in srgb, var(--navy) 7%, var(--paper)); color: var(--ink);
}
.dmap-t { cursor: pointer; transition: background .25s, color .25s, transform .34s var(--m-pop); }
.dmap-t:hover { background: color-mix(in srgb, var(--navy) 16%, var(--paper)); transform: translateY(-3px) scale(1.06); }
.dmap-t:active { transform: translateY(0) scale(.97); transition-duration: var(--m-instant); }
/* The chosen country gives one pulse, so the eye finds it after the tap. */
.dmap-t.is-on { animation: dmap-pick .42s var(--m-pop); }
@keyframes dmap-pick { 0% { transform: scale(.9); } 60% { transform: scale(1.12); } 100% { transform: none; } }
.dmap-t.is-on { background: var(--navy); color: #fff; }
.dmap-t.is-dim { background: transparent; color: var(--ink-faint); }
.dmap-t.is-dim .dmap-dots { opacity: .35; }
.dmap-home { background: var(--gold); color: #183868; }
.dmap-code { font-size: 12.5px; font-weight: 800; letter-spacing: .04em; line-height: 1; }
.dmap-dots { display: flex; gap: 3px; height: 5px; }
.dmap-dot { display: inline-block; width: 5px; height: 5px; border-radius: 50%; }
.dmap-scope { background: var(--scope); }
.dmap-score { background: var(--score); border-radius: 1px; transform: rotate(45deg) scale(.9); }
/* A shape as well as a colour: SCOPE and SCORE are both blues, close in dark
   mode and closer still for colour-blind readers (WCAG 1.4.1). */
.dmap-t.is-on .dmap-dot { box-shadow: 0 0 0 1.5px #fff; }
.dmap-route { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; z-index: 2; }
.dmap-route path {
  fill: none; stroke: var(--gold); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 4 5;
  opacity: 0; transition: opacity .3s;
}
.dmap-route.is-on path { opacity: 1; animation: dmap-fly .9s var(--ease-out) both; }
@keyframes dmap-fly { from { stroke-dashoffset: 120; opacity: 0; } to { stroke-dashoffset: 0; opacity: 1; } }
.dmap-legend {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; margin-top: 14px;
  font-size: 12px; color: var(--ink-soft);
}
.dmap-key { display: inline-flex; align-items: center; gap: 7px; }
.dmap-key .dmap-dot { width: 8px; height: 8px; }
.dmap-key-home { font-size: 9.5px; font-weight: 800; background: var(--gold); color: #183868; border-radius: 3px; padding: 2px 4px; }
.dmap-hint { color: var(--ink-faint); }
@media (max-width: 640px) {
  .dmap-grid { gap: 3px; }
  .dmap-code { font-size: 10px; }
  .dmap-t, .dmap-home { gap: 3px; border-radius: 4px; }
  .dmap-dot { width: 4px; height: 4px; }
}
@media (prefers-reduced-motion: reduce) {
  .dmap-t { transition: none; }
  .dmap-t:hover { transform: none; }
  .dmap-route.is-on path { animation: none; }
}

.dest-filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 20px; }
.dest-f {
  cursor: pointer; background: none; border: 1px solid var(--line);
  font-family: inherit; font-size: 12.5px; font-weight: 700; color: var(--ink-soft);
  padding: 9px 15px; display: inline-flex; align-items: center; gap: 8px;
  transition: border-color .25s, color .25s, background .25s;
}
.dest-f span { font-family: ui-monospace, monospace; font-size: 10.5px; color: var(--ink-faint); }
.dest-f:hover { border-color: var(--gold); color: var(--ink); }
.dest-f.is-on { background: var(--navy); border-color: var(--navy); color: #fff; }
/* The count inherits the button's own text colour, at reduced weight, rather
   than hard-coding white. In light mode the selected button is navy with white
   type, and 70% white worked. In dark mode it flips to a light blue ground with
   near-black type - and the count, still hard-coded white, sat at 2.5:1 on it.
   Inheriting means it follows whichever flip the theme makes. */
/* No opacity on top: dimming near-black toward the light-blue ground brought
   it back to 4.0:1. The monospace face already marks the count as secondary. */
.dest-f.is-on span { color: inherit; }
:root[data-theme="dark"] .dest-f.is-on,
.dest-search { color: var(--ink); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .dest-f.is-on { color: var(--navy-deep); } }
:root[data-theme="dark"] .dest-f.is-on { color: var(--navy-deep); }

.dest-search {
  flex: 1 1 200px; min-width: 160px;
  border: 1px solid var(--line); background: var(--paper);
  font-family: inherit; font-size: 13px; padding: 9px 13px; color: var(--ink);
}
.dest-search::placeholder { color: var(--ink-faint); }
.dest-search:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.dest-count { font-size: 12.5px; color: var(--ink-faint); font-weight: 600; margin-bottom: 18px; min-height: 1.2em; }

.dest-grid {
  list-style: none; padding: 0; margin: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  background: var(--line); border: 1px solid var(--line);
}
@media (max-width: 1000px) { .dest-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .dest-grid { grid-template-columns: 1fr; } }

.dest {
  background: var(--paper); padding: 22px 22px 24px; position: relative;
  display: flex; flex-direction: column;
}
.dest::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--c, var(--navy)); opacity: .85; }
.dest-head { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
.dest h3 { font-size: 18px; letter-spacing: -.025em; line-height: 1.2; }
.dest-units {
  flex: none; font-family: ui-monospace, monospace; font-size: 11px;
  color: var(--ct, var(--c, var(--navy))); font-weight: 600; white-space: nowrap;
}
.dest-prog {
  display: block; margin-top: 6px;
  font-size: 10px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint);
}
.dest-facts { margin: 16px 0 0; }
.dest-fact { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--line); }
.dest-fact dt { font-size: 10px; font-weight: 800; letter-spacing: .11em; text-transform: uppercase; color: var(--ink-faint); flex: none; }
.dest-fact dd { margin: 0; font-size: 12.5px; color: var(--ink); text-align: right; line-height: 1.45; }
.dest-req { font-size: 12px; color: var(--ink-soft); line-height: 1.55; margin-top: 14px; }
.dest-note, .dest-foot { font-size: 13px; color: var(--ink-soft); line-height: 1.65; margin-top: 22px; max-width: 76ch; }
.dest-note { color: var(--ink-faint); font-size: 12.5px; }
.dest-foot abbr { border-bottom: 1px dotted var(--gold-deep); text-decoration: none; cursor: help; }

@media (max-width: 400px) {
  .dest-fact { flex-direction: column; align-items: flex-start; gap: 2px; }
  .dest-fact dd { text-align: left; }
}

/* [hidden] is a UA rule of `display:none`, which any explicit display in the
   stylesheet outranks. Filtering set .hidden on cards that then stayed on
   screen because `.dest { display:flex }` won. Anything the site hides by
   attribute needs saying explicitly. */
.dest[hidden], .rcard[hidden], .proj[hidden], .cc[hidden] { display: none !important; }

/* ==================================================================
   PROPOSAL PRE-CHECK  (proposal-check.html)
   A self-check against the national guideline. It measures what is
   measurable — word limits, objective counts — and is explicit on the
   page that it does not judge quality. Data in
   assets/data/proposal-guideline.json, behaviour in assets/js/proposal.js.
   ================================================================== */
.pc-intro {
  display: flex; align-items: center; justify-content: space-between;
  gap: 26px; flex-wrap: wrap; margin-bottom: 22px;
}
.pc-summary { flex: 1 1 380px; min-width: 260px; }
.pc-bar { height: 3px; background: var(--line); overflow: hidden; margin-bottom: 14px; }
.pc-bar-fill {
  display: block; height: 100%; background: var(--gold-deep);
  transform-origin: left center; transform: scaleX(0);
  transition: transform .35s var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .pc-bar-fill { transition: none; } }
.pc-summary-text { font-size: 14px; color: var(--ink-soft); line-height: 1.6; }
.pc-summary-text strong { color: var(--ink); }
.pc-actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* a size modifier only - it must not bring a background with it, or it
   fights .btn-ghost and the dark-mode button correction lands near-black
   text on the dark page */
.btn-tight { padding: 10px 18px; font-size: 12.5px; }

.pc-privacy {
  font-size: 13px; color: var(--ink-soft); line-height: 1.7;
  border-left: 3px solid var(--gold); padding: 14px 18px;
  background: var(--paper-warm); max-width: 84ch; margin-bottom: 46px;
}
.pc-privacy strong { color: var(--ink); }

.pc-part { margin-bottom: 44px; }
.pc-part-title {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); padding-bottom: 12px; border-bottom: 2px solid var(--line);
}
.pc-part-score {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 11.5px;
  letter-spacing: .04em; color: var(--ink-faint); flex: none;
}
.pc-part-score.is-done { color: var(--gold-deep); }

.pc-items { list-style: none; padding: 0; margin: 0; }
.pc-item { padding: 18px 0; border-bottom: 1px solid var(--line); }

.pc-check { display: flex; gap: 13px; align-items: flex-start; cursor: pointer; }
.pc-check input {
  flex: none; width: 19px; height: 19px; margin-top: 2px;
  accent-color: var(--navy); cursor: pointer;
}
.pc-label { font-size: 15.5px; font-weight: 700; letter-spacing: -.015em; line-height: 1.4; }
.pc-limit {
  display: block; margin-top: 3px;
  font-family: ui-monospace, monospace; font-size: 10.5px; font-weight: 600;
  letter-spacing: .06em; color: var(--gold-deep); text-transform: none;
}
.pc-hint { font-size: 13px; color: var(--ink-soft); line-height: 1.6; margin: 9px 0 0 32px; max-width: 76ch; }

.pc-choices {
  list-style: none; padding: 0; margin: 11px 0 0 32px;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.pc-choices li {
  font-size: 11.5px; color: var(--ink-soft); font-weight: 600;
  background: var(--paper-warm); padding: 4px 10px;
}

.pc-tool { margin: 13px 0 0 32px; max-width: 70ch; }
.pc-text {
  width: 100%; font-family: inherit; font-size: 13.5px; line-height: 1.6;
  padding: 11px 13px; border: 1px solid var(--line); background: var(--paper);
  color: var(--ink); resize: vertical;
}
.pc-text:focus-visible, .pc-count:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pc-num { display: flex; align-items: center; gap: 12px; font-size: 13.5px; color: var(--ink-soft); }
.pc-count {
  width: 78px; font-family: inherit; font-size: 14px; padding: 8px 10px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
}
.pc-meter {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 11.5px; margin-top: 8px; color: var(--ink-faint); min-height: 1.2em;
}
.pc-meter.pc-good { color: #3f8f5c; }
.pc-meter.pc-bad { color: #b0442a; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .pc-meter.pc-good { color: #6fcf8f; }
  :root:not([data-theme="light"]) .pc-meter.pc-bad { color: #f08a68; }
}
:root[data-theme="dark"] .pc-meter.pc-good { color: #6fcf8f; }
:root[data-theme="dark"] .pc-meter.pc-bad { color: #f08a68; }

.pc-error { font-size: 14px; color: var(--ink-soft); }

@media (max-width: 560px) {
  .pc-hint, .pc-choices, .pc-tool { margin-left: 0; }
}

/* ---- theme toggle ----
   The three-state theme CSS above has existed since the first build with
   nothing to drive it. assets/js/theme.js is that control. */
.nav-theme {
  display: inline-flex; align-items: center; gap: 8px; flex: none; cursor: pointer;
  background: none; border: 1px solid var(--line); color: var(--ink-soft);
  font-family: inherit; font-size: 12.5px; font-weight: 600;
  padding: 8px 12px; transition: border-color .25s, color .25s;
}
.nav-theme:hover { border-color: var(--gold); color: var(--ink); }
.nav-theme svg { width: 15px; height: 15px; flex: none; }
/* The nav ran out of room, and the first fix for it was wrong in an
   instructive way.

   Symptom: "Verify a certificate" sat 53px past the right edge at 1280 - a
   13" MacBook, not an edge case - on eight pages. It had fitted until the
   language toggle added a sixth control.

   The first attempt put the fix behind `@media (max-width: 1360px)`, which
   made 1280 pass and left 1366 broken. Measuring properly showed why: with
   every label shown the six controls need 1257px, and `.wrap` caps at 1200px
   at EVERY width. The nav had been overflowing its own wrap the whole time.
   Between roughly 1360 and 1400 the page margins are thin enough that the
   overflow leaves the viewport as well, which is the only reason it showed up
   there and not at 1920.

   So the tightening is unconditional, not a breakpoint: 20px gaps, and the
   theme control drops to its icon (it keeps its aria-label - nothing is lost
   but the word "System"). That brings it to ~1145px, leaving real headroom
   rather than a hairline, which matters because Thai sets wider than English.

   The lesson worth keeping: a viewport-overflow symptom does not mean a
   viewport-width cause. Measure what the content needs against what the
   container gives it before reaching for a media query. */
.nav-inner { gap: 20px; }
.nav-theme .nav-theme-label { display: none; }
.nav-theme { padding: 8px; }

/* full conditions, opened from a destination card */
.dest-more {
  margin-top: auto; padding: 14px 0 0; align-self: flex-start;
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 11.5px; font-weight: 800;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--navy)));
  display: inline-flex; align-items: center; gap: 7px;
}
.dest-more span { transition: transform .3s var(--ease); }
.dest-more:hover span { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) { .dest-more span { transition: none; } }

.modal-dl { margin: 22px 0 0; }
.modal-dl dt {
  font-size: 10.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-faint); margin-top: 16px;
}
.modal-dl dt:first-child { margin-top: 0; }
.modal-dl dd { margin: 5px 0 0; font-size: 14px; line-height: 1.6; color: var(--ink); }

/* Overlays hide themselves with the `hidden` attribute, but `[hidden]` is
   only a UA `display:none` and any explicit display outranks it — both of
   these set `display:flex`. Without this the closed dialog stays in the
   layout, invisible but still covering the page. Third instance of this
   exact bug; see .dest[hidden] above. */
.modal[hidden], .srch[hidden] { display: none !important; }

/* A role card is a portfolio name and what it covers. That is a label and its
   detail — markup's job, not an em-dash's. Splitting them also lets the
   portfolio name carry the weight a reader scanning for "who do I write to"
   is actually looking for. */
.role-area { display: block; font-weight: 700; color: var(--ink); margin-bottom: 2px; }


/* --gold-deep is tuned for small text at 4.5:1. The hero word is ~70px, which
   is "large text" under WCAG and needs only 3:1, so it uses a gold much
   closer to the brand's own #E5C158 instead of the olive the small-text
   value reads as at display size. */
.hero-title .gold { color: #9a7c24; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hero-title .gold { color: var(--gold); }
}
:root[data-theme="dark"] .hero-title .gold { color: var(--gold); }

/* ==================================================================
   TOUCH ERGONOMICS  (found by tools/layout_check.js)
   The header controls measured 42x29 and 32x32. Apple's HIG and Google's
   Material both put the floor at 44px, and the hamburger is the single
   most important control on a phone — if it is hard to hit, nothing else
   on the page matters. Sized by pointer capability rather than viewport,
   so a touchscreen laptop gets it too.
   ================================================================== */
@media (pointer: coarse) and (max-width: 1180px) {
  .nav-toggle {
    min-height: 44px; min-width: 44px;
    display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  }
}
@media (pointer: coarse) {
  .nav-search,
  .nav-theme {
    min-height: 44px;
    min-width: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* an inline link inside prose cannot grow a box without breaking the line
     box, so it gets its hit area from padding instead */
  .inline-link { position: relative; }
  .inline-link::after { content: ""; position: absolute; inset: -11px -4px; }
  .faq-item summary { padding-top: 26px; padding-bottom: 26px; }
  .dest-more { padding-top: 18px; padding-bottom: 10px; }
}

/* At 320px the headline's clamp floor of 38px is wider than the column.
   Below the common phone width it steps down rather than overflowing. */
@media (max-width: 360px) {
  /* phone-scoped: rem term calibrated at 390px, not 1280px */
  .hero-title { font-size: clamp(1.875rem, 0.329rem + 7.65vw, 2.375rem); }
  .page-title { font-size: clamp(1.6875rem, 0.311rem + 7.225vw, 2.125rem); }
  .sc-hero-title { font-size: clamp(2.375rem, 0.475rem + 11.05vw, 3.75rem); }
}

/* More from the device matrix. */
@media (pointer: coarse) {
  /* the checklist's boxes were 19px. Their label is clickable and large, but
     the box itself is what a thumb aims at. WCAG 2.5.8 floors this at 24. */
  .pc-check input { width: 24px; height: 24px; margin-top: 0; }
  .pc-check { align-items: center; }
  /* "Open ->" on a committee card was 18px tall */
  .cc-foot { padding-bottom: 8px; }
  .cc-go { padding: 10px 0; }
}
/* 12px was too small for the requirement note on a destination card — it is
   the line that tells someone whether they are even eligible. */
.dest-req { font-size: 13px; }

@media (pointer: coarse) {
  /* the contact address in a committee's facts panel was a 16px-tall line of
     text acting as a link */
  .sc-fact dd a { display: inline-block; padding: 5px 0; }
  .sc-fact { padding: 10px 0; }
}

/* ==================================================================
   COMMITTEE ARTICLES  (articles.html)
   Self-service publishing: a Google Form feeds a Sheet, the Sheet is
   published as CSV, this page reads it. A tick in one column is the
   publish. Behaviour in assets/js/articles.js.
   ================================================================== */
.art-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.art-f {
  cursor: pointer; background: none; border: 1px solid var(--line);
  font-family: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  color: var(--ink-soft); padding: 8px 14px;
  transition: border-color .25s, color .25s, background .25s;
}
.art-f:hover { border-color: var(--gold); color: var(--ink); }
.art-f.is-on { background: var(--navy); border-color: var(--navy); color: #fff; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .art-f.is-on { color: var(--navy-deep); } }
:root[data-theme="dark"] .art-f.is-on { color: var(--navy-deep); }

.art-status { font-size: 13.5px; color: var(--ink-soft); line-height: 1.7; max-width: 74ch; margin-bottom: 26px; }

.art-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 2px; background: var(--line); border: 1px solid var(--line); }
.art-list:empty { display: none; }
.art {
  background: var(--paper); padding: 26px 26px 28px; position: relative;
}
.art::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--c, var(--navy)); }
.art-meta { display: flex; gap: 14px; align-items: baseline; }
.art-sco {
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--navy)));
}
.art-date { font-family: ui-monospace, monospace; font-size: 11px; color: var(--ink-faint); }
.art h3 { font-size: 19px; letter-spacing: -.022em; line-height: 1.3; margin-top: 10px; max-width: 46ch; }
.art p { font-size: 14px; line-height: 1.68; color: var(--ink-soft); margin-top: 12px; max-width: 74ch; }
.art-by { font-size: 12.5px !important; color: var(--ink-faint) !important; margin-top: 10px !important; }
.art-link {
  display: inline-block; margin-top: 16px;
  font-size: 12px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ct, var(--c, var(--navy)));
}
.art-link:hover { border-bottom: 2px solid var(--gold); }
.art-empty { background: var(--paper); padding: 30px 26px; font-size: 14px; color: var(--ink-faint); }
.art-ask { font-size: 14px; color: var(--ink-soft); margin-top: 32px; }

/* ==================================================================
   LOCAL COMMITTEE FINDER  (start.html)
   Grouped by region rather than alphabetically: someone looking for
   their own school knows where they are before they know its
   abbreviation. Data in assets/data/local-committees.json.
   ================================================================== */
.lc-search-wrap { margin-bottom: 30px; max-width: 520px; }
.lc-search {
  width: 100%; font-family: inherit; font-size: 16px; color: var(--ink);
  padding: 14px 16px; border: 1px solid var(--line); background: var(--paper);
}
.lc-search::placeholder { color: var(--ink-faint); }
.lc-search:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.lc-count { font-size: 12.5px; color: var(--ink-faint); font-weight: 600; margin-top: 10px; min-height: 1.2em; }

.lc-groups { display: grid; grid-template-columns: repeat(2, 1fr); gap: 34px 44px; }
@media (max-width: 820px) { .lc-groups { grid-template-columns: 1fr; } }
.lc-region[hidden] { display: none; }

.lc-region-name {
  font-size: 11px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); padding-bottom: 10px; border-bottom: 2px solid var(--line);
  display: flex; justify-content: space-between; align-items: baseline;
}
.lc-region-name span { font-family: ui-monospace, monospace; font-size: 10.5px; }

.lc-list { list-style: none; padding: 0; margin: 0; }
.lc {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 14px;
  padding: 13px 0; border-bottom: 1px solid var(--line); align-items: baseline;
}
.lc[hidden] { display: none; }
.lc-abbr { grid-column: 1; font-size: 13px; font-weight: 800; color: var(--navy); letter-spacing: -.01em; }
.lc-name { grid-column: 1; font-size: 12.5px; color: var(--ink-soft); line-height: 1.45; }
.lc-progs { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: 5px; align-self: center; }
.lc-prog {
  font-size: 9.5px; font-weight: 800; letter-spacing: .09em;
  padding: 3px 7px; color: #fff; white-space: nowrap;
}
/* The BAND variants, not the raw committee colours.
   These chips carry 9.5px bold white type. On the official SCOPE blue that
   measures 4.27:1 - close, and under AA's 4.5 for text this size. The band
   tokens exist precisely for "white type on a committee ground" and are
   already darkened for it; the raw colours stay correct everywhere they are
   used as a rule, a chip edge or a logo plate. */
.lc-score { background: var(--score-band); }
.lc-scope { background: var(--scope-band); }

/* The map beside the list. Thailand is drawn from Natural Earth by
   tools/build_thailand_outline.py; pins are links to their region, so the
   map needs no JavaScript. On a phone it sits above the list, smaller. */
.lc-layout { display: grid; grid-template-columns: minmax(220px, 300px) 1fr; gap: 44px; align-items: start; }
@media (max-width: 820px) { .lc-layout { grid-template-columns: 1fr; gap: 26px; } }
.lc-map { position: sticky; top: 96px; margin: 0; }
@media (max-width: 820px) { .lc-map { position: static; max-width: 230px; margin: 0 auto; } }
.lc-map svg { width: 100%; height: auto; display: block; overflow: visible; }
.lc-map figcaption { font-size: 11.5px; color: var(--ink-faint); margin-top: 10px; line-height: 1.5; }
.lc-land {
  fill: color-mix(in srgb, var(--navy) 9%, transparent);
  stroke: var(--navy); stroke-width: 1.2; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.lc-pin { cursor: pointer; outline: none; }
.lc-pin-dot { fill: var(--gold); stroke: var(--navy); stroke-width: 1.4; transition: transform var(--m-quick, .18s) ease; transform-box: fill-box; transform-origin: center; }
.lc-pin-pulse { fill: var(--gold); opacity: 0; transform-box: fill-box; transform-origin: center; pointer-events: none; }
.lc-land { pointer-events: none; }
.lc-pin-hit { fill: transparent; }   /* a 30-unit tap target around every pin (WCAG 2.5.8) */
@media (max-width: 820px) { .lc-pin text { font-size: 12.5px; } .lc-pin .lc-pin-n { font-size: 10px; } }
.lc-pin text { font-size: 9.5px; font-weight: 700; fill: var(--ink-soft); pointer-events: none; }
.lc-pin .lc-pin-n { font-size: 8.5px; font-weight: 800; fill: var(--navy); }
.lc-pin:hover .lc-pin-dot, .lc-pin:focus-visible .lc-pin-dot, .lc-pin.is-on .lc-pin-dot { transform: scale(1.35); fill: #fff; }
.lc-pin:focus-visible text, .lc-pin.is-on text { fill: var(--navy); font-weight: 800; }
.lc.is-lit { background: color-mix(in srgb, var(--gold) 14%, transparent); }
:root[data-theme="dark"] .lc-land { fill: color-mix(in srgb, #fff 6%, transparent); stroke: #8fa6cf; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .lc-land { fill: color-mix(in srgb, #fff 6%, transparent); stroke: #8fa6cf; } }

@media (prefers-reduced-motion: no-preference) {
  /* a slow breath on each pin, staggered, so the map reads as alive */
  @keyframes lc-breathe { 0% { opacity: .45; transform: scale(1); } 100% { opacity: 0; transform: scale(2.6); } }
  /* two breaths when the map comes into view, then still: anything that moves on
     its own for over 5 s needs a pause control (WCAG 2.2.2). lcs.js adds .is-seen. */
  .lc-map.is-seen .lc-pin-pulse { animation: lc-breathe 1.4s ease-out 2; animation-delay: calc(.6s + var(--i) * .12s); }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* the coastline draws itself as the section scrolls in; pins drop after it */
    @keyframes lc-draw { from { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; } 70% { fill-opacity: 0; } to { stroke-dasharray: 1; stroke-dashoffset: 0; fill-opacity: 1; } }
    @keyframes lc-drop { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: none; } }
    /* one named timeline on the figure: SVG children have no box of their own to track */
    .lc-map { view-timeline: --lcmap block; }
    .lc-land { animation: lc-draw linear both; animation-timeline: --lcmap; animation-range: entry 0% entry 100%; }
    .lc-pin { animation: lc-drop linear both; animation-timeline: --lcmap; animation-range: entry calc(45% + var(--i) * 4%) entry calc(70% + var(--i) * 4%); }
  }
}
.lc-note { font-size: 13.5px; color: var(--ink-soft); line-height: 1.7; margin-top: 34px; max-width: 78ch; }
.lc-note strong { color: var(--ink); }

/* ---- opportunities board ----
   Read from a sheet VPAO already keeps. A listing removes itself the day
   after it closes, because taking it down by hand is the step that never
   happens — and a stale recruitment page costs someone an application. */
.opp-status { font-size: 13.5px; color: var(--ink-soft); line-height: 1.7; max-width: 76ch; margin-bottom: 24px; }
.opp-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 2px; background: var(--line); border: 1px solid var(--line); }
.opp-list:empty { display: none; }
.opp { background: var(--paper); padding: 24px 26px 26px; }
.opp-top { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.opp-team { font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
.opp-close {
  font-family: ui-monospace, monospace; font-size: 11px; font-weight: 600;
  color: var(--ink-faint); white-space: nowrap;
}
.opp-close.is-urgent { color: var(--gold-deep); }
.opp h3 { font-size: 18px; letter-spacing: -.02em; margin-top: 9px; }
.opp p { font-size: 13.5px; line-height: 1.65; color: var(--ink-soft); margin-top: 10px; max-width: 72ch; }
.opp-link {
  display: inline-block; margin-top: 14px;
  font-size: 12px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--navy); border-bottom: 2px solid var(--gold); padding-bottom: 2px;
}

/* ---- project tracker ----
   Four approval gates, rendered from the approvals sheet VPAO already keeps. The
   Note column — reviewers' written feedback to a named student — is never
   read. See assets/js/tracker.js. */
.trk-status { font-size: 13.5px; color: var(--ink-soft); line-height: 1.7; max-width: 76ch; margin-bottom: 24px; }
.trk-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 2px; background: var(--line); border: 1px solid var(--line); }
.trk-list:empty { display: none; }
.trk { background: var(--paper); padding: 22px 24px 24px; position: relative; }
.trk::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--c, var(--navy)); }
.trk-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.trk h3 { font-size: 16.5px; letter-spacing: -.018em; line-height: 1.35; max-width: 52ch; }
.trk-meta { font-size: 12px; color: var(--ink-faint); margin-top: 5px; font-weight: 600; }
.trk-overall {
  font-size: 10.5px; font-weight: 800; letter-spacing: .11em; text-transform: uppercase;
  white-space: nowrap; padding: 4px 9px; color: #fff; background: var(--ink-faint);
}
.trk-overall.is-approved { background: #3f8f5c; }
.trk-overall.is-returned { background: #b0442a; }
.trk-overall.is-review   { background: var(--gold-deep); }
.trk-overall.is-waiting  { background: var(--ink-faint); }

.trk-gates { list-style: none; padding: 0; margin: 16px 0 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; }
@media (max-width: 560px) { .trk-gates { grid-template-columns: repeat(2, 1fr); } }
.trk-gate {
  padding: 9px 11px; background: var(--paper-warm);
  border-top: 2px solid var(--line);
  display: flex; flex-direction: column; gap: 2px;
}
.trk-gate-name { font-size: 10.5px; font-weight: 800; letter-spacing: .1em; color: var(--ink); }
.trk-gate-state { font-size: 11px; color: var(--ink-faint); }
.trk-gate.is-approved { border-top-color: #3f8f5c; }
.trk-gate.is-returned { border-top-color: #b0442a; }
.trk-gate.is-review   { border-top-color: var(--gold); }

/* ---- language switch ----
   Sits with the theme control. The button shows the language it will switch
   TO, which is the convention people expect from a two-state toggle. */
.nav-lang {
  flex: none; cursor: pointer; background: none;
  border: 1px solid var(--line); color: var(--ink-soft);
  font-family: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  padding: 8px 11px; min-width: 44px;
  transition: border-color .25s, color .25s;
}
.nav-lang:hover { border-color: var(--gold); color: var(--ink); }
.nav-lang[lang="th"] { font-family: 'Kanit', 'Montserrat', sans-serif; letter-spacing: 0; }
@media (pointer: coarse) { .nav-lang { min-height: 44px; } }

/* Shown only in Thai: says plainly that long-form text is still English,
   rather than leaving a reader to wonder whether the page failed. */
.lang-notice {
  font-family: 'Kanit', 'Montserrat', sans-serif;
  font-size: 13px; line-height: 1.75; color: var(--ink-soft);
  background: var(--paper-warm); border-left: 3px solid var(--gold);
  padding: 13px 18px; margin: 0 0 8px;
}
.lang-notice a { color: var(--navy); border-bottom: 1px solid var(--gold); }

/* At the 320px floor the header carries brand, language, theme, search and
   the menu, and that is one control too many. Theme is the one to drop: the
   system setting already works without it, and language matters far more to
   this audience than a manual dark toggle does. */
@media (max-width: 400px) {
  .nav-theme { display: none; }
  .nav-lang { padding: 8px; min-width: 40px; font-size: 11.5px; }
  .nav-search { padding: 8px 6px; }
  .nav-inner { gap: 6px; }
  .nav-toggle { padding: 10px 4px; }
}

/* Thai runs wider than English at the same point size, so the header needs
   to collapse to the menu sooner when Thai is selected. Measured, not
   guessed: the six Thai labels plus three controls stop fitting around
   1300px where the English set holds to 1180. */
:root:lang(th) .nav-links { gap: 20px; font-size: 13.5px; }
.nav-extra { display: none; }
@media (max-width: 1180px) { .nav-links .nav-extra { display: block; } .nav-links .nav-extra:first-of-type { border-top: 2px solid var(--line); margin-top: 6px; } }
@media (max-width: 1300px) { :root:lang(th) .nav-links .nav-extra { display: block; } }
@media (max-width: 1300px) {
  :root:lang(th) .nav-links { display: none; }
  :root:lang(th) .nav-toggle { display: block; }
}
:root:lang(th) .nav-open .nav-links { display: flex; }

/* ---------------------------------------------------------------------------
   CALENDAR  (calendar.html)

   The year as dates you can act on. Three status kinds, and the styling has
   to make the difference legible at a glance without adding a fourth colour
   to the view - the PR regulation caps a view at three. So status is carried
   by the weight and fill of one gold stroke, not by a palette of its own:
   confirmed is solid gold, last-year is a hairline, month-only is dotted.
   --------------------------------------------------------------------------- */

.cal-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.cal-filter {
  cursor: pointer; background: none; border: 1px solid var(--line);
  font-family: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  color: var(--ink-soft); padding: 8px 14px; min-height: 34px;
  transition: border-color .25s, color .25s, background .25s;
}
.cal-filter:hover { border-color: var(--gold); color: var(--ink); }
.cal-filter.is-on { background: var(--navy); border-color: var(--navy); color: #fff; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cal-filter.is-on { color: var(--navy-deep); }
}
:root[data-theme="dark"] .cal-filter.is-on { color: var(--navy-deep); }

.cal-status {
  font-size: 13.5px; color: var(--ink-soft); line-height: 1.7;
  max-width: 74ch; margin-bottom: 30px;
}

.cal-grid { display: grid; gap: 46px; }

.cal-month { display: grid; grid-template-columns: 160px 1fr; gap: 26px; }
.cal-mname {
  font-size: 15px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft); margin: 0; padding-top: 22px; position: relative;
  align-self: start;
}
/* the site's signature skewed gold stroke, as the month rule */
.cal-mname::before {
  content: ''; position: absolute; left: 0; top: 0; width: 46px; height: 4px;
  background: var(--gold); transform: skewX(-30deg);
}
.cal-month.is-now .cal-mname { color: var(--ink); }
.cal-now {
  display: block; font-size: 10.5px; letter-spacing: .12em; font-weight: 700;
  color: var(--gold-deep); margin-top: 6px;
}

.cal-events { list-style: none; padding: 0; margin: 0; display: grid; gap: 2px;
  background: var(--line); border-top: 1px solid var(--line); }

.cev { background: var(--paper); padding: 24px 26px 26px; position: relative; }
.cev::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--gold);
}
/* Confirmed is the default stroke above. Named explicitly so a maintainer
   reading the three statuses finds three rules, not two and an inference. */
.cev.is-confirmed::before { width: 3px; }
.cev.is-observed::before { width: 1px; }
.cev.is-window::before {
  width: 3px; background: none;
  border-left: 3px dotted var(--gold); opacity: .75;
}

.cev-when {
  font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--gold-deep); margin-bottom: 8px;
}
.cev-title { font-size: 18px; font-weight: 700; margin: 0 0 8px; line-height: 1.3; }
.cev-what {
  font-size: 13.5px; color: var(--ink-soft); line-height: 1.7;
  max-width: 68ch; margin: 0 0 14px;
}

.cev-meta { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: baseline; margin: 0 0 16px; }
.cev-status {
  font-size: 10.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink); border-bottom: 2px solid var(--gold); padding-bottom: 2px;
  cursor: help;
}
.cev.is-observed .cev-status { border-bottom-width: 1px; }
.cev.is-window .cev-status { border-bottom-style: dotted; }
.cev-who { font-size: 12px; color: var(--ink-faint); }

.cev-acts { display: flex; flex-wrap: wrap; gap: 10px 20px; align-items: center; }
.cev-add {
  cursor: pointer; background: none; border: 1px solid var(--line);
  font-family: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  color: var(--ink); padding: 9px 16px; min-height: 36px;
  transition: border-color .25s, background .25s, color .25s;
}
.cev-add:hover { border-color: var(--navy); background: var(--navy); color: #fff; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cev-add:hover { color: var(--navy-deep); }
}
:root[data-theme="dark"] .cev-add:hover { color: var(--navy-deep); }
.cev-noics { font-size: 12px; color: var(--ink-faint); font-style: italic; }
.cev-link {
  font-size: 12.5px; font-weight: 700; color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--gold); padding-bottom: 2px;
}
.cev-link:hover { color: var(--gold-deep); }

.cal-empty { font-size: 14px; color: var(--ink-soft); line-height: 1.7; }
.cal-reset {
  cursor: pointer; background: none; border: 0; padding: 0;
  font-family: inherit; font-size: inherit; font-weight: 700; color: var(--ink);
  border-bottom: 1px solid var(--gold);
}

/* definition list used by the "how to read this" panel */
.deflist { margin: 0 0 34px; max-width: 76ch; }
.deflist dt {
  font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink); margin-top: 22px; padding-top: 14px; position: relative;
}
.deflist dt::before {
  content: ''; position: absolute; left: 0; top: 0; width: 28px; height: 3px;
  background: var(--gold); transform: skewX(-30deg);
}
.deflist dd {
  margin: 8px 0 0; font-size: 13.5px; color: var(--ink-soft); line-height: 1.75;
}

/* `--paper-warm` is exactly what `.section-warm` already paints, so a callout
   using it inside one was a box with no edges - invisible as a panel, which
   is the opposite of what a callout is for. Sit it on `--paper` and mark it
   with the site's skewed gold stroke rather than a frame, which the PR
   regulation rules out anyway. */
.callout {
  padding: 26px 28px; background: var(--paper); max-width: 76ch;
  position: relative; margin-top: 6px;
}
.callout::before {
  content: ''; position: absolute; left: 28px; top: 0; width: 52px; height: 4px;
  background: var(--gold); transform: skewX(-30deg);
}
.callout h3 { font-size: 15px; font-weight: 700; margin: 12px 0 10px; }
.callout p { font-size: 13.5px; color: var(--ink-soft); line-height: 1.75; margin: 0; }

@media (max-width: 860px) {
  .cal-month { grid-template-columns: 1fr; gap: 14px; }
  .cal-mname { padding-top: 18px; }
  .cal-now { display: inline; margin-left: 10px; }
}
@media (max-width: 520px) {
  .cev { padding: 20px 18px 22px; }
  .cev-title { font-size: 16.5px; }
}

/* ---------------------------------------------------------------------------
   PHOTOGRAPHY

   The regulation caps a view at three colours and asks for 60% white space, so
   photographs cannot simply be dropped in at full saturation and left to fight
   the palette. Two treatments, both deliberate:

   .photo-band   full-bleed, navy wash over the top, text sitting on it. The
                 wash is what keeps the view inside three colours and what
                 makes white text legible over an unpredictable image - the
                 contrast comes from the overlay, not from luck about how dark
                 the photograph happens to be.

   .figure       an editorial image at content width with a caption. Generous
                 margins, no frame - the regulation rules out solid rectangular
                 frames and connected-line borders, so the caption's skewed
                 gold stroke does the anchoring instead.

   The slow zoom on a band is scroll-driven (animation-timeline: view()), so it
   is tied to reading position rather than to a clock - it cannot loop, cannot
   distract, and costs nothing on browsers that do not support it, which simply
   show a still image.
   --------------------------------------------------------------------------- */

.photo-band {
  position: relative; isolation: isolate; overflow: hidden;
  padding: 120px 0; margin: 0;
}
.photo-band > .wrap { position: relative; z-index: 2; }
.photo-band picture,
.photo-band img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.photo-band::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(105deg,
    rgba(10, 22, 48, .93) 0%,
    rgba(10, 22, 48, .86) 42%,
    rgba(10, 22, 48, .62) 100%);
}
.photo-band h2 { color: #fff; font-size: clamp(1.625rem, 0.408rem + 2.89vw, 2.625rem); margin: 0 0 14px; max-width: 20ch; }
.photo-band p { color: rgba(255, 255, 255, .84); font-size: 15px; line-height: 1.75; max-width: 56ch; margin: 0; }
.photo-band .eyebrow { color: var(--gold); }
.photo-band .eyebrow::before { background: var(--gold); }
.photo-band-actions { margin-top: 26px; display: flex; flex-wrap: wrap; gap: 12px; }

/* Slow drift, tied to scroll position. Only runs where the browser supports a
   view timeline; everywhere else the image is simply still. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .photo-band img {
      animation: band-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}
@keyframes band-drift {
  from { transform: scale(1.12) translateY(-1.4%); }
  to   { transform: scale(1.02) translateY(1.4%); }
}

.figure { margin: 34px 0 0; }
.figure img {
  display: block; width: 100%; height: auto;
  filter: saturate(.92);
}
.figure figcaption {
  margin-top: 14px; padding-top: 12px; position: relative;
  font-size: 12.5px; color: var(--ink-soft); line-height: 1.7; max-width: 62ch;
}
.figure figcaption::before {
  content: ''; position: absolute; left: 0; top: 0; width: 30px; height: 3px;
  background: var(--gold); transform: skewX(-30deg);
}
/* --ink-faint measures 4.47:1 here in dark mode, just under AA's 4.5. Rather
   than nudge a token used in a dozen other places - and quietly change every
   one of those contrasts - this label takes --ink-soft, which clears AA with
   room to spare and gives the event name the presence it should have had. */
.figure-year {
  display: block; font-size: 10.5px; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-soft); margin-bottom: 4px;
}

/* Two figures side by side, collapsing to one column on a phone. */
.figure-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; margin-top: 34px; }
.figure-pair .figure { margin-top: 0; }
/* One photograph on its own sits at a readable measure rather than stretching
   the full content width - a lone image at 1200px reads as a banner, not as a
   figure, and the caption's line length goes with it. */
.figure-single { max-width: 760px; }

/* A figure inside a navy section needs light type on it. The APRM photograph
   sits inside history's dark record band, and the default caption colours -
   built for the light paper ground - measured 1.57:1 there. Gold on navy is
   6.7:1 and the softened white is 8.2:1, both comfortably past AA. */
.section-navy .figure figcaption { color: rgba(255, 255, 255, .82); }
.section-navy .figure-year { color: var(--gold); }
.section-navy .figure figcaption::before { background: var(--gold); }

@media (max-width: 760px) {
  .photo-band { padding: 84px 0; }
  .figure-pair { grid-template-columns: 1fr; gap: 30px; }
}

/* ---------------------------------------------------------------------------
   WAYFINDING  —  reading progress and the floating rail

   The rail is the one thing taken wholesale from Chinese sites: persistent,
   always-reachable jump-and-return, instead of scrolling back to the header to
   hunt for a link. What is NOT taken is their information density - the PR
   regulation caps a view at three colours and asks for 60% white space, so the
   rail stays to navy, gold and white, and stays out of the way until there is
   somewhere to go back to.

   Targets are 44px. WCAG 2.5.8 asks for 24; 44 is what a thumb actually wants.
   --------------------------------------------------------------------------- */

.read-bar {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 120;
  pointer-events: none; background: transparent;
}
.read-bar span {
  display: block; height: 100%; width: 100%;
  background: var(--gold); transform: scaleX(0); transform-origin: 0 50%;
  will-change: transform;
}

.wayfind {
  position: fixed; right: 22px; bottom: 26px; z-index: 118;
  display: flex; flex-direction: column; align-items: flex-end; gap: 14px;
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity .3s var(--ease-out), transform .3s var(--ease-out),
              visibility .3s;
}
.wayfind.is-on { opacity: 1; visibility: visible; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .wayfind { transition: opacity .001s, visibility .001s; transform: none; }
}

.wf-dots {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
}
/* The dot is a 44px hit area with a small visible mark inside it, so the
   target is comfortable without the rail looking heavy. */
.wf-dot {
  position: relative; display: flex; align-items: center; justify-content: flex-end;
  width: 44px; height: 30px; padding: 0 12px 0 0;
  background: none; border: 0; cursor: pointer; color: var(--ink-soft);
}
.wf-dot::after {
  content: ''; width: 16px; height: 3px; background: currentColor;
  opacity: .38; transform: skewX(-30deg);
  transition: width .25s var(--ease-out), opacity .25s, background-color .25s;
}
.wf-dot:hover::after, .wf-dot:focus-visible::after { opacity: .9; width: 22px; }
.wf-dot.is-here::after { background: var(--gold); opacity: 1; width: 26px; }

.wf-tip {
  position: absolute; right: 100%; margin-right: 8px; white-space: nowrap;
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  color: #fff; background: var(--band); padding: 6px 10px;
  opacity: 0; transform: translateX(6px); pointer-events: none;
  transition: opacity .2s var(--ease-out), transform .2s var(--ease-out);
}
.wf-dot:hover .wf-tip, .wf-dot:focus-visible .wf-tip {
  opacity: 1; transform: none;
}

.wf-top {
  width: 44px; height: 44px; display: grid; place-items: center;
  cursor: pointer; border: 0; background: var(--band); color: #fff;
  transition: background-color .25s, color .25s, transform .25s var(--ease-out);
}
.wf-top svg { width: 16px; height: 16px; }
.wf-top:hover { background: var(--gold); color: var(--navy-deep); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .wf-top:hover { transform: none; } }

/* On a phone the dot column competes with the content for the right edge, and
   there is not room for both. Keep the return-to-top, drop the jump list -
   the mobile menu already reaches every section. */
@media (max-width: 760px) {
  .wayfind { right: 14px; bottom: 16px; }
  .wf-dots { display: none; }
}

/* ---------------------------------------------------------------------------
   LINK PREVIEW  —  what is behind an internal link, before you spend a click

   Zhihu, Bilibili and Wikipedia all do a version of this. It stays inside the
   three-colour rule: paper, ink, one gold stroke. No frame, per the
   regulation - a shadow and the ground do the separating.
   --------------------------------------------------------------------------- */

.lnkprev {
  position: absolute; z-index: 130; width: 310px; max-width: calc(100vw - 24px);
  background: var(--paper); padding: 16px 18px 14px;
  box-shadow: 0 18px 44px rgba(10, 22, 48, .17), 0 2px 6px rgba(10, 22, 48, .08);
  opacity: 0; transform: translateY(4px);
  transition: opacity .18s var(--ease-out), transform .18s var(--ease-out);
  pointer-events: auto;
}
.lnkprev.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .lnkprev { transition: none; transform: none; }
}
/* the gold stroke, on the edge nearest the link it describes */
.lnkprev::before {
  content: ''; position: absolute; left: 18px; top: 0; width: 34px; height: 3px;
  background: var(--gold); transform: skewX(-30deg);
}
.lnkprev.is-above::before { top: auto; bottom: 0; }

.lnkprev-kind {
  display: block; font-size: 10px; font-weight: 800; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ink-faint); margin: 6px 0 5px;
}
.lnkprev-title {
  display: block; font-size: 15px; font-weight: 700; line-height: 1.3;
  color: var(--ink); margin-bottom: 7px;
}
.lnkprev-text {
  display: block; font-size: 12.5px; line-height: 1.65; color: var(--ink-soft);
}
.lnkprev-go {
  display: block; margin-top: 11px; font-size: 11px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--gold-deep);
}

/* ---------------------------------------------------------------------------
   VIEW TRANSITIONS  —  nineteen HTML files that behave like one application

   `@view-transition { navigation: auto }` hands the browser both documents
   during a same-origin navigation and lets it cross-fade between them. No
   framework, no router, no client-side rendering: the pages stay nineteen
   plain HTML files that work with JavaScript off, and the browser does the
   animation natively.

   Where a browser does not support it, nothing happens - the navigation is an
   ordinary one. That is the whole appeal: it is a progressive enhancement in
   the literal sense, not a polyfill with a fallback path to maintain.

   The named elements are what make it more than a cross-fade. The header, the
   brand and the footer are continuous across every page, so they are told to
   stay put rather than fade out and back in. A committee card on the index is
   given the same name as the committee page's own mark, so clicking it MOVES
   the mark to its new home instead of replacing one picture with another -
   the animation tells the reader where the thing they clicked went.

   Timings are short. 260ms is at the top of the range that still reads as
   instant; anything slower puts a wait between a click and a page.
   --------------------------------------------------------------------------- */

@view-transition { navigation: auto; }

::view-transition-group(*) { animation-duration: 260ms; }
::view-transition-old(root) {
  /* 140ms, down from 200: the outgoing page's text overlapped the incoming
     page's for most of its fade, which read as ghosting mid-transition. */
  animation: vt-out 140ms var(--ease) both;
}
::view-transition-new(root) {
  animation: vt-in 260ms var(--ease-out) both;
}
@keyframes vt-out { to { opacity: 0; transform: translateY(-6px); } }
@keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }

/* Continuous furniture: present on every page, so it should not flicker.
   Naming it keeps it out of the root cross-fade entirely. */
.nav { view-transition-name: site-nav; }
.brand { view-transition-name: site-brand; }
.footer { view-transition-name: site-footer; }
::view-transition-old(site-nav), ::view-transition-new(site-nav),
::view-transition-old(site-brand), ::view-transition-new(site-brand),
::view-transition-old(site-footer), ::view-transition-new(site-footer) {
  animation: none;
}

/* The reading bar and the rail are fixed furniture the transition should not
   drag across the screen. */
.read-bar, .wayfind { view-transition-name: none; }

/* A committee mark travels from the card to the page it opens. `.cc-mark` on
   committees.html and `.sc-hero-mark` on the committee page carry the SAME
   generated name, so the browser matches them and moves one to the other. */
::view-transition-group(.sc-mark) { animation-duration: 320ms; }

/* Motion is the whole point, so honour the request to have none of it: the
   navigation still works, it simply cuts. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* ===========================================================================
   MOTION SYSTEM
   ===========================================================================

   Grounded in the interaction literature rather than taste. The findings that
   actually changed decisions here:

   Sundar et al. (2014), comparing six on-screen techniques head to head, found
   SLIDE best for memory and MOUSEOVER best for engagement, while ZOOM was the
   least favoured and DRAG had negative effects. So this site leans on hover
   and slide and does not use drag or zoom as primary interactions - the rail
   scroll-snaps, it does not drag.

   Ma et al. (2021, 2024) and O'Brien & Toms (2008) converge on PERCEIVED
   CONTROL as a mediator of engagement, alongside immediate feedback. Boyd et
   al. (2021) add that acknowledging input instantly, with motion that behaves
   like a physical object, raises perceived usability. Hence: every control
   answers on press, within one frame, with a small real displacement.

   One motion vocabulary, used everywhere. Ma et al. (2021) found that
   continuity and consistency of the interface's motion narrative contribute
   to a coherent interface; a site where each component invents its own timing
   reads as several sites.
   --------------------------------------------------------------------------- */

:root {
  --m-instant: 90ms;    /* input acknowledgement - must beat the next frame */
  --m-quick:   180ms;   /* hover, small state changes */
  --m-base:    260ms;   /* panels, transitions between views */
  --m-slow:    420ms;   /* entrances, things arriving from off-screen */
  --m-spring:  cubic-bezier(.2, .9, .3, 1.2);   /* slight overshoot: physical */
  --m-pop:     cubic-bezier(.2, 1.5, .3, 1);     /* pronounced overshoot: things ARRIVING
                  (a dialog, a card lifting, the opener's digits). Merged here
                  2026-09-23 from a duplicate --ease-pop defined elsewhere. */
  --m-exit:    cubic-bezier(.4, 0, 1, 1);
}
/* Real springs where the browser can draw them: a cubic-bezier can overshoot
   once but cannot settle, a sampled linear() curve does (settles like an
   object, Boyd et al. 2021). Values from a damped spring, sampled at 30 points. */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    --m-spring: linear(0, .009, .035 2.1%, .141 4.4%, .723 12.9%, .938 16.7%, 1.017 19.4%, 1.067, 1.099 24.3%, 1.104 26.1%, 1.098 28.1%, 1.061 33.6%, 1.019 39.2%, .997 45.4%, .992 51.4%, 1 69.5%, 1);
    --m-pop: linear(0, .013, .05 2.3%, .2 4.9%, .854 13.8%, 1.06 18.4%, 1.15 22.2%, 1.17 25.2%, 1.15 28.6%, 1.04 37.2%, .99 44.3%, .985 50.7%, 1.003 66.3%, 1);
  }
}

/* ---- immediate acknowledgement of a press -------------------------------
   Every interactive control moves when pressed. Not decoration: this is the
   "acknowledge the input instantly" finding, and it is the cheapest possible
   signal that the interface heard you. */
.btn, .cev-add, .cal-filter, .art-f, .wf-top, .nav-theme, .nav-search,
.nav-lang, button.chan, .cc {
  transition: transform var(--m-instant) var(--m-exit),
              background-color var(--m-quick), color var(--m-quick),
              border-color var(--m-quick), box-shadow var(--m-quick);
}
.btn:active, .cev-add:active, .cal-filter:active, .art-f:active,
.wf-top:active, .nav-theme:active, .nav-search:active, .nav-lang:active {
  transform: translateY(1px) scale(.985);
  transition-duration: var(--m-instant);
}

/* ---- loading, with motion rather than a full stop -----------------------
   Wang et al. (2025) and Hohenstein et al. (2016) both find that a DYNAMIC
   indicator shortens perceived wait against a static one - Wang traces the
   mechanism to reduced temporal attention, which is to say that a moving
   thing stops you counting. Every fetch on this site previously announced
   itself with the static words "Loading…", which is the worst case in that
   literature: it draws attention to the wait and then does nothing.

   A skeleton also sets the shape of what is coming, so the layout does not
   jump when it lands. */
.is-loading { position: relative; min-height: 84px; }
.skeleton {
  --sk: color-mix(in srgb, var(--ink) 8%, transparent);
  background: var(--sk);
  border-radius: 2px; overflow: hidden; position: relative;
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--ink) 5%, transparent) 50%,
    transparent 100%);
  transform: translateX(-100%);
  animation: sk-sweep 1.25s ease-in-out infinite;
}
@keyframes sk-sweep { to { transform: translateX(100%); } }

.sk-row { height: 13px; margin-bottom: 10px; }
.sk-row.is-title { height: 19px; width: 46%; margin-bottom: 16px; }
.sk-row.is-short { width: 62%; }
.sk-card { padding: 24px 26px 26px; background: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; }
  .btn:active, .cev-add:active, .cal-filter:active, .art-f:active,
  .wf-top:active, .nav-theme:active, .nav-search:active, .nav-lang:active {
    transform: none;
  }
}

/* ---- pointer-reactive surfaces ------------------------------------------
   A committee card lifts toward the pointer and lights from where the pointer
   is. The variables are written by delight.js; with no JS the card still has
   its ordinary hover state, because the gradient simply has nowhere to centre
   and falls back to the card's own colour. */
.cc, .dest, .cev, .rcard {
  position: relative;
}
.cc::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  opacity: 0; transition: opacity var(--m-quick);
  background: radial-gradient(
    420px circle at var(--px, 50%) var(--py, 50%),
    color-mix(in srgb, var(--c, var(--navy)) 13%, transparent) 0%,
    transparent 62%);
}
.cc:hover::after, .cc:focus-visible::after { opacity: 1; }

/* The same idea on a dark band, where it reads as light rather than tint. */
.photo-band, .cta-band, .section-navy { position: relative; }
.spot::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: radial-gradient(
    560px circle at var(--px, 50%) var(--py, 50%),
    rgba(229, 193, 88, .10) 0%, transparent 60%);
  opacity: 0; transition: opacity var(--m-base);
}
.spot.is-lit::before { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .cc::after, .spot::before { display: none; }
}

/* ---- toast, and the copy-address control --------------------------------
   Polite, not modal: it announces through a live region, takes no focus, and
   leaves on its own. Bottom-left, so it never covers the wayfinding rail. */
.toasts {
  position: fixed; left: 22px; bottom: 26px; z-index: 140;
  display: flex; flex-direction: column-reverse; gap: 8px;
  pointer-events: none;
}
.toast {
  display: block; background: var(--band); color: #fff;
  font-size: 13px; font-weight: 600; letter-spacing: .01em;
  padding: 11px 16px; max-width: min(78vw, 380px);
  opacity: 0; transform: translateY(8px);
  transition: opacity var(--m-base) var(--m-spring),
              transform var(--m-base) var(--m-spring);
}
.toast.is-on { opacity: 1; transform: none; }
.toast::before {
  content: ''; display: inline-block; width: 16px; height: 3px;
  background: var(--gold); transform: skewX(-30deg);
  margin-right: 10px; vertical-align: middle;
}
@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity .001s; transform: none; }
}
@media (max-width: 620px) {
  .toasts { left: 14px; right: 14px; bottom: 76px; }
}

.copy-addr {
  display: inline-grid; place-items: center; width: 26px; height: 26px;
  margin-left: 6px; vertical-align: -6px;
  background: none; border: 0; cursor: pointer; color: var(--ink-faint);
  transition: color var(--m-quick), transform var(--m-instant) var(--m-exit);
}
.copy-addr svg { width: 14px; height: 14px; }
.copy-addr:hover, .copy-addr:focus-visible { color: var(--ink); }
.copy-addr:active { transform: scale(.9); }
.copy-addr.is-done { color: var(--gold-deep); }

/* ===========================================================================
   PRINT

   Two of these pages are genuinely printed. A Project Director works through
   the proposal checklist away from the screen, and somebody pins the calendar
   up. Printed as-is they carried the navigation, the floating rail, the
   reading bar, the search button and the whole footer across the first page -
   and set white text on backgrounds that print as nothing at all.

   The rule that matters: a printed page has no interaction, so anything whose
   only job is interaction should not be on it. What survives is the content
   and enough identity to know where it came from.
   =========================================================================== */

@media print {
  /* Interaction furniture has no meaning on paper. */
  .nav, .footer, .wayfind, .read-bar, .toasts, .lnkprev,
  .nav-toggle, .nav-search, .nav-theme, .nav-lang, .skip-link,
  .cta-band, .cta-actions, .pc-actions, .cal-filters, .art-filters,
  .anniv, .copy-addr, .cev-add, .cev-link, .search-overlay {
    display: none !important;
  }

  /* Reveal animations never run, because nothing scrolls. Without this, every
     element the reader had not yet scrolled past printed as blank space -
     which is the same trap that made the screenshot tool lie. */
  .reveal, .reveal.in {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    transition: none !important;
  }

  :root {
    --paper: #fff; --paper-warm: #fff; --ink: #111; --ink-soft: #333;
    --ink-faint: #555; --line: #ccc; --band: #fff; --navy: #183868;
  }

  html, body { background: #fff !important; color: #000 !important; }

  /* A dark band prints as a dark rectangle in most browsers and as nothing in
     the rest. Either way the white text on it is lost, so invert them. */
  .section-navy, .photo-band, .hero-record, .band {
    background: #fff !important; color: #000 !important;
    padding: 0 !important;
  }
  .section-navy *, .photo-band *, .hero-record * { color: #000 !important; }
  .photo-band picture, .photo-band img, .photo-band::after { display: none !important; }

  .wrap { max-width: none; padding: 0; }
  .section { padding: 18pt 0; }
  .page-head { padding: 0 0 12pt; }

  body { font-size: 10.5pt; line-height: 1.45; }
  h1 { font-size: 20pt; }
  h2 { font-size: 14pt; }
  h3 { font-size: 11.5pt; }

  /* Keep a heading with what it introduces, and never split a checklist item
     or an event across a page break. */
  h1, h2, h3 { break-after: avoid; page-break-after: avoid; }
  li, .cev, .proj, .figure, .callout, .deflist dd {
    break-inside: avoid; page-break-inside: avoid;
  }

  /* Print the destination of a link, once, so a printed page is still
     navigable by someone sitting with it and a browser. Skip fragments and
     anything already showing its own address. */
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 8.5pt; color: #555; word-break: break-all;
  }
  a[href^="mailto:"]::after { content: ""; }

  .figure img { max-height: 6cm; width: auto; }

  /* The paste-boxes are a counting tool: you put text in and the browser
     counts the words. On paper they are empty rectangles carrying the
     instruction "Paste this section to count it", which is both useless and
     expensive - hiding them takes the printed checklist from six pages to
     three. The checklist ITEMS are what is worth carrying away. */
  textarea, .pc-count, .pc-bar, input[type="search"] { display: none !important; }

  /* Say where the sheet came from, since the footer is gone. */
  main::after {
    content: "IFMSA-Thailand · ifmsa-thailand.pages.dev";
    display: block; margin-top: 16pt; padding-top: 8pt;
    border-top: 1px solid #ccc;
    font-size: 8.5pt; color: #555;
  }

  @page { margin: 16mm 14mm; }
}

/* ---------------------------------------------------------------------------
   CONTRAST CORRECTIONS  —  found by axe-core, not by eye

   Eight real failures that a hand-rolled contrast check had missed for weeks.
   Each fix moves the TEXT, never the official committee colour: the palette
   is set by the organisation's regulation and is not ours to adjust for
   convenience.
   --------------------------------------------------------------------------- */

/* The award numerals were --gold on white paper: 1.73:1, which fails even the
   relaxed 3:1 threshold that large text is allowed. Gold reads beautifully on
   navy and is nearly invisible on white. Dark mode keeps the gold, because
   there it is gold on near-black and measures comfortably. */
.award-place { color: var(--gold-deep); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .award-place { color: var(--gold); }
}
:root[data-theme="dark"] .award-place { color: var(--gold); }

/* Softened white over a committee band. At 80% the type blends toward the
   band colour, and on SCOPH's orange - the lightest of the seven - that fell
   to 3.29:1. Full white on the same ground is 4.88:1. The softening bought
   nothing that mattered and cost the reading. */
.sc-hero-sub { color: #fff; }
.sc-hero .eyebrow { color: #fff; }

/* ===========================================================================
   EXECUTIVE BOARD  —  about.html, generated by tools/build_eb.py

   A roster before its reveal. Each seat is a small dark stage lit from behind,
   with the figure in near-black and a gold rim light along its edge - which
   is what makes a silhouette read as a deliberate portrait rather than a
   missing one. Navy, gold and near-black navy: inside the three-colour rule.

   The stage light follows the pointer (delight.js writes --px/--py), so the
   board responds to being looked at. Mouseover is the interaction the
   engagement literature favours (Sundar et al. 2014); the whole card is one
   button, and the dialog behind it is the depth.
   =========================================================================== */

.eb-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  gap: 26px 20px;
}
.eb-card {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  text-align: left; color: inherit; font: inherit; cursor: pointer;
}
.eb-card:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }

/* Named seats are cards; seats with nobody named yet are a compact roster
   beside them (tools/build_eb.py). Top rules only - no boxes. */
.eb-board { display: grid; grid-template-columns: minmax(180px, 230px) 1fr; gap: 26px 48px; align-items: start; }
.eb-board > .eb-grid { grid-template-columns: 1fr; }
@media (max-width: 760px) { .eb-board { grid-template-columns: 1fr; } .eb-board > .eb-grid { grid-template-columns: repeat(2, 1fr); } }
.eb-roster { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 34px; }
@media (max-width: 560px) { .eb-roster { grid-template-columns: 1fr; } }
.eb-rowbtn {
  display: grid; grid-template-columns: 78px 1fr; gap: 2px 12px; width: 100%;
  padding: 14px 0; border: 0; border-top: 1px solid var(--line); background: none;
  text-align: left; color: inherit; font: inherit; cursor: pointer;
  transition: border-color var(--m-quick, .18s);
}
.eb-rowbtn:hover { border-top-color: var(--gold); }
.eb-rowbtn .eb-code { grid-row: 1 / span 2; font-weight: 800; color: var(--navy); font-size: 13px; letter-spacing: .02em; }
.eb-row-title { font-size: 13.5px; font-weight: 700; color: var(--ink); line-height: 1.35; }
.eb-row-mail { font-size: 12px; color: var(--ink-faint); }
:root[data-theme="dark"] .eb-rowbtn .eb-code { color: var(--gold); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .eb-rowbtn .eb-code { color: var(--gold); } }
.eb-modal-text-only { grid-template-columns: 1fr !important; }

.eb-portrait {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 4 / 5;
  background:
    radial-gradient(120% 90% at var(--px, 50%) var(--py, 38%),
      #33568f 0%, #1c3564 38%, #0b1730 78%);
  transition: filter var(--m-base) var(--m-exit);
}
/* the site's gold skew stroke, in the corner of every stage */
.eb-portrait::before {
  content: ''; position: absolute; left: 14px; top: 16px; z-index: 2;
  width: 30px; height: 3px; background: var(--gold); transform: skewX(-30deg);
}
.eb-sil {
  position: absolute; left: 50%; bottom: 0; width: 88%; height: auto;
  transform: translateX(-50%); fill: #060c18;
  filter:
    drop-shadow(-1.5px -1px 0 rgba(229, 193, 88, .55))
    drop-shadow(1.5px -1px 7px rgba(229, 193, 88, .22));
  transition: transform var(--m-base) var(--m-spring), filter var(--m-base);
}
.eb-photo {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--m-slow) var(--m-exit);
}

.eb-card:hover .eb-portrait, .eb-card:focus-visible .eb-portrait { filter: brightness(1.18); }
.eb-card:hover .eb-sil, .eb-card:focus-visible .eb-sil {
  transform: translateX(-50%) translateY(-5px);
  filter:
    drop-shadow(-1.5px -1px 0 rgba(229, 193, 88, .9))
    drop-shadow(2px -2px 12px rgba(229, 193, 88, .38));
}
.eb-card:hover .eb-photo { transform: scale(1.04); }

.eb-code {
  display: block; margin-top: 14px;
  font-size: 19px; font-weight: 800; letter-spacing: .02em; color: var(--ink);
}
.eb-name { display: block; margin-top: 3px; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.eb-name.is-tba { font-weight: 500; font-style: italic; color: var(--ink-soft); }
.eb-area {
  display: block; margin-top: 7px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
.eb-note { margin-top: 28px; font-size: 12.5px; color: var(--ink-soft); max-width: 70ch; }

/* ---- the dialog ---- */
.eb-modal { display: grid; grid-template-columns: 190px 1fr; gap: 28px; align-items: start; }
.eb-modal-portrait {
  position: relative; overflow: hidden; aspect-ratio: 4 / 5;
  background: radial-gradient(120% 90% at 50% 38%, #33568f 0%, #1c3564 38%, #0b1730 78%);
}
.eb-modal-portrait .eb-sil { width: 90%; }
.eb-modal-text h2 { font-size: clamp(1.25rem, 0.288rem + 2.04vw, 1.625rem); margin: 6px 0 8px; }
.eb-modal-name { font-size: 15px; font-weight: 700; margin: 0 0 12px; }
.eb-modal-summary { font-size: 14px; line-height: 1.7; color: var(--ink-soft); margin: 0 0 20px; }

@media (max-width: 620px) {
  .eb-grid { grid-template-columns: repeat(2, 1fr); gap: 22px 14px; }
  .eb-code { font-size: 16px; }
  .eb-modal { grid-template-columns: 1fr; }
  .eb-modal-portrait { max-width: 200px; }
}
@media (prefers-reduced-motion: reduce) {
  .eb-sil, .eb-photo, .eb-portrait { transition: none; }
  .eb-card:hover .eb-sil, .eb-card:focus-visible .eb-sil { transform: translateX(-50%); }
  .eb-card:hover .eb-photo { transform: none; }
}
@media print {
  .eb-portrait, .eb-modal-portrait { display: none !important; }
}

/* ==================================================================
   MOTION THAT EXPLAINS  (2026-09-23)
   Each rule below says what it tells the reader. Everything new to
   browsers sits inside @supports; everything is stilled for readers
   who ask for less motion (the block at the end).
   ================================================================== */

/* The spectrum assembles: seven committees arriving as one federation.
   Rows enter in sequence and each colour bar grows into place. Starts at
   first paint, depends on no script, and ends visible - the same rule as
   the reveal above: nothing a reader can see waits on JavaScript. From
   opacity .01, not 0, so it counts for Largest Contentful Paint at once. */
@keyframes spec-in  { from { opacity: .01; transform: translateX(-14px); } }
@keyframes spec-bar { from { transform: scaleY(0); } }
.spec-row { animation: spec-in .55s var(--m-pop) backwards; }
.spec-row::before { transform-origin: top; animation: spec-bar .5s var(--ease-out) backwards; }
.spec-row:nth-child(1), .spec-row:nth-child(1)::before { animation-delay: .12s; }
.spec-row:nth-child(2), .spec-row:nth-child(2)::before { animation-delay: .18s; }
.spec-row:nth-child(3), .spec-row:nth-child(3)::before { animation-delay: .24s; }
.spec-row:nth-child(4), .spec-row:nth-child(4)::before { animation-delay: .30s; }
.spec-row:nth-child(5), .spec-row:nth-child(5)::before { animation-delay: .36s; }
.spec-row:nth-child(6), .spec-row:nth-child(6)::before { animation-delay: .42s; }
.spec-row:nth-child(7), .spec-row:nth-child(7)::before { animation-delay: .48s; }

/* The committee's colour carries you in. A committee card (committees.html)
   or spectrum row (homepage) and that committee's page header share one
   view-transition name, band-<slug>, so on navigation the browser grows the
   card into the header instead of swapping pages: you went INTO SCOPH, you
   did not load a new site. The names are written by tools/build_committees.py
   (cards, header) and in index.html (the seven rows) - one per page, never
   twice, or the browser abandons the transition. */
.cc, .spec-row, .sc-hero { view-transition-class: band; }
::view-transition-group(*.band) {
  animation-duration: var(--m-slow);
  animation-timing-function: cubic-bezier(.2, .8, .2, 1);
}

/* The kanok stroke draws itself. The skewed gold stroke is the site's
   signature; each one draws across as its section enters. Driven by a
   registered property so the stroke is FULL wherever scroll timelines are
   not supported, and full for any eyebrow already on screen at load. */
@property --stroke { syntax: '<number>'; inherits: true; initial-value: 1; }
.eyebrow::before { transform: skewX(-30deg) scaleX(var(--stroke)); transform-origin: left center; }
@supports (animation-timeline: view()) {
  /* Timed to the SECTION, not the eyebrow. An eyebrow is ~14px tall, so a
     timeline on its own box finished drawing within a few pixels of scroll:
     attached, correct, and invisible. The section's entry phase is a real
     stretch of scrolling. Named on every container an eyebrow sits in; an
     eyebrow uses the nearest one. */
  .section, .page-head, .sc-hero, .hero, .cta-band { view-timeline: --sec block; }
  @keyframes stroke-draw { from { --stroke: 0; } to { --stroke: 1; } }
  .eyebrow {
    animation: stroke-draw linear both;
    animation-timeline: --sec;
    animation-range: entry 15% entry 55%;
  }

  /* Photographs are places, not stickers: the image drifts a little slower
     than the page as the band crosses the screen. At most 26px, and scaled
     up 8% so no edge ever shows. */
  @keyframes band-drift {
    from { transform: translateY(-26px) scale(1.08); }
    to   { transform: translateY(26px)  scale(1.08); }
  }
  /* A NAMED timeline on the band, not view() on the image. .photo-band has
     overflow: hidden, which makes it a scroll container of its own - so
     view() on the image measured its place inside a box it never moves in,
     and the drift sat frozen at its midpoint (caught 2026-09-23 by reading
     the computed transform at two scroll positions: identical). The band's
     own timeline tracks the page's scroll, which is what was meant. */
  .photo-band { view-timeline: --band block; }
  .photo-band img {
    animation: band-drift linear both;
    animation-timeline: --band;
    animation-range: cover;
  }
}

@media (prefers-reduced-motion: reduce) {
  .spec-row, .spec-row::before, .eyebrow, .photo-band img { animation: none !important; }
  .eyebrow::before { transform: skewX(-30deg); }
  ::view-transition-group(*.band) { animation-duration: 0s; }
}

/* ===========================================================================
   NO BOXED GRIDS (design council batch 2, 2026-10-01)
   These grids drew their dividers by painting the container --line and
   letting a 2px gap show through. The UX audit found three costs: an empty
   last cell shows as a solid grey block, the grey flashes while items fade
   in, and the result is a frame of boxes, which the PR regulation rules out.
   Now each item carries one top rule instead, the way the committees index
   and the steps already do. Overriding here, in one place, so it can be
   reverted as one block. The calendar keeps its own layout: its items carry a
   status stroke on the left that needs the padding.
   =========================================================================== */
.cal-events { background: none; gap: 0; }
.cal-events > .cev { background: none; border-top: 1px solid var(--line); }
.proj-grid, .dest-grid, .exchange-grid, .soc-grid, .award-grid, .value-row,
.chan-grid, .route-grid, .nf-grid, .art-list, .opp-list, .trk-list {
  background: none; border: 0; gap: 8px 40px;
}
.proj-grid > *, .dest-grid > *, .exchange-grid > *, .soc-grid > *, .award-grid > *, .value-row > *,
.chan-grid > *, .route-grid > *, .nf-grid > *, .art-list > *, .opp-list > *, .trk-list > * {
  background: none; border: 0; border-top: 2px solid var(--line);
  padding-left: 0; padding-right: 0;
  transition: border-color var(--m-quick, .18s);
}
.proj-grid > :hover, .dest-grid > :hover, .chan-grid > :hover, .route-grid > :hover, .nf-grid > :hover {
  border-top-color: var(--c, var(--gold));
}
/* the project accent bar moves from the left edge to the top rule */
.proj::after { top: -2px; bottom: auto; left: 0; right: 0; width: auto; height: 2px; }

/* Exchange on a phone: each destination shows country, places and programme,
   and its full conditions open in the dialog it already has. The audit
   measured the page at 21 phone screens with every fact printed inline.
   Cards without a dialog keep their facts, so nothing becomes unreachable. */
@media (max-width: 640px) {
  .dest:has(.dest-more) .dest-facts,
  .dest:has(.dest-more) .dest-req { display: none; }
  .dest-grid { grid-template-columns: 1fr 1fr; gap: 4px 20px; }
  .dest .dest-head h3 { font-size: 15px; }
}
/* destination cards keep their committee-colour edge, so they keep the inset beside it */
.dest-grid > .dest { padding-left: 14px; padding-right: 8px; }

/* the wordmark never breaks across two lines */
.brand-text, .nav-verify { white-space: nowrap; }
.dest { min-width: 0; }
@media (max-width: 380px) { .dest-grid { grid-template-columns: 1fr; } }

/* ---- Moments (assets/js/seasons.js): one quiet greeting line under the header.
   Two colours only (navy ink, gold mark) on the page's own ground; no motion. */
.moment {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 9px 44px 9px 16px; position: relative;
  background: var(--paper-warm); border-bottom: 1px solid var(--line);
  font-size: 14px; color: var(--navy); text-align: center;
}
.moment-mark { flex: none; fill: none; stroke: var(--gold-deep); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.moment-close {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; border: 0; background: none; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--ink-soft); border-radius: 50%;
}
.moment-close:hover { color: var(--navy); background: var(--line); }
.moment-close:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
