/* ==========================================================================
   GEBI — landing page v2
   The reference artwork (assets/theme-landing.png, 1024 x 1536) is used as the
   page itself, in three aspect-ratio-locked crops. Real HTML is layered on top:
   the header (which replaces the logo baked into the image), the three program
   buttons on the blue band, the "201 programs" block, contact and footer.
   Aligned percentages are derived from pixel measurements of the source image;
   changing the image means re-measuring (see README.md).
   ========================================================================== */

:root{
  /* palette sampled from the reference artwork */
  --cream:#f4f1ea;
  --cream-hi:#fbf9f4;
  --ink:#223549;
  --ink-soft:#485f75;
  --line:rgba(34,53,73,.16);
  --sky:#cee5f0;
  --band:#d9eaf2;
  --slate:#5a86a6;
  --navy:#44607f;
  --btn-navy:#3a5877;
  --contact-bg:#3a5775;
  --green:#6a7850;      /* Vector's circle in the artwork */
  --blue:#4a88b0;       /* BASE's circle */
  --yellow:#dfc57d;     /* SHIELD's circle */
  --sun:#e0c581;        /* the sun disc in the artwork's top-right sky */
  --green-deep:#4e7a4b;
  --tile-off:#d9dfdc;
  --ink-copy:#2c4a60;      /* hero paragraph */
  --link-green:#374a24;    /* "Explore our work" in the artwork */

  /* Side gutters and content column follow bluedot.org's own scale (their --spacing-x /
     --max-width): the gutter is a step, not a fluid clamp — 1.5rem below 48rem,
     3rem up to 80rem, 5rem from 80rem up — and the copy column is as wide as their
     1436px container allows once its own 5rem gutter is taken out, so text sits at
     exactly the same inset as bluedot's at every width. */
  --pad:1.5rem;
  --max-w:calc(1436px - 2 * 5rem);
  /* the same left edge .wrap lands on, for copy that sits on full-bleed artwork
     (hero, map band): the gutter, or the centred column's margin once it is wider */
  --bleed-x:max(var(--pad), calc((100% - var(--max-w)) / 2));
  --head-x:var(--bleed-x);   /* the bar lines up with the sections below it */
  --head:'Figtree',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'Figtree',system-ui,-apple-system,'Segoe UI',sans-serif;
  --pop-bg:#fdfbf6;

  color-scheme:light;
  box-sizing:border-box;
}
*,*::before,*::after{box-sizing:inherit}

/* the gutter steps, exactly as bluedot.org's --spacing-x-sm/md/lg/xl do */
@media (min-width:48rem){:root{--pad:3rem}}
@media (min-width:80rem){:root{--pad:5rem}}

html{
  background:var(--cream);
  scroll-behavior:smooth;
  scroll-padding-top:5.5rem;
  -webkit-text-size-adjust:100%;
}
body{
  margin:0;
  background:var(--cream);
  color:var(--ink);
  font:400 1.0625rem/1.65 var(--body);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
h1,h2,h3{font-family:var(--head);font-weight:600;margin:0;letter-spacing:-.02em}
h2{font-size:clamp(1.9rem,3.4vw,2.7rem);line-height:1.12;margin-bottom:1.1rem}
h3{font-size:1.3rem;line-height:1.25;margin-bottom:.6rem}
p{margin:0 0 1rem;max-width:62ch}
a{color:inherit}
button{font:inherit;color:inherit;cursor:pointer}
img{max-width:100%}
:focus-visible{outline:3px solid var(--slate);outline-offset:3px;border-radius:6px}

.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;padding:0;margin:-1px}
.skip{position:absolute;left:50%;top:-7rem;transform:translateX(-50%);z-index:80;background:var(--ink);color:#fff;padding:.6rem 1.1rem;border-radius:0 0 14px 14px;text-decoration:none;font-weight:600;transition:top .2s}
.skip:focus{top:0}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
}

/* ---------- intro loader (v60) ----------
   The mark from the old landing page, carried over: the GEBI emblem sits as a pale
   ghost and the solid version fills from the bottom edge up, then the panel
   dissolves off the hero.

   Landing page only — index.html is the one page that ships the #loader markup.
   Its head script adds .no-intro (and js/main.js tears the panel down) when the
   intro should not play: a refresh always plays it, arriving from another page
   never does. Keep the timings in sync with js/main.js: 0.05 s delay + 3.1 s fill
   (fillMs = 3150), the curtain drops 250 ms before the fill tops out. */
.loader{
  position:fixed;inset:0;z-index:100;
  display:grid;place-items:center;
  background:var(--cream);
}
.loader__ghost,.loader__img{position:absolute;inset:0}
/* the un-inked die: the same emblem as a flat wash */
.loader__ghost{
  background:rgba(34,53,73,.14);
  -webkit-mask:url('../assets/gebi-emblem.png') center/contain no-repeat;
  mask:url('../assets/gebi-emblem.png') center/contain no-repeat;
}
.loader__img{
  width:100%;height:100%;object-fit:contain;
  clip-path:inset(100% 0 0 0);   /* hidden until the ink runs up it */
}
/* 482 x 518 is the emblem's own box */
.loader__mark{position:relative;width:clamp(200px,32vmin,300px);aspect-ratio:482/518}
/* one 0 -> 100 run so the mark keeps moving until it is full, with no
   done-looking plateau at the end */
body.is-running .loader__img{animation:logo-fill 3.1s cubic-bezier(.66,0,.34,1) .05s forwards}
@keyframes logo-fill{
  0%{clip-path:inset(100% 0 0 0)}
  100%{clip-path:inset(0 0 0 0)}
}
/* exit: the mark fades, the panel dissolves (no slab sliding over the hero) */
.loader--exit{opacity:0;visibility:hidden;transition:opacity .55s ease,visibility 0s linear .6s}
.loader--exit .loader__mark{opacity:0;transition:opacity .3s ease}
/* internal navigation back to the landing page never replays the intro */
html.no-intro #loader{display:none}


/* ---------- header (replaces the bar baked into the artwork) ---------- */
.site-header{
  position:fixed;inset:0 0 auto 0;z-index:50;
  background:var(--cream);
  border-bottom:1px solid rgba(34,53,73,.08);
  /* v65: the mark was drawn flush to the bar (.2rem of padding, less than the art's own
     bleed), so it read as touching the top and bottom rules. The mark is now 56px
     (38px on phones) and the slack became real padding: the bar's total height is the
     same as before, so the hero art behind it is still covered by exactly as much, and
     the page-level budgets written against a ~71px bar (programs, tools) still hold. */
  padding:calc(env(safe-area-inset-top,0px) + .4rem) var(--head-x);
  transition:box-shadow .3s;
}
.site-header.solid{box-shadow:0 6px 24px rgba(34,53,73,.10)}
/* The bar's cream covers the top of the artwork's sun, so the sun is redrawn here as a
   complete disc: same centre and radius as the poster's (centre 921,38, r 21.5 in a
   1024x813 artwork), expressed against the bar's box, which is the same width as the
   artwork. v63: z-index -1 puts it behind everything the bar contains (the nav, the
   pills) while still painting over the header's own cream. */
.site-header::before{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  top:1.611vw;right:7.959%;width:4.199%;aspect-ratio:1;border-radius:50%;
  background:var(--sun);
  /* v10: the disc belongs to the page, not to the viewport. --sun-y is only ever set
     by the fallback in js/main.js, for engines with no scroll-driven animation. */
  transform:translateY(var(--sun-y,0px));
}
/* v10: pinned to the fixed bar, the disc used to hang in the viewport's top-right
   corner at every scroll position, and the artwork's own sun slid out from under it.
   This scroll-driven animation keeps the disc exactly on the artwork's sun and lifts
   it out of the viewport 1:1 with the scroll, so it reads as part of the hero and
   leaves with it. No JS, reversible, runs off the main thread. */
@supports (animation-timeline:scroll()){
  @media (prefers-reduced-motion:no-preference){
    .site-header::before{
      animation:sun-leave linear both;
      animation-timeline:scroll(root block);
      animation-range:0 100vh;
    }
    @keyframes sun-leave{from{translate:0 0}to{translate:0 -100vh}}
  }
}
/* v63: .bar is NOT positioned. The phone drawer is full-bleed, so the only box it may
   be measured against is the header itself - a positioned .bar would inset it by the
   gutter. (v64: the dropdown panels are no longer full-bleed either; they hang off
   their own .nav-item, which carries position:relative.) Nothing is lost: the header
   forms the stacking context (z-index 50), and the sun it draws sits at z-index -1. */
.bar{display:flex;align-items:center;justify-content:space-between;gap:1rem;min-height:52px}
.brand{display:flex;align-items:center;text-decoration:none}
.brand img{display:block;height:56px;width:auto;mix-blend-mode:multiply;transform-origin:left center;transition:transform .22s ease}
/* Grow on hover — 1.08 of 56px is ~60px, which still sits inside the bar's ~69px, so the
   mark keeps its clearance above and below even while it grows (it used to touch the
   bottom border at this scale). */
.brand:hover img,.brand:focus-visible img{transform:scale(1.08)}
/* v63 - the bar's layout is bluedot.org/missions': the mark sits alone on the left
   (with the menu button, on phones) and everything else is one right-hand cluster -
   the link row, then a wider gap, then the action pills, flush to the same gutter the
   sections below use. .nav is that cluster on desktop and the drawer on phones, so
   .nav-links / .nav-actions only change direction between the two, not markup. */
.bar-start{display:flex;align-items:center;gap:.375rem;min-width:0}
.nav{display:flex;align-items:center;gap:clamp(1.75rem,4vw,3rem)}
.nav-links{display:flex;align-items:center;gap:clamp(1.1rem,2.2vw,2.25rem)}
.nav-actions{display:flex;align-items:center;gap:1rem}
.nav .link{border:0;text-decoration:none;font-weight:500;font-size:1rem;padding:.35rem .1rem;background:linear-gradient(var(--ink),var(--ink)) 0 100%/0 2px no-repeat;transition:background-size .25s}
.nav .link:hover{background-size:100% 2px}
/* the page you are on keeps its underline, the way bluedot marks "Missions", and the
   parent item of an open section (About > Team) carries it too. :has() is a nicety -
   without it the row simply looks the way it did before. */
.nav-links>.link[aria-current="page"],
.nav-item.drop:has(.drop-menu a[aria-current="page"]) .drop-btn{background-size:100% 2px;font-weight:600}
/* v61: pills step down a notch - font .95rem and a tighter pad, so the same button
   reads as a control next to the copy rather than as a headline block. Every page
   shares this rule (styles.css is loaded by all of them), so the size stays uniform;
   the hero's own pills inherit it too, see .hero-btn below. */
.btn{display:inline-block;border:0;border-radius:999px;padding:.6rem 1.25rem;font-size:.95rem;font-weight:600;text-decoration:none;line-height:1.2;transition:transform .2s,filter .2s}
.btn:hover{transform:translateY(-2px);filter:brightness(1.06)}
.btn-navy{background:var(--btn-navy);color:#fff}
.btn-light{background:var(--cream-hi);color:var(--ink)}
/* v63: the second pill of the pair - bluedot's "Sign in", an outline on the bar
   rather than a filled button, so "Start Today" is the only solid one */
.btn-ghost{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1px rgba(34,53,73,.30)}
.btn-ghost:hover{filter:none;background:rgba(34,53,73,.07)}
/* v63: the menu button became the small square glyph bluedot uses, and it moved to
   the left of the mark. Two icons, one shown at a time, so the open state reads as
   a close control without any JS swapping the markup. */
.menu-btn{display:none;place-items:center;width:2.25rem;height:2.25rem;padding:0;background:none;border:0;border-radius:999px;color:var(--ink)}
.menu-btn svg{display:block;width:1.35rem;height:1.35rem}
.menu-btn:hover{background:rgba(34,53,73,.08)}
.menu-btn .mb-close{display:none}
.menu-btn[aria-expanded="true"] .mb-open{display:none}
.menu-btn[aria-expanded="true"] .mb-close{display:block}
@media (max-width:880px){
  .bar{min-height:38px}
  /* the bar is only ~53px here and the only thing over the sun would be the Menu button,
     which is transparent — so the sun stays hidden as it always was */
  .site-header::before{display:none}
  /* the same .4rem of slack as the desktop bar, on the smaller mark */
  .brand img{height:38px}
  .menu-btn{display:grid}
  /* v63: the drawer is full-bleed and hangs off the bar's bottom border, like
     bluedot's - not a floating rounded card inset by the gutter */
  .nav{position:absolute;top:calc(100% + 1px);left:0;right:0;flex-direction:column;align-items:stretch;gap:0;background:var(--cream-hi);border-bottom:1px solid var(--line);padding:.35rem var(--head-x) 1.25rem;display:none;max-height:calc(100dvh - 3.5rem);overflow:auto;box-shadow:0 18px 40px rgba(20,40,60,.18)}
  .nav.open{display:flex}
  .nav-links{flex-direction:column;align-items:stretch;gap:0}
  .nav .link{display:flex;align-items:center;justify-content:space-between;min-height:2.75rem;padding:.7rem 0;border-bottom:1px solid var(--line);background:none}
  /* the pills keep the link rows' rhythm at the foot of the drawer; the last row
     already draws a rule, so the group does not add a second one */
  .nav-actions{flex-direction:column;align-items:stretch;gap:.6rem;margin-top:1.1rem}
  .nav-actions .btn{display:block;text-align:center}
}

/* ---------- toast for not-yet-built nav items ---------- */
.toast{
  position:fixed;left:50%;bottom:1.5rem;transform:translateX(-50%);
  z-index:70;margin:0;max-width:none;
  background:var(--ink);color:#fff;font-size:.95rem;font-weight:500;
  padding:.6rem 1.1rem;border-radius:999px;box-shadow:0 14px 30px rgba(20,40,60,.28);
}
.toast[hidden]{display:none}

/* ---------- dropdown menus (Tools, About) ----------
   v64: the panel is a small card tethered to its own item - only as wide as its
   longest label - instead of the full-bleed sheet across the window that v63 had.
   .nav-item is the containing block, so left:0 lines the panel up under its own
   section (both items sit far enough from the window's edges for that at every
   width the row is shown at). The 1rem drop to the bar's bottom border is spanned
   by the panel's ::before, so the pointer can cross the gap without the menu
   blinking shut on the way down. The phone drawer resets all of it below. */
.nav-item{position:relative}
.nav-item .drop-btn{border:0;border-radius:0;cursor:pointer;font:inherit;display:inline-flex;align-items:center;gap:.4rem}
.drop-caret{flex:none;width:.7rem;height:.7rem;transition:transform .25s ease}
.drop-btn[aria-expanded="true"]{background-size:100% 2px}
.drop-btn[aria-expanded="true"] .drop-caret{transform:rotate(180deg)}
.drop-menu{
  position:absolute;top:calc(100% + 1rem);left:0;right:auto;
  width:max-content;max-width:min(20rem,calc(100vw - 2rem));
  display:flex;flex-direction:column;align-items:stretch;
  list-style:none;margin:0;padding:.4rem;
  background:var(--cream-hi);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 18px 40px rgba(20,40,60,.14);z-index:60;
  animation:drop-in .22s ease both;
}
/* invisible bridge over the gap between the button and the panel */
.drop-menu::before{content:"";position:absolute;top:-1rem;left:0;right:0;height:1rem}
@keyframes drop-in{from{opacity:0;translate:0 -.35rem}}
.drop-menu li{width:auto}
.drop-menu a{display:block;padding:.7rem 1rem;border-radius:10px;text-decoration:none;font-weight:500}
.drop-menu a:hover{background:rgba(34,53,73,.06)}
.drop-menu a[aria-current="page"]{font-weight:700}
.drop-menu[hidden]{display:none}
@media (max-width:880px){
  .nav-item{display:block}
  /* v63: a dropdown row is a <button>, and `.nav-item .drop-btn` resets its border and
     display for the desktop pill. In the drawer that reset would strip the row's 1px rule
     and shrink the row to its label - so the chevron would sit against the text instead of
     at the drawer's right edge, as bluedot's does. These three declarations put a dropdown
     row back on the same footing as a plain one. */
  .nav-item .drop-btn{display:flex;width:100%;border-bottom:1px solid var(--line)}
  .drop-menu{position:static;top:auto;left:auto;right:auto;width:auto;max-width:none;align-items:stretch;animation:none;box-shadow:none;border:0;border-radius:0;background:none;padding:.15rem 0 .3rem .9rem}
  .drop-menu::before{content:none}
}

/* ---------- the home button is gone (v63) ----------
   v38-v62 the link row opened with a small green house back to the landing page. The
   reference bar has no home item at all - its wordmark is the way home - so the house
   went with it and the mark took over the job. The rules and the `.nav-home` markup
   were removed together; nothing else referenced the class. */

/* ---------- artwork: text-free crops with the poster's words as real text ----------
   The poster (theme-landing.png, 1024 x 1536) is split into three crops. Every
   word baked into it is reproduced as real HTML, positioned from pixel
   measurements of the source and sized in cqw, so the copy sits exactly where
   the artwork had it and scales with the artwork at any viewport width. */

/* hero: source y 76 -> 889 (h 813) of the poster, but v12 crops the artwork at
   row 470: the landscape's drawing ends by row ~409 (verified with a pixel scan —
   every row from 409 down is uniform #f4f1ea), so the 61 px of cream kept below it
   are seamless. What used to sit over that lower cream (the two-line heading and the
   three cards) has moved out of the artwork into the "Plenty of interest" band below. */
.hero{position:relative;background:var(--cream);--work-pull:clamp(1.25rem,4vw,4.5rem)}
.hero-art{position:relative;aspect-ratio:1668/624;overflow:hidden}
/* The illustration only goes pale on its upper-left; the lower-left corner is dense
   dark foliage, which is where the copy's last lines land. This cream scrim (an ellipse
   anchored off the left edge, plus the v46 pool in the bottom-left corner) keeps the
   block legible without hiding the trail.
   Colours are literal rgba() because CSS gradients cannot vary the alpha of var(--cream). */
.hero-art::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(56% 124% at -8% 46%,
      rgba(244,241,234,.95) 0%,
      rgba(244,241,234,.87) 26%,
      rgba(244,241,234,.58) 54%,
      rgba(244,241,234,.24) 78%,
      rgba(244,241,234,0) 100%),
    /* v46: a second, low cream pool in the bottom-left corner. The mask edge is a horizontal
       curve; this lifts the cream higher on the left, so the picture hands over to the page
       along a diagonal instead of a line, and the copy's side of the frame is the palest. */
    radial-gradient(54% 64% at -6% 98%,
      rgba(244,241,234,.92) 0%,
      rgba(244,241,234,.5) 46%,
      rgba(244,241,234,0) 84%),
    /* v47: and the same pool mirrored in the bottom-right corner, so the cream rises at both
       ends of the frame and the illustration is left standing, longest, in the middle-left
       where its trail runs off the bottom edge. Corners clear first, the trail clears last:
       the silhouette is now the picture's own composition instead of a straight edge. */
    radial-gradient(46% 56% at 104% 102%,
      rgba(244,241,234,.9) 0%,
      rgba(244,241,234,.46) 48%,
      rgba(244,241,234,0) 86%);
}
/* v46: the picture used to stop on a ruled line across the page. It now dissolves into the
   cream instead, which is the one thing the composition wanted: the trail runs off the bottom
   edge of the illustration, so a long feathered edge lets it leave the picture and "continue"
   as the cream the next section already sits on. Nothing is added to the artwork - it is the
   same <img>, masked - so no new element and no new file.
   The fade is deliberately slow (the bottom third of the box) and eased over six stops rather
   than straight, so there is no band where "picture" ends and "fade" begins; the stops are
   percentages of the box, which is aspect-locked, so the curve is identical at every width. */
.hero-img{
  display:block;width:100%;height:auto;
  -webkit-mask-image:linear-gradient(180deg,#000 0 66%,rgba(0,0,0,.97) 75%,rgba(0,0,0,.84) 83%,rgba(0,0,0,.58) 90%,rgba(0,0,0,.3) 95%,rgba(0,0,0,.09) 98.5%,rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(180deg,#000 0 66%,rgba(0,0,0,.97) 75%,rgba(0,0,0,.84) 83%,rgba(0,0,0,.58) 90%,rgba(0,0,0,.3) 95%,rgba(0,0,0,.09) 98.5%,rgba(0,0,0,0) 100%);
  /* the mask is relative to the box, not the picture: with width:100%;height:auto the two
     agree on desktop (aspect-ratio locked) and on phones, where the img is `cover`-fitted
     inside a fixed-height box */
}
/* the section below is pulled up over the bottom of the picture (see #work), so the
   copy reserves that strip at its own foot: without it the last line lands on the next
   section's heading. --work-pull is the same clamp #work uses to pull itself up, so the
   reserve follows the overlap at every width instead of guessing at a padding. */
.hero-copy{position:absolute;inset:0;container-type:inline-size;display:flex;flex-direction:column;justify-content:center;padding:4.4rem 0 calc(1.5rem + var(--work-pull)) var(--bleed-x)}
.hero-h1,.hero-sub{position:static;margin:0}
/* The headline sits in the pale sky on the left of the illustration. The artwork is
   the supplied LandingPageBackground trail scene, which has no type baked in, so the
   copy is no longer pinned to the poster's pixel offsets: it is one flex column,
   centred in the hero, with every size still in cqw so the block scales with the
   picture. Nothing can collide with the fixed bar, whatever the viewport. */
.hero-h1{font-size:3.5cqw;line-height:1.07;letter-spacing:-.012em;color:var(--ink);max-width:11em;margin:0 0 .38em}
/* "GEBI is a 501(c)(3) nonprofit establishing biosecurity professionals and
   ecosystems across the globe." */
.hero-sub{font-size:1.45cqw;line-height:1.55;color:var(--ink-copy);max-width:18em;margin:0 0 1.05em}
/* v59: the hero's two entry points ("Explore Our Programs" -> the programs page,
   "Get Involved" -> the contact panel). The arrow is the only ornament: up-and-right
   for the page that opens elsewhere, plain right for the step that stays on this page.
   v62: their size comes from .btn (see .hero-btn below), so only the row layout is
   decided here. */
.hero-actions{display:flex;flex-wrap:wrap;align-items:center;gap:1.1cqw;max-width:none;margin:0;padding:0}
/* v62: the two entry points take their size straight from .btn now - the same .6rem/
   1.25rem padding and .95rem type as the bar's "Start Today" pill, so the hero's
   buttons and the site's buttons read as one set. Only the layout is local: inline-flex
   so the arrow sits beside the label instead of after an inline-block's whitespace. */
.hero-btn{
  display:inline-flex;align-items:center;gap:.55em;
  border-radius:999px;
}
/* The second entry point is a filled pill with no outline: the site's pale blue band
   colour (--band) on the navy label, so it is unmistakably a button on both the artwork
   and the plain cream of a phone, and stays the quieter of the pair against the solid
   navy primary. The 1.5px navy inset ring was removed - the fill alone carries it, and
   the hover step below is what the ring used to signal. */
.hero-btn--line{background:var(--band);color:var(--navy)}
/* the .btn hover lift is kept, and the fill deepens a step instead of going clear again */
.hero-btn--line:hover{filter:none;background:#c9dfec}
.hero-arrow{flex:none;width:.95em;height:.95em;transition:transform .2s ease}
.hero-btn:hover .hero-arrow--up{transform:translate(2px,-2px)}
.hero-btn:hover .hero-arrow--right{transform:translateX(3px)}
/* Below 1100 px the hero box is only ~310-410 px tall, so the same cqw sizes leave the
   copy leaning on the trail illustration's hiker. The column is pulled in one line
   sooner there, which keeps every line inside the picture's pale left side. */
@media (max-width:1099px){
  /* never less than the fixed bar's height, or the headline starts behind it */
  .hero-copy{padding-top:max(3.7rem,5.2cqw)}
  .hero-sub{max-width:19em}
}
/* v59: the bar above and the pull reserved at the foot below are fixed sizes, so on a
   short hero the copy is left with less room than the cqw type assumes -- and the two
   buttons are the last straw. Between the phone layout and a full desktop the copy steps
   down a notch so the buttons still clear the bar and the "Plenty of interest" heading. */
@media (min-width:881px) and (max-width:1200px){
  .hero-h1{font-size:3.3cqw}
  .hero-sub{font-size:1.4cqw;max-width:20em}
}
/* narrowest desktop hero: the picture is under ~420 px tall, so the section below is
   allowed to pull up a little less and the copy steps down once more */
@media (min-width:881px) and (max-width:1100px){
  .hero{--work-pull:clamp(1rem,3vw,2rem)}
  .hero-h1{font-size:3.1cqw}
  .hero-sub{font-size:1.3cqw}
  .hero-copy{padding-bottom:calc(1.25rem + var(--work-pull))}
}
/* the three cards the poster draws (x 57-348 / 365-657 / 676-967, y 524-776 of
   the crop) lived here as real links. v12: the whole lower cream slot — that heading
   plus these three tiles — is gone; the space now carries the "Plenty of interest"
   band (section#work, below). The band's three program buttons are the named entry
   points now. */

/* map band: a watercolour world map (2143x734 source), cropped to 2143x660 so the band
   above meets painted water instead of the picture's empty top margin. v58: the source art
   carries ~74 px (10%) of bare paper above its first wash; taken in full, that slice read as
   a white gap between the band and the map. The crop is done with object-fit, so the box is
   always exactly the cropped picture - no horizontal change, all x positions untouched.
   Anything expressed as a % of height here (dots, the svg viewBox, the headline's bottom)
   is relative to the 660 px crop, i.e. y(660) = y(source) - 74. */
.map-section{position:relative;background:var(--cream)}
.map-art{position:relative;container-type:inline-size;aspect-ratio:2143/660}
.map-art img{display:block;width:100%;height:100%;object-fit:cover;object-position:center bottom}
/* v51: the band above now dissolves into cream, so the map's own top edge has to arrive
   in cream too - its top rows are pale blue watercolour, which would put a step straight
   back where the seam was. v51 lengthened the veil from 5% to 13% and eased it: at 5% the
   artwork came up through the cream inside ~22 px, which the eye still reads as a ruled
   line, however soft the individual pixels are. */
.map-art::after{
  content:"";position:absolute;left:0;right:0;top:0;height:13%;pointer-events:none;z-index:1;
  background:linear-gradient(180deg,
    rgba(244,241,234,1) 0%,rgba(244,241,234,.88) 16%,rgba(244,241,234,.66) 34%,
    rgba(244,241,234,.42) 52%,rgba(244,241,234,.22) 70%,rgba(244,241,234,.08) 86%,
    rgba(244,241,234,0) 100%);
}
/* "Built locally. / Connected globally." sits in the pale southern Pacific wash at the
   bottom-left, the quietest corner of the artwork: the continents run all the way across
   the top, so the copy is anchored low and left, where the water is lightest. */
.map-head{position:absolute;left:var(--bleed-x);bottom:13.33%;margin:0;font-size:2.832cqw;line-height:3.32cqw;letter-spacing:-.01em;color:var(--ink)}

/* A single hairline route from one hub to the next: pale ink, no fill, drawn behind the
   pips (z-index 1 vs 2) and never intercepting the pointer. `non-scaling-stroke` keeps it
   a true 1.25 px at every band width, since preserveAspectRatio="none" stretches the
   viewBox but not the stroke. */
.map-links{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:2}
.map-links path{fill:none;stroke:rgba(58,88,119,.3);stroke-width:1.25;stroke-linecap:round;vector-effect:non-scaling-stroke}

/* ---------- map dots with hover tooltips ---------- */
/* the watercolour map draws no nodes of its own, so each marker is a real dot: a small
   ink pip ringed in cream so it reads on land or water, with the town name on hover */
.map-dot{
  position:absolute;width:max(2.9cqw,24px);height:max(2.9cqw,24px);border-radius:50%;
  transform:translate(-50%,-50%);
  cursor:pointer;z-index:3;
}
.map-dot::before{
  content:"";position:absolute;inset:0;margin:auto;
  width:max(.7cqw,10px);height:max(.7cqw,10px);border-radius:50%;
  background:var(--ink);
  box-shadow:0 0 0 3px rgba(255,255,255,.9),0 0 0 4px rgba(34,53,73,.18),0 2px 6px rgba(34,53,73,.3);
  transition:box-shadow .2s ease,transform .2s ease;
}
.map-dot:hover::before,.map-dot:focus-visible::before{
  transform:scale(1.25);
  box-shadow:0 0 0 3px rgba(255,255,255,.95),0 0 0 6px rgba(34,53,73,.16),0 2px 8px rgba(34,53,73,.35);
}
.map-dot:focus-visible{outline:2px solid rgba(34,53,73,.55);outline-offset:4px}
.map-dot-tip{
  position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);
  background:var(--ink);color:#fff;font-size:.78rem;font-weight:600;
  padding:.3rem .7rem;border-radius:8px;white-space:nowrap;
  opacity:0;pointer-events:none;transition:opacity .2s ease,transform .2s ease;
  transform:translateX(-50%) translateY(4px);
}
.map-dot-tip::after{
  content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);
  border:5px solid transparent;border-top-color:var(--ink);
}
.map-dot:hover .map-dot-tip,.map-dot:focus-visible .map-dot-tip{
  opacity:1;transform:translateX(-50%) translateY(0);
}

/* ---------- shared section layout ---------- */
.section{position:relative;padding:clamp(3.25rem,7vw,6rem) var(--pad)}
/* v12: the "Plenty of interest. Few ways in." band now sits directly under the cropped
   hero art, filling the cream slot the poster reserved for its own two-line heading and
   three cards — so it hugs the artwork instead of carrying a full section's top padding.
   v46: the hero's picture now dissolves into the cream (see .hero-img's mask), so the heading
   is pulled up to just under where the illustration is still visible - it reads as arriving out
   of the picture rather than starting a new block below a ruled edge.
   v47: pulled up far enough that the heading and the 201-programs panel actually stand *in* the
   dissolve - the panel's top edge crosses the last of the illustration and its shadow lands on
   it, the way the reference stacks its card over the flowing artwork. Nothing is added: it is
   the section this page already had, moved up over the fade it already had. The amount is a
   clamp so a phone (where the copy sits under the banner and must not be crowded) gets ~20 px
   and a desktop ~72 px. */
#work{
  padding-top:clamp(.6rem,1.5vw,1.4rem);padding-bottom:clamp(2.5rem,5.5vw,4.5rem);
  margin-top:calc(-1 * var(--work-pull,clamp(1.25rem,4vw,4.5rem)));z-index:1;
}
/* v50: the map's sea, carried up into this cream section. The water is cut from the world map's
   own painting, at map pixel scale, so its wave bands are the same weight as the map band's.
   v51: the sea used to lie down - a wide shallow wash along the bottom of the section, its bands
   running left to right, cut from the band itself (assets/sea-bleed.webp, a quarter turn of it).
   It now stands up the left edge, and it is cut from the *full* artwork instead:
   _build/sea_bleed_vert.py takes assets/map-world-master.png - the master the band is a 199-row
   crop of - and keeps columns 0-205, the open ocean, so the strip's top is the painting's own
   surface curling down out of the cream rather than a fade over a cut edge.
   The box spans the section's full height (top and bottom both 0) and takes its width from the
   strip's aspect ratio (273/840), so the waves are never squashed however tall the prose runs,
   and its left edge sits on the viewport edge - ~223 px wide at 1440.
   The slope: the strip is a standing column of the map, and its bands lean 3.7 deg off vertical,
   falling rightwards towards the copy. The fade edge is angled to match (86.3deg rather than
   90deg: the same 3.7 deg), so the wash thins along the grain it is painted with. Opaque at the
   page edge, gone before its own right edge, and both ends are already dissolved in the asset's
   alpha - the top cleared to 10% of the height so no blue sits over the hero - so there is no
   straight cut anywhere. */
.work-sea{
  position:absolute;left:0;top:0;bottom:0;width:auto;aspect-ratio:273/840;z-index:0;pointer-events:none;
  background:url(../assets/sea-bleed-vert.webp) left top/100% 100% no-repeat;
  -webkit-mask-image:linear-gradient(86.3deg,#000 0 26%,rgba(0,0,0,.86) 45%,rgba(0,0,0,.5) 62%,rgba(0,0,0,.18) 78%,rgba(0,0,0,0) 92%);
  mask-image:linear-gradient(86.3deg,#000 0 26%,rgba(0,0,0,.86) 45%,rgba(0,0,0,.5) 62%,rgba(0,0,0,.18) 78%,rgba(0,0,0,0) 92%);
}
#work > .wrap{position:relative;z-index:1}
/* The prose and the 201-programs panel stack below 1100 px, so the panel would sit straight on
   top of the sea and pick up a blue cast through its translucent white. The bleed is a
   two-column flourish only: the map meets the cream edge-to-edge there. */
@media (max-width:1099px){.work-sea{display:none}}
.wrap{max-width:var(--max-w);margin:0 auto;position:relative}
.cols{display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,5vw,4.5rem);align-items:start}
@media (max-width:900px){.cols{grid-template-columns:1fr}}
/* v13: inside this section the 201-programs panel takes the larger share, and the prose
   keeps a bigger distance from it. Scoped to #work because .cols is shared with the
   contact form; below 1100 px the pair stacks so the panel can still run wide and
   shallow instead of collapsing into a tall column. */
#work .cols{gap:clamp(2.25rem,6vw,6.5rem)}
@media (max-width:1099px){#work .cols{grid-template-columns:1fr}}
@media (min-width:1100px){#work .cols{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr)}}
.card{background:rgba(255,255,255,.62);border:1px solid var(--line);border-radius:28px;padding:clamp(1.25rem,2.6vw,2rem)}
.card h3{font-size:1.25rem}
.card .sub{color:var(--ink-soft);font-size:.98rem;margin-bottom:1.1rem}
.chips{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.2rem;padding:0;list-style:none}
.chip{border:1px solid var(--line);background:transparent;border-radius:999px;padding:.45rem .95rem;font-size:.95rem;font-weight:500;transition:background .2s,color .2s,border-color .2s}
.chip:hover{border-color:var(--navy)}
.chip[aria-pressed="true"]{background:var(--navy);border-color:var(--navy);color:#fff}
/* v12: the block sits in the hero's former lower slot. v13: more columns than before so
   the panel is wider than it is tall — 24 in the stacked layout, 28 once the pair is
   side by side (tiles stay ~15-33 px across 1024-1920 px), while the ≤520 px rule below
   drops to 12 so phone tiles stay legible. */
.weave{display:grid;grid-template-columns:repeat(24,1fr);gap:3px;margin-bottom:1.2rem}
@media (min-width:1100px){.weave{grid-template-columns:repeat(28,1fr)}}
/* v14: the tiles are painted rather than printed. Each one carries its own wash strength,
   size, corner roundness and a degree or two of tilt (set per tile in js/main.js), plus a
   soft off-centre highlight, so the grid reads as watercolour instead of a lattice. */
.weave i{
  aspect-ratio:1;
  background-color:var(--tile-off);
  background-image:radial-gradient(120% 120% at 34% 30%,rgba(255,255,255,.45),rgba(255,255,255,0) 62%);
  border-radius:var(--r,14%);
  opacity:var(--wash,1);
  transform:rotate(var(--tilt,0deg)) scale(var(--j,1));
  transition:background-color .35s,opacity .35s,transform .45s;
  transition-delay:calc(var(--i) * 3ms);
}
.weave i.lit{
  background-color:var(--lit);
  opacity:var(--lit-wash,.85);
  transform:rotate(calc(45deg + var(--tilt,0deg))) scale(calc(var(--j,1) * .78));
}
.readout{margin:0;font-weight:500;min-height:3.4em}
.small{font-size:.88rem;color:var(--ink-soft);margin:.6rem 0 0}
.as-of{display:inline-block;font-size:.72em;font-weight:600;letter-spacing:.06em;text-transform:uppercase;background:rgba(34,53,73,.08);color:var(--ink-soft);border-radius:999px;padding:.15em .7em;vertical-align:middle;margin-left:.3em}
.report-btn{margin-top:1.1rem}

/* ---------- contact ---------- */
.contact{background:var(--contact-bg);color:#f3f1ea;padding-top:clamp(3.75rem,8vw,6.5rem)}
/* v15: the blue panel meets the cream map band above it on a wave, echoing the mobile
   band wave. The height is capped (96px) and the copy sits on its own layer, so the
   deepest cream (≈77% of the wave height) never crowds the heading or covers it. */
.contact-wave{
  display:block;position:absolute;top:0;left:0;right:0;
  width:100%;height:clamp(38px,6.6vw,96px);
  color:var(--cream);pointer-events:none;
}
/* v52: index.html only - there the panel arrives under the full-bleed map picture, which
   is not a flat colour, so the trick used on programs.html (paint the wave in the colour
   of the section above) cannot apply: any fill picked would draw a hard edge of its own
   across the watercolour. Left cream, the wave's fill had nothing to merge with either,
   and stood as a blank 44-82 px strip under the picture's straight bottom edge - the
   white gap. So the wave is inverted there instead: navy, flipped so its wavy edge is the
   panel's top, and hung above the section (bottom:100%, no magic offset) so it laps over
   the band's bottom edge. The picture is then cut by the wave itself and the panel runs
   straight up to it: no strip, no seam, and no ruled line under the artwork. Same path and
   same height, so the rhythm at every width is unchanged. */
.contact-after-map .contact-wave{
  top:auto;bottom:100%;color:var(--contact-bg);transform:scaleY(-1);
  /* The wave is a lap now, not a strip: whatever height it has is how far the panel
     reaches up into the picture, so the shared clamp (floor 38px, 6.6vw) is far too
     deep here - at 360px it put the navy 28px into a 111px band and over the bottom of
     "Connected globally.", and it dunked the Brisbane pip at every width. The ramp below
     is the largest that clears both across 320-2000px: measured clearances at 1440 are
     65px under the copy and 32px under the pip's ring, and ~5px at either on a 360px
     phone, where the band is only 111px tall. Still deep enough (>=9px) to cover the
     pale bottom fade baked into the artwork, which is why no hairline creeps back. */
  height:clamp(9px,3.2vw,96px);
}
.contact > .wrap{position:relative;z-index:1}
.contact h2{color:#fff}
.contact p{color:#d6e1ea}
.contact .chip{border-color:rgba(255,255,255,.35);color:#f3f1ea}
.contact .chip:hover{border-color:#fff}
.contact .chip[aria-pressed="true"]{background:#f3f1ea;color:var(--ink);border-color:#f3f1ea}
.step-row h3{font-family:var(--body);font-size:1.05rem;font-weight:700;margin-bottom:.7rem}
.field{display:block;margin-bottom:1rem}
.field span{display:block;font-weight:600;font-size:.95rem;margin-bottom:.35rem}
.field input,.field textarea{width:100%;font:inherit;color:#fff;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.3);border-radius:14px;padding:.75rem 1rem}
.field input::placeholder,.field textarea::placeholder{color:rgba(255,255,255,.55)}
.field textarea{min-height:7.5rem;resize:vertical}
.contact :focus-visible{outline-color:#fff}
.contact .hint{color:#c3d2de;font-size:.9rem;margin:1rem 0 0}

/* ---------- footer (artwork: GEBIFooter.jpg) ---------- */
.site-footer{background:var(--cream)}
/* the strip of icons: full-bleed; `cover` keeps the icons at a readable size on
   narrow screens by cropping the ends instead of squashing them */
.footer-tiles{
  height:calc(100vw * 109 / 2928);
  min-height:2.75rem;
  background:#93b6c9 url(../assets/footer-tiles.png) center/cover no-repeat;
}
/* the cream band with its watercolour ribbon; the asset's cream was normalised to
   var(--cream), so the background colour continues it downwards without a seam */
.footer-body{
  background:var(--cream) url(../assets/footer-wave.png) top center/100% auto no-repeat;
  min-height:calc(100vw * 164 / 2928);
  padding:clamp(2.5rem,5.5vw,4.5rem) var(--pad) clamp(1.5rem,3vw,2.25rem);
}
.footer-inner{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:1.5rem 2rem}
.foot-left{display:grid;gap:1.1rem;justify-items:start}
/* the small logo sits at the footer's bottom-left, on the same line as "Our Work" */
.foot-brand-row{display:flex;flex-wrap:wrap;align-items:center;gap:.55rem clamp(1rem,2.6vw,1.9rem)}
.foot-brand{display:block;height:2.25rem;width:auto;mix-blend-mode:multiply}
.foot-nav{display:flex;flex-wrap:wrap;gap:clamp(.9rem,2.2vw,2rem)}
.foot-nav a{text-decoration:none;font-weight:500;font-size:.98rem;color:var(--ink-soft)}
.foot-nav a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:4px}
.foot-note{margin:0;font-size:.88rem;color:var(--ink-soft)}
/* the channel buttons: X, LinkedIn, YouTube — round, in the footer's own palette */
.foot-social{display:flex;flex-wrap:wrap;align-items:center;gap:.55rem;margin-top:.1rem}
.foot-social a{
  display:inline-flex;align-items:center;justify-content:center;
  width:2.4rem;height:2.4rem;border-radius:999px;
  background:rgba(34,53,73,.08);color:var(--ink);
  text-decoration:none;
  transition:background-color .16s ease,color .16s ease,transform .16s ease;
}
.foot-social a:hover{background:var(--btn-navy);color:var(--cream);transform:translateY(-1px)}
.foot-social a:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.foot-social svg{display:block;width:1.05rem;height:1.05rem;fill:currentColor}

/* ---------- hero: banner layout ----------
   v59: this used to start at 760 px. It now starts at 880, the width where the nav
does too, because the hero's cqw type alone no longer carries the copy: with the two
buttons under the paragraph the desktop column needs more room than a 880 px wide
picture (≈330 px tall, minus the fixed bar) has. Below that width the artwork shows
only its top strip and every word flows under it as ordinary text. */
@media (max-width:880px){
  /* On phones the poster can't be shrunk to legibility, so the hero becomes a
     real banner: the artwork shows only its top strip (clipped behind the fixed
     header), and every word flows below it as ordinary text. */
  .hero{padding-top:3.5rem}
  /* the 1668x624 crop is only ~146 px tall at a phone's width, which reads as a
     sliver; the banner gets a real height and the picture covers it, keeping the
     trail and its hikers in frame. The copy is no longer over the art, so the
     left scrim is replaced by a soft cream fade along the bottom edge only. */
  .hero-art{aspect-ratio:auto;height:clamp(10.5rem,44vw,15rem)}
  .hero-img{
    height:100%;object-fit:cover;object-position:56% 46%;
    /* the box is only ~170-240 px tall here, so the dissolve is shorter: the same long fade
       would eat half the banner */
    -webkit-mask-image:linear-gradient(180deg,#000 0 74%,rgba(0,0,0,.78) 86%,rgba(0,0,0,.34) 94%,rgba(0,0,0,0) 100%);
    mask-image:linear-gradient(180deg,#000 0 74%,rgba(0,0,0,.78) 86%,rgba(0,0,0,.34) 94%,rgba(0,0,0,0) 100%);
  }
  .hero-art::after{background:radial-gradient(120% 160% at -10% 40%,rgba(244,241,234,.55) 0%,rgba(244,241,234,0) 62%)}
  .hero-copy{position:static;padding:1.5rem var(--pad) 3rem}
  .hero-h1,.hero-sub{position:static;height:auto;margin:0 0 1.05rem}
  .hero-h1 br,.hero-sub br{display:none}
  .hero-h1{font-size:clamp(1.95rem,8.6vw,2.7rem);line-height:1.04}
  .hero-sub{font-size:.9rem;line-height:1.5;max-width:none}
  /* the copy is ordinary text down here, so the buttons leave cqw for real type sizes */
  .hero-actions{gap:.6rem;margin-top:.15rem}
}

/* ---------- narrow screens: swap the band artwork for a real accordion ---------- */
@media (max-width:760px){
  /* the tile strip keeps a usable height and crops to its middle instead of
     shrinking to a sliver */
  .footer-tiles{min-height:3rem}
  .footer-body{padding-top:2.75rem}
  .footer-inner{align-items:stretch;gap:1.6rem}
  /* the header logo drops to 38 px here, and the footer's stays smaller still */
  .foot-brand{height:1.75rem}
  /* the world map band is only ~130 px tall at a phone's width, so the pips shrink to read
     as markers rather than buttons, and the heading takes a real type size instead of the
     2.8 cqw the desktop band uses */
  .map-head{font-size:clamp(.95rem,3.6vw,1.2rem);line-height:1.28}
  .map-dot::before{width:7px;height:7px;box-shadow:0 0 0 2px rgba(255,255,255,.9),0 0 0 3px rgba(34,53,73,.16)}
}

/* ---------- small phones ---------- */
@media (max-width:520px){
  /* two pills no longer fit side by side, so they stack as full-width rows with the
     arrow pushed to the far end, the way a menu reads */
  .hero-actions{flex-direction:column;align-items:stretch}
  .hero-btn{justify-content:space-between}
  /* v12: 18 columns of tiles get too small at a phone's card width; 12 keeps each
     tile around 20 px across at a 390 px viewport. */
  .weave{grid-template-columns:repeat(12,1fr)}
  .card{border-radius:22px}
  .foot-bar{flex-direction:column;align-items:flex-start}
}
