/* ══════════════════════════════════════════════════════════════════════════
   Pre-COP31 — the public homepage.

   Loaded on one route only. precop.css supplies the identity (the palette, the
   masi geometry, the shared chrome); this file supplies the *composition*: the
   layered ocean hero, the wave transitions between sections, the programme
   rail and the illustration set.

   Three rules held throughout:

     1. Depth comes from layers moving at different speeds, not from shadows.
        The hero is six planes deep and each one moves at its own rate.

     2. Motion is atmospheric or it is absent. Waves drift, light breathes,
        particles rise. Nothing bounces, spins or demands attention, because a
        page a Minister's office opens should feel composed, not eager.

     3. Every section transition is a curve. Straight horizontal joins between
        full-width bands are what makes a page read as a stack of templates.

   All of it is gated: motion sits behind prefers-reduced-motion, reveals sit
   behind .pc-js, and the whole document is readable with neither.
   ══════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────── Foundations ───────────────────────────── */

/* The page's own scale, from the two greens outward. No third hue: the
   programme has exactly two greens and this file may not invent another —
   anything else here is a tint of one of them, or white, or the blue.

   The `--deep-*` shades that used to sit here went with the dark-green panel
   they were mixed for (see .hp-journey) — nothing paints with them now. */
.hp {
  --leaf:   #3ab97c;       /* the light green */
  --leaf-wash: #f1faf5;    /* the light green at a whisper, for tinted bands */
  --reef-glow: rgba(58, 185, 124, .38);

  --shell: clamp(1.1rem, 4vw, 2.2rem);
  overflow-x: clip;         /* decoration can overhang; the page never scrolls sideways */
  background: #fff;
}

/* Section rhythm. Generous, and never the same twice in a row — equal spacing
   between every band is the other half of what makes a page look templated. */
/* Section rhythm.
   These were 8vw / 5vw, which is fine at 1280 and turns into 133px of empty
   space above every heading at 1920 — and, since two sections meet, 266px
   between one band and the next. The vw term is what ran away: a gap that
   grows with the width of the screen keeps growing long after it has stopped
   being generous and started being a hole. Capped much sooner now. */
.hp-section { position: relative; padding: clamp(2.75rem, 5vw, 4.75rem) 0; }
.hp-section-tight { padding: clamp(1.75rem, 3vw, 2.75rem) 0; }

.hp-eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  font-size: .7rem; font-weight: 800; letter-spacing: .2em; text-transform: uppercase;
  color: var(--brand); margin-bottom: .9rem;
}
.hp-eyebrow::before {
  content: ''; width: 1.9rem; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--brand), var(--reef-2));
}
.hp-eyebrow-light { color: rgba(255,255,255,.86); }
.hp-eyebrow-light::before { background: linear-gradient(90deg, #fff, var(--reef-2)); }

.hp-h2 {
  font-size: clamp(1.85rem, 4.2vw, 3.15rem);
  font-weight: 800; letter-spacing: -.035em; line-height: 1.05;
  margin: 0 0 .8rem;
}
.hp-lede {
  font-size: clamp(1rem, 1.35vw, 1.14rem); line-height: 1.65;
  color: var(--muted); max-width: 38rem;
}
.hp-on-ocean .hp-h2   { color: #fff; }
.hp-on-ocean .hp-lede { color: rgba(255,255,255,.78); }

/* ═════════════════════════════════ HERO ═══════════════════════════════ */

.hp-hero {
  position: relative;
  /* Sized so the whole composition — the lockup, the title, the copy and the
     buttons — lands inside one screen. A hero taller than the viewport hides
     its own best moment below the fold. */
  min-height: min(76svh, 42rem);
  display: flex; flex-direction: column; justify-content: center;
  isolation: isolate; overflow: hidden;
  /* The tallest of the three wave bands, declared once here and read by both
     the waves themselves and the padding below — they cannot drift apart. */
  --wave-h: clamp(70px, min(11vw, 15svh), 158px);

  /* The header sits in flow above this now rather than floating over it, so
     the hero no longer has to leave room for a bar it cannot see.

     The FOOT has to clear two things the copy does not own: the wave stack
     that closes the hero, and the stat ribbon that is pulled up over the join
     by a negative margin. Without that the call-to-action buttons ended up
     sitting behind the counters. */
  padding: clamp(2rem, 5vh, 4rem) 0
           calc(var(--wave-h) + clamp(1rem, min(3vw, 4svh), 2.5rem));

  /* White is the ground, not a panel colour. The programme's stated primary
     direction is "white with dark and light green", and the moment this fills
     with colour the shapes stop reading as paper cut out of a white page and
     start reading as a gradient with decoration on it. */
  background: #fff;
  color: var(--ink);
}

/* The contour shapes are NOT in here, on purpose. The programme's title card
   is a photograph, one green curve, and then clear white for the words; the
   paper-cut shapes are what it puts on the banners that follow. Behind a
   title they swallow the lockup and the navigation both. See .hp-contour. */

/* — The divider —————————————————————————————————————————
   One curve at six offsets: the photograph is clipped along it, a dark-green
   shadow and a light-green band run down the page side of it, and three
   hairlines cross the picture beside it.

   The clip path and this overlay share one coordinate space — both span the
   photograph's box exactly — which is the only reason the band sits on the
   cut instead of near it. Anything that changes one has to change the other. */
.hp-hero-curtain {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: -4;
  width: var(--hp-photo, 50vw); height: 100%;
  pointer-events: none;
}
.hp-cur-dark  { fill: var(--reef); }
.hp-cur-light { fill: var(--reef-2); }
.hp-cur-lines { stroke: rgba(255,255,255,.85); stroke-width: 2; }

/* — Layer 1 · atmosphere ————————————————————————————————
   Two slow lights: a cold one high and left, a warm reef-green one low and
   right. They breathe out of phase, which is what stops a static gradient
   from reading as a flat printed panel. */
.hp-glow {
  position: absolute; inset: -20% -10%; z-index: -6; pointer-events: none;
  background:
    radial-gradient(38rem 26rem at 18% 14%, rgba(120, 200, 255, .30), transparent 68%),
    radial-gradient(44rem 30rem at 86% 74%, var(--reef-glow), transparent 66%);
  animation: hpBreathe 16s ease-in-out infinite alternate;
}
@keyframes hpBreathe {
  from { opacity: .75; transform: scale(1) translate3d(0, 0, 0); }
  to   { opacity: 1;   transform: scale(1.07) translate3d(-1.5%, 1.5%, 0); }
}

/* — Layer 2 · masi cloth, drifting —————————————————————————
   The tapa geometry from the identity layer, laid as a runner down the left
   the way a masi actually hangs, and drifting slowly upward. */
.hp-tapa {
  position: absolute; inset: -10% 0; z-index: -5; pointer-events: none;
  background-image: var(--tapa); background-size: 148px 148px;
  opacity: .3;
  animation: hpDrift 60s linear infinite;
  -webkit-mask-image: linear-gradient(105deg, #000 0%, rgba(0,0,0,.35) 42%, transparent 72%);
          mask-image: linear-gradient(105deg, #000 0%, rgba(0,0,0,.35) 42%, transparent 72%);
}
@keyframes hpDrift {
  from { background-position: 0 0; }
  to   { background-position: 148px -296px; }
}

/* — Layer 3 · particles ————————————————————————————————
   Sea spray. Positions and timings come from the template as custom
   properties, so the field is deterministic — the same page every load, no
   random-number generator running in the browser. */
.hp-particles { position: absolute; inset: 0; z-index: -4; pointer-events: none; overflow: hidden; }
.hp-particle {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--s); height: var(--s); border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, rgba(255,255,255,.95), rgba(150,220,255,.15) 70%);
  opacity: 0;
  animation: hpFloat var(--d) ease-in-out var(--delay) infinite;
}
@keyframes hpFloat {
  0%   { opacity: 0;   transform: translate3d(0, 18px, 0) scale(.7); }
  18%  { opacity: .85; }
  70%  { opacity: .5; }
  100% { opacity: 0;   transform: translate3d(var(--dx, 14px), -78px, 0) scale(1.15); }
}

/* — Layer 4 · islands ——————————————————————————————————
   The horizon. Sits behind the copy and ahead of the cloth, and moves at a
   third of scroll speed so the hero opens up as the reader leaves it. */
.hp-islands {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: -3;
  pointer-events: none; line-height: 0;
  will-change: transform;
}
.hp-islands svg { display: block; width: 100%; height: auto; }

/* — Layer 5 · the wave stack ————————————————————————————
   Three bands, each a doubled path translating a full half of its own width,
   so the loop is seamless. Different speeds and different opacities is the
   whole trick: one wave reads as decoration, three read as water. */
.hp-waves {
  position: absolute; left: 0; right: 0; bottom: -1px; z-index: -2;
  line-height: 0; pointer-events: none;
}
.hp-wave { display: block; width: 200%; height: clamp(64px, 9vw, 132px); }
.hp-wave + .hp-wave { position: absolute; left: 0; bottom: 0; }
.hp-wave-anim { animation: hpWave var(--dur, 22s) linear infinite; }
@keyframes hpWave {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* — The words ——————————————————————————————————————————— */
.hp-hero-inner { position: relative; z-index: 1; width: 100%; }

/* The copy clears the photograph.
   The container is centred, so the distance from its left edge to the middle
   of the viewport is exactly half the container's own width — which is what
   `50%` resolves to here. No custom property and no measurement in script. */
@media (min-width: 62em) {
  .hp-hero-copy {
    margin-left: max(
      calc(50% + 2.5rem),
      calc(var(--hp-photo) * 1.02 + 2.5rem - (100vw - 100%) / 2)
    );
  }
}

/* NOT RENDERED ON THE HERO ANY MORE. The three marks moved into the
   navigation bar (.pc-nav-marks in precop.css), which every page carries, so
   the homepage no longer prints the same lockup twice within forty pixels.

   Kept rather than deleted for two reasons: _marks.php still names this class
   as its fallback for a caller that sets none, and these are the sizes the
   hero would want if the Ministry ever asks for the lockup back. Nothing
   emits the class today — a change here does not affect the page. */
/* ── The lockup ──────────────────────────────────────────────────────────────
   Two marks side by side with a rule between them, above everything else,
   the way the programme sets its own. No white plate any more: the marks are
   transparent PNGs with blue and green type, and the ground behind them is
   now white, so a plate would be a white rectangle on white. */
.hp-hero-marks {
  display: flex; align-items: center; gap: clamp(.8rem, 1.8vw, 1.4rem);
  margin-bottom: clamp(1.4rem, 3vw, 2.1rem);
}
/* These are two governments' marks and the conference's own, so they are sized
   to be read rather than to be tidy. Height rises as the mark gets squarer,
   because a lockup matches optical weight rather than height — see
   _marks.php, which buckets each file by its measured aspect ratio. */
.hp-hero-marks .pc-lockup        { width: auto; }
.hp-hero-marks .pc-lockup-wide   { height: clamp(2.5rem, 3.6vw, 3.2rem); }
.hp-hero-marks .pc-lockup-mid    { height: clamp(3.5rem, 5.1vw, 4.5rem); }
.hp-hero-marks .pc-lockup-square { height: clamp(3.6rem, 5.2vw, 4.6rem); }
.hp-hero-marks .rule {
  width: 1px; align-self: stretch; min-height: 2.4rem;
  background: color-mix(in srgb, var(--brand) 30%, transparent);
}

.hp-hero-dates {
  display: inline-flex; align-items: center; gap: .6rem;
  font-size: clamp(.7rem, 1.05vw, .8rem); font-weight: 800;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--brand); margin-bottom: .9rem;
}
.hp-hero-dates .dot {
  width: .42rem; height: .42rem; border-radius: 50%; background: var(--reef-2);
  box-shadow: 0 0 0 0 rgba(58,185,124,.7);
  animation: hpPulse 2.6s ease-out infinite;
}
@keyframes hpPulse {
  0%   { box-shadow: 0 0 0 0 rgba(58,185,124,.65); }
  70%  { box-shadow: 0 0 0 .7rem rgba(58,185,124,0); }
  100% { box-shadow: 0 0 0 0 rgba(58,185,124,0); }
}

/* Small over large, in one blue — the programme's title shape exactly. The
   kicker carries the conference name so the page's single top-level heading
   still says what this is; the large line carries the theme, which is the
   part the lockup above does not already say. */
.hp-hero-title {
  font-size: clamp(2.1rem, 4.6vw, 3.9rem);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.04;
  margin: 0; color: var(--brand); max-width: 17em;
}
.hp-hero-title .kicker {
  display: block; font-weight: 700;
  font-size: clamp(.86rem, 1.5vw, 1.15rem); line-height: 1.2;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--brand-2); margin-bottom: .5em;
}
.hp-hero-lede {
  font-size: clamp(.98rem, 1.3vw, 1.1rem); line-height: 1.65;
  color: var(--muted); max-width: 34rem; margin: 1.15rem 0 0;
}
/* One venue per line, each behind its own pin.

   The conference runs across two hotels. This used to be a single row —
   "Sofitel and Crowne Plaza, Nadi, Fiji" behind one pin — which reads as one
   place with an odd name. Stacked as a column instead, so each hotel is a
   line of its own; a one-venue conference renders a single line and looks
   exactly as it did before.

   A column rather than <br>s between them, because this is a flex container
   and a <br> inside one has no effect at all — the first version of this
   change printed both hotels side by side for exactly that reason. */
.hp-hero-where {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .3rem;
  color: var(--muted); margin-top: .8rem; font-size: .95rem;
}
.hp-hero-venue { display: inline-flex; align-items: center; gap: .55rem; }
.hp-hero-where i { color: var(--reef); }

/* Buttons. Blue on white, because the programme sets every action in the same
   blue it sets its type in; the greens stay with the shapes. The dark-green
   band later on the page inverts them — see .hp-on-ocean below — since blue on
   that green is barely three to one. */
.hp-cta { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: clamp(1.6rem, 3.5vw, 2.4rem); }
.hp-btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .82rem 1.5rem; border-radius: 999px; border: 1.5px solid transparent;
  font-weight: 700; font-size: .96rem; text-decoration: none; cursor: pointer;
  transition: transform .24s cubic-bezier(.22,1,.36,1), box-shadow .24s ease,
              background .24s ease, border-color .24s ease, color .24s ease;
}
.hp-btn i, .hp-btn svg { transition: transform .28s cubic-bezier(.22,1,.36,1); }
.hp-btn:hover, .hp-btn:focus-visible { transform: translateY(-2.5px); }
.hp-btn:focus-visible { outline: 3px solid var(--reef-2); outline-offset: 3px; }

.hp-btn-primary {
  background: #0e3d7c; color: #fff;
  box-shadow: 0 12px 28px -14px rgba(14, 61, 124, .8);
}
.hp-btn-primary:hover, .hp-btn-primary:focus-visible {
  background: var(--brand-deep); color: #fff;
  box-shadow: 0 18px 38px -16px rgba(19, 79, 160, .95);
}
.hp-btn-primary:hover i { transform: translateX(3px); }

.hp-btn-ghost {
  background: transparent; color: var(--brand);
  border-color: color-mix(in srgb, var(--brand) 34%, transparent);
}
.hp-btn-ghost:hover, .hp-btn-ghost:focus-visible {
  background: var(--brand-soft); border-color: var(--brand); color: var(--brand-deep);
}

/* On the dark-green band the same two invert: white carries the primary and
   the outline goes white, because the blue does not survive that ground. */
.hp-on-ocean .hp-btn-primary {
  background: #fff; color: var(--brand-deep);
  box-shadow: 0 12px 30px -12px rgba(0, 40, 25, .6);
}
.hp-on-ocean .hp-btn-primary:hover, .hp-on-ocean .hp-btn-primary:focus-visible {
  background: #fff; color: var(--brand-deep);
  box-shadow: 0 18px 40px -14px rgba(0, 40, 25, .7);
}
.hp-on-ocean .hp-btn-ghost {
  background: rgba(255,255,255,.1); color: #fff; border-color: rgba(255,255,255,.45);
}
.hp-on-ocean .hp-btn-ghost:hover, .hp-on-ocean .hp-btn-ghost:focus-visible {
  background: rgba(255,255,255,.2); border-color: #fff; color: #fff;
}

.hp-btn-solid {
  background: #0e3d7c;
  color: #fff; box-shadow: 0 12px 28px -12px rgba(14,61,124,.75);
}
.hp-btn-solid:hover { color: #fff; }
.hp-btn-solid:hover i { transform: translateX(3px); }

/* The scroll cue that used to sit at the foot of the hero has gone with the
   dark ocean it was legible on. On a white hero it would be a grey word in
   the middle of the page's clearest space, telling a reader something their
   scrollbar already tells them. */

/* ══════════════════════ STAT RIBBON — on the water ════════════════════
   The counts sit in the trough between the hero's waves and the first band
   of content, half in each. Boxing them would have made a fifth row of
   rectangles; letting them straddle the join makes the two sections one
   composition instead of two stacked ones. */

/* No bottom padding of its own.
 *
 * Whatever follows the ribbon already brings its own top spacing — the
 * announcements band its section padding, the Pacific band its contour
 * artwork — so padding here was always doubled up. It only became obvious
 * when there were no announcements to publish: the announcements band is
 * rendered conditionally, so with none live the ribbon's 45px sat directly
 * on top of the next band's, leaving a strip of dead white between the stat
 * card and the contours with nothing in it at all. */
.hp-ribbon {
  position: relative; z-index: 2;
  margin-top: clamp(-4.5rem, -7vw, -6.5rem);
  padding-bottom: 0;
}

/* The ribbon's negative margin-bottom exists to pull the CONTOUR band up under
   the card, so the hero's water runs straight into the artwork. The
   announcements band must not be pulled up like that — it is reading matter,
   and it landed underneath the card — so it adds the same magnitude back as
   padding, plus a gap.
   Written as a matching positive clamp rather than :has(), which keeps it
   working wherever :has() does not. */
.hp-ribbon + .hp-section-tight {
  padding-top: calc(clamp(3rem, 5.6vw, 7.5rem) + clamp(1rem, 2vw, 2rem));
}
/* Opaque, not glass.
 *
 * At 86% + blur, what showed through the card depended on what was BEHIND it —
 * and the card straddles a join, so its top ~80px sit over the hero's green
 * water while the rest of its height (it runs to ~190px) sits over plain page
 * white. Two different things bleeding through one translucent surface is a
 * visible band across the card itself, cutting it in two right where the
 * overlap ends. A solid surface looks identical no matter how much of the
 * card sits on the hero and how much sits on the page, which is the only way
 * this reads as one card rather than two glued together. */
/* Five figures now, not three — days, pillars, events, booths and networking
   centres. auto-fit rather than a fixed count so the row does not have to be
   re-declared at every breakpoint if the Ministry adds or drops one; the 8rem
   floor is where "Exhibition booths" stops fitting on two lines. */
.hp-ribbon-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: clamp(.6rem, 2vw, 1.6rem);
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--brand) 14%, transparent);
  border-radius: clamp(16px, 2.5vw, 26px);
  padding: clamp(1.2rem, 3vw, 2rem) clamp(1rem, 3vw, 2.4rem);
  box-shadow: 0 30px 70px -34px rgba(2, 26, 62, .55);
}
.hp-stat { text-align: center; position: relative; }
.hp-stat + .hp-stat::before {
  content: ''; position: absolute; left: calc(-1 * clamp(.3rem, 1vw, .8rem)); top: 12%; bottom: 12%;
  width: 1px; background: var(--line);
}
.hp-stat-value {
  font-size: clamp(1.7rem, 4.4vw, 3.1rem); font-weight: 800;
  letter-spacing: -.05em; line-height: 1;
  background: linear-gradient(135deg, var(--brand-deep), var(--brand-2) 60%, var(--reef));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
}
.hp-stat-label {
  margin-top: .45rem; font-size: clamp(.62rem, 1.1vw, .74rem); font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}

/* ═══════════════════════════ ANNOUNCEMENTS ════════════════════════════
   Rendered only when the Ministry has published something. An empty
   "latest news" panel on a conference homepage says the conference is not
   happening. */
.hp-notice {
  display: grid; grid-template-columns: auto 1fr auto; gap: 1rem; align-items: start;
  padding: 1.15rem 1.3rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 4px solid var(--nc, var(--brand));
  transition: transform .3s cubic-bezier(.22,1,.36,1), box-shadow .3s ease;
}
.hp-notice:hover { transform: translateX(4px); box-shadow: var(--shadow); }
/* Three colours, not five. The amber that used to carry "important" was a
   fourth hue on a page built from two greens and a blue, and read as a stock
   admin theme showing through the identity. */
.hp-notice.urgent    { --nc: var(--coral); }
.hp-notice.important { --nc: var(--reef); }
.hp-notice.notice    { --nc: var(--brand); }
.hp-notice.info      { --nc: var(--brand); }
.hp-notice-icon {
  width: 2.3rem; height: 2.3rem; border-radius: 10px; flex: none;
  display: grid; place-items: center; color: var(--nc);
  background: color-mix(in srgb, var(--nc) 12%, transparent);
}
.hp-notice-title { font-weight: 700; margin-bottom: .15rem; }
.hp-notice-when  { color: var(--muted); font-size: .8rem; white-space: nowrap; }

/* ══════════════════ THE PACIFIC IS AT THE HEART ═══════════════════════
   An asymmetric composition: the illustration takes the wider column and
   overhangs its grid cell, the facts orbit it. Deliberately not a
   two-equal-columns text-and-picture row. */

/* ══════════════════ THE CONTOUR BANNERS ═══════════════════════════════
   This is where the programme's paper-cut shapes belong: on the bands, the
   way its own banners use them — shapes entering from the edges of a white
   frame and leaving the middle clear for the words.

   Traced from the deck's artwork, so the geometry is its own. Decorative and
   inert: they sit behind the content and take no pointer events, and the band
   reads correctly with the image missing entirely. */
/* The artwork covers the WHOLE band and the content sits inside it — shapes
   coming in at every edge, words in the clear middle. That is what the
   programme's banners are, and it is the one thing that has to be right here.

   It was built the other way round first: fixed-height decorative strips
   pinned above and below, with the content in white space underneath. That
   produced empty green stripes with nothing in them, sliced through the middle
   of a contour by the strip's own height. Both faults came from the same
   mistake — treating the artwork as a border instead of as the frame.

   ── The artwork is NEVER stretched ──────────────────────────────────────────
   Two crops sit at the section's edges, each at its own natural proportions.
   The crops are cut on a row of the original banner that carries no contour,
   so each ends in clear ground instead of a shape sliced off square.

   Everything follows from one number per crop: its height as a percentage of
   its width.

     height:      that percentage of the viewport width
     padding:     the same, plus a gap
     background:  100% 100% of an element already at the right shape

   So the element is exactly the artwork's aspect ratio at every viewport
   width, `100% 100%` is its natural size rather than a stretch, and the copy
   can never land on a contour. Change the crop, change the one number.

   Three earlier versions of this all failed the same way — a fixed pixel
   height with the art forced into it. At 1920px that smeared every curve
   sideways and sliced the ends off; the shapes stopped reading as cut paper
   and started reading as spills. Aspect ratio is not negotiable here. */
/* Each crop is drawn at its own proportions, and nothing crops it further.
 *
 * That is the whole rule, and three earlier versions broke it three different
 * ways: a fixed pixel height smeared the curves sideways; a viewBox cut at the
 * split row sliced the tail off any shape hanging past it; and shrinking the
 * band with `cover` cropped away the very edge each shape was anchored to and
 * left it floating in the corner as a detached green sliver.
 *
 * So the band is exactly as deep as its artwork — the numbers below ARE the
 * crops' aspect ratios — and each crop is anchored to the frame edge its
 * shapes hang from. The only thing that cuts a shape is the section's own
 * boundary, which is where the deck cuts them too. */
.hp-has-contour {
  /* vd-band-crest.svg 1200x218 · vd-band-base.svg 1200x184 */
  --crest: 18.167vw;
  --base:  15.333vw;

  /* How far into the band the copy has to be pushed — which is NOT the band's
     full depth. The artwork's deep shapes are in the corners; across the
     middle 80% of the frame, where the container actually sits, the crest
     reaches 37% of its height and the base 58%. Clearing the full band
     instead reserved a third of a screen of white above every heading, and is
     the single reason these sections felt so far apart. Measured, not
     guessed: scratchpad/profile.php walks the rendered outline column by
     column. */
  --crest-clear: .40;
  --base-clear:  .60;
  --contour-gap: clamp(.75rem, 1.4vw, 1.5rem);

  padding-top:    calc(var(--crest) * var(--crest-clear) + var(--contour-gap));
  padding-bottom: calc(var(--base)  * var(--base-clear)  + var(--contour-gap));
}

.hp-contour {
  position: absolute; left: 0; right: 0; z-index: 0; pointer-events: none;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
/* Lift the section's real content above the artwork — but nothing that is
   itself decoration. Without the :not()s this matched the OTHER contour (which
   then dropped into normal flow as a stray band of shapes mid-section) and the
   hero's wave stack (which landed at the top of the hero instead of its foot).
   Both were the same mistake: a sibling combinator cannot tell content from
   the layers around it, so the exclusions have to say so. */
.hp-contour ~ *:not(.hp-contour):not([aria-hidden="true"]) {
  position: relative; z-index: 1;
}

/* The crest's shapes hang from the top of the artwork, so its top edge is the
   section's top edge; the base's rise from the bottom, so its bottom is the
   section's floor. */
.hp-contour-crest {
  top: 0; height: var(--crest);
  background-position: center top;
  background-image: var(--img-band-crest);
}
.hp-contour-base {
  bottom: 0; height: var(--base);
  background-position: center bottom;
  background-image: var(--img-band-base);
}

/* Below the split a 5.5:1 strip is 71px tall on a phone — proportionally
   right and visually nothing. The deck's portrait crops carry the same shapes
   at 4.1:1 and 2.8:1, which is what it uses on its own narrow formats, so the
   artwork swaps rather than shrinks. */
@media (max-width: 61.99em) {
  /* vd-portrait-crest.svg 820x199 · vd-portrait-base.svg 820x293 */
  .hp-has-contour { --crest: 24.268vw; --base: 35.732vw; --crest-clear: .85; --base-clear: 1; }
  .hp-contour-crest { background-image: var(--img-portrait-crest); }
  .hp-contour-base  { background-image: var(--img-portrait-base); }
}

.hp-pacific {
  position: relative; overflow: hidden;
  background: #fff;
}
.hp-pacific-grid {
  display: grid; grid-template-columns: 1.15fr .85fr;
  gap: clamp(2rem, 5vw, 4.5rem); align-items: center;
}


.hp-fact { display: flex; gap: 1rem; padding: 1.1rem 0; border-bottom: 1px solid var(--line); }
.hp-fact:last-child { border-bottom: 0; }
.hp-fact-mark {
  width: 2.7rem; height: 2.7rem; flex: none; border-radius: 12px;
  display: grid; place-items: center;
  background: linear-gradient(140deg,
              color-mix(in srgb, var(--brand) 12%, transparent),
              color-mix(in srgb, var(--reef-2) 14%, transparent));
  color: var(--brand);
}
.hp-fact-title { font-weight: 700; margin-bottom: .15rem; }
.hp-fact-text  { color: var(--muted); font-size: .93rem; line-height: 1.55; }

/* ════════════════════ PROGRAMME — the cinematic rail ══════════════════
   A horizontal, snapping rail rather than a grid of cards. The programme is
   a sequence in time, so it reads along an axis; a 3 × 2 grid of identical
   boxes throws that away and looks like every other conference site. */

.hp-rail-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap; margin-bottom: clamp(1.6rem, 3vw, 2.4rem);
}
.hp-rail-wrap { position: relative; }

/* Bleeds to the viewport edge so the last card is visibly cut off — the
   signal that the rail continues. Padding restores the container gutter. */
.hp-rail {
  display: flex; gap: clamp(.9rem, 2vw, 1.4rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding: .5rem calc(50vw - min(50vw, 600px) + .25rem) 1.6rem;
  margin: 0 calc(50% - 50vw);
  scrollbar-width: thin; scrollbar-color: var(--brand) transparent;
}
.hp-rail::-webkit-scrollbar { height: 6px; }
.hp-rail::-webkit-scrollbar-track { background: var(--line); border-radius: 6px; }
.hp-rail::-webkit-scrollbar-thumb { background: var(--brand); border-radius: 6px; }

.hp-ev {
  flex: 0 0 clamp(16rem, 27vw, 20.5rem); scroll-snap-align: start;
  position: relative; display: flex; flex-direction: column;
  padding: 1.4rem; border-radius: 20px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line);
  overflow: hidden; isolation: isolate;
  transition: transform .34s cubic-bezier(.22,1,.36,1), box-shadow .34s ease,
              border-color .34s ease;
}
/* The type colour washes up from the foot on hover — the card's own accent
   arriving, rather than a generic grey lift. */
.hp-ev::before {
  content: ''; position: absolute; inset: auto 0 0 0; height: 100%; z-index: -1;
  background: linear-gradient(0deg, color-mix(in srgb, var(--evc, var(--brand)) 13%, transparent), transparent 55%);
  opacity: 0; transition: opacity .34s ease;
}
.hp-ev:hover, .hp-ev:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 26px 50px -26px rgba(2, 30, 70, .5);
  border-color: color-mix(in srgb, var(--evc, var(--brand)) 40%, transparent);
}
.hp-ev:hover::before, .hp-ev:focus-visible::before { opacity: 1; }

.hp-ev-date {
  display: flex; align-items: baseline; gap: .45rem; margin-bottom: .9rem;
  color: var(--evc, var(--brand)); font-weight: 800;
}
.hp-ev-day   { font-size: 2.1rem; line-height: .9; letter-spacing: -.05em; }
.hp-ev-month { font-size: .74rem; letter-spacing: .16em; text-transform: uppercase; }
.hp-ev-time  { margin-left: auto; font-size: .78rem; color: var(--muted); font-weight: 700; }

.hp-ev-title {
  font-size: 1.06rem; font-weight: 700; line-height: 1.35; color: var(--ink);
  margin: 0 0 .55rem;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.hp-ev-meta { font-size: .84rem; color: var(--muted); display: grid; gap: .28rem; }
.hp-ev-foot {
  margin-top: auto; padding-top: 1rem;
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
}
.hp-ev-type {
  font-size: .66rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--evc, var(--brand));
  background: color-mix(in srgb, var(--evc, var(--brand)) 12%, transparent);
  padding: .3rem .6rem; border-radius: 999px;
}
.hp-ev-state { font-size: .78rem; font-weight: 700; display: inline-flex; align-items: center; gap: .35rem; }
.hp-ev-state::before { content: ''; width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; }
.hp-ev-state.open   { color: var(--reef); }
.hp-ev-state.wait   { color: var(--brand); }
.hp-ev-state.closed { color: var(--muted); }
.hp-ev-go {
  position: absolute; right: 1.1rem; bottom: 1.1rem; opacity: 0;
  transform: translateX(-6px); transition: opacity .3s ease, transform .3s ease;
  color: var(--evc, var(--brand));
}
.hp-ev:hover .hp-ev-go, .hp-ev:focus-visible .hp-ev-go { opacity: 1; transform: none; }

/* ═══════════════════════ PILLARS — offset stack ═══════════════════════
   Five items in a three-column grid, with the second row nudged down and
   inward. An even grid of five leaves a hole; an offset one reads as a
   deliberate arrangement. */
/* --pillar-count is set inline from the real number of pillars (capped at 4),
   so four cards sit in one row instead of the old fixed 3-column grid, which
   left a 4th card wrapped onto a line by itself. */
.hp-pillars-grid {
  display: grid; grid-template-columns: repeat(var(--pillar-count, 3), 1fr);
  gap: clamp(1rem, 2.4vw, 1.6rem);
}
.hp-pillar {
  position: relative; display: block; text-decoration: none;
  padding: clamp(1.4rem, 2.6vw, 2rem); border-radius: 20px;
  background: var(--surface); border: 1px solid var(--line); overflow: hidden;
  transition: transform .34s cubic-bezier(.22,1,.36,1), box-shadow .34s ease;
}
.hp-pillar::after {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: var(--pc, var(--brand)); transform: scaleX(0); transform-origin: left;
  transition: transform .42s cubic-bezier(.22,1,.36,1);
}
.hp-pillar:hover, .hp-pillar:focus-visible {
  transform: translateY(-5px); box-shadow: 0 24px 46px -26px rgba(2, 30, 70, .45);
}
.hp-pillar:hover::after, .hp-pillar:focus-visible::after { transform: scaleX(1); }
.hp-pillar-icon { color: var(--pc, var(--brand)); margin-bottom: 1rem; }
.hp-pillar-title { font-size: 1.08rem; font-weight: 700; color: var(--ink); margin: 0 0 .45rem; }
.hp-pillar-text  { font-size: .92rem; line-height: 1.6; color: var(--muted); margin: 0; }

/* ═══════════════ ONE PLATFORM, EVERY EVENT — the journey ══════════════
   The five steps a participant actually passes through, on a rail with a
   light travelling along it. The QR panel beside it assembles itself out of
   its own modules, which is a truer illustration of what the platform does
   than a stock icon of a QR code. */

/* The programme's secondary direction: the same contours on a dark-green
   ground with everything on it in white. One band of it on the page, where
   the platform explains itself — enough to read as the other half of the
   identity, not so much that the white ground stops being the ground. */
/* White, with the blue carrying it.
 *
 * This band was the deck's dark-green treatment, and it sat directly beneath
 * the themes band — which already carries the contour artwork — so the page
 * ran green straight into green. The artwork moved one section down, this one
 * went white, and the only green left in it is the scan line and the frame of
 * the QR illustration, where green means a successful read. */
.hp-journey {
  position: relative; color: var(--ink); overflow: hidden;
  background: #fff;
}
.hp-journey::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--tapa); background-size: 160px 160px; opacity: .05;
}
.hp-journey > .container { position: relative; z-index: 1; }
.hp-journey .hp-h2   { color: var(--ink); }
.hp-journey .hp-lede { color: var(--muted); }
.hp-journey .hp-eyebrow { color: var(--brand); }

.hp-journey-grid {
  display: grid; grid-template-columns: .95fr 1.05fr;
  gap: clamp(2rem, 5vw, 4rem); align-items: center;
}

.hp-steps { position: relative; margin-top: 2rem; }
/* The rail behind the steps, and the light that runs down it. */
.hp-steps::before {
  content: ''; position: absolute; left: 1.35rem; top: 1.4rem; bottom: 1.4rem; width: 2px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--brand) 8%, transparent),
    color-mix(in srgb, var(--brand) 22%, transparent),
    color-mix(in srgb, var(--brand) 8%, transparent));
}
.hp-steps::after {
  content: ''; position: absolute; left: 1.25rem; top: 0; width: 4px; height: 4.5rem;
  border-radius: 4px; background: linear-gradient(180deg, transparent, var(--reef-2), transparent);
  filter: blur(1px);
  animation: hpRun 5.5s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes hpRun {
  0%   { transform: translateY(0);      opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translateY(calc(100% * 5)); opacity: 0; }
}

.hp-step { display: flex; gap: 1.15rem; padding: .85rem 0; position: relative; }
.hp-step-num {
  width: 2.7rem; height: 2.7rem; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-weight: 800; font-size: .9rem;
  background: var(--brand-soft); color: var(--brand);
  border: 1.5px solid color-mix(in srgb, var(--brand) 22%, transparent);
  position: relative; z-index: 1;
  transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s ease;
}
/* Green on hover, and only on hover: the one place in this band where the
   pattern colour is allowed, because it marks the step you are pointing at. */
.hp-step:hover .hp-step-num {
  background: var(--reef-2); border-color: var(--reef-2); color: #fff; transform: scale(1.08);
}
.hp-step-title { font-weight: 700; color: var(--ink); margin-bottom: .1rem; }
.hp-step-text  { color: var(--muted); font-size: .92rem; line-height: 1.55; }

/* The QR illustration. Modules fade in one after another, then the whole
   thing holds — an assembly, not a flicker. */
.hp-qr { position: relative; display: grid; place-items: center; }
.hp-qr svg { width: min(100%, 26rem); height: auto; overflow: visible; }
/* A lighter slate rather than var(--ink) — the real navy-black next to the
   pale dots read as heavy, blocky squares rather than a QR pattern. */
.hp-qr-mod { fill: #64748b; opacity: .55; }
.hp-qr-anim { animation: hpQr 4.5s ease-in-out infinite; }
@keyframes hpQr {
  0%, 8%    { opacity: .12; }
  38%, 76%  { opacity: .82; }
  100%      { opacity: .12; }
}
.hp-qr-frame { fill: none; stroke: var(--reef-2); stroke-width: 2.5; stroke-linecap: round; }
.hp-qr-scan {
  fill: url(#hpScanGrad);
  animation: hpScan 3.6s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes hpScan {
  0%, 100% { transform: translateY(0); opacity: 0; }
  10%      { opacity: 1; }
  50%      { transform: translateY(180px); opacity: 1; }
  90%      { opacity: 0; }
}

/* ═════════════════ WHAT YOU CAN DO — illustrated cards ════════════════ */
.hp-do-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: clamp(1rem, 2.2vw, 1.5rem);
}
.hp-do {
  padding: clamp(1.5rem, 2.6vw, 2rem); border-radius: 20px;
  background: var(--surface); border: 1px solid var(--line);
  transition: transform .34s cubic-bezier(.22,1,.36,1), box-shadow .34s ease;
}
.hp-do:hover { transform: translateY(-5px); box-shadow: 0 24px 46px -26px rgba(2,30,70,.4); }
.hp-do-icon { margin-bottom: 1.1rem; color: var(--brand); }
.hp-do-title { font-size: 1.06rem; font-weight: 700; margin: 0 0 .4rem; color: var(--ink); }
.hp-do-text  { font-size: .93rem; line-height: 1.6; color: var(--muted); margin: 0; }

/* ═══════════════════════════ ICON SYSTEM ══════════════════════════════
   Line illustrations rather than a glyph font: they carry the palette, they
   scale to any size, and each one can animate the part of itself that
   actually means something — the wave moves, the ring pulses, the signal
   travels. Drawing animations run once when the card is revealed. */
.hp-ico { width: 3.1rem; height: 3.1rem; display: block; overflow: visible; }
.hp-ico .s { fill: none; stroke: currentColor; stroke-width: 1.7;
             stroke-linecap: round; stroke-linejoin: round; }
.hp-ico .f { fill: currentColor; }
.hp-ico .accent { stroke: var(--reef-2); }

/* Stroke-draw, triggered when the parent card is revealed. */
.pc-js [data-reveal] .hp-ico .draw { stroke-dasharray: 200; stroke-dashoffset: 200; }
.pc-js [data-reveal].is-in .hp-ico .draw {
  animation: hpDraw 1.5s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes hpDraw { to { stroke-dashoffset: 0; } }

.hp-ico .bob   { animation: hpBob 4.5s ease-in-out infinite; }
.hp-ico .bob-2 { animation: hpBob 4.5s ease-in-out .8s infinite; }
@keyframes hpBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-2.2px); }
}
.hp-ico .spin { animation: hpSpin 18s linear infinite; transform-origin: 50% 50%; }
@keyframes hpSpin { to { transform: rotate(360deg); } }
.hp-ico .beat { animation: hpBeat 3.4s ease-in-out infinite; transform-origin: 50% 50%; }
@keyframes hpBeat {
  0%, 100% { transform: scale(1);    opacity: .85; }
  50%      { transform: scale(1.09); opacity: 1; }
}

/* ═════════════════════════════ PARTNERS ═══════════════════════════════
   A slow rail of the institutions taking part. Duplicated once in the
   markup and translated by exactly half, so it loops without a seam; paused
   on hover so a name can actually be read. */
.hp-partners { background: var(--surface-2); overflow: hidden; }
.hp-marquee {
  display: flex; width: max-content; gap: 0;
  animation: hpMarquee 62s linear infinite;
}
.hp-marquee:hover, .hp-marquee:focus-within { animation-play-state: paused; }
@keyframes hpMarquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
.hp-marquee-set { display: flex; gap: clamp(.7rem, 1.8vw, 1.2rem); padding-right: clamp(.7rem, 1.8vw, 1.2rem); }
.hp-partner {
  flex: none; display: flex; align-items: center; gap: .7rem;
  padding: .85rem 1.35rem; border-radius: 999px; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line);
  font-size: .9rem; font-weight: 600; color: var(--ink-2);
}
.hp-partner-tag {
  font-size: .62rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--brand); background: var(--brand-soft);
  padding: .2rem .5rem; border-radius: 999px;
}
/* Fade the rail into the page at both ends rather than cutting it off. */
.hp-marquee-mask {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}

/* ═══════════════════════ SECTION TRANSITIONS ══════════════════════════
   Curved joins between bands. A shared element so the shape is defined once
   and only the fill and flip change. */
.hp-curve { display: block; width: 100%; height: clamp(40px, 5vw, 84px); line-height: 0; }
.hp-curve.flip { transform: rotate(180deg); }

/* ══════════════════════════ RESPONSIVE ════════════════════════════════
   Not "stack everything". Each composition is re-thought at the width where
   it stops working: the rail keeps its horizontal axis (it is a phone's
   natural gesture), the stat ribbon goes two-up rather than four-across, the
   Pacific composition puts the illustration above the facts. */

@media (max-width: 74.99em) {
  .hp-pacific-grid,
  .hp-journey-grid { grid-template-columns: 1fr; }
  .hp-journey-grid .hp-qr { order: -1; }
  .hp-pillars-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 47.99em) {
  .hp-hero {
    min-height: auto;
    padding: clamp(6rem, 20vh, 8rem) 0 clamp(6.5rem, 18vh, 9rem);
  }
  .hp-hero-title { font-size: clamp(2.6rem, 15vw, 4.4rem); }
  .hp-cue { display: none; }             /* a phone reader already knows to scroll */
  .hp-cta { gap: .6rem; }
  .hp-btn { padding: .78rem 1.25rem; font-size: .92rem; flex: 1 1 auto; justify-content: center; }

  /* Two-up, and the divider becomes a grid line rather than four hairlines.
     The ribbon's own margins are set further down, after the desktop rule that
     would otherwise override anything written here — see .hp-ribbon below. */
  .hp-ribbon-grid { grid-template-columns: repeat(2, 1fr); gap: 1.4rem .6rem; }
  .hp-stat + .hp-stat::before { display: none; }

  .hp-pillars-grid { grid-template-columns: 1fr; }

  .hp-notice { grid-template-columns: auto 1fr; }
  .hp-notice-when { grid-column: 2; }

  .hp-ev { flex-basis: min(80vw, 19rem); }
  .hp-rail { padding-left: var(--shell); padding-right: var(--shell); }

  .hp-steps::before { left: 1.2rem; }
  .hp-steps::after  { left: 1.1rem; }
}

@media (max-width: 29.99em) {
  .hp-hero-marks { display: flex; gap: .8rem; }
  .hp-hero-marks .arms  { height: 2.5rem; }
  .hp-hero-marks .event { height: 2.1rem; }
  .hp-ribbon-grid { padding: 1.1rem .9rem; }
}

/* ═══════════════════════════ REDUCED MOTION ═══════════════════════════
   Everything decorative stops. The page keeps every layer, every colour and
   every piece of information — it simply holds still. Nothing here is load-
   bearing, which is the test a motion system has to pass. */
@media (prefers-reduced-motion: reduce) {
  .hp-glow, .hp-tapa, .hp-particle, .hp-wave-anim,
  .hp-marquee, .hp-qr-anim, .hp-qr-scan, .hp-hero-dates .dot,
  .hp-ico .bob, .hp-ico .bob-2, .hp-ico .spin, .hp-ico .beat,
  .hp-steps::after, .hp-cue-rail::after {
    animation: none !important;
  }
  .hp-particle { opacity: .5; }
  .hp-qr-mod   { opacity: .85; }
  .pc-js [data-reveal] .hp-ico .draw { stroke-dashoffset: 0; }
  .hp-islands  { transform: none !important; }
  .hp-ev:hover, .hp-do:hover, .hp-pillar:hover, .hp-btn:hover { transform: none; }
  /* The marquee is a moving element by definition; held still it must at
     least not clip its own content off the right edge. */
  .hp-marquee { flex-wrap: wrap; width: 100%; }
}

/* A filled accent shape takes the reef green as fill, not stroke. */
.hp-ico .f.accent { fill: var(--reef-2); stroke: none; }

/* ─────────────────── Hero corrections after first render ────────────────
   Written from looking at the rendered page rather than at the markup.

   1. The cue sat under the buttons in the centre column and collided with
      them. It belongs at the edge, out of the reading path.
   2. The island ridge was hidden behind the wave stack: lifting it clear
      puts the horizon where a horizon goes — above the water.
   3. One visible wave is decoration; the depth only reads when all three
      bands are distinguishable, which means separating them vertically as
      well as by opacity.
   4. The spray was so faint it may as well not have been rendered. */

.hp-cue {
  left: auto; right: clamp(1rem, 3vw, 2.4rem);
  bottom: clamp(7rem, 15vw, 10.5rem);
  transform: none;
  writing-mode: vertical-rl;
  gap: .9rem;
}
.hp-cue-rail { width: 1px; height: 3.4rem; }
@media (max-height: 46rem) { .hp-cue { display: none; } }

.hp-islands { bottom: clamp(34px, 4.5vw, 62px); }

/* Each band sits a little higher than the one in front, so the crests stay
   distinguishable instead of collapsing into a single silhouette. */
/* The tallest reads --wave-h from .hp-hero, which is also what the hero's
   bottom padding clears — one number, so the copy can never end up behind the
   water. The two behind it are proportions of the same value. */
.hp-waves .hp-wave:nth-child(1) { height: var(--wave-h); }
.hp-waves .hp-wave:nth-child(2) { height: calc(var(--wave-h) * .92); }
.hp-waves .hp-wave:nth-child(3) { height: calc(var(--wave-h) * .78); }

.hp-particle {
  background: radial-gradient(circle at 32% 30%, #fff, rgba(160,225,255,.35) 62%, transparent 78%);
  box-shadow: 0 0 8px rgba(180,235,255,.5);
}
@keyframes hpFloat {
  0%   { opacity: 0;   transform: translate3d(0, 18px, 0) scale(.7); }
  16%  { opacity: 1; }
  72%  { opacity: .7; }
  100% { opacity: 0;   transform: translate3d(var(--dx, 14px), -86px, 0) scale(1.2); }
}

.hp-tapa { opacity: .42; }

/* ─────────────── Pacific composition and rail, after review ─────────────
   The illustration was sized by its grid cell rather than by what it had to
   say, so at desktop width it became a pale 700px disc with a few dots in
   it. Capped, deepened, and given its rings back. */

/* The rail is aligned with the heading above it and bleeds off the right
   edge only. Running it off BOTH edges detached it from the column its own
   title sits in, which read as a mistake rather than as a device. */
.hp-rail-wrap { margin-right: calc(50% - 50vw); }
.hp-rail {
  margin: 0;
  padding: .5rem calc(50vw - 50%) 1.6rem 0;
  scroll-padding-left: 0;
}
@media (max-width: 47.99em) {
  .hp-rail { padding-right: var(--shell); }
}

/* ═══════════════ Corrections from the full-page render ════════════════ */

/* The Pacific composition. Sized by its grid cell, the illustration came out
   as a pale disc adrift in half a screen of nothing. It now fills a column
   sized for it, and the water is deep enough to read as water.

   Scoped to the desktop width on purpose. Unscoped, this sat AFTER the
   max-width:74.99em block that stacks the section to one column — same
   specificity, later in the file, so it won everywhere. On a phone that left
   the globe a 177px track hanging 26px off the left edge and squeezed the
   facts beside it, which is what "the Pacific section looks broken on mobile"
   was. A correction appended to the end of a stylesheet has to carry the
   media query it is correcting within. */
@media (min-width: 75em) {
  .hp-pacific-grid { grid-template-columns: 1fr 1.06fr; gap: clamp(2rem, 4vw, 3.5rem); }
}

/* Auto-fit gave four columns and left a ragged two-card row underneath.
   Three and three is a composition; four and two is a leftover. */
@media (min-width: 62em) {
  .hp-do-grid { grid-template-columns: repeat(3, 1fr); }
}
/* Cards in a row share a height, so the calls to action line up instead of
   floating at whatever height each card's copy happens to end. */
.hp-do { display: flex; flex-direction: column; }
.hp-do-text { flex: 1; }

.hp-ico { width: 3.4rem; height: 3.4rem; }
.hp-fact-mark .hp-ico { width: 1.6rem; height: 1.6rem; }

@media (prefers-reduced-motion: reduce) {
  /* The rail's second copy exists only so the animation can loop seamlessly.
     Held still it is simply every name printed twice, so it goes. */
  .hp-marquee-set[aria-hidden="true"] { display: none; }
  .hp-marquee-set { flex-wrap: wrap; row-gap: .7rem; }
}

/* On a phone the hero's padding was in viewport-height units, which on a tall
   handset reserved 450px of empty ocean around 350px of content. Width-based
   units keep the proportions the design intends without letting the device's
   height dictate how much nothing is on screen. */
@media (max-width: 47.99em) {
  .hp-hero { padding: clamp(5.5rem, 15vw, 7.5rem) 0 clamp(4.5rem, 15vw, 7rem); }
}

/* ══════════════════════════ THE GLOBE ═════════════════════════════════
   A sphere, not a disc. Three things do all the work: the ocean's radial
   gradient puts the light source up and to the left, the limb gradient
   darkens the edge where a sphere curves away, and the specular highlight
   sits where that light would actually catch. Take any one of them out and
   it collapses back into a flat circle with a map on it. */

.hp-globe { position: relative; max-width: 32rem; margin-inline: auto; }
.hp-globe svg { width: 100%; height: auto; display: block; overflow: visible; }

.hp-globe-land path,
.hp-globe-land circle { fill: url(#hpTerra); }

.hp-globe-grid ellipse {
  fill: none; stroke: #cfe9ff; stroke-opacity: .13; stroke-width: 1;
}
.hp-globe-grid .equator { stroke-opacity: .22; stroke-dasharray: 5 7; }

.hp-globe-rim {
  fill: none; stroke: #7fd0ff; stroke-opacity: .3; stroke-width: 1.5;
}
.hp-globe-spec { fill: url(#hpSpec); }

/* One revolution. The strip is drawn twice and slid by exactly one map
   width, so the end state is pixel-identical to the start and the loop is
   invisible. Slow on purpose: this is a background that happens to move,
   not a thing demanding to be watched. */
.hp-globe-spin { animation: hpGlobeSpin 64s linear infinite; }
@keyframes hpGlobeSpin {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-1 * var(--globe-w)), 0, 0); }
}

/* ── Fiji ───────────────────────────────────────────────────────────────
   The marker travels with the land, because it has to: a spotlight pinned
   to the middle of the screen would spend most of every revolution
   pointing at open ocean. */
.hp-fiji-halo {
  fill: url(#hpSpot);
  transform-box: fill-box; transform-origin: center;
  animation: hpFijiBreathe 3.4s ease-in-out infinite;
}
@keyframes hpFijiBreathe {
  0%, 100% { transform: scale(.86); opacity: .75; }
  50%      { transform: scale(1.12); opacity: 1; }
}

.hp-fiji-ring {
  fill: none; stroke: #6bf0a8; stroke-width: 1.4; opacity: 0;
  transform-box: fill-box; transform-origin: center;
  animation: hpFijiPing 2.3s cubic-bezier(.2,.6,.3,1) infinite;
}
@keyframes hpFijiPing {
  0%   { transform: scale(.35); opacity: .95; }
  70%  { opacity: .35; }
  100% { transform: scale(3.6); opacity: 0; }
}

.hp-fiji-isle { fill: #7dffbe; }
.hp-fiji-dot  { fill: #ffffff; }

/* The legend. The marker is on the far side of the world for part of every
   revolution, so what identifies it has to live outside the sphere. */
.hp-globe-note {
  display: flex; align-items: center; justify-content: center; gap: .55rem;
  margin: .4rem 0 0; font-size: .84rem; font-weight: 600; color: var(--muted);
  letter-spacing: .01em;
}
.hp-globe-key {
  width: .62rem; height: .62rem; border-radius: 50%; flex: none;
  background: var(--reef-2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--reef-2) 22%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .hp-globe-spin { animation: none; }
  .hp-fiji-halo  { animation: none; opacity: 1; }
  .hp-fiji-ring  { animation: none; opacity: .5; }
}

/* ═════════════════ SECTION JOINS ════════════════════════════════════════
   The sine waves that used to open and close these bands have gone. They were
   a second, unrelated wave language on a page whose identity is now the
   programme's contours, and its own banners are plain rectangles with the
   shapes sitting inside them.

   The padding that kept copy clear of those waves is now set by
   .hp-has-contour-top / -bottom, which are on the sections themselves — one
   number, next to the strip height it has to match. */

/* The band's colour is set once, further down — see the .hp-journey rule
   below the rail. Three separate rules used to paint it, each overriding the
   last, which is how it stayed navy through a recolour that changed the
   first two. */

/* The rail's scrollbar was a heavy blue bar the width of the section — a
   control shouting louder than the cards it scrolls. Thin, quiet, and only
   fully present when the reader is on the rail. */
.hp-rail { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--brand) 30%, transparent) transparent; }
.hp-rail::-webkit-scrollbar { height: 4px; }
.hp-rail::-webkit-scrollbar-track { background: transparent; }
.hp-rail::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--brand) 22%, transparent);
  border-radius: 4px;
}
.hp-rail:hover::-webkit-scrollbar-thumb,
.hp-rail:focus-within::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--brand) 55%, transparent);
}

/* The figures card straddles the join: pulled up onto the hero's water, and
   pulling the band below up under itself by the amount it overhangs.
 *
 * Without the second half it only LOOKED like it straddled. The card is about
 * 160px tall and sat 54px into the hero, so 105px of it hung below — and that
 * overhang was a full-width strip of page background between the hero's waves
 * and the next band's contours, with nothing in it either side of the card.
 * That is the white stripe; it was never the ribbon's padding.
 *
 * ── The clamps are ordered most-negative first ──────────────────────────────
 * clamp(MIN, VAL, MAX) requires MIN <= MAX *numerically*. Written as
 * clamp(-3rem, -5vw, -4.5rem) the bounds are inverted, CSS returns the first
 * value, and the vw term never applies at any width — which is why this card
 * did not move when the viewport grew. */
.hp-ribbon {
  margin-top:    clamp(-4.5rem, -5vw, -3rem);
  margin-bottom: clamp(-7.5rem, -5.6vw, -3rem);
}

/* Below the split the card is two rows tall, so it overhangs the hero by much
   more and needs a correspondingly bigger pull under it.
   This sits AFTER the rule above deliberately: an earlier media query lost to
   it on source order — same specificity, later wins — so the mobile values
   silently did nothing at any width. */
@media (max-width: 47.99em) {
  .hp-ribbon {
    margin-top:    clamp(-4rem, -8vw, -3rem);
    margin-bottom: clamp(-15rem, -34vw, -6rem);
  }
}

/* ─────────────────── Globe and ocean band, after review ─────────────────
   The globe was drawn on a 480-unit canvas but only occupied the middle 388
   of it, so a fifth of every pixel it was given went to empty margin. The
   viewBox is cropped to the artwork and the cap raised: same file, half again
   the presence. */
.hp-globe { max-width: 37rem; }

/* The dark-green panel that used to be set here has gone. It sat directly
   beneath the themes band, which carries the contour artwork, so the page ran
   green into green with nothing white between them. This band is white now
   and the artwork moved a section down; its colour is set at .hp-journey
   further up, and there is no second declaration to override it. */

/* The Pacific section had a column of nothing under a small globe. Tighter
   rhythm, and the two halves meet in the middle of the page rather than
   drifting apart. */
/* Padding for this band is set by .hp-has-contour, which has to clear the
   artwork's shapes; a tighter value here would drop the last fact onto one. */
.hp-pacific-grid { align-items: center; gap: clamp(1.5rem, 3.5vw, 3rem); }

/* `margin-inline: auto` on a grid item overrides justify-self:stretch, so the
   item stops filling its column and shrinks to fit its content instead. Its
   content here is an <svg width="100%"> — which, with nothing to be 100% OF,
   falls back to the SVG default of 300px. That is the whole reason this
   illustration has been rendering at a third of the size it was given: not the
   viewBox, not the max-width, but two words of centring. An explicit width
   restores the fill, and the auto margins then do only what they were for. */
.hp-globe { width: 100%; max-width: 37rem; }


/* ══════════════ GLOBE — real coastlines, real projection ═══════════════
   The land and the graticule are single paths whose `d` the server writes
   once and home.js rewrites as the Earth turns, so everything here is
   presentation only. */
.hp-globe-land {
  /* Filled, not stroked: at 500px across a 2.6px coastline reads as a
     wire diagram. The fill-rule keeps overlapping rings from cancelling. */
  fill: none;
  stroke: url(#hpTerra); stroke-width: 2.6;
  stroke-linejoin: round; stroke-linecap: round;
  paint-order: stroke;
  filter: drop-shadow(0 0 3px rgba(0, 190, 100, .35));
}
.hp-globe-grid {
  fill: none; stroke: #cfe9ff; stroke-opacity: .14; stroke-width: .9;
}
.hp-fiji { transition: opacity .35s linear; }

/* Filled, not merely outlined. Subpaths broken at the limb close on a chord,
   which is where the coastline genuinely meets the horizon, so the fill is
   correct rather than a happy accident. */
.hp-globe-land {
  fill: url(#hpTerra); fill-rule: nonzero;
  stroke: #7df0b0; stroke-opacity: .5; stroke-width: .8;
  filter: none;
}

/* ── The rail, centred ───────────────────────────────────────────────────
   Bleeding the rail off the right edge was meant to signal "this continues".
   It reads instead as a section that has slipped off its axis, because the
   heading above it is centred in the page and the cards are not. The rail now
   sits in the same column as its own title, and a fade at the right edge does
   the signalling the bleed was there for. */
.hp-rail-wrap {
  margin-right: 0;
  position: relative;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 4.5rem), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 4.5rem), transparent);
}
.hp-rail { padding: .5rem 4.5rem .4rem 0; }
/* With nothing left to scroll there is nothing to hint at, so the fade goes. */
.hp-rail-wrap.is-end {
  -webkit-mask-image: none; mask-image: none;
}
@media (max-width: 47.99em) {
  .hp-rail { padding-right: 2.5rem; }
  .hp-rail-wrap {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
            mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
  }
}

/* ══════════════════════ HERO PHOTOGRAPH ═══════════════════════════════
   Down the left of the hero, cut along the divider's curve.

   The clip lives on this box and the parallax lives on the <picture> inside
   it. Putting both on the same element would carry the cut along with the
   transform, and the green band — which does not move — would drift off the
   edge it is supposed to be sitting on. */

.hp-hero-media {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: -5;
  width: var(--hp-photo, 50vw);
  overflow: hidden;
  -webkit-clip-path: url(#hpHeroCut);
          clip-path: url(#hpHeroCut);
}
.hp-hero-media picture {
  display: block; position: absolute;
  /* Taller than the frame at both edges so the parallax has travel room;
     without it, shifting the layer exposes bare white at whichever edge it
     moved away from. */
  left: 0; right: 0; top: -9%; bottom: -9%;
  will-change: transform;
  /* home.js sets this transform straight from scroll position — a pure
     function of where the page is, nothing on a timer. This transition is
     what turns that into smooth motion: a mouse wheel fires in discrete
     jumps, not a continuous stream, and each update eases into place
     instead of snapping. Short and bounded on purpose, so it settles a
     fixed moment after the *last* scroll rather than drifting on once the
     reader has stopped — a photograph still moving after you stop
     scrolling is what reads as wrong, not as depth. */
  transition: transform .15s cubic-bezier(.22, .61, .36, 1);
}
.hp-hero-media img {
  width: 100%; height: 100%; object-fit: cover;
  /* hero_new.jpg is a portrait shoreline: the headland, the horizon and the
     ship sit across its upper third, foreground rock in the lower. Holding
     the crop at 38% keeps the horizon in a tall panel rather than filling it
     with water. */
  object-position: 50% 38%;
}

/* The scrim is gone with the dark hero. It existed to hold white type off a
   photograph; the type is blue on white now and sits beside the picture
   rather than on it, so a wash over the room would only mute the one real
   thing in the composition. */

/* Below the split the photograph and its divider both go, and the hero
   becomes the programme's other layout: the white banner with contour shapes
   at the edges. Neither carries meaning, so a phone reader loses nothing —
   and a half-width photograph on a 390px screen would be a stripe. */
.hp-hero-crown { display: none; }

@media (max-width: 61.99em) {
  /* The curve and the contour crown both go — the photograph is the hero's
     opening image on a phone, and three decorative devices stacked above the
     title is a page that never gets to the words. */
  .hp-hero-curtain { display: none; }
  .hp-hero-crown   { display: none; }

  /* Out of the absolute layer and into flow, as the first flex item, so it
     sits above the copy instead of behind it. order:-1 rather than relying on
     source position, because the crown divs sit between it and the copy. */
  .hp-hero-media {
    display: block; order: -1;
    position: relative; left: auto; top: auto; bottom: auto;
    z-index: 0;
    width: 100%; height: clamp(230px, 66vw, 330px);
    -webkit-clip-path: none; clip-path: none;
    margin-bottom: clamp(1.4rem, 5vw, 2.25rem);
  }
  /* A hair more sky than the tall desktop panel wants: in a wide, short frame
     38% puts the horizon almost on the bottom edge. */
  .hp-hero-media img { object-position: 50% 42%; }


  .hp-hero {
    min-height: auto;
    /* No crown to clear any more, so the picture starts at the very top of
       the hero. The foot still clears the wave stack. */
    padding-top: 0;
    padding-bottom: calc(var(--wave-h) + clamp(1.25rem, 3vw, 2.5rem));
  }
  /* A long theme line at the desktop floor runs to five lines on a phone. */
  .hp-hero-title { font-size: clamp(1.72rem, 7.6vw, 2.4rem); }
}

@media (prefers-reduced-motion: reduce) {
  .hp-hero-media picture { transition: none; transform: none !important; }
}

/* The islands and the cloth are now moved by home.js as scroll-driven layers,
   so they need the same short, bounded easing the photograph has — otherwise
   they snap between a wheel's discrete ticks while the picture behind them
   glides, which reads as three separate faults rather than one effect. */
.hp-islands, .hp-tapa { transition: transform .15s cubic-bezier(.22,.61,.36,1); }
@media (prefers-reduced-motion: reduce) {
  .hp-islands, .hp-tapa { transition: none; transform: none !important; }
}

/* ── Hero framing ────────────────────────────────────────────────────────
   How much of the width the photograph takes. One custom property, read by
   both the picture and the divider that sits on its edge, so they cannot
   drift apart. */
.hp-hero { --hp-photo: 52vw; }

/* ── Laptop-height compression ────────────────────────────────────────────
   1366x768, 1440x900, 1536x864, a maximized 1920x1080 with a bookmarks bar —
   the common case where the SCREEN is desktop-width but the browser's actual
   content height is 700-900px. The hero was sized as if height scaled with
   width the way it does on a tall monitor; on these it did not, and the
   result was 1.1-1.6 screens of hero before the page said anything.

   Bound on height alone, not paired with a max-width, so a window resized
   short on any desktop compresses the same way — which is correct: the
   problem is short, not narrow. */
@media (min-width: 62em) and (max-height: 980px) {
  .hp-hero {
    padding-top: clamp(1rem, min(3vw, 3.4svh), 1.75rem);
  }
  .hp-hero-marks { margin-bottom: clamp(.7rem, min(1.7vw, 2.4svh), 1.3rem); }
  .hp-hero-title {
    /* 3.9rem's ceiling is what forces a third line at 1440px+; brought down
       enough to hold TWO lines at 1440px and wider. At 1366px it still wraps
       to three — the column is narrow enough there that only a font size
       too small for the brand would avoid it — but by that width the button
       fix below has already done the larger share of the work, and three
       short lines at a smaller size cost less than the original three at
       full desktop size did. */
    font-size: clamp(1.85rem, min(3.3vw, 6svh), 2.85rem);
    line-height: 1.06;
  }
  .hp-hero-lede {
    font-size: clamp(.9rem, 1vw, 1rem); line-height: 1.45;
    margin-top: clamp(.5rem, 1.2svh, .8rem);
  }
  .hp-hero-where { margin-top: clamp(.4rem, 1svh, .65rem); }
  .hp-cta { margin-top: clamp(.75rem, min(1.8vw, 2.6svh), 1.3rem); }

  /* The wave stack at the hero's foot is still sized off the general,
     all-screen rule (15svh), which leaves a laptop-height viewport paying
     close to a fifth of its own height for a decorative flourish. Cut
     further only inside this bracket — a tall desktop never sees this
     block, so the wave keeps its full presence there. */
  .hp-hero { --wave-h: clamp(52px, min(11vw, 10svh), 130px); }

  /* The two buttons wrap to a second row at exactly the widths this bracket
     targets — 1366px gives the row 473px and the pair needs 512px at their
     full desktop padding. A second button row costs as much vertical space
     as the whole reduction above it, so it is worth closing here rather than
     leaving as a separate problem: same text, tighter padding. */
  .hp-cta { gap: .5rem; }
  .hp-cta .hp-btn { padding: .66rem 1.15rem; font-size: .89rem; }
}

@media (min-width: 100em) {
  /* Past about 1600px a proportional panel turns into half a wall of
     photograph, so it stops growing with the viewport. */
  .hp-hero { --hp-photo: 52rem; }
}

/* .hp-no-base used to live here, for a "For participants" section that was
   briefly the last thing on the page before the footer. It isn't anymore —
   the FAQ and Organisations sections both sit after it now — and dropping
   its base band left it the one banded section on the page showing only its
   top half. Removed along with the class in home.php; see the comment there. */

/* ═══════════════════════ FREQUENTLY ASKED ══════════════════════════════
   The section's own frame only. Everything inside it — the rows, the chevron,
   the open/closed behaviour — is .pc-faq in precop.css, shared with the FAQ
   page so the two cannot look like different components.

   No .hp-contour here on purpose: the section above carries the crest band and
   the page alternates banded/plain/banded. See the comment in home.php. */
.hp-faq-list {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: .35rem clamp(1rem, 2.4vw, 1.9rem);
  max-width: 56rem;
}
