/* ==========================================================================
   RHIC 2026 — Design System · "The Thread"
   Layers: fonts → tokens → base → utilities → buttons → header → hero →
           sections → embeds → coming-soon → footer → motion/reduced-motion
   ========================================================================== */

/* 1. FONTS ---------------------------------------------------------------- */
@font-face {
  font-family: "Archivo";
  src: url("../fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* 2. TOKENS --------------------------------------------------------------- */
:root {
  --navy-900: #081a2b;
  --navy-800: #0d2c49;
  --navy-700: #124166;
  --blue-500: #227ec2;
  --teal-600: #1e9789;
  --teal-500: #23ad9e;
  --teal-300: #7ed9cd;
  --surface-0: #ffffff;
  --surface-1: #f4f8fa;
  --sand-50: #faf7f2;
  --ink-900: #0e2233;
  --ink-600: #3e5468;
  --ink-400: #6b7e8f;
  --border: #dce6ee;
  --border-dark: rgba(255, 255, 255, 0.14);
  --gradient-brand: linear-gradient(135deg, #124166, #1b6e96, #23ad9e);
  --overlay: rgba(8, 26, 43, 0.72);
  --focus-ring: 0 0 0 3px rgba(35, 173, 158, 0.4);

  /* Shape of the RHIC mark, traced from the supplied logo artwork: the angled
     top-right corner, the teal tab on the left, and the notch the thread runs
     through. Defined once here because it is used in several places and they
     must never drift apart.

     The previous decagon facet is kept in backup/hero-facet-decagon/ — see the
     README there to revert. */
  --logo-clip: polygon(
    69.4% 0.0%, 85.3% 21.7%, 85.9% 25.8%, 99.4% 43.3%, 99.4% 96.7%,
    98.2% 99.2%, 61.2% 99.2%, 34.1% 97.5%, 20.6% 78.3%, 9.4% 77.5%,
    0.6% 65.0%, 0.6% 52.5%, 22.4% 51.7%, 18.8% 46.7%, 0.6% 45.8%,
    1.2% 27.5%, 17.1% 26.7%, 17.1% 2.5%, 18.8% 0.0%
  );
  /* The artwork's own proportions. The hero media follows this so the mark is
     never stretched. */
  --logo-aspect: 170 / 120;

  /* Form validation. A muted brick rather than a pure red: it has to sit
     beside the teal accent without shouting over it. */
  --af-bad: #b5452f;
  --af-bad-bg: rgba(181, 69, 47, 0.07);
  --af-bad-ink: #8c3423;

  --shadow-1: 0 1px 2px rgba(8, 26, 43, 0.06);
  --shadow-2: 0 8px 24px rgba(8, 26, 43, 0.08);
  --shadow-3: 0 20px 48px rgba(8, 26, 43, 0.12);

  --r-sm: 8px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-pill: 999px;

  --font-display: "Archivo", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;

  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 0.2s;
  --t-med: 0.3s;

  --container: 1240px;
  --gutter: 32px;
  --section-y: clamp(64px, 9vw, 120px);
  --header-h: 76px;
  --announce-h: 40px;
  /* What the sticky header occupies once the announce bar has scrolled away:
     the brand row plus the nav row on desktop, the brand row alone below
     1024px where the nav collapses into a drawer. Anything sized to "one
     screenful" must subtract this, not --header-h. */
  --header-pinned: 128px;
}
/* Below the drawer breakpoint the nav row is display:contents, so only the
   brand row stays pinned. */
@media (max-width: 1023px) { :root { --gutter: 24px; --header-pinned: 64px; } }
@media (max-width: 767px)  { :root { --gutter: 20px; --header-h: 64px; } }

/* 3. BASE ------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
/* Decorative layers are deliberately oversized and offset (hero facets, page-hero
   deco, the pillars branch), and GSAP's pin-spacer wraps pinned sections at their
   own measured width. Any of those can round a fraction of a pixel past the
   viewport and raise a horizontal scrollbar across the whole page.
   `clip` rather than `hidden`: hidden makes the element a scroll container, which
   breaks the sticky site header. clip contains the overflow without that side
   effect, and the fallback keeps older browsers on the current behaviour rather
   than silently breaking the header. */
@supports (overflow-x: clip) {
  html { overflow-x: clip; }
}
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink-900);
  background: var(--surface-0);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  color: inherit;
  line-height: 1.12;
  margin: 0 0 0.5em;
  font-weight: 700;
  text-wrap: balance;
}
p { margin: 0 0 1em; }
img, video, svg { max-width: 100%; height: auto; }
a { color: var(--blue-500); text-decoration: none; transition: color var(--t-fast) var(--ease); }
a:hover { color: var(--teal-600); }
ul, ol { list-style: none; margin: 0; padding: 0; }
button { font: inherit; cursor: pointer; background: none; border: 0; color: inherit; }
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 4px; }
::selection { background: var(--teal-500); color: var(--navy-900); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--teal-500); color: var(--navy-900);
  padding: 12px 20px; border-radius: 0 0 var(--r-sm) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* 4. UTILITIES ------------------------------------------------------------- */
.container { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section-y); }
/* Adjacent sections stack a full pad each (~240px at desktop), which reads as
   dead air. Trim the seam so the boundary keeps a clear but not cavernous
   break; a background change still separates the two visually. */
.section + .section,
.section + .submission-categories,
.section + .submission-guidelines,
.submission-categories + .section,
.submission-guidelines + .section,
.submission-categories + .submission-guidelines,
.submission-guidelines + .submission-categories { padding-top: calc(var(--section-y) * 0.62); }
.section--dark { background: var(--navy-900); color: #fff; }
.section--dark .section-lede { color: rgba(255, 255, 255, 0.78); }
.section--tint { background: var(--surface-1); }
.section--sand { background: var(--sand-50); }

.section-tag {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-size: 0.8125rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--teal-600);
  margin-bottom: 14px;
}
.section-tag::before { content: ""; width: 28px; height: 2px; background: var(--teal-500); border-radius: 2px; }
.section--dark .section-tag { color: var(--teal-300); }
.section-title { font-size: clamp(1.9rem, 3.5vw, 2.75rem); font-stretch: 110%; letter-spacing: -0.01em; }
.section-lede { font-size: 1.125rem; color: var(--ink-600); max-width: 46em; }
.section-head { margin-bottom: clamp(32px, 5vw, 56px); }
.section-head--center { text-align: center; }
.section-head--center .section-lede { margin-inline: auto; }

/* Reveal system (IO adds .is-in; stagger via --d set by JS) */
.js .reveal { opacity: 0; transform: translateY(24px);
  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
  transition-delay: var(--d, 0s); }
.js .reveal.is-in { opacity: 1; transform: none; }

/* 5. BUTTONS --------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.9375rem;
  letter-spacing: 0.02em; line-height: 1;
  padding: 16px 30px; border-radius: var(--r-pill);
  border: 1.5px solid transparent; text-decoration: none;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease);
  white-space: nowrap;
}
.btn:focus-visible { box-shadow: var(--focus-ring); }
.btn--accent { background: var(--teal-500); color: var(--navy-900); }
.btn--accent:hover { background: var(--teal-600); color: var(--navy-900); transform: translateY(-1px); }
.btn--accent:active { transform: none; background: #17847a; }
.btn--primary { background: var(--navy-700); color: #fff; }
.btn--primary:hover { background: #0d3350; color: #fff; transform: translateY(-1px); }
.btn--primary:active { transform: none; background: #0a2a42; }
.btn--ghost { border-color: rgba(255, 255, 255, 0.4); color: #fff; }
.btn--ghost:hover { background: rgba(255, 255, 255, 0.1); border-color: var(--teal-300); color: #fff; }
.btn--outline { border-color: var(--navy-700); color: var(--navy-700); }
.btn--outline:hover { background: var(--navy-700); color: #fff; }
.btn--sm { padding: 11px 22px; font-size: 0.875rem; }
/* Touch devices: keep small buttons at the 44px minimum tap target. */
@media (pointer: coarse) {
  .btn--sm { min-height: 44px; }
}
.btn[disabled], .btn.is-disabled { opacity: 0.45; pointer-events: none; }
@media (max-width: 560px) {
  .btn { white-space: normal; }
  .hero__cta .btn, .band__cta .btn, .cd-band .btn { width: 100%; }
}
.btn .btn__arrow { transition: transform var(--t-fast) var(--ease); }
.btn:hover .btn__arrow { transform: translateX(3px); }

/* 6. ANNOUNCEMENT BAR ------------------------------------------------------ */
.announce {
  background: var(--navy-700); color: rgba(255, 255, 255, 0.88);
  font-size: 0.8125rem; letter-spacing: 0.04em;
  text-align: center; padding: 10px var(--gutter);
  /* The item is nowrap, so at any width where the marquee is off but the line
     still does not fit it would push the whole page sideways. Two halves to
     the fix: clip here so no viewport is ever left without containment, and
     run the marquee below the width where the line fits (see the media query
     further down). Re-measure that boundary whenever this text changes — it
     dropped from 859px to 519px when the patronage clause was removed. */
  overflow: hidden;
}
.announce b { color: #fff; font-weight: 600; }
.announce__item { white-space: nowrap; }
/* Desktop: one centred line, the duplicate is not needed. */
.announce__item--dup { display: none; }

@media (max-width: 519px) {
  .announce {
    font-size: 0.75rem; padding-inline: 0;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  }
  .announce__track {
    display: flex; width: max-content; max-width: none; padding: 0;
    animation: announce-scroll 26s linear infinite;
  }
  .announce__item { padding-inline: 1.6em; }
  .announce__item--dup { display: inline; }
  /* Half the distance, because the content is two identical copies — the loop
     restarts exactly where the first copy began, so there is no jump. */
  @keyframes announce-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
  }
  /* Pause on touch, so anyone reading it can stop it. */
  .announce:active .announce__track { animation-play-state: paused; }
}

@media (prefers-reduced-motion: reduce) {
  .announce__track { animation: none; }
  /* Static and readable rather than a clipped, frozen marquee. */
  .announce__item--dup { display: none; }
  @media (max-width: 519px) {
    .announce { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
    .announce__track { width: auto; }
  }
}

/* 7. HEADER / NAV ----------------------------------------------------------

   The header is light so the original colour logo reads on it — the artwork is
   navy and teal, and was invisible on the old dark bar. Everything inside is
   driven by these three tokens; the mobile drawer overrides them back to the
   dark set, because it covers the page rather than sitting on it. */
.site-header {
  --hdr-fg: var(--ink-900);
  --hdr-fg-dim: var(--ink-600);
  --hdr-line: var(--border);
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, 0.94);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hdr-line);
  transition: box-shadow var(--t-med) var(--ease);
}
.site-header.is-scrolled { box-shadow: 0 8px 24px rgba(8, 26, 43, 0.12); }
.site-header__row {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  height: var(--header-h);
  transition: height var(--t-med) var(--ease);
}
.site-header.is-scrolled .site-header__row { height: calc(var(--header-h) - 8px); }
.brand { display: inline-flex; align-items: center; flex-shrink: 0; }
.brand img { height: 44px; width: auto; }
/* Only one mark shows at a time — see body.nav-open in the drawer block. */
.brand__mark--white { display: none; }
@media (max-width: 767px) { .brand img { height: 36px; } }

/* Second header row — the full nav gets its own band, so 10 items never
   compete with the brand and actions for width. */
.site-header__navrow {
  border-top: 1px solid var(--hdr-line);
  background: rgba(8, 26, 43, 0.025);
  /* Deliberately NOT overflow:hidden. That was added as a guard against a nav
     wider than the viewport, but the dropdown panels are positioned below this
     row — clipping the row hid every one of them. The compact link tier
     (1024-1279px) is what actually keeps the list inside the viewport now. */
}
/* Centre the row so the links sit balanced under the brand/actions row. */
@media (min-width: 1024px) {
  /* .nav is a block, so the list was only as wide as its items and
     justify-content had no free space to distribute — it read as
     left-aligned. Giving the list the full row width lets it centre. */
  .site-header__navrow .nav { display: block; width: 100%; }
  .site-header__navrow .nav__list { width: 100%; justify-content: center; }
}
.nav { position: relative; }
.nav__list { display: flex; align-items: center; gap: 0; flex-wrap: nowrap; }
.nav__item { position: relative; }
.nav__link {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  font-family: var(--font-display); font-size: 0.85rem; font-weight: 500;
  color: var(--hdr-fg-dim); padding: 13px 11px;
  position: relative; text-decoration: none; background: none; border: 0;
  transition: color var(--t-fast) var(--ease);
}
/* Three tiers, because nine items with six carets need ~1000px at the default
   size. Without the compact tier the whole nav has to collapse to a burger at
   1199px, which wastes a desktop-width screen. */
@media (min-width: 1024px) and (max-width: 1279px) {
  .nav__link { font-size: 0.78rem; padding: 13px 7px; letter-spacing: -0.005em; }
}
@media (min-width: 1280px) {
  .nav__link { font-size: 0.9rem; padding: 13px 15px; }
}
.nav__link:hover, .nav__item.is-open > .nav__link { color: var(--hdr-fg); }

/* Glide pill: one shared highlight that travels between items rather than
   each link fading its own background. Positioned from JS. */
.nav__glide {
  position: absolute; top: 50%; left: 0; height: 34px; width: 0;
  transform: translateY(-50%); border-radius: var(--r-pill);
  background: rgba(18, 65, 102, 0.06);
  box-shadow: inset 0 0 0 1px rgba(30, 151, 137, 0.35);
  opacity: 0; pointer-events: none; z-index: 0;
  transition: transform var(--t-med) var(--ease-out-expo),
              width var(--t-med) var(--ease-out-expo),
              opacity var(--t-fast) var(--ease);
}
.nav.has-glide .nav__glide { opacity: 1; }
.nav__link > * , .nav__link { position: relative; z-index: 1; }

/* Thread underline — grows from the left, echoing the site's thread motif. */
.nav__link::after {
  content: ""; position: absolute; left: 11px; right: 100%; bottom: 6px; height: 2px;
  background: linear-gradient(90deg, var(--teal-500), var(--teal-300));
  border-radius: 2px; transition: right var(--t-med) var(--ease-out-expo);
}
.nav__link:hover::after, .nav__item.is-open > .nav__link::after { right: 11px; }
@media (min-width: 1280px) {
  .nav__link::after { left: 15px; }
  .nav__link:hover::after, .nav__item.is-open > .nav__link::after { right: 15px; }
}
.nav__caret { transition: transform var(--t-med) var(--ease-out-expo); }
.nav__item.is-open > .nav__link .nav__caret,
.nav__item:hover > .nav__link .nav__caret { transform: rotate(180deg); }

.nav__sub {
  position: absolute; top: calc(100% + 8px); left: 0; min-width: 262px;
  background: linear-gradient(180deg, var(--navy-800), var(--navy-900));
  border: 1px solid rgba(126, 217, 205, 0.18);
  border-radius: 14px; box-shadow: var(--shadow-3);
  padding: 14px 14px 14px 40px; /* left inset holds the ladder rails */
  opacity: 0; visibility: hidden;
  /* Folding ladder: the panel unfolds downward from its top hinge. */
  transform: perspective(900px) rotateX(-82deg); transform-origin: top center;
  transition: opacity 0.2s var(--ease),
              transform 0.42s var(--ease-out-expo),
              visibility 0.42s;
  z-index: 110;
}
/* Two threads running the height of the panel — the ladder's rails. */
.nav__sub::after {
  content: ""; position: absolute; left: 19px; top: 16px; bottom: 16px; width: 9px;
  background:
    linear-gradient(var(--teal-500), var(--teal-500)) left / 1.5px 100% no-repeat,
    linear-gradient(var(--teal-500), var(--teal-500)) right / 1.5px 100% no-repeat;
  opacity: 0.45; transform: scaleY(0); transform-origin: top;
  transition: transform 0.4s var(--ease-out-expo) 0.1s;
  pointer-events: none;
}
/* Panels near the right edge open leftwards, otherwise they push the page
   wider (a hidden panel still counts toward scroll width). */
.nav__item:nth-last-child(2) > .nav__sub,
.nav__item:nth-last-child(3) > .nav__sub { left: auto; right: 0; }
/* Twin filaments dropping from the parent link into the ladder rails. */
.nav__sub::before {
  content: ""; position: absolute; left: 19px; top: -9px; width: 9px; height: 9px;
  background:
    linear-gradient(180deg, transparent, var(--teal-500)) left / 1.5px 100% no-repeat,
    linear-gradient(180deg, transparent, var(--teal-500)) right / 1.5px 100% no-repeat;
  transform: scaleY(0); transform-origin: top;
  transition: transform 0.3s var(--ease-out-expo) 0.06s;
}
.nav__item.is-open > .nav__sub,
.nav__item:focus-within > .nav__sub {
  opacity: 1; visibility: visible; transform: perspective(900px) rotateX(0deg);
}
.nav__item.is-open > .nav__sub::before,
.nav__item:focus-within > .nav__sub::before { transform: scaleY(1); }
.nav__item.is-open > .nav__sub::after,
.nav__item:focus-within > .nav__sub::after { transform: scaleY(1); }
@media (hover: hover) and (min-width: 1024px) {
  .nav__item:hover > .nav__sub {
    opacity: 1; visibility: visible; transform: perspective(900px) rotateX(0deg);
  }
  .nav__item:hover > .nav__sub::before,
  .nav__item:hover > .nav__sub::after { transform: scaleY(1); }
}
/* Rungs: each row is spaced so the ladder reads as separate steps. */
.nav__sub li + li { margin-top: 6px; }
.nav__sub a {
  display: block; padding: 11px 14px; border-radius: var(--r-sm);
  color: rgba(255, 255, 255, 0.82); font-size: 0.9rem; position: relative;
  transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease-out);
}
/* Nut: a hex bolt seated across the two rails at each rung. */
.nav__sub a::before {
  content: ""; position: absolute; left: -25px; top: 50%; width: 11px; height: 11px;
  margin-top: -5.5px;
  background: var(--navy-700);
  box-shadow: inset 0 0 0 1.5px var(--teal-500);
  clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
  transition: background-color var(--t-fast) var(--ease), transform var(--t-med) var(--ease-out);
}
.nav__sub a:hover, .nav__sub a:focus-visible {
  color: #fff; background: rgba(255, 255, 255, 0.07); transform: translateX(3px);
}
/* the nut turns as you pick the rung */
.nav__sub a:hover::before, .nav__sub a:focus-visible::before {
  background: var(--teal-300); transform: rotate(90deg);
}
/* Sub-items cascade in, so the panel unfolds rather than just appearing. */
@media (prefers-reduced-motion: no-preference) {
  /* Rows cascade in so the panel unfolds instead of appearing all at once.
     The transition (incl. per-row delay) lives on the base rule — the open
     state only flips the values, so the delays are not overridden. */
  /* Each rung swings down on its own hinge, one after the next. */
  .nav__sub li {
    opacity: 0; transform: perspective(600px) rotateX(-70deg);
    transform-origin: top center;
    transition: opacity 0.24s var(--ease), transform 0.34s var(--ease-out-expo);
  }
  .nav__item.is-open > .nav__sub li,
  .nav__item:focus-within > .nav__sub li {
    opacity: 1; transform: perspective(600px) rotateX(0deg);
  }
  @media (hover: hover) and (min-width: 1024px) {
    .nav__item:hover > .nav__sub li {
      opacity: 1; transform: perspective(600px) rotateX(0deg);
    }
  }
  /* Closing collapses the whole ladder together (no reverse cascade), so the
     panel folds shut cleanly instead of unzipping rung by rung. */
  .nav__sub li { transition-delay: 0s; }
  .nav__item.is-open > .nav__sub li:nth-child(1),
  .nav__item:focus-within > .nav__sub li:nth-child(1) { transition-delay: 0.10s; }
  .nav__item.is-open > .nav__sub li:nth-child(2),
  .nav__item:focus-within > .nav__sub li:nth-child(2) { transition-delay: 0.16s; }
  .nav__item.is-open > .nav__sub li:nth-child(3),
  .nav__item:focus-within > .nav__sub li:nth-child(3) { transition-delay: 0.22s; }
  .nav__item.is-open > .nav__sub li:nth-child(4),
  .nav__item:focus-within > .nav__sub li:nth-child(4) { transition-delay: 0.28s; }
  .nav__item.is-open > .nav__sub li:nth-child(5),
  .nav__item:focus-within > .nav__sub li:nth-child(5) { transition-delay: 0.34s; }
  @media (hover: hover) and (min-width: 1024px) {
    .nav__item:hover > .nav__sub li:nth-child(1) { transition-delay: 0.10s; }
    .nav__item:hover > .nav__sub li:nth-child(2) { transition-delay: 0.16s; }
    .nav__item:hover > .nav__sub li:nth-child(3) { transition-delay: 0.22s; }
    .nav__item:hover > .nav__sub li:nth-child(4) { transition-delay: 0.28s; }
    .nav__item:hover > .nav__sub li:nth-child(5) { transition-delay: 0.34s; }
  }
}

.site-header__actions { display: flex; align-items: center; gap: 14px; margin-left: 6px; flex-shrink: 0; }
.brand { flex-shrink: 0; }
.patronage-chip {
  /* The white plate existed to lift the ministry mark off the old dark bar.
     On the light header it vanishes, so the chip is now outlined instead. */
  display: none; background: #fff; border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 5px 10px;
  line-height: 0; flex-shrink: 0;
}
.patronage-chip img { height: 30px; width: auto; }
/* The 1240px container is too narrow for 10 nav items plus the brand and
   actions, so the header alone widens on large screens. */
/* Even widened, the chip only fits on very large screens; below that it would
   sit under the last links. It still appears in the footer. */
/* Ministry mark: logo left, ministry right, burger far right. Kept on phones
   too (scaled down); hidden only on the very narrowest, where the logo and
   burger need the whole row. */
@media (min-width: 380px) { .patronage-chip { display: inline-flex; } }
@media (max-width: 1023px) {
  .patronage-chip { padding: 4px 8px; margin-left: auto; }
  .patronage-chip img { height: 20px; }
}
@media (max-width: 480px) { .patronage-chip img { height: 17px; } }
/* The ministry mark carries no width attribute, and the brand, actions and
   chip are all flex-shrink:0 — below this the row has no slack left, so the
   chip goes rather than letting the toggle be pushed off-screen. */
@media (max-width: 420px) { .patronage-chip { display: none; } }

.nav-toggle {
  display: none; width: 44px; height: 44px; border-radius: var(--r-sm);
  position: relative; z-index: 130; flex-shrink: 0;
}
.nav-toggle span, .nav-toggle::before, .nav-toggle::after {
  content: ""; display: block; width: 22px; height: 2px; background: var(--hdr-fg);
  border-radius: 2px; margin: 5px auto; transition: transform var(--t-med) var(--ease),
    opacity var(--t-fast) var(--ease);
}
/* Open: the three bars become an X. This IS the close control — a second
   dedicated close button next to it only produced two X's doing one job. */
body.nav-open .nav-toggle::before { transform: translateY(7px) rotate(45deg); }
body.nav-open .nav-toggle span { opacity: 0; transform: scaleX(0); }
body.nav-open .nav-toggle::after { transform: translateY(-7px) rotate(-45deg); }
/* A ring appears around it while open, so it reads as a dismiss affordance
   rather than a menu button that happens to have changed shape. */
body.nav-open .nav-toggle {
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: rgba(255, 255, 255, 0.06);
}
body.nav-open .nav-toggle:hover {
  border-color: var(--teal-500); background: rgba(35, 173, 158, 0.18);
}
body.nav-open .nav-toggle:hover::before { transform: translateY(7px) rotate(135deg); }
body.nav-open .nav-toggle:hover::after  { transform: translateY(-7px) rotate(-225deg); }

@media (max-width: 1023px) {
  .nav-toggle { display: block; }
  .site-header .btn--register { display: none; }
  /* The nav becomes a fixed drawer; its row band must not reserve height or
     act as a containing block for it. */
  .site-header__navrow { border-top: 0; background: none; }
  .site-header__navrow, .site-header__navrow > .container {
    display: contents; /* drawer escapes the wrapper entirely */
  }
  .nav__glide { display: none; }
  /* the nav is position:fixed on mobile (out of flow) — push actions to the right edge */
  .site-header__actions { margin-left: auto; }
  /* drawer open: header sits ABOVE the overlay, fully opaque — nothing bleeds
     through. The drawer covers the page rather than sitting on it, so it keeps
     the dark palette; the tokens flip back so the logo, bars and links invert
     with it. */
  body.nav-open .site-header {
    --hdr-fg: #fff;
    --hdr-fg-dim: rgba(255, 255, 255, 0.82);
    --hdr-line: var(--border-dark);
    z-index: 130; background: var(--navy-900);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  /* The colour logo disappears on the dark drawer, so swap in the white cut. */
  body.nav-open .brand__mark--colour { display: none; }
  body.nav-open .brand__mark--white { display: block; }
  #site-nav.nav {
    position: fixed; inset: 0; z-index: 120; background: var(--navy-900);
    padding: calc(var(--header-h) + 32px) var(--gutter) 40px;
    overflow-y: auto; margin: 0;
    opacity: 0; visibility: hidden; transition: opacity var(--t-med) var(--ease), visibility var(--t-med);
  }
  body.nav-open #site-nav.nav { opacity: 1; visibility: visible; }
  body.nav-open { overflow: hidden; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .nav__item { border-bottom: 1px solid var(--border-dark); }
  .nav__link {
    width: 100%; justify-content: space-between; padding: 19px 4px;
    font-size: 1.32rem; font-weight: 700; font-stretch: 108%;
    text-transform: uppercase; letter-spacing: 0.03em; color: #fff;
    opacity: 0; transform: translateY(12px);
    transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
    transition-delay: var(--d, 0s);
  }
  body.nav-open .nav__link { opacity: 1; transform: none; }
  .nav__link::after { display: none; }
  .nav__sub {
    position: static; min-width: 0; background: none; border: 0; box-shadow: none;
    padding: 0 0 0 12px; transform: none; display: block;
    max-height: 0; overflow: hidden; opacity: 0; visibility: hidden;
    transition: max-height 0.5s var(--ease-out-expo), opacity 0.35s var(--ease),
      visibility 0.5s, padding 0.5s var(--ease-out-expo);
  }
  .nav__item.is-open > .nav__sub {
    max-height: 440px; opacity: 1; visibility: visible; padding-bottom: 10px;
  }
  /* The desktop ladder fold is 3D and rail-based; the mobile accordion
     expands in flow instead, so reset the rungs, rails and nuts here. */
  .nav__sub li { opacity: 1; transform: none; transition: none; }
  .nav__sub::before, .nav__sub::after { display: none; }
  .nav__sub a::before { display: none; }
  .nav__sub li + li { margin-top: 0; }
  /* beat the desktop open-state fold, which also targets .is-open > .nav__sub */
  .nav__item.is-open > .nav__sub,
  .nav__item:focus-within > .nav__sub { transform: none; }
  .nav__sub a { opacity: 0; transform: translateY(8px);
    transition: opacity 0.35s var(--ease-out-quint), transform 0.35s var(--ease-out-quint); }
  .nav__item.is-open > .nav__sub a { opacity: 1; transform: none; }
  .nav__item.is-open > .nav__sub a:nth-child(1) { transition-delay: 0.08s; }
  .nav__item.is-open > .nav__sub li:nth-child(2) a { transition-delay: 0.12s; }
  .nav__item.is-open > .nav__sub li:nth-child(3) a { transition-delay: 0.16s; }
  .nav__item.is-open > .nav__sub li:nth-child(4) a { transition-delay: 0.2s; }
  .nav__item.is-open > .nav__sub li:nth-child(5) a { transition-delay: 0.24s; }
  .nav__item.is-open > .nav__sub li:nth-child(6) a { transition-delay: 0.28s; }
  /* drawer backdrop: subtle RHIC facet field, no glassmorphism */
  #site-nav.nav::after {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background:
      radial-gradient(420px 420px at 88% 12%, rgba(35, 173, 158, 0.1), transparent 65%),
      radial-gradient(360px 360px at 8% 92%, rgba(34, 126, 194, 0.08), transparent 65%);
  }
  .nav__sub a { font-size: 1rem; padding: 12px 10px; }
  .nav__mobile-cta { display: block; margin-top: 28px; }
  .nav__mobile-cta .btn { width: 100%; }
}
@media (min-width: 1024px) { .nav__mobile-cta { display: none; } }

/* 8. HERO ------------------------------------------------------------------ */
.hero {
  position: relative; background: var(--navy-900); color: #fff; overflow: hidden;
  /* Budget against the header that actually stays pinned, not the token.
     --header-h is 76px (the brand row), but on desktop the nav row sits below
     it and the whole 128px block is sticky — so reserving only 76px left the
     hero ~50px taller than the screen and pushed the CTAs under the fold. */
  min-height: min(calc(100svh - var(--header-pinned)), 1040px);
  display: flex; align-items: center;
}
.hero__bg { position: absolute; inset: 0; pointer-events: none; }
.hero__bg::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(900px 600px at 82% 18%, rgba(34, 126, 194, 0.14), transparent 60%),
    radial-gradient(700px 700px at 8% 85%, rgba(35, 173, 158, 0.1), transparent 60%);
}
.hero__facets { position: absolute; right: -4%; top: -6%; width: 56%; opacity: 0.5; }
/* The thread is a fixed-width strip pinned to the left of the hero copy.

   It used to be a full-bleed SVG with preserveAspectRatio="slice", which tied
   the line's x-position to the viewport while the copy is centred in a 1240px
   container — between roughly 1200 and 1370px the two collided and the line
   ran through the headline. A fixed strip whose right edge tracks the
   container removes the coupling: the vertical run always lands the same
   distance to the left of the text. */
.hero__thread {
  position: absolute; top: 0; bottom: 0; height: 100%;
  /* The run is at x=64 of the 148-wide strip, so offsetting by -64 puts it on
     the strip's left origin; the extra 26px is the gap to the copy. Once the
     container stops growing the value goes negative and the strip simply
     slides off the left edge, which is the graceful end of the gesture —
     better than the line creeping into the headline. */
  left: calc(50% - var(--container) / 2 - 90px);
  width: 148px;
}
/* Narrow desktops have no room for it beside the copy at all. */
@media (max-width: 1365px) { .hero__thread { display: none; } }
.hero__thread path { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.hero__thread circle { fill: var(--teal-300); }

/* Text left, logo-shaped media right — the original two-column hero. The mark
   is landscape, so the media column is the wider of the two; stacking it under
   the headline pushed the hero past two screens. */
.hero__grid {
  position: relative; z-index: 1; display: grid;
  /* The headline leads, so it gets the wider column — an even split forced
     "INTERNATIONAL" to break mid-word. */
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.9fr);
  gap: clamp(28px, 3.5vw, 52px); align-items: center;
  padding-block: clamp(40px, 6vh, 72px);
  width: 100%;
}
.hero__eyebrow {
  font-family: var(--font-display); font-size: 0.8125rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--teal-300);
  margin-bottom: 22px; display: flex; align-items: center; gap: 12px;
}
.hero__eyebrow::before { content: ""; width: 34px; height: 2px; background: var(--teal-500); }
.hero__title {
  font-size: clamp(1.8rem, 4.5vw, 3.9rem); font-weight: 800; font-stretch: 110%;
  text-transform: uppercase; letter-spacing: 0.005em; line-height: 1.06; margin-bottom: 24px;
}
.hero__title .line { display: block; overflow: hidden; }
.hero__title .line > span { display: inline-block; }
.hero__title em { font-style: normal; color: var(--teal-300); }
.js:not(.has-gsap) .hero:not(.is-ready) .hero__title .line > span { transform: translateY(110%); }
.js:not(.has-gsap) .hero.is-ready .hero__title .line > span {
  animation: hero-line 0.75s var(--ease-out) both;
  animation-delay: calc(0.12s + var(--l, 0) * 0.09s);
}
.has-gsap .hero__title .line > span { transform: translateY(112%); }
@keyframes hero-line { from { transform: translateY(110%); } to { transform: translateY(0); } }

.hero__tagline {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem); color: rgba(255, 255, 255, 0.85);
  font-weight: 400; letter-spacing: 0.01em; margin-bottom: 30px;
}
.hero__tagline b { color: var(--teal-300); font-weight: 600; }

.hero__meta {
  display: flex; flex-wrap: wrap; gap: 20px 36px;
  border-block: 1px solid var(--border-dark); padding-block: 18px; margin-bottom: 32px;
}
.hero__meta .meta__label {
  display: block; font-family: var(--font-display); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.55);
  margin-bottom: 4px;
}
.hero__meta .meta__value { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; color: #fff; }

.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 30px; }
.hero__patronage { font-size: 0.8125rem; color: rgba(255, 255, 255, 0.55); letter-spacing: 0.03em; margin: 0; }

/* Short-viewport hero.
   The hero's job is to get the reader to "Register Now" / "Submit Your
   Abstract" without scrolling. Its content needs ~640px, which a 14" laptop at
   100% zoom (~460px under the pinned header) cannot give — the CTAs landed
   below the fold while the same page was fine on a 17".

   Rather than shrink everything everywhere, the spacing tightens only when the
   viewport is genuinely short. Height is the right query here, not width: a
   1920x800 window has the same problem as a 1366x768 one, and a tall portrait
   monitor has none. Type sizes are trimmed a little; the gaps between blocks
   carry most of the reduction, because whitespace is what the reader can spare
   and legibility is not. */
@media (min-width: 1024px) and (max-height: 860px) {
  .hero__grid { padding-block: clamp(24px, 3.2vh, 40px); }
  .hero__eyebrow { margin-bottom: 14px; }
  .hero__title { font-size: clamp(1.8rem, 3.6vw, 3.1rem); margin-bottom: 16px; }
  .hero__tagline { font-size: clamp(1rem, 1.35vw, 1.15rem); margin-bottom: 20px; }
  .hero__meta { padding-block: 13px; margin-bottom: 22px; gap: 12px 32px; }
  .hero__cta { margin-bottom: 20px; }
}

/* Shorter still (a 14" at 100%, or any window with the dev tools docked). */
@media (min-width: 1024px) and (max-height: 720px) {
  .hero__grid { padding-block: 20px; }
  .hero__eyebrow { margin-bottom: 10px; font-size: 0.75rem; }
  .hero__title { font-size: clamp(1.7rem, 3.1vw, 2.6rem); margin-bottom: 12px; }
  .hero__tagline { font-size: 1rem; margin-bottom: 14px; }
  .hero__meta { padding-block: 11px; margin-bottom: 16px; gap: 10px 28px; }
  .hero__meta .meta__value { font-size: 0.95rem; }
  .hero__cta { margin-bottom: 14px; }
  /* The patronage line is the least load-bearing thing in the hero, and the
     same credit already appears in the announce bar and the footer. */
  .hero__patronage { display: none; }
}

.js .hero__stagger { opacity: 0; transform: translateY(18px); }
.js:not(.has-gsap) .hero.is-ready .hero__stagger {
  animation: hero-fade 0.7s var(--ease-out) both;
  animation-delay: calc(0.45s + var(--s, 0) * 0.12s);
}
@keyframes hero-fade { to { opacity: 1; transform: none; } }

/* Logo-masked media.
   The mark is landscape (192:60) where the old facet was portrait, so the
   media column is given the full width of the hero grid rather than a narrow
   430px sliver — otherwise the video would be reduced to a letterbox strip. */
.hero__media {
  position: relative; justify-self: stretch;
  width: 100%; max-width: 100%;
}
.hero__facet {
  position: relative; aspect-ratio: var(--logo-aspect); overflow: hidden;
  clip-path: var(--logo-clip);
  background: var(--navy-800);
}
.hero__facet video, .hero__facet img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.hero__facet::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(200deg, rgba(8, 26, 43, 0) 55%, rgba(8, 26, 43, 0.55));
}
.hero__facet-outline {
  position: absolute; inset: 0; transform: translate(14px, 14px); z-index: -1;
  clip-path: var(--logo-clip);
  background: linear-gradient(160deg, rgba(35, 173, 158, 0.35), rgba(34, 126, 194, 0.12));
  opacity: 0.55;
}
.js:not(.has-gsap) .hero.is-ready .hero__media { animation: hero-media 1s var(--ease-out) 0.3s both; }
@keyframes hero-media { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }

/* The hero thread turns the corner at the bottom-left and runs horizontally
   at the same height as this label, so the line used to strike through the
   word. Sit the cue clear of that run: above the corner, and indented past
   the thread's vertical leg. */
/* Sits in the hero's bottom-left corner, below the content rather than beside
   it: at its old inset it shared a band with the CTA row and the word "SCROLL"
   ran into the Register button. Vertical, so it occupies the narrow strip left
   of the container and cannot collide with anything again. */
.hero__scrollcue {
  /* Right of the social rail's column (it is 55px wide and vertically centred),
     so the two never share space at any viewport height. */
  position: absolute; left: calc(var(--gutter) + 30px); bottom: 26px; z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  font-family: var(--font-display); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.24em; text-transform: uppercase; color: rgba(255, 255, 255, 0.42);
  writing-mode: vertical-rl;                 /* reads bottom-to-top up the line */
  pointer-events: none;
}
.hero__scrollcue::after {
  content: ""; width: 1px; height: 40px; background: linear-gradient(var(--teal-500), transparent);
  animation: cue 2.4s var(--ease) infinite;
  transform-origin: top;
}
/* Only where there is room beside the container for it to live. */
@media (max-width: 1279px) { .hero__scrollcue { display: none; } }
@keyframes cue { 0%, 100% { transform: scaleY(0.4); opacity: 0.5; } 50% { transform: scaleY(1); opacity: 1; } }

@media (max-width: 1023px) {
  .hero__grid { grid-template-columns: 1fr; }
  /* One video, not two. The logo-shaped cut IS the mobile background now: the
     separate full-bleed .hero__bg-mobile film used to play behind it, so the
     same footage ran twice, out of sync, and read as a mistake. */
  .hero__bg-mobile { display: none; }

  .hero__media {
    display: block;
    position: absolute; inset: 0; z-index: 0;
    width: 100%; margin: 0;
    pointer-events: none;                 /* text above stays selectable */
  }
  .hero__facet {
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 132%;                          /* bleed past the edges so the mark
                                             reads as a backdrop, not a card */
    aspect-ratio: var(--logo-aspect);
  }
  /* Brand wash over the film, so the headline stays readable — this is the
     gradient that used to live on .hero__bg-mobile::after. */
  .hero__facet::after {
    background: linear-gradient(180deg,
      rgba(8, 26, 43, 0.84) 0%,
      rgba(8, 26, 43, 0.62) 34%,
      rgba(8, 26, 43, 0.64) 62%,
      rgba(8, 26, 43, 0.95) 100%);
  }
  .hero__content { position: relative; z-index: 1; }
  .hero__facet-outline { display: none; }   /* too fussy at this size */
  .hero { min-height: 0; }
  .hero__thread { display: none; }
  .hero__scrollcue { display: none; }
}

/* 9. PAGE HERO (subpages) -------------------------------------------------- */
.page-hero {
  position: relative; background: var(--navy-900); color: #fff; overflow: hidden;
  padding-block: clamp(56px, 9vw, 104px);
}
.page-hero__bg { position: absolute; inset: 0; opacity: 0.5; pointer-events: none; }
.page-hero__bg img { width: 100%; height: 100%; object-fit: cover; }
.page-hero__bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(8, 26, 43, 0.95) 30%, rgba(8, 26, 43, 0.55));
}
.page-hero .container { position: relative; z-index: 1; }
.page-hero__title {
  font-size: clamp(2rem, 4.2vw, 3.2rem); font-weight: 800; font-stretch: 112%;
  text-transform: uppercase; margin-bottom: 10px;
}
.page-hero__lede { color: rgba(255, 255, 255, 0.8); font-size: 1.1rem; max-width: 44em; margin: 0; }

.breadcrumb-nav { margin-top: 22px; }
.breadcrumbs { display: flex; flex-wrap: wrap; gap: 8px; font-size: 0.85rem; }
.breadcrumbs a { color: rgba(255, 255, 255, 0.65); }
.breadcrumbs a:hover { color: var(--teal-300); }
.breadcrumbs li + li::before { content: "/"; margin-right: 8px; color: rgba(255, 255, 255, 0.35); }
.breadcrumbs [aria-current] { color: var(--teal-300); }

/* 10. EMBED SHELL (Eventegration) ------------------------------------------ */
/* A little breathing room inside the frame. The embedded form draws its own
   content flush to its edges, so without this its first step marker sat hard
   against our border and read as clipped. */
.embed-shell {
  position: relative; background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-2); overflow: hidden;
  padding: clamp(10px, 1.6vw, 22px);
}
@media (max-width: 640px) { .embed-shell { padding: 8px; } }
.embed-shell iframe { display: block; width: 100%; border: 0; }
.embed-skeleton {
  position: absolute; inset: 0; z-index: 2; background: var(--surface-1);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; text-align: center; padding: 32px;
  transition: opacity 0.5s var(--ease), visibility 0.5s;
}
.embed-shell.is-loaded .embed-skeleton { opacity: 0; visibility: hidden; }
.embed-skeleton__mark {
  width: 96px; height: 30px;
  clip-path: var(--logo-clip);
  background: linear-gradient(160deg, var(--navy-700), var(--teal-500));
  animation: pulse-mark 1.6s var(--ease) infinite;
}
@keyframes pulse-mark { 0%, 100% { opacity: 0.45; transform: scale(0.96); } 50% { opacity: 1; transform: scale(1); } }
.embed-skeleton__text { font-family: var(--font-display); font-weight: 600; color: var(--ink-600); margin: 0; }
.embed-fallback { display: none; }
.embed-shell.is-failed .embed-skeleton { opacity: 0; visibility: hidden; }
.embed-shell.is-failed .embed-fallback {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; position: absolute; inset: 0; z-index: 3; background: var(--surface-1);
  text-align: center; padding: 32px;
}
/* The "open it in its own tab" offer, which now sits ABOVE the frame
   (partials/embed-chooser.php). Styled as a note-card rather than the old grey
   one-liner so it is read before the visitor starts typing, not after. */
.embed-open {
  margin: 0 0 16px;
  padding: 16px 20px;
  text-align: center;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-left: 4px solid var(--teal-500);
  border-radius: var(--r-md);
}
.embed-open__btn { display: inline-flex; align-items: center; gap: 8px; }
.embed-open__help { margin: 14px 0 0; font-size: 0.88rem; color: var(--ink-600); }
.embed-open__help a { font-weight: 600; }

@media (max-width: 640px) {
  .embed-open { padding: 14px 16px; }
  /* Full-width on a phone: this is the recommended route there, so it should be
     the easiest thing on screen to hit. */
  .embed-open__btn { display: flex; width: 100%; justify-content: center; }
}

/* 11. COMING SOON ----------------------------------------------------------- */
.coming-soon {
  position: relative; background: var(--navy-900); color: #fff; overflow: hidden;
  /* Same reservation as the hero: the pinned header, not the token. */
  min-height: min(calc(100svh - var(--header-pinned)), 860px);
  display: flex; align-items: center; text-align: center;
}
.coming-soon .container { position: relative; z-index: 1; max-width: 760px; }
.coming-soon__tag { color: var(--teal-300); }
.coming-soon__title { font-size: clamp(2.1rem, 4.5vw, 3.4rem); font-stretch: 112%; text-transform: uppercase; }
.coming-soon__lede { color: rgba(255, 255, 255, 0.75); font-size: 1.1rem; }
.coming-soon__meta {
  display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 12px 28px;
  border-block: 1px solid var(--border-dark); padding: 14px 8px; margin: 26px 0 30px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.95rem;
}
.coming-soon__cta { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }

/* 12. FOOTER ---------------------------------------------------------------- */
.site-footer { background: var(--navy-900); color: rgba(255, 255, 255, 0.78); position: relative; }
.site-footer__horizon { display: block; width: 100%; height: 70px; }
.site-footer__horizon path { stroke: var(--teal-500); stroke-width: 1.5; fill: none; opacity: 0.65; }
.site-footer__horizon circle { fill: var(--teal-300); }
.site-footer__grid {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.1fr;
  gap: clamp(32px, 4vw, 56px); padding-block: 24px 56px;
}
.site-footer h2 {
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--teal-300); margin-bottom: 18px;
}
.site-footer__brand img { height: 52px; width: auto; margin-bottom: 18px; }
.site-footer__brand p { font-size: 0.95rem; margin-bottom: 6px; }
.site-footer a { color: rgba(255, 255, 255, 0.78); }
.site-footer a:hover { color: var(--teal-300); }
.site-footer__links li { margin-bottom: 10px; font-size: 0.95rem; }
.footer-patronage { background: #fff; border-radius: var(--r-sm); padding: 10px 14px; display: inline-block; line-height: 0; margin-top: 6px; }
.footer-patronage img { height: 40px; width: auto; }
.social-row { display: flex; gap: 10px; margin-top: 16px; }
.social-row a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 50%;
  border: 1px solid var(--border-dark); color: #fff;
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}
.social-row a:hover { background: var(--teal-500); border-color: var(--teal-500); color: var(--navy-900); transform: translateY(-2px); }
.site-footer__bottom {
  border-top: 1px solid var(--border-dark); padding-block: 20px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 24px;
  justify-content: space-between;
  font-size: 0.85rem; color: rgba(255, 255, 255, 0.55);
}
/* Design credit sits last on desktop, centred when the row wraps on mobile. */
.site-footer__credit { margin-inline-start: auto; }
.site-footer__credit a { color: rgba(255, 255, 255, 0.72); }
.site-footer__credit a:hover,
.site-footer__credit a:focus-visible { color: var(--teal-300); }
@media (max-width: 767px) {
  .site-footer__bottom { justify-content: center; text-align: center; }
  .site-footer__credit { margin-inline-start: 0; }
}
@media (max-width: 1023px) { .site-footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px)  { .site-footer__grid { grid-template-columns: 1fr; } }

/* 13. HOME — SCALE STRIP ---------------------------------------------------- */
.stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.stat-tile {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 30px 28px 26px; position: relative; overflow: hidden;
}
.stat-tile::after {
  content: ""; position: absolute; top: 0; right: 0; width: 26px; height: 26px;
  background: linear-gradient(225deg, var(--surface-1) 50%, transparent 50%);
}
.stat-tile__num {
  font-family: var(--font-display); font-weight: 800; font-stretch: 110%;
  font-size: clamp(2.3rem, 3.6vw, 3rem); line-height: 1; color: var(--navy-700);
  font-variant-numeric: tabular-nums; display: flex; align-items: baseline; gap: 2px;
}
.stat-tile__num i { font-style: normal; color: var(--teal-500); font-size: 0.72em; }
.stat-tile__label { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; margin: 10px 0 6px; color: var(--ink-900); }
.stat-tile__desc { font-size: 0.9rem; color: var(--ink-400); margin: 0; }
.stat-tile--seal .stat-tile__num { color: var(--teal-600); font-size: clamp(1.7rem, 2.6vw, 2.1rem); }
@media (max-width: 1023px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .stat-grid { grid-template-columns: 1fr; gap: 16px; } }

/* 14. SPLIT (About) --------------------------------------------------------- */
.split { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: clamp(40px, 6vw, 88px); align-items: center; }
.split__media { position: relative; }
.split__media img {
  width: 100%; aspect-ratio: 4 / 5; object-fit: cover; border-radius: var(--r-lg); display: block;
  clip-path: polygon(0 0, calc(100% - 48px) 0, 100% 48px, 100% 100%, 0 100%);
}
.split__media::before {
  content: ""; position: absolute; inset: auto -16px -16px auto; width: 60%; height: 60%;
  border: 1.5px solid var(--teal-500); border-radius: var(--r-lg); opacity: 0.35; z-index: -1;
  clip-path: polygon(0 0, calc(100% - 48px) 0, 100% 48px, 100% 100%, 0 100%);
}
.split__body .btn { margin-top: 10px; }
@media (max-width: 1023px) {
  .split { grid-template-columns: 1fr; }
  .split__media img { aspect-ratio: 16 / 10; }
}

/* 15. COUNTDOWN BAND -------------------------------------------------------- */
.cd-band { background: var(--navy-900); color: #fff; padding-block: clamp(44px, 6vw, 64px); position: relative; overflow: hidden; }
.cd-band::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(700px 320px at 85% 50%, rgba(35, 173, 158, 0.12), transparent 65%);
}
.cd-band .container { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 28px 48px; justify-content: space-between; }
.cd-band__title { font-size: 1.35rem; font-stretch: 110%; margin: 0 0 6px; }
.cd-band__deadline { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); margin: 0; }
.cd-band__deadline b { color: var(--teal-300); font-weight: 600; }
.cd-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.cd-box {
  min-width: 82px; text-align: center; padding: 14px 10px 11px;
  border: 1px solid var(--border-dark); border-radius: var(--r-md); background: rgba(255, 255, 255, 0.04);
}
.cd-box b { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.9rem; line-height: 1; font-variant-numeric: tabular-nums; }
.cd-box span { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255, 255, 255, 0.55); }
@media (max-width: 560px) { .cd-box { min-width: 66px; padding: 11px 6px 9px; } .cd-box b { font-size: 1.5rem; } }
/* Narrow phones: 4-across overflows, so drop the countdown to an even 2x2 grid. */
@media (max-width: 400px) {
  .cd-row { display: grid; grid-template-columns: repeat(2, 1fr); width: 100%; }
  .cd-box { min-width: 0; }
}

/* 16. FEATURE CARDS (Why attend) ------------------------------------------- */
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.feature-card {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 30px 28px;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}
.feature-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-2); border-color: var(--teal-500); }
.feature-card__icon {
  width: 52px; height: 52px; border-radius: 14px; display: flex; align-items: center; justify-content: center;
  background: rgba(35, 173, 158, 0.12); color: var(--teal-600); margin-bottom: 20px;
}
.feature-card h3 { font-size: 1.14rem; margin-bottom: 8px; }
.feature-card p { font-size: 0.95rem; color: var(--ink-600); margin: 0; }
@media (max-width: 1023px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .feature-grid { grid-template-columns: 1fr; gap: 16px; } }

/* 17. TRACKS (dark) --------------------------------------------------------- */
.tracks-cta { display: flex; flex-wrap: wrap; gap: 12px; }
.track-chip {
  font-family: var(--font-display); font-weight: 600; font-size: clamp(0.95rem, 1.4vw, 1.12rem);
  padding: 14px 24px; border: 1px solid var(--border-dark); border-radius: var(--r-pill);
  color: rgba(255, 255, 255, 0.88); text-decoration: none;
  transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
a.track-chip:hover { border-color: var(--teal-500); background: rgba(35, 173, 158, 0.14); color: #fff; }
.track-chip--more { border-style: dashed; color: var(--teal-300); }
.tracks-footer { margin-top: 36px; }

/* 18. JOURNEY TIMELINE ------------------------------------------------------ */
.journey ol { display: grid; grid-template-columns: repeat(5, 1fr); gap: 28px; position: relative; counter-reset: none; }
.journey ol.journey-cols-4 { grid-template-columns: repeat(4, 1fr); }
.journey ol::before {
  content: ""; position: absolute; top: 7px; left: 8px; right: 8px; height: 2px;
  background: linear-gradient(90deg, var(--teal-500), var(--border) 85%); border-radius: 2px;
}
.journey li { position: relative; padding-top: 30px; }
.journey li::before {
  content: ""; position: absolute; top: 0; left: 0; width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface-0); border: 3px solid var(--teal-500);
}
.journey__day { font-family: var(--font-display); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--teal-600); display: block; }
.journey__date { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--ink-900); display: block; margin: 2px 0 10px; }
.journey h3 { font-size: 1.02rem; margin-bottom: 6px; }
.journey p { font-size: 0.88rem; color: var(--ink-600); margin: 0; }
@media (max-width: 1023px) {
  .journey ol, .journey ol.journey-cols-4 { grid-template-columns: 1fr; gap: 0; padding-left: 8px; }
  .journey ol::before { top: 8px; bottom: 8px; left: 7px; right: auto; width: 2px; height: auto;
    background: linear-gradient(180deg, var(--teal-500), var(--border) 90%); }
  .journey li { padding: 0 0 28px 34px; }
  .journey li::before { top: 2px; }
}

/* 19. OMAN INTERLUDE -------------------------------------------------------- */
.oman { background: var(--navy-900); color: #fff; padding-block: var(--section-y) 0; overflow: hidden; }
.oman .section-lede { color: rgba(255, 255, 255, 0.78); }
.oman .section-tag { color: var(--teal-300); }
/* Five 220x800 portrait slats, always in ONE row at every breakpoint, and
   always shown whole. Height comes from the source ratio rather than a fixed
   value, so nothing is cropped and nothing scrolls sideways: as the viewport
   narrows the columns narrow with it and the row simply gets shorter. */
.oman__slats {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px;
  margin-top: clamp(36px, 5vw, 64px);
}
/* min-width:0 lets a grid item shrink below its content's intrinsic width —
   without it the 220px-wide image floors the column and overflows the track. */
.oman__slats figure { margin: 0; overflow: hidden; min-width: 0; }
.oman__slats img {
  width: 100%; height: auto; aspect-ratio: 220 / 800; object-fit: cover;
  display: block; transition: transform 0.6s var(--ease-out);
}
@media (hover: hover) { .oman__slats figure:hover img { transform: scale(1.05); } }
@media (max-width: 767px) { .oman__slats { gap: 4px; } }

/* 20. GRADIENT BAND + CLOSING ----------------------------------------------- */
.band { background: var(--gradient-brand); color: #fff; text-align: center; padding-block: clamp(56px, 8vw, 88px); }
.band .section-title { color: #fff; }
.band p { color: rgba(255, 255, 255, 0.88); max-width: 52em; margin-inline: auto; }
.band__cta { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin-top: 28px; }

.closing { background: var(--navy-900); color: #fff; text-align: center; padding-block: clamp(72px, 10vw, 120px); position: relative; overflow: hidden; }
.closing::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(800px 400px at 50% 110%, rgba(35, 173, 158, 0.14), transparent 70%);
}
.closing .container { position: relative; }
.closing .section-title { font-size: clamp(2rem, 4.4vw, 3.2rem); }
.closing p { color: rgba(255, 255, 255, 0.78); max-width: 46em; margin-inline: auto; }

/* 20a. ABOUT PAGE ----------------------------------------------------------- */
.num-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 40px; }
.num-item { display: flex; align-items: center; gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--border); }
.num-item__chip {
  flex-shrink: 0; width: 44px; height: 44px; border-radius: 50%;
  border: 1.5px solid var(--teal-500); color: var(--navy-700);
  font-family: var(--font-display); font-weight: 700; font-size: 0.9rem;
  display: flex; align-items: center; justify-content: center;
}
.num-item__label { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; }
@media (max-width: 767px) { .num-grid { grid-template-columns: 1fr; gap: 0; } }

/* Audience photo cards — "Who Should Attend" ---------------------------------- */
.aud-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 20px; }
.aud-card { margin: 0; }
.aud-card__media {
  position: relative; overflow: hidden; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface-1);
  aspect-ratio: 307 / 280;
}
.aud-card__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--t-med) var(--ease-out);
}
/* Navy scrim anchors the number and keeps the photos reading as one set. */
.aud-card__media::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(8, 26, 43, 0.55) 0%, rgba(8, 26, 43, 0) 46%);
}
.aud-card__num {
  position: absolute; left: 10px; bottom: 8px; z-index: 1;
  font-family: var(--font-display); font-weight: 700; font-size: 1.15rem;
  color: #fff; letter-spacing: 0.02em; text-shadow: 0 1px 6px rgba(8, 26, 43, 0.5);
}
.aud-card__label {
  font-family: var(--font-display); font-weight: 600; font-size: 0.94rem;
  color: var(--ink-900); margin-top: 10px; line-height: 1.35;
}
.aud-card:hover .aud-card__media img { transform: scale(1.06); }
.aud-card:hover .aud-card__media { border-color: var(--teal-500); }

@media (max-width: 1200px) { .aud-grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 900px)  { .aud-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; } }
@media (max-width: 620px)  { .aud-grid { grid-template-columns: repeat(2, 1fr); gap: 14px; } }
@media (max-width: 360px)  { .aud-card__label { font-size: 0.86rem; } }
@media (prefers-reduced-motion: reduce) {
  .aud-card__media img { transition: none; }
  .aud-card:hover .aud-card__media img { transform: none; }
}

.exp-banner { position: relative; border-radius: var(--r-lg); overflow: hidden; min-height: 440px; display: flex; align-items: center; }
.exp-banner__bg { position: absolute; inset: 0; }
.exp-banner__bg img { width: 100%; height: 100%; object-fit: cover; }
.exp-banner__bg::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(8, 26, 43, 0.15), rgba(8, 26, 43, 0.82) 65%); }
.exp-banner__panel { position: relative; z-index: 1; color: #fff; max-width: 520px; margin-left: auto; padding: clamp(28px, 5vw, 56px); }
.exp-banner__panel .section-tag { color: var(--teal-300); }
.exp-banner__panel h2 { font-size: clamp(1.8rem, 3vw, 2.5rem); font-stretch: 110%; }
.exp-banner__panel p { color: rgba(255, 255, 255, 0.88); margin: 0; }

.leader-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: stretch; }
.leader-card {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 30px; display: flex; flex-direction: column; gap: 16px;
  position: relative; overflow: hidden; height: 100%;
  transition: border-color var(--t-med) var(--ease), box-shadow var(--t-med) var(--ease),
              transform var(--t-med) var(--ease-out);
}
/* Facet notch, echoing .stat-tile — ties the card into the wider system. */
.leader-card::after {
  content: ""; position: absolute; top: 0; right: 0; width: 26px; height: 26px;
  background: linear-gradient(225deg, var(--surface-1) 50%, transparent 50%);
}
.leader-card:hover { border-color: var(--teal-500); box-shadow: var(--shadow-2); transform: translateY(-3px); }
/* Fixed two-line eyebrow so both avatar rows start on the same baseline. */
.leader-card__tag {
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--teal-600); margin: 0 0 4px;
  line-height: 1.5; min-height: 3em; /* reserve 2 lines so avatar rows align */
}
.leader-card__head { display: flex; gap: 18px; align-items: center; }
.leader-card__head img {
  width: 84px; height: 84px; border-radius: 50%; object-fit: cover; object-position: top center;
  flex-shrink: 0; border: 2px solid var(--border); background: var(--surface-1);
}
.leader-card__role { font-size: 0.85rem; color: var(--ink-400); margin: 2px 0 0; }
.leader-card h3 { font-size: 1.15rem; margin: 0; }
.leader-card blockquote { margin: 0; font-size: 0.97rem; color: var(--ink-600); border-left: 3px solid var(--teal-500); padding-left: 16px; }
/* Push the CTA to the bottom so buttons align across cards of unequal text. */
.leader-card .btn { align-self: flex-start; margin-top: auto; }
@media (max-width: 1023px) {
  .leader-grid { grid-template-columns: 1fr; }
  .leader-card__tag { min-height: 0; }
}

/* Leadership message modal — branded shell over Bootstrap's dialog ------------- */
/* Navy brand scrim instead of Bootstrap's flat black. */
.modal-backdrop.show { background-color: var(--overlay); opacity: 1; }
@supports (backdrop-filter: blur(2px)) {
  .leader-modal { backdrop-filter: blur(3px); }
}
.leader-modal .modal-content {
  border: 0; border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--shadow-3); background: var(--surface-0);
}
.leader-modal .modal-dialog { max-width: 860px; }

.leader-modal__head {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--gradient-brand); color: #fff;
  padding: clamp(28px, 4vw, 40px) clamp(24px, 4vw, 44px) clamp(26px, 3.4vw, 36px);
}
.leader-modal__facets { position: absolute; inset: 0; width: 100%; height: 100%; z-index: -2; }
.leader-modal__thread {
  position: absolute; left: 0; bottom: 0; width: 100%; height: 68px; z-index: -1;
  fill: none; stroke: var(--teal-300); stroke-opacity: 0.5; stroke-width: 1.5;
}
.leader-modal__thread circle { fill: var(--teal-300); stroke: none; }

.leader-modal__close {
  position: absolute; top: 16px; right: 16px; z-index: 2;
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.08); color: #fff; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}
.leader-modal__close svg { width: 18px; height: 18px; }
.leader-modal__close:hover { background: rgba(255, 255, 255, 0.18); border-color: var(--teal-300); transform: rotate(90deg); }
.leader-modal__close:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.leader-modal__id { display: flex; gap: clamp(16px, 2.4vw, 24px); align-items: center; padding-right: 48px; }
.leader-modal__id img {
  width: 96px; height: 96px; flex-shrink: 0; border-radius: 50%;
  object-fit: cover; object-position: top center;
  border: 3px solid rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.12);
}
.leader-modal__eyebrow {
  font-family: var(--font-display); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--teal-300); margin: 0 0 8px;
}
.leader-modal__name {
  font-family: var(--font-display); font-size: clamp(1.15rem, 2.2vw, 1.5rem); font-stretch: 110%;
  font-weight: 700; line-height: 1.2; color: #fff; margin: 0;
}
.leader-modal__role { font-size: 0.9rem; color: rgba(255, 255, 255, 0.75); margin: 6px 0 0; }

.leader-modal__body { padding: clamp(26px, 3.4vw, 40px) clamp(24px, 4vw, 44px); position: relative; }
/* Oversized quote mark, tying back to the cards' blockquote rule. */
.leader-modal__body::before {
  content: "\201C"; position: absolute; top: 4px; left: clamp(12px, 2vw, 22px);
  font-family: var(--font-display); font-size: 6rem; line-height: 1;
  color: var(--teal-500); opacity: 0.14; pointer-events: none;
}
.leader-modal__body p {
  position: relative; margin: 0; color: var(--ink-600);
  font-size: 1.02rem; line-height: 1.75;
  border-left: 3px solid var(--teal-500); padding-left: clamp(16px, 2vw, 22px);
}
/* Entrance: lift + settle, matching the site's ease-out-expo motion language. */
.leader-modal.fade .modal-dialog {
  transform: translateY(24px) scale(0.98);
  transition: transform var(--motion-med, 350ms) var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)),
              opacity var(--motion-med, 350ms) var(--ease);
}
.leader-modal.show .modal-dialog { transform: translateY(0) scale(1); }

/* The header must never be squeezed by the scrollable body — it holds the
   name/role and would clip them. Let it keep its intrinsic height. */
.leader-modal .modal-dialog-scrollable .leader-modal__head { flex-shrink: 0; }
.leader-modal__name { overflow-wrap: anywhere; }

@media (max-width: 575px) {
  .leader-modal__id { flex-direction: column; align-items: flex-start; text-align: left; padding-right: 40px; }
  .leader-modal__id img { width: 76px; height: 76px; }
  /* Narrow screens: scroll the whole dialog so the header can't be clipped. */
  .leader-modal .modal-dialog-scrollable { max-height: calc(100% - 1rem); }
  .leader-modal .modal-dialog-scrollable .modal-content { overflow-y: auto; }
  .leader-modal .modal-dialog-scrollable .modal-body { overflow-y: visible; }
}
@media (prefers-reduced-motion: reduce) {
  .leader-modal.fade .modal-dialog { transform: none; transition: none; }
  .leader-card { transition: none; }
  .leader-card:hover { transform: none; }
  .leader-modal__close:hover { transform: none; }
}

.rh-chips { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }
.rh-chip {
  display: inline-flex; align-items: baseline; gap: 8px; padding: 12px 20px;
  border: 1px solid var(--border-dark); border-radius: var(--r-pill);
  font-family: var(--font-display); font-weight: 600; font-size: 0.92rem; color: rgba(255, 255, 255, 0.9);
}
.rh-chip b { font-size: 1.25rem; color: var(--teal-300); font-variant-numeric: tabular-nums; }
.rh-quote { border-left: 3px solid var(--teal-500); padding: 6px 0 6px 20px; margin: 24px 0; color: rgba(255, 255, 255, 0.85); font-size: 1.05rem; }

/* Pillars + The Thread */
.pillars { position: relative; display: grid; gap: 28px; }
.pillars__thread { position: absolute; left: 47px; top: -36px; height: calc(100% + 72px); width: 4px; z-index: 0; pointer-events: none; }
.pillars__thread path { stroke: var(--teal-500); stroke-width: 2.5; fill: none; }
.pillar {
  position: relative; z-index: 1; color: #fff; border-radius: var(--r-lg);
  padding: clamp(28px, 4vw, 44px) clamp(28px, 4vw, 48px) clamp(28px, 4vw, 44px) 96px;
  clip-path: polygon(0 0, calc(100% - 36px) 0, 100% 36px, 100% 100%, 0 100%);
}
.pillar--sustainability { background: linear-gradient(135deg, #14835d, #22b287); }
.pillar--leadership { background: linear-gradient(135deg, #183454, #22476b); }
.pillar--innovation { background: linear-gradient(135deg, #148eb8, #25abd1); }
.pillar__node {
  position: absolute; left: 36px; top: clamp(28px, 4vw, 44px); width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface-0); border: 4px solid var(--teal-500); transform: translateX(-50%);
}
.pillar__kicker { font-size: 0.78rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.75); margin-bottom: 6px; }
.pillar h3 { font-size: 1.5rem; font-stretch: 110%; margin-bottom: 4px; }
.pillar__sub { font-family: var(--font-display); font-weight: 600; color: rgba(255, 255, 255, 0.85); margin-bottom: 14px; }
.pillar p { color: rgba(255, 255, 255, 0.92); margin-bottom: 10px; }
.pillar p:last-child { margin-bottom: 0; font-weight: 600; }
@media (max-width: 640px) {
  .pillar { padding-left: 64px; }
  .pillars__thread { left: 31px; }
  .pillar__node { left: 24px; }
}

.thread-story { position: relative; overflow: hidden; }
.thread-story__svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.thread-story__svg path { stroke: var(--teal-500); stroke-width: 2; fill: none; opacity: 0.8; }
.thread-story .container { position: relative; z-index: 1; }

.luban-media { position: relative; text-align: center; }
.luban-media img { max-width: min(78%, 380px); filter: drop-shadow(0 24px 40px rgba(8, 26, 43, 0.28)); }
@media (prefers-reduced-motion: no-preference) {
  .luban-media img { animation: luban-float 5.5s ease-in-out infinite; }
}
@keyframes luban-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }

/* 20c. SCIENTIFIC PROGRAMME -------------------------------------------------- */
.journey ul { margin-top: 8px; }
/* Nested inside .journey li, which carries padding-top:30px for the timeline
   step dots. That padding must be cleared here, otherwise the absolutely
   positioned dash below pins to the top of a 30px-taller box and reads as an
   orphaned bullet floating above its own text. */
.journey ul li {
  font-size: 0.86rem; color: var(--ink-600); position: relative;
  padding: 0 0 0 16px; margin-bottom: 4px;
}
.journey ul li::before { content: ""; position: absolute; left: 0; top: 0.6em; width: 8px; height: 2px; background: var(--teal-500); }

.tracks-index { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px 32px; }
.tracks-index li {
  position: relative; display: flex; align-items: center; gap: 14px;
  padding: 10px 0; border-bottom: 1px solid var(--border);
  font-family: var(--font-display); font-weight: 600; font-size: 0.98rem; color: var(--ink-900);
  transition: border-color var(--t-med) var(--ease);
}
/* Thumbnail replaces the dot marker, so the dot must not also render. */
.tracks-index li::before { content: none; }
.track-ico {
  flex: 0 0 auto; width: 52px; height: 52px; object-fit: contain;
  border-radius: var(--r-sm); background: var(--surface-1); padding: 2px;
  transition: transform var(--t-med) var(--ease-out), background-color var(--t-med) var(--ease);
}
.tracks-index li:hover { border-bottom-color: var(--teal-500); }
.tracks-index li:hover .track-ico { transform: scale(1.08); background: rgba(35, 173, 158, 0.12); }
@media (max-width: 1023px) { .tracks-index { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px)  { .tracks-index { grid-template-columns: 1fr; } }
@media (max-width: 400px)  { .track-ico { width: 46px; height: 34px; } }
@media (prefers-reduced-motion: reduce) {
  .track-ico, .tracks-index li { transition: none; }
  .tracks-index li:hover .track-ico { transform: none; }
}

.overview-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: stretch; }
.overview-card { background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-lg); padding: clamp(24px, 3vw, 40px); }
.overview-card h3 { font-size: 1.3rem; }
.check-list li { position: relative; padding: 8px 0 8px 30px; font-weight: 500; }
.check-list li::before {
  content: ""; position: absolute; left: 0; top: 12px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--teal-500);
  clip-path: none;
}
.check-list li::after {
  content: ""; position: absolute; left: 4px; top: 17px; width: 10px; height: 6px;
  border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(-45deg);
}
@media (max-width: 1023px) { .overview-grid { grid-template-columns: 1fr; } }

.status-chip {
  display: inline-flex; align-items: center; gap: 10px; padding: 12px 22px;
  border: 1.5px dashed var(--teal-300); border-radius: var(--r-pill);
  font-family: var(--font-display); font-weight: 600; font-size: 0.92rem; color: var(--teal-300);
  max-width: 100%; /* never push past the container on narrow screens */
}
.status-chip::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--teal-300);
  flex: 0 0 auto; /* dot must not shrink when the label wraps */
}

/* Numbered because the four dates are a sequence — the order is the
   information, not decoration. The counter lives on the grid so the markup
   stays plain; nothing has to hardcode "1". */
.dates-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; counter-reset: rhic-date; }
.date-card {
  position: relative; counter-increment: rhic-date;
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  /* Top padding clears the badge: the card is centred, so without the reserved
     band a long date would run underneath the number. */
  padding: 40px 20px 22px; text-align: center;
}
.date-card::before {
  content: counter(rhic-date);
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  width: 26px; height: 26px; border-radius: 50%;
  font-family: var(--font-display); font-weight: 700; font-size: 0.82rem;
  line-height: 26px; text-align: center;
  color: var(--ink-400); background: var(--surface-1); border: 1px solid var(--border);
}
.date-card--hot::before {
  color: #fff; background: var(--teal-600); border-color: var(--teal-600);
}
.date-card .date { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--navy-700); margin-bottom: 6px; }
.date-card h3, .date-card h4 { font-size: 0.88rem; color: var(--ink-600); font-weight: 500; font-family: var(--font-body); margin: 0; }
.date-card--hot { border-color: var(--teal-500); box-shadow: var(--shadow-1); }
.date-card--hot .date { color: var(--teal-600); }
/* Four cards divide evenly into 2x2, so the last one no longer needs to span
   the row (it did when there were five). */
@media (max-width: 1023px) { .dates-grid { grid-template-columns: repeat(2, 1fr); } }
/* Two columns leave ~150px per card, which is not enough for a label like
   "Final Presentation Submission" beside a date. */
@media (max-width: 520px)  { .dates-grid { grid-template-columns: 1fr; gap: 12px; } }

/* Abstract submission tabs (restyled legacy block) */
.submission-categories { padding-block: var(--section-y); background: var(--surface-1); }
.submission-categories .section-heading h2 { font-size: clamp(1.9rem, 3.5vw, 2.75rem); font-stretch: 110%; }
.submission-categories .section-heading p { color: var(--ink-600); max-width: 46em; margin-inline: auto; }
/* Bootstrap's .nav sets flex-wrap: wrap. Combined with the fixed max-height
   below, the column would wrap into a SECOND column once the tracks exceeded
   620px — doubling the width and giving the rail a horizontal scrollbar, with
   the left-hand cards clipped out of view. The list must only ever scroll
   down, so wrapping is turned off and overflow-x is pinned to hidden
   (setting overflow-y alone computes the other axis to auto). */
.track-tabs {
  gap: 8px;
  max-height: 620px;
  flex-wrap: nowrap;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 6px;
  scrollbar-width: thin;
}
.track-tabs .nav-link {
  text-align: left; width: 100%; background: var(--surface-0); border: 1px solid var(--border);
  border-radius: 12px; color: var(--ink-900); font-family: var(--font-display); font-weight: 600;
  font-size: 0.93rem; padding: 14px 18px; transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.track-tabs .nav-link:hover { border-color: var(--teal-500); background: var(--surface-0); color: var(--ink-900); }
.track-tabs .nav-link.active { background: var(--navy-700); border-color: var(--navy-700); color: #fff; }
.submission-categories .tab-content {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: clamp(24px, 3vw, 40px); height: 100%;
}
.track-content h3 { font-size: 1.35rem; margin-bottom: 20px; }
.content-block { margin-bottom: 22px; }
.content-block:last-child { margin-bottom: 0; }
.content-block h4, .content-block h5 {
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--teal-600); margin-bottom: 8px;
}
.content-block p { color: var(--ink-600); margin-bottom: 0; font-size: 0.97rem; }
.content-block ul li { position: relative; padding-left: 20px; margin-bottom: 5px; color: var(--ink-600); font-size: 0.97rem; }
.content-block ul li::before { content: ""; position: absolute; left: 0; top: 0.62em; width: 10px; height: 2px; background: var(--teal-500); }
.submission-note { text-align: center; margin-top: 28px; color: var(--ink-400); font-size: 0.92rem; }
@media (max-width: 991px) { .track-tabs { max-height: none; flex-direction: row !important; flex-wrap: wrap; overflow: visible; margin-bottom: 18px; } .track-tabs .nav-link { width: auto; } }

/* Submission guidelines (restyled legacy block) */
.submission-guidelines { padding-block: var(--section-y); }
@media (max-width: 767px) {
  .submission-guidelines .row, .submission-categories .row { --bs-gutter-x: 1.5rem; }
}
.guideline-card, .presentation-wrapper { background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-lg); padding: clamp(24px, 3vw, 40px); height: 100%; }
.guideline-card h3, .presentation-wrapper h3 { font-size: 1.3rem; margin-bottom: 18px; }
.guideline-list li { display: flex; gap: 12px; align-items: baseline; padding: 8px 0; color: var(--ink-600); }
.guideline-list li i { color: var(--teal-500); }
.presentation-card { display: flex; gap: 16px; align-items: flex-start; padding: 16px 0; border-top: 1px solid var(--border); }
.presentation-card:first-of-type { border-top: 0; }
.presentation-card .icon {
  flex-shrink: 0; width: 46px; height: 46px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
  background: rgba(35, 173, 158, 0.12); color: var(--teal-600); font-size: 1.1rem;
}
.presentation-card h4 { font-size: 1.05rem; margin-bottom: 4px; }
.presentation-card p { color: var(--ink-600); font-size: 0.95rem; margin: 0; }

/* 20d. WORKSHOPS + REGISTRATION SHELLS -------------------------------------- */
/* Fixed height with the form scrolling inside it — deliberately NOT auto-sized
   to the content.

   Auto-sizing did work: the frame grew to the form's full height and the page
   scrolled as one. But the workshops form opens its cart and checkout as
   `position: fixed` overlays, and a fixed element is positioned against the
   iframe's own viewport. Once that viewport was the entire 7000px document,
   those overlays were laid out far outside what the reader could see — the
   cart appeared not to open at all.

   So the frame keeps a viewport of its own. The embedded page's overlays land
   inside it, where they belong. The height message is still handled (it lifts
   the "failed" notice and proves the embed is alive); it just no longer sets
   the height. */
/* Height is budgeted against what the *pinned* header actually leaves.
   The header is sticky, so once the reader scrolls into the form it never
   gives its height back: 128px on desktop (76px brand row + 51px nav row) and
   64px below 1024px, where the nav collapses into a drawer. Subtracting only
   a flat 150px understated that by ~30px on desktop and, worse, the 760px
   minimum exceeded the usable space entirely on short laptops (800px tall →
   672px usable), which is what produced the frame-inside-page double scroll.

   --embed-chrome is that reservation: pinned header + the shell's own padding
   and breathing room below, so the frame ends just above the fold and the two
   scrollbars never compete. The floor is deliberately low (460px) — on a short
   screen a slightly shorter frame that fits beats a taller one that forces the
   page to scroll as well. The ceiling keeps the frame from becoming an
   unreadably tall column on a large monitor. */
:root { --embed-chrome: 172px; }
@media (max-width: 1023px) { :root { --embed-chrome: 104px; } }

.embed-shell iframe {
  height: clamp(460px, calc(100svh - var(--embed-chrome)), 1000px);
}
/* The workshops app carries its own internal toolbar, so it earns a little
   more room than the plain registration form. */
.embed-shell--app iframe {
  height: clamp(500px, calc(100svh - var(--embed-chrome) + 40px), 1100px);
}
.embed-wrap { max-width: 1240px; margin-inline: auto; }
.embed-wrap--form { max-width: 960px; }
@media (max-width: 767px) {
  .embed-wrap { padding-inline: 0; }
  .embed-wrap .embed-shell { border-radius: 0; border-left: 0; border-right: 0; }
}

.note-card {
  background: var(--surface-0); border: 1px solid var(--border); border-left: 4px solid var(--teal-500);
  border-radius: var(--r-md); padding: 26px 28px;
}
.note-card h3, .note-card h4 { font-size: 1.08rem; margin-bottom: 10px; }
.note-card ul li { display: flex; gap: 10px; align-items: baseline; padding: 5px 0; color: var(--ink-600); font-size: 0.95rem; }
.note-card ul li i { color: var(--teal-600); }
/* `.note-card ul li` is display:flex, built for the icon lists used elsewhere in the card;
   that removes the bullet marker. The registration-instructions lists (register.php) are
   plain prose bullets, so they opt out and keep normal list rendering. */
.note-card ul.note-card__bullets li { display: list-item; gap: 0; padding: 0 0 6px; }
.note-card ul.note-card__bullets { list-style: disc; }

.info-chips { display: flex; flex-wrap: wrap; gap: 12px 28px; border-block: 1px solid var(--border); padding-block: 16px; margin-bottom: 26px; }
.info-chips .meta__label { display: block; font-family: var(--font-display); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-400); margin-bottom: 3px; }
.info-chips .meta__value { font-family: var(--font-display); font-weight: 600; font-size: 1rem; color: var(--ink-900); }

/* 20e. REGISTRATION --------------------------------------------------------- */
.price-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.price-card {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 28px; display: flex; flex-direction: column; gap: 6px;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}
.price-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-2); border-color: var(--teal-500); }
.price-card--featured { border-color: var(--teal-500); box-shadow: var(--shadow-1); }
.price-card__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.price-card__icon {
  width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
  background: rgba(35, 173, 158, 0.12); color: var(--teal-600); font-size: 1.15rem;
}
.price-card__amount { display: flex; align-items: baseline; gap: 6px; font-family: var(--font-display); }
.price-card__amount small { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.08em; color: var(--ink-400); }
.price-card__amount b { font-size: 2.2rem; font-weight: 800; color: var(--navy-700); font-variant-numeric: tabular-nums; }
.price-card h3 { font-size: 1.08rem; margin: 0; }
/* Eligibility note — only some categories carry one. */
.price-card__note {
  margin: 8px 0 0; font-size: 0.845rem; line-height: 1.55; color: var(--ink-400);
}
.price-card__note b { color: var(--ink-600); font-weight: 600; }
/* Only some categories carry a note, so the text blocks differ in height. A
   spacer with `margin-top: auto` absorbs the difference and drops the button to
   the bottom, keeping the buttons aligned across a grid row. Applied to the
   button itself, `auto` would collapse the 16px gap on the tallest card — hence
   the explicit floor via padding on the card instead. */
.price-card .btn { margin-top: auto; align-self: flex-start; }
.price-card h3, .price-card__note { padding-bottom: 16px; }
.price-card__note { padding-bottom: 16px; margin-bottom: 0; }
@media (max-width: 1023px) { .price-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px)  { .price-grid { grid-template-columns: 1fr; } }

/* FEE TABLE ---------------------------------------------------------------
   Three categories read against three registration periods. A real <table>
   because the row/column relationship is the content, not decoration — the
   markup gives screen readers the header association for free.

   The wrapper scrolls, not the page: without `overflow-x: auto` here the
   table's min-width would widen <body> on a phone and reintroduce the
   sitewide horizontal scroll the announce bar already cost us once. */
.fee-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-0);
  box-shadow: var(--shadow-1);
}
.fee-table {
  width: 100%;
  min-width: 640px;              /* below this the prices collide; wrapper scrolls */
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.fee-table th, .fee-table td {
  padding: 18px 20px;
  text-align: center;
  border-bottom: 1px solid var(--border);
}
.fee-table thead th {
  background: var(--surface-1);
  /* middle, not bottom: the Early Bird cell is the tallest (two date lines plus the CURRENT
     pill), and bottom-aligning dropped "Registration Category" to the floor of a header that
     tall, leaving a band of empty space above it. */
  vertical-align: middle;
  position: relative;
  font-family: var(--font-display);
}
.fee-table thead th:first-child { text-align: left; }
.fee-table__period {
  display: block; font-size: 1rem; font-weight: 700; color: var(--navy-700);
}
.fee-table__dates {
  display: block; margin-top: 4px;
  font-family: var(--font-body);
  font-size: 0.8rem; font-weight: 500; color: var(--ink-400);
  /* A date range reads as one unit; letting it break mid-range ("1 October – 30 / November")
     is what made the header three lines tall and the columns uneven. */
  text-wrap: balance;
}
.fee-table__badge {
  display: inline-block; margin-top: 8px;
  padding: 3px 10px; border-radius: 999px;
  background: var(--teal-500); color: #fff;
  font-family: var(--font-body);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
/* Row headers are the category names — left-aligned, they read as labels.
   NOT display:flex. A flex <th> leaves the table layout algorithm entirely, so the browser stops
   sizing that column against the others: the category cell grew to swallow the row and pushed the
   price columns into whatever space was left. The icon gets inline-grid instead, which keeps it
   on the label's line without taking the cell out of the table. */
.fee-table tbody th {
  text-align: left; font-size: 0.98rem; font-weight: 600; color: var(--ink-900);
  vertical-align: middle;
}
/* One stated width is enough: the three period columns then divide the rest evenly, instead of
   each sizing itself to its own longest date string. */
.fee-table thead th:first-child,
.fee-table tbody th { width: 38%; }
.fee-table__icon {
  flex: none; width: 34px; height: 34px; border-radius: 10px;
  display: inline-grid; place-items: center; vertical-align: middle;
  margin-right: 10px;
  background: var(--surface-1); color: var(--teal-600); font-size: 1rem;
}
.fee-table__amount { display: inline-flex; align-items: baseline; gap: 5px; font-family: var(--font-display); }
.fee-table__amount small { font-size: 0.72rem; font-weight: 600; letter-spacing: 0.08em; color: var(--ink-400); }
.fee-table__amount b { font-size: 1.6rem; font-weight: 800; color: var(--navy-700); }
.fee-table tbody tr:last-child th, .fee-table tbody tr:last-child td { border-bottom: 0; }

/* The live period, tinted down the whole column. */
.fee-table th.is-current, .fee-table td.is-current { background: rgba(35, 173, 158, 0.07); }
.fee-table thead th.is-current { box-shadow: inset 0 3px 0 var(--teal-500); }
.fee-table td.is-current .fee-table__amount b { color: var(--teal-600); }

.fee-status {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  flex-wrap: wrap; text-align: center;
  margin: 0 0 22px; padding: 12px 18px;
  border: 1px solid rgba(35, 173, 158, 0.3); border-radius: 999px;
  background: rgba(35, 173, 158, 0.08);
  color: var(--ink-600); font-size: 0.94rem;
}
.fee-status b { color: var(--navy-700); }
.fee-status__dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--teal-500);
  box-shadow: 0 0 0 4px rgba(35, 173, 158, 0.18);
}
.fee-status--closed {
  border-color: var(--border); background: var(--surface-1);
}
.fee-actions { display: flex; justify-content: center; margin-top: 26px; }
.fee-subhead {
  margin: 46px 0 20px; text-align: center;
  font-size: 1.25rem; color: var(--navy-700);
}
.fee-note {
  margin: 26px auto 0; max-width: 760px; text-align: center;
  color: var(--ink-400); font-size: 0.9rem; line-height: 1.6;
}
@media (max-width: 640px) {
  .fee-table th, .fee-table td { padding: 14px 14px; }
  .fee-table__amount b { font-size: 1.4rem; }
  .fee-status { border-radius: var(--r-md); }
}

.mini-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.mini-card {
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 20px; display: flex; align-items: center; gap: 14px;
}
.mini-card .icon {
  flex-shrink: 0; width: 42px; height: 42px; border-radius: 11px; display: flex; align-items: center; justify-content: center;
  background: rgba(35, 173, 158, 0.12); color: var(--teal-600); font-size: 1.05rem;
}
.mini-card h3 { font-family: var(--font-display); font-weight: 600; font-size: 0.94rem; margin: 0; }
@media (max-width: 1023px) { .mini-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px)  { .mini-grid { grid-template-columns: 1fr; } }

.doc-list { display: grid; gap: 8px; margin-top: 10px; }
.journey .doc-list li,
.doc-list li {
  display: flex; gap: 10px; align-items: baseline; color: var(--ink-600); font-size: 0.93rem;
  padding: 0; margin: 0; position: static;
}
/* The bi-icon is this list's marker — suppress the inherited .journey ul li
   dash, which would otherwise render as a second, orphaned bullet. Needs to
   out-specify `.journey ul li::before`. */
.journey ul.doc-list li::before,
.doc-list li::before { content: none; display: none; }
.doc-list li i { color: var(--teal-600); flex: 0 0 auto; line-height: 1.5; }

/* 20b. QA MODE (dev screenshots: ?qa=1) ------------------------------------- */
body.qa-mode *, body.qa-mode *::before, body.qa-mode *::after { animation: none !important; transition: none !important; }
body.qa-mode .reveal, body.qa-mode .hero__stagger { opacity: 1 !important; transform: none !important; }
.js body.qa-mode .hero__title .line > span, body.qa-mode .hero__title .line > span { transform: none !important; }
body.qa-mode .hero__media { opacity: 1 !important; transform: none !important; }

/* ==========================================================================
   MOTION 2.0 — signature layer (GSAP-owned; CSS provides fallbacks + states)
   ========================================================================== */
:root {
  --motion-fast: 180ms;
  --motion-med: 350ms;
  --motion-slow: 700ms;
  --motion-cine: 1200ms;
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
}

/* When GSAP is active it owns the hero — disable the CSS keyframe fallbacks */
.has-gsap .hero.is-ready .hero__title .line > span,
.has-gsap .hero.is-ready .hero__stagger,
.has-gsap .hero.is-ready .hero__media { animation: none; }
.has-gsap .hero__facet video, .has-gsap .hero__facet img { will-change: transform; }

/* Journey: scrub-driven progress line + sequential node activation */
.journey ol::before {
  background: linear-gradient(90deg, var(--teal-500) calc(var(--jp, 0) * 100%), var(--border) calc(var(--jp, 0) * 100%));
}
.journey li::before {
  transition: transform var(--motion-med) var(--ease-out-quint), box-shadow var(--motion-med) var(--ease),
    background-color var(--motion-med) var(--ease), border-color var(--motion-med) var(--ease);
}
.journey li.is-active::before {
  background: var(--teal-500); transform: scale(1.2);
  box-shadow: 0 0 0 7px rgba(35, 173, 158, 0.16);
}
.journey li .journey__day { transition: color var(--motion-med) var(--ease); }
.journey li.is-active .journey__day { color: var(--teal-600); }
@media (max-width: 1023px) {
  .journey ol::before, .journey ol.journey-cols-4::before {
    background: linear-gradient(180deg, var(--teal-500) calc(var(--jp, 0) * 100%), var(--border) calc(var(--jp, 0) * 100%));
  }
}

/* Tracks: spotlight field + node-dot hover (no layout shift) */
.tracks-spot { position: relative; }
.tracks-spot::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 30%), rgba(35, 173, 158, 0.12), transparent 70%);
  transition: opacity 0.4s var(--ease);
}
.tracks-spot.is-lit::before { opacity: 1; }
.track-chip { position: relative; transition: border-color var(--motion-fast) var(--ease), background-color var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease); }
.track-chip::before {
  content: ""; position: absolute; left: 11px; top: 50%; width: 6px; height: 6px; margin-top: -3px;
  border-radius: 50%; background: var(--teal-300); opacity: 0; transform: scale(0.4);
  transition: opacity var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease-out-quint);
}
a.track-chip:hover, a.track-chip:focus-visible { transform: translateY(-2px); }
a.track-chip:hover::before, a.track-chip:focus-visible::before { opacity: 1; transform: scale(1); }

/* About pillars: scroll-story focus states (GSAP toggles .is-lit) */
.has-gsap .pillar { opacity: 0.45; transform: scale(0.985); transition: opacity 0.55s var(--ease-out-quint), transform 0.55s var(--ease-out-quint); }
.has-gsap .pillar.is-lit { opacity: 1; transform: scale(1); }
.has-gsap .pillar .pillar__node { transition: box-shadow 0.4s var(--ease), transform 0.4s var(--ease-out-quint); }
.has-gsap .pillar.is-lit .pillar__node { box-shadow: 0 0 0 8px rgba(35, 173, 158, 0.18); transform: translateX(-50%) scale(1.12); }

/* Closing CTA: thread arrival */
.closing__thread { position: absolute; left: 50%; top: 0; transform: translateX(-50%); height: 130px; width: 40px; overflow: visible; }
.closing__thread path { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.closing__thread circle { fill: var(--teal-300); }

/* Buttons: press + focus polish */
.btn:active { transform: scale(0.985); }

/* Nav: current page indicator */
.nav__link[aria-current="page"] { color: #fff; }
.nav__link[aria-current="page"]::after { right: 13px; }

/* Mobile menu: wipe reveal instead of plain fade */
@media (max-width: 1023px) {
  #site-nav.nav {
    clip-path: inset(0 0 100% 0);
    transition: clip-path 0.55s var(--ease-out-expo), opacity 0.3s var(--ease), visibility 0.55s;
  }
  body.nav-open #site-nav.nav { clip-path: inset(0 0 0% 0); }
}

/* Page-hero entrance states (GSAP-owned) */
.has-gsap .page-hero__bg img { will-change: transform; }

/* ==========================================================================
   MOTION 3.0 — high-intensity layer (text-fill, intro, transitions)
   ========================================================================== */

/* Scroll-driven text fill — signature typographic system.
   --fill scrubbed 0%→100% by motion.js on [data-tfill] elements. */
.has-gsap [data-tfill] {
  --fill: 0%;
  background-image: linear-gradient(90deg, var(--tf-on) var(--fill), var(--tf-off) var(--fill));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.has-gsap [data-tfill="up"] {
  background-image: linear-gradient(0deg, var(--tf-on) var(--fill), var(--tf-off) var(--fill));
}
/* palette per context */
[data-tfill] { --tf-on: var(--navy-700); --tf-off: #b9c9d6; }
.section--dark [data-tfill], .oman [data-tfill], .closing [data-tfill], .cd-band [data-tfill] {
  --tf-on: var(--teal-300); --tf-off: rgba(255, 255, 255, 0.28);
}
.pillar [data-tfill] { --tf-on: #ffffff; --tf-off: rgba(255, 255, 255, 0.38); }
/* outline variant for giant editorial moments */
.has-gsap [data-tfill].tfill-outline { -webkit-text-stroke: 1px rgba(255, 255, 255, 0.35); }
/* fallbacks: readable final state without GSAP / with reduced motion */
html:not(.has-gsap) [data-tfill] { background: none; color: inherit; -webkit-text-fill-color: currentColor; }
@media (prefers-reduced-motion: reduce) {
  [data-tfill] { background: none !important; color: inherit !important; -webkit-text-fill-color: currentColor !important; }
}

/* Journey dates fill sequentially with the progress line (set via --dfill) */
.has-gsap .journey__date {
  background-image: linear-gradient(90deg, var(--teal-600) var(--dfill, 0%), var(--ink-900) var(--dfill, 0%));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
@media (prefers-reduced-motion: reduce) {
  .has-gsap .journey__date { background: none; color: var(--ink-900); -webkit-text-fill-color: currentColor; }
}

/* Tracks: living-system hover — siblings recede, target lifts */
@media (hover: hover) {
  .tracks-cta:hover .track-chip:not(:hover) { opacity: 0.45; }
  .track-chip { transition: border-color var(--motion-fast) var(--ease), background-color var(--motion-fast) var(--ease),
    color var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease), opacity var(--motion-med) var(--ease); }
}

/* Branded intro overlay (≤1s, skipped on revisit + reduced motion + no JS) */
.intro {
  position: fixed; inset: 0; z-index: 400; background: var(--navy-900);
  display: flex; align-items: center; justify-content: center; pointer-events: none;
}
.intro { flex-direction: column; }
.intro svg { width: min(230px, 54vw); height: auto; overflow: visible; }
/* The finished artwork, stacked exactly over the outline the SVG draws.
   Hidden until motion.js fades it in; if JS never runs, the whole overlay is
   already display:none (html:not(.has-gsap) .intro below). */
.intro [data-intro-logo] {
  position: absolute; width: min(230px, 54vw); height: auto; opacity: 0;
}
.intro path { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.intro [data-intro-mark] { filter: drop-shadow(0 0 5px rgba(35, 173, 158, 0.4)); }
.intro [data-intro-comet] {
  stroke: #ffffff; stroke-width: 3.5; stroke-linecap: round; opacity: 0;
  filter: drop-shadow(0 0 9px rgba(126, 217, 205, 1));
}
.intro circle { fill: var(--teal-300); }
.intro [data-intro-node] { filter: drop-shadow(0 0 8px rgba(126, 217, 205, 0.9)); }
.intro [data-intro-pulse] { opacity: 0; fill: #ffffff; filter: drop-shadow(0 0 8px rgba(126, 217, 205, 1)); }
.intro [data-intro-pulse] { r: 3; }
html:not(.js) .intro, html:not(.has-gsap) .intro { display: none; }
@media (prefers-reduced-motion: reduce) { .intro { display: none; } }
/* absolute failsafe: overlay can never trap the page */
.intro { animation: intro-failsafe 0.3s var(--ease) 2.4s forwards; }
@keyframes intro-failsafe { to { opacity: 0; visibility: hidden; } }

/* Mobile menu: thread line accompanies the wipe */
@media (max-width: 1023px) {
  #site-nav.nav::before {
    content: ""; position: absolute; left: var(--gutter); top: calc(var(--header-h) + 20px); bottom: 40px;
    width: 2px; background: linear-gradient(180deg, var(--teal-500), transparent);
    transform: scaleY(0); transform-origin: top;
    transition: transform 0.7s var(--ease-out-expo) 0.15s;
  }
  body.nav-open #site-nav.nav::before { transform: scaleY(1); }
  .nav__list { position: relative; padding-left: 18px; }
}

/* Scientific network (about thread-story) */
.net-node { fill: var(--teal-300); }
.thread-story__svg .net-line { stroke: var(--teal-500); stroke-opacity: 0.55; stroke-width: 1.5; fill: none; }

/* Footer social: inline SVG glyphs */
.social-row a svg { width: 17px; height: 17px; display: block; }

/* ==========================================================================
   MOTION 4.0 — cinematic scene layer (desktop pins, stages, ghost type)
   ========================================================================== */

/* Tracks → typography wall (desktop, GSAP present) */
@media (min-width: 1024px) {
  .has-gsap .tracks-cta { gap: 6px 34px; }
  .has-gsap .tracks-cta .track-chip {
    border: 0; background: transparent; border-radius: 0;
    font-size: clamp(1.5rem, 2.2vw, 2.1rem); font-stretch: 108%; font-weight: 700;
    padding: 10px 4px 10px 26px; letter-spacing: 0.005em;
  }
  .has-gsap .tracks-cta .track-chip::before { left: 4px; width: 8px; height: 8px; margin-top: -4px; }
  .has-gsap .tracks-cta a.track-chip:hover { background: transparent; transform: translateX(6px); }
  .has-gsap .tracks-cta .track-chip--more { font-size: 1.05rem; align-self: center; border: 1px dashed var(--border-dark); border-radius: var(--r-pill); padding: 12px 22px; }
}

/* Journey ghost date — giant watermark digits behind the timeline */
.journey .container { position: relative; }
.journey ol { position: relative; z-index: 1; }
.journey__ghost {
  position: absolute; right: 2vw; top: 46%; transform: translateY(-50%);
  font-family: var(--font-display); font-weight: 800; font-stretch: 110%;
  font-size: clamp(180px, 22vw, 300px); line-height: 1; pointer-events: none;
  color: transparent; -webkit-text-stroke: 1.5px rgba(18, 65, 102, 0.13);
  z-index: 0; opacity: 0;
}

/* Oman takeover scale (desktop)
   The cinematic scene used to pin a fixed slat height, which re-cropped the
   220x800 source. Client asked for the full photo in a single row, so the
   height is left to aspect-ratio and the scene keeps only its scroll effects. */
.oman__lead { display: block; width: 100%; height: 44px; margin-top: clamp(28px, 4vw, 48px); }
.oman__lead path { stroke: var(--teal-500); stroke-width: 2; fill: none; }

/* Pillars cinematic stage (desktop pinned scene; mobile keeps stacked flow) */
.pillars__branch { display: none; }
.pillars--stage { height: 100vh; min-height: 660px; display: block; }
.pillars--stage .pillar {
  position: absolute; left: 50%; top: 50%; width: min(860px, 90%);
  margin: 0; will-change: transform;
  /* GSAP owns transform/opacity on the stage. Stated here too so a pillar is
     never left invisible by the .reveal defaults if the timeline has not run
     yet (or the script failed) — an absolutely-positioned card stuck at
     opacity:0 leaves a blank slot and no way to recover it. */
  opacity: 1;
}
.pillars--stage .pillar__node { display: none; }
.pillars--stage .pillars__thread { display: none; }
.pillars--stage .pillars__branch {
  display: block; position: absolute; inset: -4% 0; width: 100%; height: 108%; z-index: 0;
}
.pillars__branch path { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.pillars--stage .pillar { z-index: 1; }

/* Closing echo typography (layered depth + hover split) */
.closing .container { position: relative; }
.closing__echo {
  position: absolute; left: 0; width: 100%; text-align: center; pointer-events: none;
  font-family: var(--font-display); font-weight: 700; font-stretch: 112%;
  font-size: clamp(2rem, 4.4vw, 3.2rem); line-height: 1.12; margin: 0;
  z-index: 0; white-space: nowrap;
}
.closing__echo--a { color: rgba(126, 217, 205, 0.14); }
.closing__echo--b { color: rgba(255, 255, 255, 0.07); }
.closing .section-title { position: relative; z-index: 1; }

/* Internal page-hero decorative layer (injected by motion.js) */
.page-hero__deco { position: absolute; right: -3%; top: -18%; height: 140%; opacity: 0.55; pointer-events: none; }
.page-hero__deco path { fill: none; stroke-width: 1.5; }
.page-hero__deco .deco-facet { stroke: rgba(126, 217, 205, 0.16); }
.page-hero__deco .deco-thread { stroke: var(--teal-500); stroke-width: 2; }
.page-hero__deco circle { fill: var(--teal-300); }

/* ==========================================================================
   MOTION 6.0 — flip cards, body-text fill, why-stage, contact thread
   ========================================================================== */

/* Scroll-driven BODY text fill (applied by motion.js as [data-bfill]) */
.has-gsap [data-bfill] {
  --fill: 0%;
  background-image: linear-gradient(90deg, var(--bf-on) var(--fill), var(--bf-off) var(--fill));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
[data-bfill] { --bf-on: var(--ink-600); --bf-off: #c5d3df; }
.section--dark [data-bfill], .oman [data-bfill], .closing [data-bfill], .cd-band [data-bfill],
.band [data-bfill], .thread-story [data-bfill], .pillar [data-bfill], .coming-soon [data-bfill],
.exp-banner__panel [data-bfill], .page-hero [data-bfill] {
  --bf-on: rgba(255, 255, 255, 0.86); --bf-off: rgba(255, 255, 255, 0.3);
}
/* word-split variant: parent renders normally, each word fills in sequence
   while staying inline — natural wrapping is untouched */
.has-gsap [data-bfill].is-split { background: none; color: inherit; -webkit-text-fill-color: currentColor; }
.has-gsap [data-bfill].is-split .bfill-word {
  background-image: linear-gradient(90deg, var(--bf-on) var(--fill), var(--bf-off) var(--fill));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
/* Word spans must stay inline so they wrap normally. Inside a flex parent
   (e.g. .status-chip) each word would otherwise become a non-wrapping flex
   item and spill past the container on narrow screens. */
.has-gsap [data-bfill].is-split .bfill-word { display: inline; }
.status-chip.is-split { flex-wrap: wrap; row-gap: 2px; }

/* Submissions are open, so this is a live state rather than a "coming soon"
   placeholder: solid border, and the dot pulses instead of sitting inert. */
.status-chip--live {
  border-style: solid;
  border-color: rgba(126, 217, 205, 0.55);
  background: rgba(35, 173, 158, 0.1);
}
.status-chip--live::before { animation: chip-pulse 2.4s var(--ease) infinite; }
@keyframes chip-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(126, 217, 205, 0.55); }
  70%      { box-shadow: 0 0 0 7px rgba(126, 217, 205, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .status-chip--live::before { animation: none; }
}
html:not(.has-gsap) [data-bfill] { background: none; color: inherit; -webkit-text-fill-color: currentColor; }
@media (prefers-reduced-motion: reduce) {
  [data-bfill], [data-bfill] .bfill-line, [data-bfill] .bfill-word { background: none !important; color: inherit !important; -webkit-text-fill-color: currentColor !important; }
}

/* Flip cards (built by motion.js from [data-flip] feature cards) */
.flip-card { perspective: 1100px; min-height: 252px; padding: 0 !important; cursor: pointer; }
.flip-card:focus-visible { box-shadow: var(--focus-ring); }
.flip-inner {
  position: relative; width: 100%; height: 100%; min-height: inherit;
  transform-style: preserve-3d;
  transition: transform 0.75s var(--ease-out-expo);
}
.flip-card.is-flipped .flip-inner { transform: rotateY(180deg); }
@media (hover: hover) {
  .flip-card:hover .flip-inner, .flip-card:focus-visible .flip-inner { transform: rotateY(180deg); }
}
.flip-face {
  position: absolute; inset: 0; border-radius: var(--r-md);
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  display: flex; flex-direction: column; justify-content: center;
  padding: 28px;
}
.flip-front .feature-card__icon { margin-bottom: 18px; }
.flip-front h3 { margin-bottom: 0; }
.flip-front::after {
  content: ""; position: absolute; right: 16px; bottom: 14px; width: 18px; height: 18px;
  border-right: 2px solid var(--teal-500); border-bottom: 2px solid var(--teal-500);
  border-bottom-right-radius: 6px; opacity: 0.6;
}
.flip-back {
  transform: rotateY(180deg);
  background: linear-gradient(150deg, var(--navy-700), #16645f);
  color: #fff;
}
.flip-back h4 {
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--teal-300); margin-bottom: 10px;
}
.flip-back p { color: rgba(255, 255, 255, 0.92); margin: 0; font-size: 0.95rem; }
@media (prefers-reduced-motion: reduce) {
  .flip-inner, .flip-card:hover .flip-inner { transition: none; }
}

/* Why-Attend pinned stage (desktop, built by motion.js) */
/* Height of the pinned stage. Frames are centred in it, so any height beyond
   the tallest frame reads as dead space under the heading — keep it just
   above the two-card frames rather than filling the viewport. */
.why-stage { position: relative; height: 60vh; min-height: 430px; overflow: hidden; }
.why-frame {
  position: absolute; left: 50%; top: 50%; width: min(960px, 94%);
  display: grid; grid-template-columns: 1fr 1fr; gap: 24px; z-index: 1;
}
.why-frame::before {
  content: attr(data-num); position: absolute; right: -13%; top: 50%; transform: translateY(-50%);
  font-family: var(--font-display); font-weight: 800; font-stretch: 110%;
  font-size: clamp(150px, 15vw, 230px); line-height: 1; pointer-events: none;
  color: transparent; -webkit-text-stroke: 1.5px rgba(18, 65, 102, 0.14); z-index: -1;
}

/* Contact: connecting thread behind the contact cards.
   Four cards (email, general phone, sponsorship phone, venue) sit as a 2x2
   rather than inheriting the shared three-column .feature-grid, which would
   strand the fourth card alone on a second row. The thread is centred on the
   gap between the two rows. */
.contact-grid-wrap { position: relative; }
.contact-grid-wrap .feature-grid { position: relative; z-index: 1; grid-template-columns: repeat(2, 1fr); }
@media (max-width: 640px) { .contact-grid-wrap .feature-grid { grid-template-columns: 1fr; } }
.contact-thread { position: absolute; left: 2%; width: 96%; top: 50%; height: 44px; z-index: 0; overflow: hidden; transform: translateY(-50%); }
.contact-thread path { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.contact-thread circle { fill: var(--teal-300); }

/* Premium button label-roll (text rolls out upward, duplicate rolls in) */
.btn .btn__label { position: relative; display: inline-block; overflow: hidden; vertical-align: baseline; }
.btn .btn__label-inner { display: block; transition: transform 0.5s var(--ease-out-expo); will-change: transform; }
.btn .btn__label-inner--dup { position: absolute; inset: 0; transform: translateY(115%); }
@media (hover: hover) {
  .btn:hover .btn__label-inner:not(.btn__label-inner--dup) { transform: translateY(-115%); }
  .btn:hover .btn__label-inner--dup { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .btn .btn__label-inner { transition: none; }
  .btn:hover .btn__label-inner:not(.btn__label-inner--dup) { transform: none; }
  .btn:hover .btn__label-inner--dup { transform: translateY(115%); }
}

/* ==========================================================================
   MOTION 7.0 — track rails, stage guards, stats sweep
   ========================================================================== */

/* Track chips as two opposing rails (theme unchanged: navy field, teal accents) */
/* rails break out of the container so they read as full-bleed marquees */
.tracks-rails { display: block; width: 100vw; margin-left: calc(50% - 50vw); }
.track-rail { overflow: hidden; margin-bottom: 14px; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.track-lane { display: flex; gap: 12px; width: max-content; will-change: transform; }
.track-lane .track-chip { flex: 0 0 auto; }
.tracks-more { margin-top: 22px; padding-left: max(var(--gutter), calc(50vw - var(--container) / 2)); }
@media (max-width: 767px) {
  .track-rail { margin-bottom: 10px; }
  .track-lane { gap: 8px; }
  .track-lane .track-chip { font-size: 0.9rem; padding: 11px 18px; }
}
/* desktop typography-wall styling must not fight the rails */
@media (min-width: 1024px) {
  .has-gsap .tracks-rails .track-chip {
    border: 1px solid var(--border-dark); border-radius: var(--r-pill);
    background: rgba(255, 255, 255, 0.02);
    font-size: clamp(1rem, 1.15vw, 1.2rem); padding: 13px 24px;
  }
  .has-gsap .tracks-rails a.track-chip:hover { transform: translateY(-2px); background: rgba(35, 173, 158, 0.14); }
  .has-gsap .tracks-rails .track-chip::before { left: 11px; }
  .has-gsap .tracks-rails .track-chip--more { font-size: 0.95rem; }
}

/* Why-stage is a DESKTOP-ONLY scene — mobile always keeps the flat grid */
@media (max-width: 1023px) {
  .why-stage { height: auto !important; min-height: 0 !important; display: grid !important; }
  .why-frame { position: static !important; width: auto !important; transform: none !important;
    opacity: 1 !important; visibility: visible !important; display: contents !important; }
  .why-frame::before { display: none !important; }
  .journey__ghost { display: none !important; }
}


/* screen-reader-only utility (labels that must not render visually) */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Call-for-papers CTA: shown in a waiting state until the portal URL exists */
.cfp-cta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 20px; }
.cfp-cta .btn[disabled] { opacity: 0.55; cursor: not-allowed; pointer-events: none; }
.cfp-cta__note { font-size: 0.86rem; color: rgba(255, 255, 255, 0.6); }

/* 21. SCIENTIFIC PROGRAMME -------------------------------------------------
   An overview of every track, then ONE track at a time:

     all-tracks overview -> one track -> its days -> blocks -> sessions

   Every track's agenda is rendered server-side and only one is shown (no fetch),
   so search across the whole programme, deep links and no-JS all work off the
   same DOM. The accordion this replaces opened 27 closed boxes and told the
   reader nothing until they had guessed which one to open. */

/* --- which view is on screen ---
   `is-off` rather than the `hidden` attribute, so the no-JS rule below can
   override it. Without JavaScript nothing can switch views, and a hidden
   attribute would leave the agenda unreachable — so unscripted readers get the
   overview AND every track, stacked, which is what the accordion used to do. */
.tx-view.is-off, .tx-track.is-off { display: none; }
html:not(.js) .tx-view.is-off,
html:not(.js) .tx-track.is-off { display: block; }
html:not(.js) .tx-back { display: none; }

/* --- search bar --- */
/* The chips sit directly under the search, so the gap belongs to .tx-cats —
   keeping it here as well pushed them a long way down the page on mobile. */
.tx-search { max-width: 620px; margin: 0 auto; }
.tx-search__field { position: relative; display: flex; align-items: center; }
.tx-search__ico {
  position: absolute; left: 18px; width: 18px; height: 18px;
  color: var(--ink-400); pointer-events: none;
}
.tx-search input {
  width: 100%; font: inherit; font-size: 1rem; color: var(--ink-900);
  padding: 15px 46px 15px 48px;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-pill); box-shadow: var(--shadow-1);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.tx-search input::placeholder { color: var(--ink-400); }
/* suppress the native search widget's own clear button — we render our own */
.tx-search input::-webkit-search-cancel-button,
.tx-search input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.tx-search input:focus { outline: none; border-color: var(--teal-500); box-shadow: var(--focus-ring); }
.tx-search__clear {
  position: absolute; right: 8px; display: grid; place-items: center;
  width: 32px; height: 32px; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink-400); cursor: pointer;
  opacity: 0; visibility: hidden; transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tx-search.has-value .tx-search__clear { opacity: 1; visibility: visible; }
.tx-search__clear:hover { color: var(--ink-900); background: var(--surface-1); }
.tx-search__clear:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-search__count {
  margin: 12px 0 0; text-align: center; font-size: 0.9rem; color: var(--ink-400);
  min-height: 1.2em;
}

/* --- category chips (filter the grid; distinct from .tx-filter, which
       filters session types inside one open card) --- */
.tx-cats {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
  margin: 16px auto clamp(22px, 3vw, 34px); max-width: 1060px;
}
.tx-cat {
  font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--ink-600);
  padding: 9px 18px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-0); cursor: pointer;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.tx-cat:hover { border-color: var(--teal-500); color: var(--navy-800); }
.tx-cat.is-on {
  background: var(--navy-800); border-color: var(--navy-800); color: #fff;
}
.tx-cat:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-cat__n { opacity: 0.6; font-weight: 500; margin-left: 2px; }
.tx-cat.is-on .tx-cat__n { opacity: 0.75; }
@media (max-width: 640px) {
  .tx-cats { gap: 6px; }
  .tx-cat { font-size: 0.8rem; padding: 8px 14px; }
}

/* --- card grid --- */
.tx-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
  align-items: start;
}
.tx-grid > li::before { content: none; }
@media (max-width: 1023px) { .tx-grid { grid-template-columns: 1fr 1fr; gap: 16px; } }
@media (max-width: 640px)  { .tx-grid { grid-template-columns: 1fr; } }

.tx-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  overflow: hidden;
  /* Clears the sticky header when tracks.js scrolls an opened card (or a
     #track-<slug> deep link) into view, so its title is never hidden behind
     the header. Two nav rows on desktop, one shorter bar on mobile. */
  scroll-margin-top: calc(var(--header-h) + 78px);
  transition: box-shadow var(--t-med) var(--ease), border-color var(--t-med) var(--ease),
              transform var(--t-med) var(--ease-out);
}
@media (max-width: 1023px) { .tx-card { scroll-margin-top: calc(var(--header-h) + 16px); } }
.tx-card:hover { box-shadow: var(--shadow-2); border-color: #c5d7e4; transform: translateY(-3px); }
.tx-card[hidden] { display: none; }

/* --- card header ---
   An <a> now, not a <button>: each card links to /agenda/<slug>, which is a real
   shareable URL that serves the right page on its own. `text-decoration` and
   `color` have to be reset because a link brings both with it. */
.tx-card__btn {
  width: 100%; display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; text-align: left;
  background: none; border: 0; cursor: pointer; font: inherit;
  color: inherit; text-decoration: none;
}
.tx-card__btn:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-md); }
/* Square, because the track artwork is square. The old 54x40 box let the
   surface tint show as bars down each side, which read as a cropped image. */
.tx-card__ico {
  flex: none; width: 46px; height: 46px; object-fit: contain; padding: 5px;
  border-radius: var(--r-sm); background: var(--surface-1);
  transition: transform var(--t-med) var(--ease-out), background var(--t-med) var(--ease);
}
.tx-card__btn:hover .tx-card__ico { transform: scale(1.07); background: rgba(35, 173, 158, 0.12); }
.tx-card__txt { flex: 1 1 auto; min-width: 0; }
.tx-card__name {
  display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 1.02rem; line-height: 1.3; color: var(--navy-800);
  /* long track names must wrap, never widen the grid column */
  overflow-wrap: anywhere;
}
.tx-card__meta {
  display: block; margin-top: 4px; font-size: 0.82rem; color: var(--ink-400);
}
/* chevron */
.tx-card__chev {
  flex: none; width: 26px; height: 26px; display: grid; place-items: center;
  border-radius: 50%; background: var(--surface-1); color: var(--navy-700);
  transition: transform var(--t-med) var(--ease-out), background var(--t-med) var(--ease);
}
.tx-card__chev svg { width: 13px; height: 13px; }
.tx-card__btn:hover .tx-card__chev { background: var(--teal-500); color: #fff; transform: translateX(2px); }
/* Counted by search and written in by tracks.js: "3 sessions match". The count is
   what tells a reader whether a track is worth opening — naming the matches would
   mean 27 lists on one screen. */
.tx-card__hits {
  display: block; margin-top: 3px;
  font-size: 0.78rem; font-weight: 600; color: var(--teal-600);
}
.tx-card__hits[hidden] { display: none; }

/* "Coming soon" tracks stay visible but read as clearly pending. */
.tx-card--soon .tx-card__ico { filter: grayscale(1); opacity: 0.55; }
.tx-card--soon .tx-card__name { color: var(--ink-600); }
.tx-soon-tag {
  display: inline-block; margin-top: 5px; padding: 3px 10px;
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--ink-600); background: var(--surface-1);
  border: 1px solid var(--border); border-radius: var(--r-pill);
}

/* --- expanding panel ---------------------------------------------------
   Height is animated via a max-height set in JS from scrollHeight, so the
   transition has a real end value; JS clears it on transitionend so a card
   left open can still reflow (font load, resize, rotate). */
/* --- the track view --------------------------------------------------------
   One track, full width. It replaces the grid rather than sitting inside a card,
   which is what gives a 79-session agenda room to be read. */
.tx-track {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-2); overflow: hidden;
  /* Clears the sticky header when tracks.js scrolls the banner into view. */
  scroll-margin-top: calc(var(--header-h) + 24px);
}
.tx-track__body { padding: 4px 22px 24px; }
@media (max-width: 640px) { .tx-track__body { padding: 4px 14px 18px; } }

/* BANNER — names the selected track. There is deliberately no congress-wide date
   row: 19 of 28 tracks run on a single day, so one global date described none of
   them. This track's own days are the tabs below. */
.tx-banner {
  padding: 16px 22px 18px; border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, rgba(35, 173, 158, 0.07), transparent);
}
@media (max-width: 640px) { .tx-banner { padding: 12px 14px 14px; } }
.tx-back {
  display: inline-flex; align-items: center; gap: 5px; margin-bottom: 12px;
  font-size: 0.84rem; font-weight: 600; color: var(--teal-600); text-decoration: none;
  padding: 6px 11px 6px 8px; border-radius: 999px; background: var(--surface-0);
  border: 1px solid var(--border);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tx-back:hover { border-color: var(--teal-500); color: var(--navy-800); }
.tx-back:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-banner__main { display: flex; align-items: flex-start; gap: 14px; }
.tx-banner__ico { width: 56px; height: 56px; border-radius: var(--r-sm); flex: 0 0 auto; }
@media (max-width: 640px) { .tx-banner__ico { width: 42px; height: 42px; } }
.tx-banner__txt { min-width: 0; }
.tx-banner__name {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.15rem, 2.4vw, 1.6rem); line-height: 1.25; color: var(--navy-800);
  overflow-wrap: anywhere;
}
/* Wraps rather than overflowing: track names run long and the venue is a whole
   phrase, so on a phone this is three lines, not one clipped one. */
.tx-banner__meta {
  margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 6px 14px;
  font-size: 0.85rem; color: var(--ink-600);
}
.tx-banner__meta > span { display: inline-flex; align-items: center; gap: 6px; }
.tx-banner__meta > span + span::before {
  content: ""; width: 4px; height: 4px; border-radius: 50%;
  background: var(--teal-500); margin-right: 8px; flex: 0 0 auto;
}
.tx-banner__coord { margin: 5px 0 0; font-size: 0.83rem; color: var(--ink-400); }

/* The overview's search phrase, carried into the track and droppable. */
.tx-qpill {
  margin: 14px 0 0; padding: 7px 13px; border-radius: 999px; cursor: pointer;
  font: inherit; font-size: 0.82rem; font-weight: 600;
  color: var(--teal-600); background: rgba(35, 173, 158, 0.1);
  border: 1px solid var(--teal-500);
}
.tx-qpill:hover { background: rgba(35, 173, 158, 0.18); }
.tx-qpill:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-qpill[hidden] { display: none; }

.tx-day + .tx-day { margin-top: 22px; }
/* .tx-day__head is defined once, with the sticky behaviour, further down —
   a second declaration here only made it unclear which one applied. */
.tx-day__date {
  font-family: var(--font-display); font-weight: 600; font-size: 0.98rem; color: var(--navy-700);
}

/* --- toolbar: day tabs + type filters ------------------------------------ */
.tx-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px 20px; padding: 14px 0 4px;
}
.tx-daytabs { display: flex; flex-wrap: wrap; gap: 6px; }
.tx-daytab {
  font: inherit; font-size: 0.86rem; font-weight: 600; color: var(--ink-600);
  padding: 8px 15px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-0); cursor: pointer;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.tx-daytab:hover { border-color: var(--teal-500); color: var(--navy-800); }
.tx-daytab.is-on { background: var(--navy-700); border-color: var(--navy-700); color: #fff; }
.tx-daytab:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/*
 * A DAY AS A CALENDAR TILE.
 *
 * A date is a thing people picture, so it is drawn the way a calendar draws it: month above, the
 * number large, weekday below. The pill it replaces read "14 December" — which never said WHICH
 * WEEKDAY you were choosing, and wrapped on a phone. The tile says more in less width.
 *
 * Only applied to `.tx-daytab--card`, which the template adds when the day parses to a real date.
 * A track labelled only "Day 2", or a workbook range like "Dec 14-15", keeps the text pill: a
 * tile showing a guessed number would be worse than no tile.
 */
.tx-daytabs { align-items: stretch; gap: 10px; }
.tx-daytab--card {
  width: 74px; padding: 0 0 8px; border-radius: var(--r-md);
  text-align: center; overflow: hidden; line-height: 1.2;
  box-shadow: var(--shadow-1);
}
.tx-daytab--card:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
.tx-daytab__m {
  display: block; background: var(--surface-1); color: var(--ink-600);
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 4px 0; border-bottom: 1px solid var(--border);
}
.tx-daytab__d {
  display: block; font-family: var(--font-display);
  font-size: 1.55rem; font-weight: 700; letter-spacing: -0.02em;
  margin-top: 7px; font-variant-numeric: tabular-nums; color: var(--navy-800);
}
.tx-daytab__w {
  display: block; font-size: 0.64rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-400);
}
/* The selected tile keeps a white body and colours its header strip, rather than going solid
   navy like the pill did — a solid tile would bury the number the tile exists to show. */
.tx-daytab--card.is-on {
  background: var(--surface-0); border-color: var(--teal-600);
  box-shadow: 0 0 0 1px var(--teal-600), var(--shadow-2);
}
.tx-daytab--card.is-on .tx-daytab__m {
  background: var(--navy-700); color: #fff; border-bottom-color: var(--navy-700);
}
.tx-daytab--card.is-on .tx-daytab__d { color: var(--navy-700); }
.tx-daytab--card.is-on .tx-daytab__w { color: var(--teal-600); }

/* ---------------------------------------------------------------------------
 * OVERVIEW: PICKING A DAY BEFORE PICKING A TRACK
 *
 * Deliberately the SAME calendar tile as inside a track, one size down. A visitor who has
 * already learnt "the tile with the big number is the date" on one screen should not have to
 * learn a second control for the same choice on the other.
 *
 * Choosing a day REMOVES the tracks that do not run that day — it does not dim them. A greyed
 * card still invites a click that cannot lead anywhere, and the whole point of the filter is to
 * shorten a 27-card wall down to what is actually on.
 *
 * "All days" is a pill rather than a tile because it is not a date; drawing it as a blank tile
 * would leave a hole where every neighbour shows a number.
 * ------------------------------------------------------------------------- */
.tx-days {
  display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px;
  margin: 0 0 14px;
}
.tx-day-chip {
  font: inherit; cursor: pointer; width: 62px; padding: 0 0 6px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-0); color: var(--ink-600);
  text-align: center; overflow: hidden; line-height: 1.2;
  box-shadow: var(--shadow-1);
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.tx-day-chip:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); border-color: var(--teal-500); }
.tx-day-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-day-chip__m {
  display: block; background: var(--surface-1); color: var(--ink-600);
  font-size: 0.56rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 0; border-bottom: 1px solid var(--border);
}
.tx-day-chip__d {
  display: block; font-family: var(--font-display);
  font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em;
  margin-top: 5px; font-variant-numeric: tabular-nums; color: var(--navy-800);
}
.tx-day-chip__w {
  display: block; font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-400);
}
.tx-day-chip.is-on {
  background: var(--surface-0); border-color: var(--teal-600);
  box-shadow: 0 0 0 1px var(--teal-600), var(--shadow-2);
}
.tx-day-chip.is-on .tx-day-chip__m { background: var(--navy-700); color: #fff; border-bottom-color: var(--navy-700); }
.tx-day-chip.is-on .tx-day-chip__d { color: var(--navy-700); }
.tx-day-chip.is-on .tx-day-chip__w { color: var(--teal-600); }

/* "All days" — a pill the height of the tiles beside it, not a tile with nothing in it. */
.tx-day-chip--all {
  width: auto; padding: 0 14px; display: flex; align-items: center; justify-content: center;
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.01em;
}
.tx-day-chip--all.is-on { background: var(--navy-700); border-color: var(--navy-700); color: #fff; box-shadow: var(--shadow-2); }

@media (max-width: 640px) {
  /* The row scrolls sideways rather than wrapping to two lines: with four days plus "All",
     a wrapped second row pushes the track list below the fold on a phone. */
  .tx-days { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
  .tx-day-chip { flex: 0 0 auto; }
}

.tx-filters { display: flex; flex-wrap: wrap; gap: 6px; }
.tx-filter {
  font: inherit; font-size: 0.78rem; font-weight: 600; color: var(--ink-600);
  padding: 6px 12px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-0); cursor: pointer;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.tx-filter:hover { border-color: var(--teal-500); color: var(--navy-800); }
.tx-filter.is-on { background: rgba(35, 173, 158, 0.14); border-color: var(--teal-500); color: var(--teal-600); }
.tx-filter:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tx-filter__n { opacity: 0.65; font-weight: 500; }

.tx-day.is-hidden { display: none; }
.tx-nomatch { padding: 26px 4px; text-align: center; color: var(--ink-400); font-size: 0.9rem; }

/* --- agenda rows -------------------------------------------------------
   A definition-style two-column layout rather than a <table>: it collapses to
   one column on mobile without the horizontal scroll a table would force.
   The left column doubles as a timeline rail, echoing the site's "Thread". */
.tx-sessions { list-style: none; margin: 0; padding: 0; position: relative; }
.tx-sessions > li::before { content: none; }

/* The day header scrolls with its sessions.

   It used to be sticky, which sounded useful on a long agenda but in practice
   detached it from its own day and parked it on top of the session rows —
   the first session ended up hidden behind the coordinator line. The date is
   only ever a few rows away, and the card header above already says which
   track you are in, so pinning it bought little and cost legibility. */
.tx-day__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 14px;
  padding: 4px 0 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 6px;
}

/*
 * THE GUTTER USED TO BE 200px WIDE FOR A FIVE-MINUTE TALK.
 *
 * 158px of time column plus a 26px gap, sized for "11:00 AM – 11:15 AM" — a string that now reads
 * "11:00 – 11:15 AM", because the meridiem is the same at both ends of all but a handful of
 * sessions and saying it twice bought nothing (see $tx_time). 118px fits the new widest string
 * with room to spare, and the ~70px it returns goes to the session title, which is what the
 * reader is actually scanning.
 *
 * The variable exists so the rail, the node and the RTL mirror cannot drift apart from the
 * column again — they were four separate magic numbers, and three of them had to be edited by
 * hand every time the first one moved.
 */
.tx-sessions { --tx-gutter: 118px; --tx-gap: 20px; --tx-rail: 128px; }
@media (max-width: 640px) { .tx-sessions { --tx-gutter: 96px; --tx-gap: 14px; --tx-rail: 104px; } }

.tx-row {
  display: grid; grid-template-columns: var(--tx-gutter) 1fr; gap: 2px var(--tx-gap);
  padding: 11px 0; position: relative;
}
/* the rail itself: a hairline between the time column and the session */
.tx-row::before {
  content: ""; position: absolute; left: var(--tx-rail); top: 0; bottom: 0;
  width: 1px; background: var(--border);
}
.tx-row:first-child::before { top: 15px; }
.tx-row:last-child::before  { bottom: auto; height: 15px; }
/* the node on the rail — smaller, and hollow, so it reads as a tick and not a bullet */
.tx-row::after {
  content: ""; position: absolute; left: calc(var(--tx-rail) - 2.5px); top: 16px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--surface-0); border: 2px solid var(--teal-500);
}
.tx-row--break::after { border-color: var(--border); }

/*
 * THE TIME: START LARGE, END QUIETLY UNDERNEATH.
 *
 * One clock time in one weight said when a session began and left the reader to find the end
 * inside the same small string. Splitting the two gives the column a shape — a bold number to run
 * the eye down, a lighter one under it for the finish.
 *
 * The duration chip that used to live here is gone. Two clock times already say how long a
 * session runs, and "15 min" repeated down 650 rows competed with the titles beside it for
 * attention it did not deserve.
 */
.tx-row__time {
  font-size: 0.82rem; font-weight: 600; color: var(--navy-700);
  /* tabular figures so the times form a column the eye can run down, rather than
     shifting left and right as the digits change width */
  font-variant-numeric: tabular-nums; white-space: nowrap; padding-top: 2px;
}
.tx-row__time--tba { color: var(--ink-400); font-weight: 500; }
.tx-row__start {
  display: block; font-family: var(--font-display);
  font-size: 1rem; font-weight: 700; color: var(--navy-800); line-height: 1.2;
}
.tx-row__end {
  display: block; font-size: 0.78rem; font-weight: 500;
  color: var(--ink-400); line-height: 1.2; margin-top: 3px;
}
/* The title is what the reader is scanning for, so it is the largest thing in the row and the
   only one at full ink. Everything else — the type chip, the duration, the speaker — is support. */
.tx-row__topic {
  font-size: 1rem; font-weight: 500; color: var(--ink-900);
  line-height: 1.4; overflow-wrap: anywhere;
}
.tx-row__topic--tba { color: var(--ink-400); font-style: italic; }
/*
 * SPEAKERS, DESIGNED FOR THE DATA THAT EXISTS.
 *
 * Of 429 speakers in the published programme, ZERO have a photo — so a photo-led treatment would
 * render 429 empty frames. The initials disc is not a fallback here, it is the design, and it is
 * sized and coloured to look deliberate rather than like an image that failed to load.
 *
 * 457 sessions have exactly one speaker, 186 have none and only 86 have two or more, topping out
 * at five. A "+3 more" disclosure would therefore hide real names on a handful of rows and do
 * nothing on the rest, so the list simply prints.
 */
/*
 * A GROUP OF PEOPLE, LABELLED.
 *
 * Speakers and moderators are both printed through the same component now. A moderator is a
 * person standing on that stage; printing them as a bare comma list while the speaker beside them
 * got a picture and an affiliation said the opposite. The label above each group is what keeps
 * the two readable apart once they look alike.
 */
.tx-row__group { margin-top: 10px; }
.tx-row__group + .tx-row__group {
  margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--border);
}
.tx-row__glabel {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-400); margin-bottom: 7px;
}
.tx-row__people { display: flex; flex-direction: column; gap: 9px; }

/* speaker line: avatar + name/country/role */
.tx-row__person { display: flex; align-items: center; gap: 11px; }
/*
 * THE DISC IS THE DESIGN, NOT A FALLBACK.
 *
 * Zero of 429 speakers have a photo, so this circle is what the programme actually shows. At
 * 28px it read as a failed image; at 44px it reads as a deliberate monogram, and it is the same
 * box a photo will drop into the day one is uploaded in Reception.
 */
.tx-av {
  flex: none; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 0.88rem; font-weight: 700; letter-spacing: 0.01em;
  color: var(--teal-600); background: rgba(35, 173, 158, 0.1);
  border: 1px solid rgba(35, 173, 158, 0.22);
  font-family: var(--font-display);
}
/* A moderator gets the same circle in sand rather than teal, so the roles stay tellable apart at
   a glance now that they are drawn identically in every other respect. */
.tx-av--mod {
  color: #8a6a35; background: rgba(138, 106, 53, 0.09);
  border-color: rgba(138, 106, 53, 0.22);
}
/* The same 44px circle as the initials, so a list mixing photos and initials stays aligned.
   No speaker in the current programme has one; this is here for when they do. */
.tx-av--img { object-fit: cover; background: var(--surface-1); border-color: var(--border); }
.tx-row__who { min-width: 0; font-size: 0.85rem; line-height: 1.4; }
.tx-row__nm { font-weight: 600; color: var(--navy-800); }
.tx-flag {
  display: inline-block; margin-left: 7px; padding: 1px 7px;
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.03em;
  color: var(--blue-500); background: rgba(34, 126, 194, 0.11);
  border-radius: var(--r-pill); vertical-align: 1px; white-space: nowrap;
}
.tx-row__rl { display: block; color: var(--ink-400); }
/* .tx-row__mods removed: moderators render through .tx-row__person like speakers. */

/* session-type chip */
/*
 * In the time column now, so it stacks under the duration rather than pushing the title down.
 *
 * `display: block` with `width: fit-content`, NOT inline-block. The duration above it is only
 * present on the live-API path — the generated workbook file carries no durations at all, which is
 * what production currently renders — so with inline-block the chip would sit on the same line as
 * the time, and `white-space: nowrap` on the column would push it straight out of a 118px box.
 * A block always starts its own line whether or not the duration is there.
 */
.tx-row__time .tx-tag { display: block; width: fit-content; margin-top: 5px; }

.tx-tag {
  display: inline-block; margin-bottom: 5px; padding: 2px 9px;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--teal-600); background: rgba(35, 173, 158, 0.1);
  border-radius: var(--r-pill);
}
.tx-tag--keynote  { color: #fff; background: var(--teal-600); }
.tx-tag--workshop { color: var(--blue-500); background: rgba(34, 126, 194, 0.12); }
.tx-tag--panel    { color: #8a5a00; background: rgba(214, 158, 46, 0.16); }
.tx-tag--research { color: #6b3fa0; background: rgba(124, 77, 178, 0.13); }
.tx-tag--ceremony { color: var(--navy-700); background: rgba(18, 65, 102, 0.1); }
.tx-tag--case     { color: #0f7b6c; background: rgba(35, 173, 158, 0.13); }
.tx-tag--lecture  { color: var(--ink-600); background: var(--surface-1); }
/* Registration reads as an arrival step rather than a talk or a pause — sand, so it sits apart
   from both the teal session chips and the grey of a break. */
.tx-tag--registration { color: #8a6a35; background: rgba(138, 106, 53, 0.12); }

/* breaks read as quiet dividers, not sessions */
.tx-row--break { background: var(--surface-1); border-radius: var(--r-sm); padding: 9px 14px; margin: 4px 0; border-top: 0; }
.tx-row--break + .tx-row { border-top: 0; }
.tx-row--break .tx-row__topic { font-size: 0.88rem; color: var(--ink-400); font-weight: 500; }

/* --- A BLOCK: the level between a day and a talk --------------------------
   "Session 1: Gynaecology Surgery & Endoscopy" heads the talks beneath it, so it
   is styled as a HEADING and not as another row: it has no time rail, it sits
   outside the session indent, and it is the only thing in the list set in the
   display face. That contrast is the whole point — Track > Day > BLOCK > Session
   has to be legible at a glance, or a 79-session day reads as one flat wall. */
.tx-block {
  margin: 22px 0 10px; padding: 11px 15px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  letter-spacing: -0.01em;
  line-height: 1.4; color: var(--navy-800); background: rgba(35, 173, 158, 0.09);
  border-left: 4px solid var(--teal-500); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  /* block headings are full sentences — they must wrap, not widen the page */
  overflow-wrap: anywhere; white-space: normal;
}
.tx-block:first-child { margin-top: 8px; }
.tx-block[hidden] { display: none; }
/* the time rail must not run through a heading */
.tx-block + .tx-row::before { top: 18px; }

/* --- duration, keynote, break ---------------------------------------------
   The reference the client sent shows a duration next to the time and keynotes
   lifted out of the list. Both are drawn from data we already publish. `minutes`
   arrives only from the live API (it is NULL, never 0, for an unscheduled
   session) — the generated workbook file has no duration, so the chip is simply
   absent there rather than showing "0 min". */
/* .tx-row__mins removed: the row prints a start and an end instead of a duration. */

/* A keynote is the one session on a day people plan around, so it is a card
   rather than a row. The rail is removed because a card is not on the timeline. */
.tx-row--keynote {
  background: var(--navy-800); border-radius: var(--r-sm);
  padding: 13px 16px; margin: 8px 0; border-top: 0;
}
.tx-row--keynote + .tx-row { border-top: 0; }
.tx-row--keynote::before, .tx-row--keynote::after { display: none; }
.tx-row--keynote .tx-row__time,
.tx-row--keynote .tx-row__start { color: rgba(255, 255, 255, 0.92); }
.tx-row--keynote .tx-row__end { color: rgba(255, 255, 255, 0.72); }
.tx-row--keynote .tx-row__topic { color: #fff; font-weight: 600; }
.tx-row--keynote .tx-row__nm { color: #fff; }
.tx-row--keynote .tx-row__rl,
.tx-row--keynote .tx-row__glabel { color: rgba(255, 255, 255, 0.7); }
.tx-row--keynote .tx-tag--keynote { background: var(--teal-500); color: #fff; }
.tx-row--keynote .tx-av { background: rgba(255, 255, 255, 0.16); color: #fff; }
.tx-row--keynote .tx-flag {
  background: rgba(255, 255, 255, 0.16); color: rgba(255, 255, 255, 0.92);
  border-color: rgba(255, 255, 255, 0.25);
}

/* --- print: the selected track and day, not 28 tracks --------------------
   Printing the whole programme was 729 sessions and ~60 pages, which is why
   nobody did it. What a delegate wants on paper is the one day they are in. */
@media print {
  .tx-view--overview, .tx-search, .tx-cats, .tx-back, .tx-filters,
  .tx-qpill, .tx-card__chev, .tx-empty, .tx-nomatch { display: none !important; }
  /* The tabs are shown as plain text: on paper there is nothing to click, but the
     reader still needs to know WHICH day these sessions are. */
  .tx-daytabs { display: block !important; }
  .tx-daytab { display: none !important; }
  .tx-daytab.is-on {
    display: inline !important;
    background: none !important; color: #000 !important; border: 0 !important;
    padding: 0 !important; font-weight: 700; font-size: 1rem;
  }
  .tx-track {
    box-shadow: none !important; border: 0 !important;
  }
  .tx-banner { background: none !important; border-bottom: 1px solid #999; }
  /* A keynote card inverted on paper is a solid block of ink. */
  .tx-row--keynote { background: none !important; border: 1px solid #999; }
  .tx-row--keynote .tx-row__topic, .tx-row--keynote .tx-row__nm,
  .tx-row--keynote .tx-row__time,
  .tx-row--keynote .tx-row__start { color: #000 !important; }
  .tx-row--keynote .tx-row__rl, .tx-row--keynote .tx-row__glabel { color: #333 !important; }
  /* Never split a session, or its speaker ends up on the next page alone. */
  .tx-row { break-inside: avoid; page-break-inside: avoid; }
  .tx-block { break-after: avoid; page-break-after: avoid; }
}

/* Arabic tracks render right-to-left within their own panel only */
.tx-day[dir="rtl"] { text-align: right; }
.tx-day[dir="rtl"] .tx-row { grid-template-columns: 1fr var(--tx-gutter); }
.tx-day[dir="rtl"] .tx-row__time { grid-column: 2; grid-row: 1; }
.tx-day[dir="rtl"] .tx-row__body { grid-column: 1; grid-row: 1; }
/* mirror the rail to the right edge of the time column */
.tx-day[dir="rtl"] .tx-row::before { left: auto; right: var(--tx-rail); }
.tx-day[dir="rtl"] .tx-row::after  { left: auto; right: calc(var(--tx-rail) - 2.5px); }
.tx-day[dir="rtl"] .tx-row__person { flex-direction: row-reverse; }
.tx-day[dir="rtl"] .tx-flag { margin-left: 0; margin-right: 7px; }
.tx-day[dir="rtl"] .tx-block { border-left: 0; border-right: 4px solid var(--teal-500); border-radius: var(--r-sm) 0 0 var(--r-sm); }

@media (max-width: 640px) {
  /* one column: the rail would waste ~40% of a phone's width, so drop it */
  .tx-row { grid-template-columns: 1fr; gap: 3px; padding-left: 0; }
  .tx-row::before, .tx-row::after { display: none; }

  /*
   * EACH SLOT IS ITS OWN CARD, AND THEY ALTERNATE.
   *
   * On a phone the rail is gone and a row can run to a dozen lines once it carries a speaker and
   * three moderators, so a single hairline between rows left the eye with nothing to tell it
   * where one time slot ended and the next began — two sessions read as one long block of text.
   *
   * The gap does most of the work; the alternating tint is the second cue, for a row tall enough
   * that its neighbours have scrolled off. A section heading interrupts the alternation, which is
   * correct: a heading is a fresh start and the rhythm should restart under it.
   */
  .tx-sessions {
    display: flex; flex-direction: column; gap: 8px;
    padding: 10px; background: var(--surface-1); border-radius: var(--r-md);
  }
  .tx-row {
    background: var(--surface-0); border: 1px solid var(--border);
    border-radius: var(--r-sm); padding: 12px 13px; border-top: 1px solid var(--border);
  }
  .tx-row:nth-child(even) { background: #f8fbfd; }
  /* Breaks and keynotes keep their own identity — the zebra must not overwrite what the row IS.
     Listed after, and at matching specificity, so they win. */
  .tx-row--break:nth-child(even),
  .tx-row--break { background: rgba(35, 173, 158, 0.06); border-color: rgba(35, 173, 158, 0.18); }
  .tx-row--keynote:nth-child(even),
  .tx-row--keynote { background: var(--navy-800); border-color: var(--navy-800); }
  /* the heading sits flush in the stack rather than inside the card padding */
  .tx-block { margin: 6px 0 2px; }
  .tx-day[dir="rtl"] .tx-row { grid-template-columns: 1fr; }
  .tx-day[dir="rtl"] .tx-row__time,
  .tx-day[dir="rtl"] .tx-row__body { grid-column: 1; grid-row: auto; }
  .tx-banner, .tx-track__body { padding-inline: 14px; }
  .tx-toolbar { gap: 10px; }
  .tx-filters { width: 100%; }

  /*
   * THE TWO CLOCK TIMES GO ON ONE LINE, ABOVE THE TITLE.
   *
   * The rail is already gone above, so a stacked start-over-end would cost two lines at the top
   * of every row and push the title — the thing being scanned for — further down. Side by side
   * with an en dash they read as the range they are and cost one line.
   */
  .tx-row__time { display: flex; align-items: baseline; gap: 7px; padding-top: 0; }
  .tx-row__start { font-size: 0.9rem; }
  .tx-row__end { margin-top: 0; }
  .tx-row__end::before { content: "– "; }
  /* the type chip was made a block to sit under the duration; inline again now that the time is
     a single row and there is nothing above it to clear */
  .tx-row__time .tx-tag { display: inline-block; margin-top: 0; margin-left: 2px; }

  /* People: one per line, and the disc steps down. Two across a 360px row cannot show a name and
     an affiliation without truncating the name, and the name is what people scan for. */
  .tx-av { width: 38px; height: 38px; font-size: 0.76rem; }
  .tx-row__person { gap: 9px; }
  .tx-row__people { gap: 10px; }

  /*
   * Day tiles scroll sideways rather than wrapping. Three fit a 360px screen; a fourth would wrap
   * to a second row and push the programme itself below the fold.
   */
  .tx-daytabs {
    width: 100%; flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px;
    scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  }
  .tx-daytab { flex: none; scroll-snap-align: start; }
  .tx-daytab--card { width: 64px; }
  .tx-daytab__d { font-size: 1.3rem; margin-top: 5px; }
}

/* Below this a name and a hospital cannot share a line, and the name wins. */
@media (max-width: 380px) {
  .tx-row__rl { display: none; }
}

/*
 * A PARENT AND ITS CHILDREN, DRAWN AS ONE CONTAINER WITHOUT A WRAPPER.
 *
 * The programme has two kinds of grouping and both arrive as a flat list of rows: a timeless
 * SECTION heading, and a TIMED parent — a symposium that runs 09:00–10:30 with four talks inside
 * it. Four adjacent rows do not say "these are one symposium"; a box does.
 *
 * There is no wrapping element on purpose. `tracks.js` filters by walking `.tx-row` siblings, and
 * a real container would both break that walk and trap a child inside a parent the filter has
 * hidden. So the box is drawn by the rows themselves: the parent closes its own bottom corners,
 * the children carry the sides, and the last child closes the bottom.
 */
.tx-row--parent {
  background: rgba(35, 173, 158, 0.05);
  border: 1px solid rgba(35, 173, 158, 0.22);
  border-bottom: 0;
  border-radius: var(--r-md) var(--r-md) 0 0;
  padding-inline: 14px; margin-top: 10px;
}
.tx-row--parent .tx-row__topic { font-weight: 700; }
/* Says what the box IS, above the title, in the space the time column leaves. */
.tx-row--parent .tx-row__start::before {
  content: "Session"; display: block;
  font-family: var(--font-body); font-size: 0.6rem; font-weight: 800;
  letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--teal-600); margin-bottom: 3px;
}
.tx-row--child {
  background: rgba(35, 173, 158, 0.025);
  border-left: 1px solid rgba(35, 173, 158, 0.22);
  border-right: 1px solid rgba(35, 173, 158, 0.22);
  border-top: 1px dashed rgba(35, 173, 158, 0.2);
  padding-inline: 14px; padding-left: 30px;
}
.tx-row--child-last {
  border-bottom: 1px solid rgba(35, 173, 158, 0.22);
  border-radius: 0 0 var(--r-md) var(--r-md);
  margin-bottom: 10px;
}
/* The rail and its tick belong to the day, not inside a container. */
.tx-row--parent::before, .tx-row--parent::after,
.tx-row--child::before,  .tx-row--child::after { display: none; }
/*
 * A SECTION HEADING'S TALKS: INDENTED, NOT BOXED.
 *
 * The heading already draws a left rule and announces itself. Giving its talks the symposium box
 * as well would open a container with nothing at the top to close it, and would say "container"
 * twice. They only need to sit under it.
 */
.tx-row--under { padding-left: 30px; }
.tx-row--under::after { left: calc(var(--tx-rail) - 2.5px + 16px); }

@media (max-width: 640px) {
  /* The cards already separate the rows, so a child only needs to say it is one. */
  .tx-row--parent { margin-top: 12px; border-radius: var(--r-sm) var(--r-sm) 0 0; }
  .tx-row--child { padding-left: 20px; border-left-width: 3px; }
  .tx-row--under { padding-left: 16px; border-left: 3px solid rgba(35, 173, 158, 0.18); }
  .tx-row--child-last { border-radius: 0 0 var(--r-sm) var(--r-sm); margin-bottom: 12px; }
}

/* empty-state when a search matches nothing */
.tx-empty {
  display: none; text-align: center; padding: 40px 20px; color: var(--ink-400);
}
.tx-empty.is-on { display: block; }

/* no-JS: every track is shown (see the .tx-view rules above), so the controls
   that only JavaScript can act on are hidden rather than left there doing
   nothing — the search box, the category chips, the day tabs and the type
   filters. What remains is the whole programme as a plain document. */
html:not(.js) .tx-search,
html:not(.js) .tx-cats,
html:not(.js) .tx-toolbar,
html:not(.js) .tx-nomatch,
html:not(.js) .tx-card__chev { display: none; }
/* ...and with the day tabs gone, every day has to be visible. */
html:not(.js) .tx-day.is-hidden { display: block; }

@media (prefers-reduced-motion: reduce) {
  .tx-card, .tx-card__ico, .tx-card__chev,
  .tx-daytab, .tx-filter { transition: none; }
  .tx-card:hover { transform: none; }
  .tx-card__btn:hover .tx-card__ico { transform: none; }
}

/* 22. REDUCED MOTION -------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .js .reveal, .hero__stagger, .js .hero:not(.is-ready) .hero__title .line > span { opacity: 1 !important; transform: none !important; }
  .hero__scrollcue::after { animation: none; }
}

/* ==========================================================================
   17. ABSTRACT SUBMISSION FORM (abstracts-submit.php)
   Built on the same tokens as the rest of the site. Without JavaScript every
   fieldset is visible and the form is one long page; .af--stepped is added by
   assets/js/abstract-form.js and is what turns it into steps.
   ========================================================================== */

.af { margin-top: 8px; }

/* Progress rail. Non-interactive until a step is completed, so it is a
   status display first and a control second. */
.af-steps {
  list-style: none; margin: 0 0 30px; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.af-steps > li::before { content: none; }
.af-step {
  display: flex; align-items: center; gap: 9px;
  font-size: 0.82rem; font-weight: 600; color: var(--ink-400);
  padding: 12px 4px 0; border-top: 3px solid var(--border);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.af-step > span {
  display: grid; place-items: center; flex-shrink: 0;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface-1); color: var(--ink-400);
  font-size: 0.76rem; font-variant-numeric: tabular-nums;
}
.af-step.is-on { color: var(--navy-800); border-top-color: var(--teal-500); }
.af-step.is-on > span { background: var(--teal-500); color: #fff; }
.af-step.is-done { color: var(--ink-600); border-top-color: var(--teal-300); cursor: pointer; }
.af-step.is-done > span { background: var(--teal-300); color: var(--navy-800); }
@media (max-width: 640px) {
  .af-steps { grid-template-columns: repeat(4, 1fr); gap: 4px; }
  /* Labels stay: the rail is stacked (dot over text) rather than side by side,
     so they fit — and "Author / Abstract / Profile / Declarations" is what
     tells the reader how much is left, which a bare number does not. */
  .af-step { font-size: 0.66rem; line-height: 1.25; gap: 6px; }
  .af-step > span { font-size: 0.72rem; width: 22px; height: 22px; }
}

/* Panels. The border only appears in stepped mode, where a panel reads as a
   single card; unstepped they run together as sections of one document. */
.af-panel { border: 0; margin: 0 0 26px; padding: 0; }
.af--stepped .af-panel {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: clamp(20px, 3vw, 30px); box-shadow: var(--shadow-1);
}
.af-legend {
  font-family: var(--font-display); font-weight: 700; font-size: 1.15rem;
  color: var(--navy-800); padding: 0; margin-bottom: 18px;
}
.af--stepped .af-legend { float: left; width: 100%; }

.af-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 20px; }
@media (max-width: 700px) { .af-grid { grid-template-columns: 1fr; } }

.af-field { margin-bottom: 18px; min-width: 0; }
.af-grid .af-field { margin-bottom: 0; }

.af-label {
  display: block; margin-bottom: 7px;
  font-size: 0.9rem; font-weight: 600; color: var(--navy-800);
}
.af-req { color: var(--teal-600); }
.af-optional { font-weight: 500; color: var(--ink-400); font-size: 0.85em; }

.af-input {
  width: 100%; font: inherit; font-size: 0.95rem; color: var(--ink-900);
  background: var(--surface-0);
  padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.af-input:hover { border-color: var(--teal-300); }
.af-input:focus-visible { outline: none; border-color: var(--teal-500); box-shadow: var(--focus-ring); }
.af-input[aria-invalid="true"] { border-color: var(--af-bad); background: var(--af-bad-bg); }
.af-textarea { resize: vertical; min-height: 110px; line-height: 1.6; }
select.af-input { appearance: none; background-image:
    url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233e5468' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 17px;
  padding-right: 38px; }

.af-file {
  width: 100%; font: inherit; font-size: 0.9rem; color: var(--ink-600);
  padding: 11px 14px; border: 1.5px dashed var(--border); border-radius: var(--r-sm);
  background: var(--surface-1);
}
.af-file::file-selector-button {
  font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer;
  margin-right: 12px; padding: 8px 14px; border: 0; border-radius: var(--r-pill);
  background: var(--navy-800); color: #fff;
}

/* Radio pills and checkboxes: the whole label is the target, not just the dot. */
.af-radios { display: flex; flex-wrap: wrap; gap: 8px; }
.af-pill { position: relative; cursor: pointer; }
.af-pill input { position: absolute; opacity: 0; width: 0; height: 0; }
.af-pill > span {
  display: inline-block; font-size: 0.88rem; font-weight: 600; color: var(--ink-600);
  padding: 10px 17px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-0);
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.af-pill:hover > span { border-color: var(--teal-500); color: var(--navy-800); }
.af-pill input:checked + span { background: var(--navy-800); border-color: var(--navy-800); color: #fff; }
.af-pill input:focus-visible + span { box-shadow: var(--focus-ring); }
[role="radiogroup"][aria-invalid="true"] { padding: 6px; margin: -6px; border-radius: var(--r-sm); background: var(--af-bad-bg); }

.af-check { display: flex; gap: 11px; align-items: flex-start; cursor: pointer; font-size: 0.93rem; line-height: 1.55; }
.af-check input { flex-shrink: 0; width: 19px; height: 19px; margin-top: 2px; accent-color: var(--teal-600); cursor: pointer; }
.af-check input:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 4px; }

.af-hint { margin: 7px 0 0; font-size: 0.83rem; color: var(--ink-400); line-height: 1.5; }
.af-count { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink-600); }
.af-count.is-short { color: var(--ink-400); }
.af-count.is-over { color: var(--af-bad); }

.af-err { margin: 7px 0 0; font-size: 0.85rem; font-weight: 600; color: var(--af-bad); }

.af-alert {
  margin-bottom: 24px; padding: 15px 18px; border-radius: var(--r-sm);
  border: 1px solid var(--af-bad); background: var(--af-bad-bg);
  color: var(--af-bad-ink); font-size: 0.92rem; line-height: 1.55;
}

/* The honeypot must be reachable by a bot parsing the DOM but never seen or
   focused by a person — display:none would make it obvious to skip. */
.af-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.af-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 6px; }
.af-foot { margin: 16px 0 0; font-size: 0.85rem; color: var(--ink-400); }

/* ---- required note, moved above the stepper so it is read before the
   first field rather than after the last one ---- */
.af-required-note {
  margin: 0 0 18px; font-size: 0.85rem; color: var(--ink-400);
}

/* A native control the enhanced version has replaced. Kept in the DOM — it
   still holds the value and still posts — but taken out of the layout.
   Not display:none: some browsers skip validating and autofilling that. */
.af-hidden-native {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Flags render from the system emoji font; the stack keeps them from falling
   back to letter boxes ("OM") on Windows, where colour flags are absent. */
.af-flag {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-size: 1.15em; line-height: 1; flex: none;
}

/* ---- country combobox ---- */

.af-country { position: relative; }

.af-country__btn {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; cursor: pointer;
}
.af-country__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.af-country__caret { flex: none; color: var(--ink-400); transition: transform 0.18s ease; }
.af-country__btn[aria-expanded="true"] { border-color: var(--teal-500); box-shadow: var(--focus-ring); }
.af-country__btn[aria-expanded="true"] .af-country__caret { transform: rotate(180deg); }

.af-country__pop {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; right: 0;
  background: #fff; border: 1px solid var(--border); border-radius: var(--r-sm);
  box-shadow: 0 14px 34px rgba(8, 26, 43, 0.16);
  overflow: hidden;
  /* Opaque in its own right: the sheet sits over a dimmed page on mobile and
     must not let the form show through it. */
  background-color: #fff;
}

.af-country__search {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid var(--border); color: var(--ink-400);
}
.af-country__search input {
  flex: 1; border: 0; outline: none; background: none;
  font: inherit; font-size: 0.94rem; color: var(--ink-900); min-width: 0;
}

.af-country__list {
  list-style: none; margin: 0; padding: 4px;
  max-height: 264px; overflow-y: auto; overscroll-behavior: contain;
}
.af-country__list > li::before { content: none; }

.af-country__opt {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 7px; cursor: pointer;
  font-size: 0.94rem; line-height: 1.3;
}
.af-country__optname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.af-country__dial { flex: none; font-size: 0.86rem; color: var(--ink-400); font-variant-numeric: tabular-nums; }
/* Hover and keyboard focus share one highlight, so arrowing and pointing look
   the same rather than fighting each other. */
.af-country__opt:hover,
.af-country__opt.is-active { background: var(--surface-1); }
.af-country__opt.is-selected { background: var(--teal-50, rgba(35, 173, 158, 0.1)); font-weight: 600; }
.af-country__opt.is-selected .af-country__dial { color: var(--teal-600); }

.af-country__none { margin: 0; padding: 14px 14px 16px; font-size: 0.88rem; color: var(--ink-400); }

/* ---- phone: fixed dial code + typed national number ---- */

.af-phone { display: flex; align-items: stretch; }
.af-phone__dial {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 0 12px;
  border: 1px solid var(--border); border-right: 0;
  border-radius: var(--r-sm) 0 0 var(--r-sm);
  background: var(--surface-1);
  font-size: 0.94rem; font-variant-numeric: tabular-nums; color: var(--ink-600);
  white-space: nowrap;
}
.af-phone__num { border-radius: 0 var(--r-sm) var(--r-sm) 0; min-width: 0; flex: 1; }
/* Focus the number and the pair reads as one control. */
.af-phone:focus-within .af-phone__dial { border-color: var(--teal-500); }
.af-phone__num[aria-invalid="true"] { border-color: var(--af-bad); background: var(--af-bad-bg); }
.af-phone__num[aria-invalid="true"] + .af-phone__dial,
.af-phone:has(.af-phone__num[aria-invalid="true"]) .af-phone__dial { border-color: var(--af-bad); }

/* ---- inline field notes (duplicate email) ---- */

.af-inputwrap { position: relative; display: flex; align-items: center; }
.af-inputwrap .af-input { width: 100%; padding-right: 40px; }

.af-note {
  margin: 7px 0 0; font-size: 0.85rem; line-height: 1.5;
  display: flex; gap: 7px; align-items: flex-start;
}
.af-note::before { content: "●"; font-size: 0.7em; line-height: 1.9; flex: none; }
.af-note--warn { color: #8a6414; }
.af-note--bad  { color: var(--af-bad); font-weight: 600; }
.af-note--ok   { color: var(--teal-600); }

/* Small spinner shown inside the email field while the check is in flight. */
.af-spin {
  position: absolute; right: 13px; width: 15px; height: 15px; flex: none;
  border: 2px solid var(--border); border-top-color: var(--teal-500);
  border-radius: 50%; animation: af-spin 0.7s linear infinite;
}
@keyframes af-spin { to { transform: rotate(360deg); } }

/* ---- word counter: green in range, red over ---- */
.af-count.is-ok { color: var(--teal-600); }

/* ---- submit button with its spinner ---- */

.af-submit { display: inline-flex; align-items: center; justify-content: center; gap: 10px; }
.af-submit__spin {
  display: none; width: 15px; height: 15px; flex: none;
  border: 2px solid rgba(13, 44, 73, 0.25); border-top-color: var(--navy-900);
  border-radius: 50%; animation: af-spin 0.7s linear infinite;
}
.af-submit.is-busy { pointer-events: none; }
.af-submit.is-busy .af-submit__spin { display: block; }

.btn:disabled, .btn[disabled] {
  opacity: 0.5; cursor: not-allowed; transform: none;
  filter: grayscale(0.3);
}

@media (prefers-reduced-motion: reduce) {
  .af-spin, .af-submit__spin { animation-duration: 2.4s; }
  .af-country__caret { transition: none; }
}

/* On a phone the list becomes a bottom sheet. Anchoring it under the button
   (as on desktop) puts a 300px panel below a field that is already near the
   fold, so it opens off-screen; pinning it to the bottom keeps the search box
   above the keyboard and the options within thumb reach.
   `top: auto` on a fixed element resolves to its static position — which is
   why `bottom` and `top: auto` must both be set explicitly here. */
@media (max-width: 640px) {
  .af-country__pop {
    position: fixed; z-index: 200;
    left: 0; right: 0; bottom: 0; top: auto;
    border-radius: 14px 14px 0 0;
    border-bottom: 0;
    box-shadow: 0 -10px 40px rgba(8, 26, 43, 0.24);
    animation: af-sheet 0.2s ease-out;
  }
  .af-country__search { padding: 14px 16px; }
  .af-country__search input { font-size: 16px; }  /* 16px stops iOS zooming in */
  .af-country__list { max-height: min(52vh, 340px); padding: 6px; }
  .af-country__opt { padding: 11px 12px; }

  /* Dim the page behind the sheet so the list reads as a layer, not as part
     of the form. Drawn by the combobox wrapper rather than the panel: a
     `z-index: -1` pseudo-element on the panel paints behind the panel's own
     background, which left the sheet see-through. */
  .af-country[data-open]::after {
    content: ""; position: fixed; inset: 0; z-index: 190;
    background: rgba(8, 26, 43, 0.45);
    animation: af-fade 0.2s ease-out;
  }
}
@keyframes af-fade { from { opacity: 0; } }
@keyframes af-sheet { from { transform: translateY(14px); opacity: 0; } }

/* ---- abstract confirmation page ---- */

.thanks { padding-top: clamp(80px, 10vw, 130px); }
.thanks__wrap { max-width: 660px; }

.thanks__card {
  text-align: center;
  padding: clamp(30px, 5vw, 46px) clamp(22px, 4vw, 40px);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: 0 18px 46px rgba(8, 26, 43, 0.08);
}

.thanks__tick { color: var(--teal-500); line-height: 0; }
/* The ring draws, then the check strikes through it — the motion says
   "accepted" in a way a static tick does not. */
.thanks__tick-ring {
  stroke-dasharray: 208; stroke-dashoffset: 208;
  animation: thanks-ring 0.75s cubic-bezier(0.3, 0.9, 0.4, 1) forwards;
  opacity: 0.3;
}
.thanks__tick-mark {
  stroke-dasharray: 48; stroke-dashoffset: 48;
  animation: thanks-mark 0.42s cubic-bezier(0.4, 0.9, 0.4, 1) 0.5s forwards;
}
@keyframes thanks-ring { to { stroke-dashoffset: 0; } }
@keyframes thanks-mark { to { stroke-dashoffset: 0; } }

.thanks__title {
  margin: 16px 0 10px;
  font-size: clamp(1.85rem, 4.4vw, 2.6rem);
  line-height: 1.12; letter-spacing: -0.02em; color: var(--navy-800);
}
.thanks__lede { margin: 0; color: var(--ink-600); font-size: 1.03rem; line-height: 1.6; }

.thanks__ref {
  display: inline-flex; flex-direction: column; gap: 9px; align-items: center;
  margin: 26px 0 0; padding: 16px 22px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.thanks__ref-label {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ink-400);
}
.thanks__ref-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; }
.thanks__ref-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.98rem; letter-spacing: 0.02em; color: var(--navy-800);
  user-select: all;   /* one click selects the whole reference */
}

.thanks__copy {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 999px; background: #fff;
  font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--ink-600);
  transition: border-color 0.16s ease, color 0.16s ease, background 0.16s ease;
}
.thanks__copy:hover { border-color: var(--teal-500); color: var(--teal-600); }
.thanks__copy.is-copied { border-color: var(--teal-500); background: var(--teal-100, rgba(35,173,158,.12)); color: var(--teal-600); }

.thanks__note { margin: 14px 0 0; font-size: 0.86rem; color: var(--ink-400); line-height: 1.55; }

.thanks__next {
  margin-top: 26px; padding: clamp(22px, 3.5vw, 30px);
  background: #fff; border: 1px solid var(--border); border-radius: var(--r-md);
}
.thanks__next-title {
  margin: 0 0 18px; font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--ink-400);
}

.thanks__steps { list-style: none; margin: 0; padding: 0; }
.thanks__steps > li::before { content: none; }

.thanks__step { position: relative; display: flex; gap: 14px; padding: 0 0 20px; }
.thanks__step:last-child { padding-bottom: 0; }
/* The connector is drawn on the step, not between them, so the last one has
   no dangling tail. */
.thanks__step:not(:last-child)::before {
  content: ""; position: absolute; left: 6px; top: 18px; bottom: 2px;
  width: 2px; background: var(--border);
}
.thanks__step-dot {
  position: relative; z-index: 1; flex: none;
  width: 14px; height: 14px; margin-top: 4px; border-radius: 50%;
  background: #fff; border: 2px solid var(--border);
}
.thanks__step.is-done .thanks__step-dot { background: var(--teal-500); border-color: var(--teal-500); }
.thanks__step-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.thanks__step-body b { font-size: 0.95rem; color: var(--navy-800); }
.thanks__step-body span { font-size: 0.88rem; color: var(--ink-400); line-height: 1.55; }
.thanks__step.is-done .thanks__step-body b { color: var(--teal-600); }

.thanks__actions {
  display: flex; flex-wrap: wrap; gap: 12px;
  justify-content: center; margin-top: 28px;
}
.thanks__help { margin: 18px 0 0; text-align: center; font-size: 0.86rem; color: var(--ink-400); }

@media (prefers-reduced-motion: reduce) {
  .thanks__tick-ring, .thanks__tick-mark { animation: none; stroke-dashoffset: 0; }
}

@media (max-width: 560px) {
  .thanks__actions .btn { width: 100%; justify-content: center; }
}

.af-tick { color: var(--teal-500); }
.af-ref {
  display: inline-flex; flex-direction: column; gap: 5px; align-items: center;
  margin: 26px auto 6px; padding: 15px 26px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-md);
}
.af-ref__label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-400); }
.af-ref__code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.95rem; color: var(--navy-800); }

/* ==========================================================================
   18. VENUE — OCEC block with the 360° tour (contact.php)
   ========================================================================== */

.venue__grid {
  display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: clamp(32px, 5vw, 64px); align-items: center;
}
@media (max-width: 1023px) { .venue__grid { grid-template-columns: 1fr; } }

.venue__title-accent { color: var(--teal-600); display: block; }

/* Two-up facts. Each is an icon + a short line — no card, so the eye reads them
   as a list of attributes rather than four competing boxes. */
.venue__facts {
  list-style: none; margin: 26px 0 30px; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px 24px;
}
.venue__facts > li::before { content: none; }
.venue__fact {
  display: flex; align-items: flex-start; gap: 13px;
  font-size: 0.98rem; line-height: 1.45; color: var(--ink-600);
}
.venue__fact b { color: var(--navy-800); font-weight: 700; }
.venue__fact-ico {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 50%;
  background: rgba(35, 173, 158, 0.12); color: var(--teal-600);
}
.venue__fact-ico svg { width: 21px; height: 21px; }
@media (max-width: 520px) { .venue__facts { grid-template-columns: 1fr; gap: 16px; } }

/* Tour. Clipped to the congress mark so the embed reads as part of the brand
   rather than a bare rectangle from another site. */
.venue__tour { margin: 0; }
.venue__tour-frame {
  position: relative; aspect-ratio: 16 / 11; overflow: hidden;
  border-radius: var(--r-md); background: var(--navy-800);
  box-shadow: var(--shadow-3);
}
.venue__tour-frame iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
  opacity: 0; transition: opacity 0.6s var(--ease);
}
.venue__tour.is-loaded .venue__tour-frame iframe { opacity: 1; }

.venue__tour-skeleton {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px;
  color: rgba(255, 255, 255, 0.72); font-size: 0.9rem;
  transition: opacity 0.5s var(--ease), visibility 0.5s;
}
.venue__tour.is-loaded .venue__tour-skeleton { opacity: 0; visibility: hidden; }
.venue__tour-skeleton p { margin: 0; }
.venue__tour-mark {
  width: 74px; height: 52px;
  background: linear-gradient(150deg, var(--teal-500), var(--navy-700));
  clip-path: var(--logo-clip);
  animation: pulse-mark 1.7s var(--ease) infinite;
}

.venue__tour-cap {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 14px; font-size: 0.87rem; color: var(--ink-400);
}
.venue__tour-cap a { color: var(--teal-600); }
.venue__tour-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--teal-500);
  flex: 0 0 auto; animation: chip-pulse 2.4s var(--ease) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .venue__tour-mark, .venue__tour-dot { animation: none; }
}

/* ==========================================================================
   19. MOBILE DRAWER CLOSE  +  SOCIAL RAIL
   ========================================================================== */

/* --- Social rail: fixed to the left edge, as on v1 ----------------------- */
.social-rail {
  /* Hidden until the reader is past the hero (main.js adds .is-visible at
     100vh). translateY(-50%) does the vertical centring, so both states must
     carry it — animating transform alone would drop the rail off-centre. */
  position: fixed; left: 0; top: 50%;
  transform: translate(-100%, -50%); opacity: 0; pointer-events: none;
  transition: transform var(--t-med) var(--ease-out-expo),
              opacity var(--t-med) var(--ease);
  z-index: 90; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 8px; border-radius: 0 var(--r-md) var(--r-md) 0;
  background: rgba(8, 26, 43, 0.72);
  -webkit-backdrop-filter: blur(9px); backdrop-filter: blur(9px);
  border: 1px solid var(--border-dark); border-left: 0;
}
/* body-qualified so it outranks `body.nav-open .social-rail` below, which is
   (0,2,1) and would otherwise keep the rail hidden even when shown. The
   drawer rule still wins while the menu is open because it comes later at
   equal specificity. */
body .social-rail.is-visible {
  transform: translate(0, -50%); opacity: 1; pointer-events: auto;
}
.social-rail__link {
  display: grid; place-items: center; width: 38px; height: 38px;
  border-radius: 10px; color: rgba(255, 255, 255, 0.72);
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              transform var(--t-med) var(--ease-out-expo);
}
.social-rail__link svg { width: 17px; height: 17px; fill: currentColor; }
/* Each platform lights up in its own brand colour on hover — a single teal for
   all four made them read as one control rather than four destinations.
   --brand is set per item below; the teal is only the fallback. */
.social-rail__link:hover, .social-rail__link:focus-visible {
  color: #fff; background: var(--brand, var(--teal-600));
  transform: translateX(4px) scale(1.06);
}
.social-rail__item--linkedin  { --brand: #0a66c2; }
.social-rail__item--x         { --brand: #101418; }
.social-rail__item--facebook  { --brand: #1877f2; }
/* Instagram is a gradient, not a flat colour — a flat purple looks wrong and a
   flat pink looks like a different brand. */
.social-rail__item--instagram .social-rail__link:hover,
.social-rail__item--instagram .social-rail__link:focus-visible {
  background: radial-gradient(circle at 30% 107%,
    #fdf497 0%, #fd5949 45%, #d6249f 60%, #285aeb 90%);
}
.social-rail__link:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Label slides out of the rail on hover. */
.social-rail__label {
  position: absolute; left: calc(100% + 10px); top: 50%; transform: translate(-8px, -50%);
  padding: 5px 11px; border-radius: var(--r-pill); white-space: nowrap;
  background: var(--navy-800); color: #fff; font-size: 0.76rem; font-weight: 600;
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-fast) var(--ease), transform var(--t-med) var(--ease-out-expo);
}
.social-rail__item { position: relative; display: block; }
.social-rail__item:hover .social-rail__label,
.social-rail__link:focus-visible + .social-rail__label {
  opacity: 1; transform: translate(0, -50%);
}

/* Entrance: the rail draws itself in, one icon after the next.
   The icons are visible by default and the animation starts them off-screen
   instead — so if animations are suppressed (reduced motion, qa-mode, an
   older browser) the rail is simply there, rather than permanently invisible. */
.social-rail__item { opacity: 0; transform: translateX(-10px);
  transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out-expo); }
.social-rail.is-visible .social-rail__item {
  opacity: 1; transform: none;
  transition-delay: calc(var(--i, 0) * 0.06s);
}
@keyframes rail-in {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: none; }
}

/* Below the drawer, and out of the way of a thumb on small screens. */
@media (max-width: 767px) { .social-rail { display: none; } }
body.nav-open .social-rail, body.nav-open .social-rail.is-visible { opacity: 0; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .social-rail, .social-rail__item { transition: none; }
  .social-rail.is-visible .social-rail__item { transition-delay: 0s; }
  .social-rail__link:hover { transform: none; }
}

/* ==========================================================================
   20. CALL FOR ABSTRACTS  +  CLOSED STATE (abstracts-submit.php)
   ========================================================================== */

.cfp__intro { max-width: 880px; }
.cfp__lede {
  font-size: 1.06rem; line-height: 1.62; color: rgba(255, 255, 255, 0.84);
  max-width: 62ch; margin: 0 0 14px;
}
.cfp__lede--dim { color: rgba(255, 255, 255, 0.66); }

.status-chip--closed {
  border-style: solid; border-color: rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.05); color: rgba(255, 255, 255, 0.82);
}
.status-chip--closed::before { background: rgba(255, 255, 255, 0.5); }

/* The call-for-abstracts dates now use the shared .date-card grid (see §
   "dates-grid"), so the numbered .cfp__date variant that used to live here is
   gone. Its corner badge sat absolutely over the date text, which collided
   once the month name wrapped; the shared card has no badge and abbreviates
   the month, so the two pages match and the collision cannot recur. */
.cfp .dates-grid { margin-top: 34px; }

/* Closed state */
.af-closed { text-align: center; padding: clamp(30px, 6vw, 60px) 0; }
.af-closed__ico { width: 56px; height: 56px; color: var(--ink-400); margin-bottom: 18px; }
.af-closed .section-lede { margin-inline: auto; }

/* --- Form polish ---------------------------------------------------------
   The first pass was structurally right but visually flat: inputs and page
   shared one white, the step rail read as a stray row of numbers, and every
   field carried the same weight. This gives the form a surface of its own and
   a clearer hierarchy. */

/* Card the whole form, so it reads as a document you are filling in. */
.af--stepped .af-panel {
  box-shadow: var(--shadow-2);
  border-color: var(--border);
  position: relative; overflow: hidden;
}
/* Accent edge on the active panel — small, and it anchors the eye. */
.af--stepped .af-panel::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--teal-500), rgba(35, 173, 158, 0));
}

.af-legend {
  display: flex; align-items: center; gap: 11px;
  font-size: 1.24rem; margin-bottom: 22px;
  padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.af--stepped .af-legend { width: 100%; }

/* Inputs sit on a tinted ground so the field is visible before it is focused;
   an outline-only box on white disappears at a glance. */
.af-input {
  background: var(--surface-1);
  border-color: transparent;
  box-shadow: inset 0 0 0 1px var(--border);
  padding: 13px 15px;
}
.af-input:hover { background: var(--surface-0); box-shadow: inset 0 0 0 1px var(--teal-300); }
.af-input:focus-visible {
  background: var(--surface-0);
  border-color: transparent;
  box-shadow: inset 0 0 0 1.5px var(--teal-500), var(--focus-ring);
}
.af-input[aria-invalid="true"] {
  background: var(--af-bad-bg);
  box-shadow: inset 0 0 0 1.5px var(--af-bad);
}

.af-label { font-size: 0.86rem; letter-spacing: 0.01em; }

/* Step rail: a connected progress line rather than four loose labels. */
.af-steps { position: relative; gap: 0; }
.af-step { border-top: 0; padding: 0 4px; }
.af-step::before {
  content: ""; position: absolute; top: 11px; left: 0; right: 0; height: 2px;
  background: var(--border); z-index: 0;
}
.af-step:first-child::before { left: 50%; }
.af-step:last-child::before  { right: 50%; }
.af-step > span { position: relative; z-index: 1; border: 2px solid var(--surface-0); }
.af-step {
  flex-direction: column; gap: 7px; text-align: center;
  align-items: center; justify-content: flex-start;
}
.af-step.is-on > span {
  box-shadow: 0 0 0 4px rgba(35, 173, 158, 0.18);
}

/* Declarations read as commitments, so they get a surface of their own. */
.af-check {
  padding: 14px 16px; border-radius: var(--r-sm);
  background: var(--surface-1); border: 1px solid var(--border);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.af-check:hover { border-color: var(--teal-300); background: var(--surface-0); }
.af-check:has(input:checked) {
  border-color: var(--teal-500); background: rgba(35, 173, 158, 0.07);
}

.af-actions {
  margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--border);
  justify-content: flex-end;
}
.af-actions [data-af-back] { margin-right: auto; }

@media (max-width: 560px) {
  .af-actions { flex-direction: column-reverse; align-items: stretch; }
  .af-actions .btn { width: 100%; }
  .af-actions [data-af-back] { margin-right: 0; }
}

/* ==========================================================================
   21. NOT FOUND (404.php)
   ========================================================================== */

.nf { text-align: center; }
.nf__wrap { max-width: 660px; }

.nf__mark { width: 120px; height: auto; margin: 0 auto 6px; display: block; }
.nf__mark path, .nf__mark circle { stroke: var(--teal-500); stroke-width: 2; fill: none; }
.nf__mark .nf__node { fill: var(--teal-300); stroke: none; }
.nf__mark .nf__thread { opacity: .45; }

.nf__code {
  font-family: var(--font-display); font-weight: 800; font-stretch: 110%;
  font-size: clamp(64px, 13vw, 118px); line-height: .9; margin: 0 0 4px;
  color: transparent; -webkit-text-stroke: 1.5px rgba(18, 65, 102, 0.18);
  letter-spacing: 0.02em;
}
.nf__title { margin-bottom: 12px; }
.nf__lede { margin-inline: auto; }
.nf__asked {
  display: block; margin-top: 10px; font-size: 0.9rem; color: var(--ink-400);
}
.nf__asked code {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.88em;
  padding: 2px 7px; border-radius: 5px; background: var(--surface-1);
  overflow-wrap: anywhere;
}

.nf__actions {
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
  margin: 28px 0 40px;
}

.nf__links { text-align: left; }
.nf__links-head {
  margin: 0 0 12px; text-align: center;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-400);
}
.nf__links ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
.nf__links li::before { content: none; }
.nf__links a {
  display: flex; flex-direction: column; gap: 3px;
  padding: 15px 17px; border-radius: var(--r-md); text-decoration: none;
  background: var(--surface-0); border: 1px solid var(--border);
  transition: border-color var(--t-fast) var(--ease), transform var(--t-med) var(--ease-out),
              box-shadow var(--t-med) var(--ease);
}
.nf__links a:hover {
  border-color: var(--teal-500); transform: translateY(-2px); box-shadow: var(--shadow-2);
}
.nf__links span { font-weight: 600; color: var(--navy-800); }
.nf__links small { font-size: 0.82rem; color: var(--ink-400); }
@media (max-width: 560px) { .nf__links ul { grid-template-columns: 1fr; } }

/* --- Registration / workshop chooser -------------------------------------
   Neither embed has a src until a choice is made, so this panel is what the
   visitor meets first. It has to read as a decision, not as a loading state. */
.embed-choice__ask { text-align: center; padding: 8px 0 4px; }
.embed-choice__q {
  font-family: var(--font-display); font-weight: 700; font-size: 1.15rem;
  color: var(--ink-900); margin: 0 0 18px;
}
/* auto-fit, not repeat(2): a third card (Exhibition Only – FREE) was added 2026-09-05 and a
   hard-coded 2 left it stranded alone on a second row. This lays out 2 or 3 evenly and needs
   no edit if a fourth ever appears. */
.embed-choice__opts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px; max-width: 1020px; margin-inline: auto;
}
.embed-choice__opt {
  display: grid; gap: 6px; justify-items: center; text-align: center;
  padding: 26px 22px; cursor: pointer;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color var(--t-med) var(--ease), box-shadow var(--t-med) var(--ease),
              transform var(--t-med) var(--ease-out);
}
.embed-choice__opt:hover, .embed-choice__opt:focus-visible {
  border-color: var(--teal-500); box-shadow: var(--shadow-1);
  transform: translateY(-3px); outline: none;
}
.embed-choice__opt:focus-visible { box-shadow: var(--focus-ring); }
.embed-choice__opt .bi { font-size: 1.7rem; color: var(--teal-600); line-height: 1; }
.embed-choice__opt-label {
  font-family: var(--font-display); font-weight: 700; font-size: 1.02rem; color: var(--navy-700);
}
.embed-choice__opt-note { font-size: 0.86rem; color: var(--ink-600); line-height: 1.45; }

/* Once chosen: a quiet header naming the choice, with the way back. */
.embed-choice__chosen {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: 0 0 14px; font-family: var(--font-display); font-weight: 700;
  font-size: 1rem; color: var(--navy-700);
}
.embed-choice__change {
  background: none; border: 0; cursor: pointer; padding: 6px 2px;
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
  color: var(--teal-600); text-decoration: underline; text-underline-offset: 3px;
}
.embed-choice__change:hover { color: var(--navy-700); }
.embed-choice__change:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-sm); }

@media (max-width: 640px) {
  .embed-choice__opts { grid-template-columns: 1fr; gap: 12px; }
  .embed-choice__opt { padding: 20px 18px; }
  .embed-choice__opt { padding: 20px 18px; }
}

/* ==========================================================================
   MEDIA — social carousel, video gallery
   ==========================================================================
   The rail is a real scrolling element with scroll-snap, not a transformed
   track. Touch swipe, trackpad, keyboard and focus scrolling then all work
   without any JavaScript from us; media.js only adds the buttons, dots and
   autoplay on top. With JS off it stays a usable scrollable strip.
   ---------------------------------------------------------------------- */
.media-carousel { position: relative; }

.media-carousel__rail {
  display: grid;
  grid-auto-flow: column;
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Room for the card shadow, which a clipping overflow would shear off. */
  padding: 6px 2px 14px;
  /* The scrollbar is replaced by the dots and buttons below. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.media-carousel__rail::-webkit-scrollbar { display: none; }
.media-carousel__rail:focus-visible { outline: 2px solid var(--teal-500); outline-offset: 4px; border-radius: var(--r-md); }

.media-carousel__rail > * { scroll-snap-align: start; }

/* grid-auto-columns is what sets "how many fit" at each width, and media.js
   measures the rendered slide to derive its page size — so these two never
   need to agree in code, only visually. */
.media-carousel__rail--square { grid-auto-columns: calc((100% - 3 * 20px) / 4); }
.media-carousel__rail--wide   { grid-auto-columns: calc((100% - 2 * 20px) / 3); }

@media (max-width: 1023px) {
  .media-carousel__rail--square { grid-auto-columns: calc((100% - 2 * 20px) / 3); }
  .media-carousel__rail--wide   { grid-auto-columns: calc((100% - 20px) / 2); }
}
@media (max-width: 767px) {
  .media-carousel__rail--square { grid-auto-columns: calc((100% - 20px) / 2); }
  .media-carousel__rail--wide   { grid-auto-columns: 100%; }
}
@media (max-width: 480px) {
  .media-carousel__rail--square { grid-auto-columns: 100%; }
}

.media-carousel__controls {
  display: flex; align-items: center; justify-content: center; gap: 16px; margin-top: 18px;
}
/* One page of content needs no controls; media.js sets .is-static. */
.media-carousel.is-static .media-carousel__controls { display: none; }

.media-carousel__btn {
  width: 42px; height: 42px; flex: none; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--surface-0); color: var(--navy-700);
  border: 1px solid var(--border); box-shadow: var(--shadow-1);
  font-size: 1.1rem; transition: background .2s, color .2s, border-color .2s;
}
.media-carousel__btn:hover { background: var(--teal-500); color: #fff; border-color: var(--teal-500); }
.media-carousel__btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.media-carousel__dots { display: flex; align-items: center; gap: 8px; }
.media-carousel__dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%; cursor: pointer;
  background: var(--border); transition: width .25s, background .25s;
}
.media-carousel__dot.is-on { width: 24px; border-radius: 999px; background: var(--teal-500); }
.media-carousel__dot:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---- social slide ---- */
.social-slide { margin: 0; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.social-slide__frame {
  position: relative; border-radius: var(--r-md); overflow: hidden;
  background: var(--surface-1); border: 1px solid var(--border); box-shadow: var(--shadow-1);
}
.social-slide__img {
  display: block; width: 100%; height: auto;
  /* Square crop: designs arrive as a mix of 1:1 and 4:5, and a ragged rail
     reads as broken rather than varied. */
  aspect-ratio: 1 / 1; object-fit: cover;
  transition: transform .4s ease;
}
.social-slide__frame:hover .social-slide__img { transform: scale(1.04); }

/* The link covers the tile but shows only as a corner badge. */
.social-slide__link {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: flex-end; padding: 12px;
  color: #fff; font-size: 1.05rem; text-decoration: none;
  background: linear-gradient(to top, rgba(8, 26, 43, 0.55), rgba(8, 26, 43, 0) 45%);
  opacity: 0; transition: opacity .25s;
}
.social-slide__frame:hover .social-slide__link,
.social-slide__link:focus-visible { opacity: 1; }
.social-slide__link:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }

.social-slide__badge {
  position: absolute; right: 10px; bottom: 10px;
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(8, 26, 43, 0.55); color: #fff; font-size: 0.9rem;
}
.social-slide__cap {
  font-size: 0.9rem; color: var(--ink-600); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---- social grid (full gallery) ---- */
.social-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px;
}
@media (max-width: 1023px) { .social-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 767px)  { .social-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 420px)  { .social-grid { grid-template-columns: 1fr; } }

.social-tile { margin: 0; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.social-tile__frame {
  position: relative; border-radius: var(--r-md); overflow: hidden;
  background: var(--surface-1); border: 1px solid var(--border); box-shadow: var(--shadow-1);
}
.social-tile__img {
  display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover;
  transition: transform .4s ease;
}
.social-tile__frame:hover .social-tile__img { transform: scale(1.04); }
.social-tile__frame:hover .social-slide__link { opacity: 1; }
.social-tile__cap { display: flex; flex-direction: column; gap: 3px; }
.social-tile__text {
  font-size: 0.9rem; color: var(--ink-600); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.social-tile__date { font-size: 0.78rem; color: var(--ink-400); }

/* ---- video cards ----
   16:9 everywhere, because the client's videos are landscape. */
.video-card {
  display: flex; flex-direction: column; min-width: 0;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md); overflow: hidden; box-shadow: var(--shadow-1);
  transition: transform .25s, box-shadow .25s;
}
.video-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }

.video-card__player { position: relative; aspect-ratio: 16 / 9; background: var(--navy-900); }
.video-card__poster { display: block; width: 100%; height: 100%; object-fit: cover; }
.video-card__poster--none {
  width: 100%; height: 100%;
  background: linear-gradient(135deg, var(--navy-800), var(--teal-600));
}

/* The play control fills the frame: the whole poster is the target. */
.video-card__play,
.video-hero__play {
  position: absolute; inset: 0; width: 100%; border: 0; cursor: pointer;
  display: grid; place-items: center;
  background: rgba(8, 26, 43, 0.28); transition: background .25s;
}
.video-card__play:hover, .video-hero__play:hover { background: rgba(8, 26, 43, 0.45); }
.video-card__play:focus-visible, .video-hero__play:focus-visible { outline: 3px solid var(--teal-300); outline-offset: -5px; }

.video-card__play-ico, .video-hero__play-ico {
  width: 54px; height: 54px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.94); color: var(--navy-700);
  font-size: 1.7rem; padding-left: 4px;   /* optical centring of the triangle */
  box-shadow: 0 6px 20px rgba(8, 26, 43, 0.3);
  transition: transform .25s, background .25s;
}
.video-card__play:hover .video-card__play-ico,
.video-hero__play:hover .video-hero__play-ico { transform: scale(1.08); background: #fff; }

.video-card__time {
  position: absolute; right: 10px; bottom: 10px;
  padding: 3px 8px; border-radius: 6px;
  background: rgba(8, 26, 43, 0.78); color: #fff;
  font-size: 0.75rem; font-weight: 600; font-variant-numeric: tabular-nums;
  pointer-events: none;   /* never blocks the play button beneath it */
}
.video-card__time--lg { right: 16px; bottom: 16px; font-size: 0.85rem; padding: 5px 11px; }

.media-video__frame { display: block; width: 100%; height: 100%; border: 0; }
/* An uploaded file plays in a <video>, which unlike an iframe has intrinsic
   proportions. Letterbox it against black rather than stretching the picture. */
video.media-video__frame { object-fit: contain; background: #000; }

.video-card__body { padding: 15px 17px 18px; display: flex; flex-direction: column; gap: 7px; }
.video-card__title { margin: 0; font-size: 1.02rem; line-height: 1.4; color: var(--navy-700); }
.video-card__desc {
  margin: 0; font-size: 0.88rem; color: var(--ink-600); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---- featured video ---- */
.video-hero {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  border-radius: var(--r-lg); background: var(--navy-900);
  box-shadow: var(--shadow-2); border: 1px solid var(--border);
  max-width: 980px; margin-inline: auto;
}
.video-hero__poster { display: block; width: 100%; height: 100%; object-fit: cover; }
.video-hero__play-ico { width: 74px; height: 74px; font-size: 2.3rem; }
@media (max-width: 640px) {
  .video-hero__play-ico { width: 56px; height: 56px; font-size: 1.7rem; }
}

/* Once playing, the iframe is the only child and fills the frame. */
.video-hero.is-playing, .video-card__player.is-playing { background: #000; }

/* LEAD FORM — submit state + confirmation -------------------------------- */

/* Spinner lives in the button and only becomes visible in the busy state, so
   there is no layout shift when it appears. */
.lead-submit { position: relative; display: inline-flex; align-items: center; gap: 0; }
.lead-submit__spin {
  width: 0; height: 16px; flex: none; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35); border-top-color: #fff;
  opacity: 0; transition: width .18s var(--ease), margin-right .18s var(--ease), opacity .18s;
}
.lead-submit.is-busy { pointer-events: none; }
.lead-submit.is-busy .lead-submit__spin {
  width: 16px; margin-right: 10px; opacity: 1;
  animation: lead-spin .7s linear infinite;
}
@keyframes lead-spin { to { transform: rotate(360deg); } }
/* Reduced motion still needs a visible busy state — just not a spinning one. */
@media (prefers-reduced-motion: reduce) {
  .lead-submit.is-busy .lead-submit__spin { animation: none; }
}
.lead-submit:disabled, .lead-submit.is-busy { opacity: 0.85; cursor: progress; }

/* ---- confirmation card ---- */
.lead-done {
  max-width: 620px; margin-inline: auto; text-align: center;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-2);
  padding: clamp(30px, 5vw, 48px) clamp(22px, 4vw, 44px);
}
.lead-done__ico {
  width: 62px; height: 62px; margin: 0 auto 20px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--gradient-brand); color: #fff;
  box-shadow: 0 8px 22px rgba(35, 173, 158, 0.28);
}
.lead-done__ico svg { width: 32px; height: 32px; }
.lead-done__title {
  margin: 0 0 10px; font-family: var(--font-display);
  font-size: clamp(1.35rem, 2.6vw, 1.7rem); color: var(--navy-700); line-height: 1.3;
}
.lead-done__lede { margin: 0 0 26px; color: var(--ink-600); line-height: 1.65; }

/* Numbered steps, left-aligned inside a centred card: a centred list with
   ragged edges on both sides is hard to scan. */
.lead-done__steps {
  list-style: none; margin: 0 0 24px; padding: 0;
  text-align: left; display: flex; flex-direction: column; gap: 2px;
  counter-reset: lead-step;
}
.lead-done__steps li {
  counter-increment: lead-step;
  position: relative; padding: 12px 0 12px 46px;
  border-top: 1px solid var(--border);
}
.lead-done__steps li:first-child { border-top: 0; }
.lead-done__steps li::before {
  content: counter(lead-step);
  position: absolute; left: 0; top: 11px;
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-1); color: var(--teal-600);
  font-family: var(--font-display); font-size: 0.82rem; font-weight: 700;
}
.lead-done__steps b { display: block; color: var(--ink-900); font-size: 0.96rem; font-weight: 600; }
.lead-done__steps span { display: block; color: var(--ink-400); font-size: 0.86rem; margin-top: 2px; }

.lead-done__urgent {
  margin: 0 0 22px; padding-top: 20px; border-top: 1px solid var(--border);
  color: var(--ink-400); font-size: 0.9rem;
}
.lead-done__urgent a { color: var(--teal-600); font-weight: 600; }
.lead-done__cta { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }

/* SOCIAL RAIL — collapsed by default ------------------------------------
   The rail used to sit open over page content at narrower widths. It now
   shows only a small tab; the icons slide out on hover (pointer devices) or
   on tap (touch, where hover does not exist). */
.social-rail { padding: 0; overflow: hidden; }

.social-rail__tab {
  display: grid; place-items: center;
  width: 34px; height: 44px; padding: 0;
  background: none; border: 0; cursor: pointer;
  color: rgba(255, 255, 255, 0.8);
  transition: color var(--t-fast) var(--ease);
}
.social-rail__tab:hover { color: #fff; }
.social-rail__tab:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 8px; }
.social-rail__tab-ico { display: grid; place-items: center; }
.social-rail__tab-ico svg {
  width: 17px; height: 17px;
  transition: transform var(--t-med) var(--ease-out-expo);
}

/* The icons are laid out as always but clipped to zero height while closed.
   grid-template-rows animates cleanly where height:auto cannot. */
/* Hidden outright while collapsed rather than animated to zero: the rail is a
   flex column, and a shrinkable grid child kept squeezing four 38px icons into
   20px of total height. display toggling sidesteps that entirely. */
.social-rail__items { display: none; }
.social-rail.is-open .social-rail__items { display: block; }
@media (hover: hover) and (pointer: fine) {
  .social-rail:hover .social-rail__items { display: block; }
}
.social-rail:focus-within .social-rail__items { display: block; }
/* Top padding matters once open: the tab that used to occupy that space is
   collapsed away, so without it the first icon sits flush against the edge. */
.social-rail__inner { display: flex; flex-direction: column; gap: 2px; padding: 10px 8px; }

/* Open on hover only where hovering is real — a touch device reports
   `hover: none` and would otherwise latch the rail open after a tap. */
@media (hover: hover) and (pointer: fine) {
  
  .social-rail:hover .social-rail__tab-ico svg { transform: rotate(90deg); }
}
/* Keyboard focus anywhere inside opens it too. */


/* Explicit open state, set by main.js on tap and kept in sync with
   aria-expanded. Wins over the hover rules because it comes later. */


/* Once open, the tab has done its job and the icons speak for themselves —
   leaving it on top of them reads as a fifth, broken network. It collapses to
   zero height rather than display:none so the rail does not jump, and stays
   in the DOM so a keyboard user can still tab to it to close the rail. */
.social-rail__tab {
  overflow: hidden;
  transition: height var(--t-med) var(--ease-out-expo),
              min-height var(--t-med) var(--ease-out-expo),
              opacity var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
/* Collapsed via grid-template-rows on a wrapper, exactly as the icon list is:
   animating the button's own height fought its intrinsic grid sizing and left
   it stuck at 44px. The wrapper has no intrinsic size of its own, so 1fr → 0fr
   collapses cleanly and animates. */
/* The tab is simply removed from flow once the rail is open: the icons are
   the content at that point, and an animated collapse of a 34px button is not
   worth the flex/grid contortions it took to make it size correctly. */
.social-rail__tabwrap { display: block; }
.social-rail.is-open .social-rail__tabwrap { display: none; }
@media (hover: hover) and (pointer: fine) {
  .social-rail:hover .social-rail__tabwrap { display: none; }
}

/* While collapsed the per-item entrance animation must not re-run; it is the
   container that moves now, not each icon. */
.social-rail.is-visible .social-rail__item { opacity: 1; transform: none; animation: none; }

/* The hover label sat to the right of each icon; inside a clipped container it
   would be cut off, so it is dropped — the aria-label still names each link. */
.social-rail__label { display: none; }

/* Phones: the rail is already display:none above 767px-down, but if that ever
   changes the tab must stay tappable rather than hover-only. */
@media (max-width: 767px) { .social-rail { display: none; } }

/* CALL FOR PAPERS — deadline counter ------------------------------------
   Sits next to the submit button on the dark call-for-papers band. Reads as a
   badge rather than a second button, so it adds urgency without competing
   with the actual call to action. */
.cfp-count {
  /* Height-matched to .btn beside it rather than padded by eye: the badge and
     the button share a row, and a few pixels of difference reads as a
     misalignment on a dark band. */
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  align-self: center; min-height: 52px;
  padding: 0 18px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(126, 217, 205, 0.32);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  line-height: 1.05; white-space: nowrap;
}
.cfp-count__n {
  font-family: var(--font-display); font-size: 1.55rem; font-weight: 800;
  color: var(--teal-300); font-variant-numeric: tabular-nums;
}
.cfp-count__u {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: rgba(255, 255, 255, 0.62);
}

/* Inside two weeks the badge warms up. Colour alone is not the message — the
   number itself is already the signal — so no extra meaning is lost by it. */
.cfp-count--urgent {
  background: rgba(255, 176, 102, 0.12);
  border-color: rgba(255, 176, 102, 0.45);
}
.cfp-count--urgent .cfp-count__n { color: #ffc182; }

@media (max-width: 480px) {
  .cfp-count { min-height: 46px; padding: 0 15px; }
  .cfp-count__n { font-size: 1.3rem; }
}

/* ============================================================================
   EXHIBITION — FREE ENTRY (client, 2026-09-05)

   The homepage band is the site's own `.split` pattern, so it needs no layout
   CSS of its own — only the caveat note and the button spacing below. Every
   value is a token: an earlier draft hardcoded a green that matched no other
   element on the site and read as a third-party advert.
   ========================================================================= */
.exfree__note {
  margin-top: 18px; padding: 14px 16px;
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-500);
  border-radius: var(--r-sm);
  color: var(--ink-600); font-size: 0.95rem; line-height: 1.6;
}
.exfree__note b { color: var(--ink-900); }
/* .split__body .btn already adds 10px; this is the gap from the note above it. */
.exfree__actions { margin-top: 14px; }

/* FREE instead of "OMR 0" on the pricing card (register.php). Teal, like every
   other emphasis on that page. */
.price-card__amount--free b { color: var(--teal-600); letter-spacing: 0.01em; }

/* The free-exhibition card in the registration chooser — marked as the
   no-charge option using the same teal the featured price card uses. */
.embed-choice__opt[data-embed-pick="exhibition"] { border-color: var(--teal-500); }
.embed-choice__opt[data-embed-pick="exhibition"] .bi { color: var(--teal-600); }
.embed-choice__opt[data-embed-pick="exhibition"] .embed-choice__opt-label { color: var(--teal-600); }

/* LEGAL / POLICY PAGES (refund-policy.php) ---------------------------------
   A bilingual clause is two columns of the same text, so they are laid out as
   a grid rather than as two stacked blocks: a reader checking the Arabic
   against the English should find each pair side by side at the same height.
   Below 900px there is no honest way to hold both columns, so they stack —
   English first, its Arabic directly beneath, still visibly one clause. */
.legal__wrap { max-width: 1120px; }

.legal__meta {
  background: var(--surface-1); border: 1px solid var(--border);
  border-left: 4px solid var(--teal-500); border-radius: var(--r-md);
  padding: 20px 24px; margin-bottom: 30px;
}
.legal__meta-row { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0 0 10px; }
.legal__meta-row:last-child { margin-bottom: 0; }
.legal__meta-k {
  flex: 0 0 190px; font-family: var(--font-display); font-weight: 700;
  font-size: 0.82rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-600); padding-top: 2px;
}
.legal__meta-v { flex: 1 1 260px; color: var(--ink-900); font-weight: 500; }

.legal__clause {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 40px;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 26px 28px; margin-bottom: 18px;
}
/* The rule between the columns is the seam between the two languages; it is
   drawn on the Arabic column so it disappears by itself once they stack. */
.legal__col--ar { border-left: 1px solid var(--border); padding-left: 40px; margin-left: -40px; }

.legal__head {
  display: flex; align-items: baseline; gap: 12px;
  font-family: var(--font-display); font-size: 1.08rem; font-weight: 700;
  color: var(--navy-800); margin: 0 0 14px; line-height: 1.35;
}
.legal__num {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 26px; border-radius: 50%;
  background: var(--navy-700); color: #fff;
  font-size: 0.82rem; font-weight: 700; line-height: 1;
}
/* The badge leads the heading in both columns — which in the Arabic column
   means the right-hand edge, so it inherits the RTL order rather than being
   pushed to the end. The digit inside it is Latin and stays unmirrored. */
.legal__col--ar .legal__head { justify-content: flex-start; }

.legal__list { margin: 0; padding: 0; list-style: none; }
.legal__list li {
  position: relative; padding-left: 20px; margin-bottom: 10px;
  color: var(--ink-600); line-height: 1.7;
}
.legal__list li:last-child { margin-bottom: 0; }
.legal__list li::before {
  content: ""; position: absolute; left: 4px; top: 0.72em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--teal-500);
}
.legal__col--ar .legal__list li { padding-left: 0; padding-right: 20px; }
.legal__col--ar .legal__list li::before { left: auto; right: 4px; }

.legal__clause--contact { background: var(--surface-1); }
.legal__contact { margin: 0; padding: 0; list-style: none; }
.legal__contact li { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.legal__contact li:last-child { margin-bottom: 0; }
.legal__contact .bi { flex: none; font-size: 1.1rem; color: var(--teal-600); }
.legal__contact a { font-weight: 600; word-break: break-word; }
/* Latin values (email, phone, URL) inside the Arabic column carry dir="ltr" in
   the markup so the address itself is not reordered; the row still flows RTL,
   putting the icon on the right where the Arabic heading above it starts. */
.legal__col--ar .legal__contact a { text-align: right; }

@media (max-width: 900px) {
  .legal__clause { grid-template-columns: 1fr; gap: 0; padding: 22px; }
  .legal__col--ar {
    border-left: 0; border-top: 1px solid var(--border);
    padding-left: 0; margin-left: 0; padding-top: 20px; margin-top: 20px;
  }
  .legal__meta-k { flex-basis: 100%; }
}
@media (max-width: 640px) {
  .legal__meta { padding: 16px 18px; }
}

/* FOOTER — registered business identity ------------------------------------
   Required on the page by the payment gateway, and read by people far less
   often than the rest of the footer, so it sits below the link columns at a
   smaller size rather than competing with them. */
.site-footer__legal {
  border-top: 1px solid var(--border-dark);
  padding-top: 20px; margin-top: 8px;
  font-size: 0.85rem; line-height: 1.7; color: rgba(255, 255, 255, 0.62);
}
.site-footer__legal h2 {
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82); margin: 0 0 8px;
}
.site-footer__legal p { margin: 0 0 4px; }
.site-footer__legal b { color: rgba(255, 255, 255, 0.88); font-weight: 600; }
.site-footer__legal a { color: rgba(255, 255, 255, 0.78); }
.site-footer__legal a:hover { color: var(--teal-300); }
.site-footer__legal-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px 34px;
}
