/* ==========================================================================
   Awake Festival — full site.

   Measured off the 1600 × 5000 design artboard. Type is fixed per breakpoint,
   as the type spec is written; only the artwork scales.
   ========================================================================== */

/* --- Fonts --------------------------------------------------------------- */

/* One style of GT Alpina, the only one the page sets — and so the only one to
   license. woff2 alone: every browser that runs the rest of this page reads it. */
@font-face {
  font-family: "GT Alpina Condensed";
  src: url("../fonts/GT-Alpina-Condensed-Light.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
}

/* --- Tokens -------------------------------------------------------------- */

:root {
  --sage: #94A6A8;
  --ink: #FFFFFF;
  /* Sampled off the artboard: the rules render 175,188,190 over the sage,
     which is white at a quarter. */
  --rule: rgba(255, 255, 255, 0.25);
  --page: 1600px;        /* the artboard's width — content stops growing here */
  /* The ground every expanded panel sits on, and the colour the photographs are
     pulled toward — so a panel with a picture and one without read as the same
     kind of thing. tools/panel-treatment.py holds the matching value. */
  --panel: #2D4236;
  --ink-chosen: #444444;   /* type on a white pill: dark grey, not black */
  --hero-ground: #000000;

  /* Read off AF_Website.idml — the artboard is 1600pt wide, so a point there is
     a pixel here. InDesign tracking is thousandths of an em: 20 is 0.02em.
     Leading is absolute in the file, so the display faces carry a ratio (40 on
     42 is 0.95) rather than the body default. */

  /* One unit, and everything is a multiple of it. The artboard's frames carry
     a 30pt inset, and the gaps it leaves between blocks measure 60, 90, 120 and
     300 — all multiples of that same 30. So the gutter is the unit, and the
     scale below is the only source of spacing on the page.

     The unit is fixed per breakpoint rather than fluid: the design pins its
     content to the edge at a constant inset, and a percentage there would make
     the margin grow with the window, which is what put the hero's corner out. */
  --unit: 30px;
  --gutter: var(--unit);
  --s-half: calc(var(--unit) / 2);    /*  15 */
  --s1: var(--unit);                  /*  30 */
  --s2: calc(var(--unit) * 2);        /*  60 */
  --s3: calc(var(--unit) * 3);        /*  90 */
  --s4: calc(var(--unit) * 4);        /* 120 */
  --s10: calc(var(--unit) * 10);      /* 300 */

  /* Type, in the roles the design actually uses (pages 4 and 5, 17 Sep). The
     boards draw about four sizes each, and every piece of text here takes one
     of these rather than a size of its own, so the sizes cannot drift apart.
       display  GT Alpina: section and day titles, the lineup names, sponsors
       title    GT Alpina: programme rows, map markers and key, DAY 1
       body     Garamond: About copy, bios, location copy, day hours, disciplines
       lead     Garamond: the nav and the hero dates — desktop's one step up
       small    Garamond: times, venues, labels, pills, filters, links
     Desktop 42 / 24 / 24 / 18 / 16; tablets and phones set their own below. */

  --track-pill: 0.015em; /* tracking 15 on pills, filters, Book Your Spot */
  --cloud-dur: 40s;      /* one pass of a cloud bank across the hero */
  --pill-h: 44px;        /* page 4's pills; page 5's are 41.5 */
  /* The gutter between two programme columns. It was nothing: the columns were
     percentages laid edge to edge, and the space between them was only what the
     words in the left one happened to leave — so every string had a width at
     which it exactly filled its column and closed on its neighbour, and each of
     them had one (24 Sep). Declared here, it is a quantity rather than an
     accident, and it is taken out of the end of each column rather than added
     between them, so every column still starts where the artboard puts it.
     Eight is the most the design will take: past it, Children's Yoga & Movement
     breaks in two at 1024 and Mandala Gong Bath at the phone board's own 393. */
  --col-gutter: 8px;

}

/* --- Reset --------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, p, ul, li, figure { margin: 0; padding: 0; }
ul { list-style: none; }
img { max-width: 100%; display: block; }

body {
  background: var(--sage);
  color: var(--ink);
  font: var(--copy);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* Anchors ease rather than jump. scroll-padding keeps the landing clear of the
   fixed nav, so a section never arrives underneath it. */
/* No padding at the landing: a section arrives with its top against the top of
   the window, the way an opened programme row does. The 96 here was keeping the
   landing clear of the nav, but that left the band above still showing — the
   Location green sitting over Contact (Arian, 24 Sep). js/site.js parks the nav
   for the journey instead, which is the same answer bringIntoView came to. */
html { scroll-behavior: smooth; scroll-padding-top: 0; }

/* --- Cursor -------------------------------------------------------------- */
/* The pointer is a little eye — the same four layers as the coming-soon page,
   tracking, glancing and blinking. js/cursor.js builds it and drives it.

   The system cursor is only hidden once the eye is actually live: the class
   goes on from script, so if anything fails you keep a real pointer. Geometry
   here, behaviour there. */

.cursor {
  position: fixed;
  left: 0;
  top: 0;
  width: var(--cursor-size, 18px);
  height: var(--cursor-size, 18px);
  z-index: 90;
  pointer-events: none;               /* it must never eat a click */
  opacity: 0;
  transition: opacity .25s ease, width .25s ease, height .25s ease;
  will-change: transform;
}
.cursor.is-live { opacity: 1; }
.cursor.is-over {
  width: var(--cursor-hover-size, 24px);
  height: var(--cursor-hover-size, 24px);
}

.cursor__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.cursor__iris { will-change: transform; }

/* Clipped to the sphere, exactly as on the coming-soon page — centre
   50.1%/50.0%, diameter 95.4% — so a parked lid never shows outside the eye. */
.cursor__lids {
  position: absolute;
  inset: 0;
  -webkit-clip-path: circle(47.7% at 50.1% 50%);
  clip-path: circle(47.7% at 50.1% 50%);
}
.cursor__lid { will-change: transform; }

html.has-eye-cursor,
html.has-eye-cursor * { cursor: none; }

:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }
/* The menu panel is a holder for the focus, not something to act on, so it
   takes no ring of its own. Everything inside it still shows one. */
.menu:focus, .menu:focus-visible { outline: none; }

.label {
  font: var(--meta);
  font-style: italic;
  font-size: var(--type-small);
}
/* One rule for every section label — About, Buy Tickets, The Program,
   Location, Contact, Sponsored by, More: italic, and no colon after it. The
   boards are not consistent about it; this is the rule (Arian, 18 Sep). */
.label--centre { text-align: center; }

/* --- Nav ----------------------------------------------------------------- */
/* Fixed, so it stays with you down the page. Transparent over the dark hero;
   js/site.js adds .is-stuck once the hero is behind you, which brings in the
   sage ground — without it, white nav type would collide with white row type. */

.nav {
  position: fixed;
  inset: 0 0 auto;
  transition: transform .35s ease, background-color .4s ease, backdrop-filter .4s ease;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  padding: var(--s-half) var(--gutter);
  max-width: none;

  /* Frosted rather than solid: the collage passes under it and stays legible.
     -webkit- prefix included for Safari, which still needs it.
     Blur only. It had saturate(140%) as well, which boosts whatever is behind
     the bar — so even over plain sage the ground behind it came out a different
     colour from the page around it, and far more so over a photograph or a
     coloured panel. And no rule along its foot (Arian, 17 Sep). */
  background: rgba(148, 166, 168, 0.55);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
}

.nav__set { display: flex; gap: 14px; font: var(--meta); font-size: var(--type-lead); }
.nav__set a { padding: 11px 6px; margin: -11px 0; }   /* a 44px target */
.nav__set--right { justify-self: end; }
.nav__set a { transition: opacity .3s ease; }
.nav__set a:hover { opacity: .6; }

.nav__mark {
  justify-self: center;
  white-space: nowrap;
  font: var(--head);
  text-transform: uppercase;
  letter-spacing: 0.02em;

  /* Both states occupy the same cell, so the swap doesn't shift the row. */
  display: grid;
  place-items: center;
  min-height: 44px;
  min-width: 44px;
}
.nav__wordmark, .nav__eye { grid-area: 1 / 1; transition: opacity .35s ease; }
.nav__name { display: none; }
.hero__wordmark { display: none; }   /* desktop only, below */
.nav__eye { width: 26px; height: 26px; opacity: 0; }

.nav.is-stuck .nav__wordmark { opacity: 0; }
.nav.is-stuck .nav__eye { opacity: 1; }

/* Slid away while scrolling down, back on the way up (js/site.js). */
.nav.is-hidden { transform: translateY(-100%); }

/* Denser once you are past the hero, so it holds up over the programme rows. */
.nav.is-stuck {
  background: rgba(148, 166, 168, 0.82);
  -webkit-backdrop-filter: blur(22px);
  backdrop-filter: blur(22px);
}

/* --- Eye icon ------------------------------------------------------------ */
/* The sclera and iris layers stacked in a circle. Reuses assets the page has
   already loaded rather than shipping another file. */

.eye-icon {
  display: inline-block;
  width: 1.15em;
  height: 1.15em;
  flex: none;
  border-radius: 50%;
  vertical-align: -0.22em;
  background-image: url("../assets/eye/eye-iris-400.webp?v=b0a5554b"),
                    url("../assets/eye/eye-sclera-400.webp?v=0d926821");
  background-size: 106% 106%, 150% 150%;
  background-position: center, center;
  background-repeat: no-repeat;
}

/* --- Eyes in the artwork -------------------------------------------------- */
/* Cut out by tools/extract-eyes.py and laid back over the piece, positioned from
   the manifest it writes, so they hold their place at any size. js/site.js moves
   them. */

.art-iris {
  position: absolute;
  height: auto;
  transform: translate(-50%, -50%);
  transform-origin: 50% 50%;
  will-change: transform;
  pointer-events: none;
}

/* The hero's iris is in the markup rather than built by js/site.js, so that the
   eye is there in the first paint. Its place has to be here too: the file is
   96px square, and until the script had fetched the manifest and written the
   inline styles the browser drew it at that size, in the corner of the artwork
   — a large eye adrift for as long as the script took (Arian, 16 Sep). These
   are the manifest's own figures: assets/art/hero-eyes.json. The script then
   writes the same numbers. The layered collage is one artwork at every width,
   so the eye keeps one place in it. */
.hero__iris { left: 49.770%; top: 56.103%; width: 2.347%; }
@media (max-width: 768px) {
  .hero__iris { left: 50.270%; top: 54.627%; width: 4.780%; }
}

/* --- Hero ---------------------------------------------------------------- */
/* The collage is the hero. .hero__art is the positioning context so the live
   eye can be laid over the one painted into the artwork, and so a full hero
   image can replace the collage later without touching anything else. */

/* --- Viewport sections ---------------------------------------------------- */
/* The page reads as a series of screens. svh rather than dvh: dvh changes as
   the mobile URL bar hides, which would reflow every section mid-scroll.
   min-height, never height, so nothing is ever clipped on a short window. */

.hero, .location {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  align-content: center;
}

/* The hero stops short of a full screen by exactly enough to cut the lineup
   marquee in half at the fold — the cue that there is more below. js/site.js
   measures the shortfall; this is the fallback until it does.

   A fixed height, not a minimum, because the artwork is what gives way: the
   collage is capped by the room left over rather than by a width, so the whole
   thing is one screen at any window. The floor stops it collapsing to nothing
   on a very short one. */
.hero {
  /* The fallback lives in the var() call, not in a declaration here — declaring
     it on .hero would shadow the measured value js/site.js sets on :root. */
  height: calc(100vh - var(--fold-peek, var(--fold-peek-guess)));
  height: calc(100svh - var(--fold-peek, var(--fold-peek-guess)));
  min-height: 480px;
  position: relative;
  /* One track, the artwork — the meta is lifted out of flow into the margins.
     minmax(0, …) rather than a bare 1fr: a 1fr track floors at min-content, so
     the collage would push the hero past a screen instead of scaling down. */
  grid-template-rows: minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
  align-content: stretch;
  /* The top of the collage clears the nav by one unit, so nothing of it starts
     behind the bar: the nav is its padding, the 44px mark cell and its 1px rule.
     One unit at the foot sets the collage's base level with the dates and the
     pill, which sit one unit off the band. */
  padding: calc(var(--s-half) * 2 + 45px + var(--s1)) var(--gutter) var(--s1);
  text-align: center;
}

/* On the artboard the dates and the ticket pill sit out in the margins level
   with the foot of the collage, not stacked underneath it. That is what gives
   the artwork the whole screen. */
/* One unit from the right edge and one from the band, so the corner the dates
   and the pill leave is square. It was a fixed inset against a percentage
   bottom, which grew with the window and never matched.

   These two are corner furniture, not part of the 1600 column: they hug the
   edge at every width, which is the only way the square survives past 1600.
   The artwork between them is what caps and centres. */
.hero__meta {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: var(--s1);
}

/* Mobile only — see the note in the markup. */
.hero__mark { display: none; }

/* Page 4 draws the collage 1554 wide on its 1600 board, centred 448 down its
   900-high first screen — larger than the band it sits in, which runs from the
   foot of the nav (77) to the marquee (797), so the palm tops and the flowers'
   feet are cropped by the band (fitted against a render of the board's own
   pieces, 17 Sep; the site had drawn it whole, 1190 wide).

   So the band clips, and the artwork is sized as the board sizes it: 97.1% of
   the width. On a wide, short window that would crop harder than the design
   ever does, so its height is capped at the board's own ratio to the band —
   866 to 720 — which the container's height units express directly. */
/* A stacking context of its own, so the collage's layers — which run to z-index
   10 — stay inside the band and pass under the lineup, as they did when the
   artwork's own transform was doing this job. isolation, not a transform: the
   clouds need the artwork to stay part of its parent's context. */
.hero__art {
  isolation: isolate;
  position: absolute;
  top: calc(var(--s-half) * 2 + 45px);   /* the nav's foot */
  left: 0; right: 0; bottom: 0;
  overflow: hidden;
  container-type: size;
}
/* The layered artwork is drawn against a 3500 x 2000 box with more air around
   the collage than the flattened export had, so the box is 1.004 of the old one
   and sits 0.0069 of its width higher — measured by registering the two, not
   guessed — and the collage itself lands exactly where page 4 draws it. */
/* The artwork's box, and the band the clouds cross, read the same two numbers,
   so a cloud stays the artwork's size however the window changes. */
.hero__sky, .hero__canvas {
  --canvas-w: min(97.514cqw, 216.72cqh);  /* 1554 / 1600, and 866 / 720 × the ratio */
  --canvas-h: calc(var(--canvas-w) / 1.75);
}

/* Centred by its margins rather than by a transform: a transform would make the
   artwork a stacking context of its own, and the clouds — which live in the
   band now, so they can cross the whole window — could no longer sit between
   its layers. */
.hero__canvas {
  position: absolute;
  left: 50%;
  top: 51.79%;                           /* the collage's own centre, as page 4 has it */
  width: var(--canvas-w);
  height: var(--canvas-h);
  margin-left: calc(var(--canvas-w) / -2);
  margin-top: calc(var(--canvas-h) / -2);
}

/* The clouds' own band: the width of the window, and clipped at its edges, so
   they drift off the screen rather than off the artwork. It runs the height of
   the hero band, which the clouds never reach. */
.hero__sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Phones show page 5's flat arrangement, and it fills the band. Above the
   breakpoint the <img> holds a blank, and the layers below are what is drawn. */
.hero__collage { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
@media (min-width: 769px) { .hero__collage { display: none; } }

/* --- While the artwork is on its way -------------------------------------- */
/* A stand-in for the picture, thirty pixels across and inlined, so it is there
   in the very first paint and costs no request of its own — under 300 bytes
   each.

   On a slow line the band showed the Buddha's eye alone on an empty ground for
   four seconds, and then painted the picture down the screen in strips, with
   the three small eyes arriving after it. An eyeball floating on nothing reads
   as a fault rather than as loading (Arian, 21 Sep, on an iPad). Now the shape
   of the artwork is there from the start, softly, and the real one fades in
   over it.

   js/site.js takes .is-art-loading off once the artwork has actually decoded,
   or after six seconds if something has gone wrong, so the band can never be
   left blurred. */
:root { --art-lqip: url("data:image/webp;base64,UklGRhgBAABXRUJQVlA4IAwBAACQBgCdASoeABwAPqlEm0omJCKht/qoAMAVCWwAnTLiMNGFe8xr8A06+AQmmK8099q3pY4Uh/AYxWs+X6AAAP6zQ84rU5RQPQXvhsMkNDHIr8kLy5inyP5xej7K7W0325B24ycPIhOYgjL2WtbD5NBqf1Pve102iVQIBSlaSYeE6HL3gucv/vK12VxAZEGxDtoQqecJ+RXkC4phAJRnB4GXdr/SCTZxd3jsoO/uVTELdHNl/I28eRok1v9vYntpnG/QdFSCe9xylG2YIeWy+9N0VE8M1aTB37nmZ1RotZnQyg+quTuRjFTu3t4qlL9G+meuTA760osNsYYQpKmNHhbTjnO0VphZpp7QSgAA"); }
@media (min-width: 769px) { :root { --art-lqip: url("data:image/webp;base64,UklGRtoAAABXRUJQVlA4IM4AAABwBgCdASooABcAPrlMn0unJCKhsBVaqOAXCUAWnsSTu/4IegylZItwzs5/lgb2KQIUUrHOCpJOoLvwrsAA/ouqqQlEcPmbLsI+ex/rn0Lqw3gCgvaZHsOv79AkNSjapxLo8voijQGZw40yTV8hHnCfD8tTCOPjZz8b0qaBUkW5tdOoDgLA9PMC7na0WhXDgETw1H3XO/JIK1AFseuJHsFNd0FXzeQUxSsZGdbvI9zIKvBNPngzzTps1Lg2hCPBkHZDnbCBbSi0PIV7cwJUAA=="); } }

.hero__canvas::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--art-lqip);
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity 500ms ease;
  pointer-events: none;
}
.hero__canvas.is-art-loading::before { opacity: 1; }

/* The eyes belong to the picture. On their own they are a floating eyeball, so
   they arrive with it rather than four seconds ahead of it. */
.hero__canvas .art-iris { transition: opacity 400ms ease; }
.hero__canvas.is-art-loading .art-iris { opacity: 0; }

/* And the picture arrives whole. A WebP cannot be interlaced, so left to itself
   it paints down the screen in strips over about three seconds on a slow line —
   and one of those strips is the Buddha's eye with no iris in it yet. Held back
   until it has decoded, it crossfades out of the stand-in in one movement.
   Nothing is delayed that was not already being waited for: the stand-in is
   showing the same composition underneath the whole time. */
.hero__collage { transition: opacity 350ms ease; }
.hero__canvas.is-art-loading .hero__collage { opacity: 0; }

/* --- The collage, in its layers ------------------------------------------ */
/* Chris's export (20 Sep): ten pieces positioned against the 3500 x 2000 box as
   percentages, so the whole thing scales with the canvas. Bottom to top: the
   far gull flock, the back plate, the two cloud banks, the middle plate, the
   hanging eye-flowers, the Buddha's head, his third eye, the two shells and the
   front plate. Only the flock, the clouds and the hanging pieces move.

   They are painted by this stylesheet rather than carried in the markup, so a
   phone — which shows page 5's own arrangement instead — never fetches the
   2MB the wide artwork comes to. Each piece carries its own ratio, since a
   background gives the box no size of its own. */
@media (min-width: 769px) {

  .hero__layer, .hero__rail, .hero__rail .hero__cloud {
    position: absolute;
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }

  /* The distant flock is deliberately behind the cloud banks rather than where
     the stack would put it. Its sheet holds half a wingbeat, nine frames down;
     alternate plays it out and back for the whole beat. */
  /* The flock's sheet holds half a wingbeat, seventeen frames down (Chris's
     second export, 20 Sep — the same beat with twice the frames, so the wings
     read as turning rather than clicking). The frame is a window on it and the
     sheet slides behind that window — a transform, which the compositor can do
     by itself, where stepping background-position made the browser repaint a
     band the width of the sky on every frame. */
  .hero__flock {
    z-index: 0;
    left: 17.279%; top: 5.790%; width: 77.521%;
    aspect-ratio: 1400 / 244;
    overflow: hidden;
  }
  .hero__flock::before {
    content: '';
    position: absolute;
    left: 0; top: 0; right: 0;
    height: 1700%;
    background-image: url("../assets/art/hero/gulls.webp?v=40a4852f");
    background-size: 100% 100%;
    animation: hero-flap 1.2s steps(16) infinite alternate;
  }

  .hero__back {
    z-index: 1; left: 4.000%; top: 1.300%; width: 92.714%;
    aspect-ratio: 3245 / 1748;
    background-image: url("../assets/art/hero/back-1623.webp?v=98825b1b");
  }

  /* Each cloud rides a rail two windows wide, carrying two copies of itself one
     window apart. The rail travels exactly one window on a linear loop, so the
     second copy takes the first one's place as it leaves — a drift with no seam,
     and one that runs from edge to edge rather than stopping where the artwork
     ends (Arian, 20 Sep). The cloud keeps the artwork's measure: its width is
     the share of the collage the board gives it, and it hangs at the height the
     board hangs it, whatever the window does.

     The rail starts at the window's left edge and the artwork's own edge is half
     the difference in from there, so a cloud keeps the place on the collage the
     board gives it. The second copy sits one window to the right, off the
     screen, and takes the first one's place when the loop comes round: with
     both the copy behind and the copy ahead out of sight at that moment, the
     restart cannot be seen. */
  .hero__rail {
    position: absolute;
    z-index: 2;
    top: calc(51.79cqh - var(--canvas-h) / 2 + var(--canvas-h) * 0.363);
    left: 0;                              /* the sky is the window, so this is its edge */
    width: 200cqw;
    height: 0;
  }
  .hero__rail .hero__cloud {
    top: 0;
    aspect-ratio: 752 / 428;
    width: calc(var(--canvas-w) * var(--cloud-w));
    left: calc((100cqw - var(--canvas-w)) / 2 + var(--canvas-w) * var(--cloud-x));
  }
  /* The second copy stands one window ahead of the first, in the direction of
     travel, and takes its place as the loop comes round. */
  .hero__rail--r .hero__cloud--2 {
    left: calc((100cqw - var(--canvas-w)) / 2 + var(--canvas-w) * var(--cloud-x) + 100cqw);
  }
  .hero__rail--l .hero__cloud--2 {
    left: calc((100cqw - var(--canvas-w)) / 2 + var(--canvas-w) * var(--cloud-x) - 100cqw);
  }
  /* Each cloud carries its own travel rather than riding a rail two windows
     wide: four small pieces for the compositor to move instead of two very
     large ones, which is easier on a tablet. The pair still sits a window
     apart, so the loop repeats on itself exactly. */
  .hero__rail .hero__cloud { animation: hero-drift var(--cloud-dur) linear infinite; }
  .hero__rail--l { --cloud-w: 0.21486; --cloud-x: 0.06743; --drift: 100cqw; }
  .hero__rail--l .hero__cloud { background-image: url("../assets/art/hero/cloud-l.webp?v=44e8c4e9"); }
  .hero__rail--r { --cloud-w: 0.21514; --cloud-x: 0.71686; --drift: -100cqw; }
  .hero__rail--r .hero__cloud { background-image: url("../assets/art/hero/cloud-r.webp?v=c473f7ed"); }

  .hero__mid {
    z-index: 3; left: 22.000%; top: 20.550%; width: 55.943%;
    aspect-ratio: 1958 / 1552;
    background-image: url("../assets/art/hero/mid-979.webp?v=d5662018");
  }

  /* The hanging pieces turn about the top of their own thread, which is what
     makes them read as hanging rather than wobbling. Four periods, none a
     multiple of another, and negative delays, so they never fall into step. */
  .hero__hang { transform-origin: 50% 0; }
  .hero__eyefl--l {
    z-index: 4; left: 21.629%; top: 24.650%; width: 3.686%;
    aspect-ratio: 129 / 762;
    background-image: url("../assets/art/hero/eyefl-l.webp?v=b81f69d1");
    animation: hero-swingB 2.9s ease-in-out infinite alternate; animation-delay: -1.4s;
  }
  .hero__eyefl--r {
    z-index: 4; left: 74.371%; top: 25.800%; width: 3.714%;
    aspect-ratio: 130 / 739;
    background-image: url("../assets/art/hero/eyefl-r.webp?v=b8db4ba9");
    animation: hero-swingA 2.5s ease-in-out infinite alternate; animation-delay: -.4s;
  }

  .hero__head {
    z-index: 5; left: 41.800%; top: 41.350%; width: 16.114%;
    aspect-ratio: 564 / 747;
    background-image: url("../assets/art/hero/head.webp?v=5dfabd1c");
  }

  /* The third eye is not in the export: the eyeball is cut from the flat
     artwork and the iris laid over it, so it can look about. Both sit above the
     head and under the shells, where the artwork's own layer was. */
  .hero__eyeball {
    z-index: 6; left: 47.626%; top: 52.351%; width: 4.288%;
    aspect-ratio: 1;
    background-image: url("../assets/art/hero/eyeball.webp?v=bd413948");
  }

  .hero__shell--l {
    z-index: 8; left: 27.714%; top: 23.450%; width: 4.971%;
    aspect-ratio: 174 / 947;
    background-image: url("../assets/art/hero/shell-l.webp?v=44d36030");
    animation: hero-swingA 3.1s ease-in-out infinite alternate;
  }
  .hero__shell--r {
    z-index: 9; left: 66.914%; top: 23.450%; width: 5.086%;
    aspect-ratio: 178 / 936;
    background-image: url("../assets/art/hero/shell-r.webp?v=a18b54a8");
    animation: hero-swingB 2.7s ease-in-out infinite alternate; animation-delay: -.9s;
  }
  .hero__front {
    z-index: 10; left: 40.971%; top: 33.250%; width: 17.657%;
    aspect-ratio: 618 / 1264;
    background-image: url("../assets/art/hero/front.webp?v=127b0256");
  }
}

/* The plates come in two sizes, and which one is wanted depends on how wide the
   artwork is actually drawn as well as how fine the screen is — not on the pixel
   ratio alone, which is all image-set() asks. An iPad was taking the 3245-wide
   plate to draw it 740 across: four times the memory for nothing, and the whole
   page stuttered for it (Arian, 20 Sep). This window, or half of it on a screen
   twice as fine, is where the larger plate starts to earn its place. */
@media (min-width: 769px) {
  @media (min-width: 1700px), (min-resolution: 2x) and (min-width: 1100px) {
    .hero__back { background-image: url("../assets/art/hero/back.webp?v=58878fe1"); }
    .hero__mid  { background-image: url("../assets/art/hero/mid.webp?v=2f24295e"); }
  }
}

/* The sheet slides behind the frame; the frame holds still. Sixteen steps of
   one seventeenth, which is the sheet's own grid. */
@keyframes hero-flap { from { transform: translateY(0); } to { transform: translateY(-94.118%); } }
/* One window's travel, in whichever direction its bank is going. The distance
   is carried in a property so the keyframe itself needs no container units. */
@keyframes hero-drift { to { transform: translate3d(var(--drift), 0, 0); } }
@keyframes hero-swingA { from { transform: rotate(-3.4deg); } to { transform: rotate(3.4deg); } }
@keyframes hero-swingB { from { transform: rotate(2.9deg); } to { transform: rotate(-2.9deg); } }

/* Each iris sits directly over its own layer: the third eye between the head
   and the shells, the little figure's above the front plate, and the monkeys'
   two just above the middle one. */
.hero__canvas .art-iris { z-index: 7; }
.hero__canvas .art-iris[data-i="1"] { z-index: 11; }
.hero__canvas .art-iris[data-i="2"],
.hero__canvas .art-iris[data-i="3"] { z-index: 4; }

/* Nothing runs while the hero is off screen: js/site.js sets this once the band
   has left the window, and takes it off when it comes back. */
.hero__canvas--still * { animation-play-state: paused !important; }

/* The artwork holds still until the page's own scripts are running.
   The welcome is put up by js/welcome.js, which is deferred: the markup and the
   stylesheet can be in and painted long before it arrives. On a slow line that
   meant the birds were flapping and the clouds drifting for a second or two,
   and only then did the welcome drop over the top of them — the opening played
   second (Arian, 22 Sep). Paused here by default, so the first thing that moves
   on this page is the mark drawing itself.
   js/welcome.js lifts this the moment it has decided, whether it is playing the
   welcome or standing down; js/site.js lifts it too, so one script failing to
   load cannot leave the artwork frozen. Both failing leaves a still hero on a
   page that otherwise reads, which is the right way round to fail. */
html:not(.art-live) .hero__canvas * { animation-play-state: paused !important; }

.hero__meta {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 20px;
}

/* The pill is a filled shape, so its box edge is what you see; the dates are
   text, and what you see is the ink sitting a little inside the line box. The
   negative margin gives that back, so both corners read the same unit. */
.hero__dates {
  font: var(--meta);
  font-size: var(--type-lead);
  line-height: 1;
  margin-bottom: -3px;
}


/* --- Featuring ----------------------------------------------------------- */

/* No padding of its own: the band is the marquee's rules and its inner
   padding, nothing more, so its measured height is exactly what the hero gives
   up and the two come to one screen between them. */
.featuring { padding: 0; }

/* The band's height as js/site.js will measure it once the fonts are in, per
   breakpoint. The hero starts from this rather than a single guess, so when the
   measured value lands nothing moves — an 84px guess against a 93-104px band
   pushed the band down the screen as the page loaded. */
:root { --fold-peek-guess: 104px; }   /* the band measures 103.7 on the board, with its two rules */

@media (max-width: 768px) { :root { --fold-peek-guess: 103px; } }   /* page 5's own: the hero ends 749, the screen 852 */

.marquee {
  overflow: hidden;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 29px 0;          /* with the two rules, the artboard's 104pt band */
}

.marquee__track {
  display: flex;
  width: max-content;
  align-items: baseline;
}

/* Driven by js/site.js rather than a CSS animation: hovering eases the speed
   down instead of stopping it dead, and duration cannot be interpolated in CSS
   without the animation restarting. */

.marquee__item { display: flex; align-items: baseline; gap: 14px; padding-right: 40px; }

.marquee__lead {
  font: var(--meta);
  font-size: var(--type-lead);
  font-style: italic;
  white-space: nowrap;
  /* The word rides in the line with the names, which means it shares their
     baseline — and a baseline puts a small face low, because what the eye
     centres is the ink, not the line. The board has "Featuring" centred in the
     band with the names, so it is lifted by half the difference between the two
     faces' capitals: GT Alpina Condensed Light stands 0.681 of its size, Adobe
     Garamond Italic 0.661, both measured in the browser and constant at every
     size. It had been nudged 16 the other way on the board's layout, which put
     it 24 below the band's centre (Arian, 20 Sep). */
  position: relative;
  top: calc((var(--type-lead) * 0.661 - var(--type-display) * 0.681) / 2);
}

.marquee__name {
  font: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.marquee__what { font: var(--meta); font-size: var(--type-body); white-space: nowrap; }

/* --- About --------------------------------------------------------------- */
/* Not a viewport section. The artboard puts 130 between the ticket pills and
   The Program — 24 clear, then the 75 hairline, then 31 to the label — and
   holding this block to a screen made that gap grow with the window. Its own
   rhythm is the right height. */

.about { padding: var(--s3) var(--gutter) 0; text-align: center; }
.about__body, .about > .label { max-width: var(--page); margin-inline: auto; }
.about .label { margin-bottom: var(--s2); }

.about__body { max-width: min(34.83em, 100%); margin: 0 auto var(--s2); }
/* The copy frame is 836 of page 4's 1600, where the body face is 24 — so 34.83
   of its own ems, which is what actually sets the shape of the paragraph. As a
   share of the window (58.125vw) it was 930 at 1600 and the lines broke in the
   wrong places; as a fixed 930 it filled 93% of a 1000-wide window (18 Sep).
   Tied to the type, the block keeps page 4's shape at every width, and narrows
   with the face where the breakpoints step it down. */
.about__lead { font: var(--copy); }

.about__heading {
  margin: var(--s-half) 0 var(--s1);
  font: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.about__copy { font: var(--copy); }
/* The About was one paragraph until Arian's copy of 22 Sep, which is five. The
   same step the Location copy takes between its paragraphs, so the two blocks
   of running text on the page are set the same way. */
.about__copy + .about__copy { margin-top: 1.2em; }
.about__place { text-decoration: underline; text-underline-offset: 3px; }

/* Equal columns, so the gap between the pills is centred on the page and the
   hairline runs down the middle of it. Left to their content the two pills are
   different widths, and the axis grazed the edge of the wider one. */
.tickets {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  justify-content: center;
  gap: var(--s-half);
  margin-top: var(--s-half);
}
.tickets .pill { justify-content: center; }
/* Side by side the pair is 338 wide, which a 320 phone cannot hold, so they
   stack there — still the same width as each other. */
@media (max-width: 359px) { .tickets { grid-auto-flow: row; } }

/* Read by screen readers and search engines, not drawn: the page's one h1,
   where the visible name is the wordmark. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Not found ----------------------------------------------------------- */
/* Here rather than in a <style> block on 404.html: the CSP in _headers sets
   style-src 'self', so an inline block would be dropped. */
.nf {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: var(--gutter);
  text-align: center;
}
.nf a { font: var(--head); text-transform: uppercase; letter-spacing: 0.02em; }
.nf a:hover { opacity: .6; }

/* Pills, as the boards draw them (17 Sep): 44 tall on desktop — the 16px label's
   20.8 line, 10.6 above and below, and the rule — with 21.5 each side, which is
   what page 4's filters and Buy Tickets come to around their words. They had
   12px of padding and stood 47. Where a pill carries the eye, the board anchors
   a 19.2 image in the line and follows it with two spaces, about 8. */
.pill .eye-icon { width: 19.2px; height: 19.2px; }   /* the board's anchored image */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--pill-h);
  font: var(--meta);
  letter-spacing: var(--track-pill);
  padding: 0 21.5px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  transition: background .3s ease, color .3s ease;
}
.pill:hover { background: var(--ink); color: var(--sage); }
.pill[aria-disabled="true"] { opacity: .6; pointer-events: none; }

/* Every pill on the page reads the same: no fill, a white outline, and the fill
   arriving on hover (Arian, 23 Sep). Buy Tickets and Book Your Spot were filled
   from the start as the primary action; the modifier stays on them in the markup
   so it can be given a treatment again without hunting for them, but it no
   longer sets one. */
.pill--solid { background: none; color: inherit; border-color: var(--ink); }
.pill--solid:hover { background: var(--ink); color: var(--sage); }

/* One exception: the hero's Buy Tickets stays white (Arian, 23 Sep). It is the
   first thing on the page and the only filled button on it. */
.hero__meta .pill--solid { background: var(--ink); color: var(--sage); }
.hero__meta .pill--solid:hover { opacity: .85; background: var(--ink); color: var(--sage); }

/* The words sit with their capitals on the pill's centre line, as InDesign sets
   them. A browser centres the line box instead, and where Garamond's capitals
   fall inside that box is different in Safari and in Chrome — so padding tuned
   in one left the words high or low in the other (18 Sep). Trimmed to the
   capitals and the baseline, the span is just the capitals' height, and the
   pill centres that, the same in every browser that knows text-box. */
.pill__text { display: block; white-space: nowrap; text-box: trim-both cap alphabetic; }
@supports not (text-box: trim-both cap alphabetic) {
  .pill__text { position: relative; top: 0.12em; }
}

/* --- Programme ----------------------------------------------------------- */

/* The mandala follows the marker at the artboard's 37. */
.day__head > .day__mandala:first-child { margin-top: 37px; }

/* The first day follows the label directly; a second one needs air from the
   rows above it. */
.day { padding-top: 0; }
.day + .day { padding-top: var(--s4); }

/* Measured off the artboard: label, then the mandala at 524, then 210 to the
   date — 200 of it the hairline — then 60 to the filters and 90 to the rows.
   That run is 1106 tall — more than a screen — so this block is not one of the
   viewport sections. Holding it to a screen only moves the slack around:
   centred it opens a hole in the middle, top-aligned it opens one before the
   rows. Its own rhythm is the right height. */
.day__head {
  text-align: center;
  /* 60 from the foot of the filters to the table's first line, as page 4 has
     it — it was 108 (Arian, 18 Sep). */
  padding: 0 var(--gutter) 42px;
}
.day__head > * { max-width: var(--page); margin-inline: auto; }

/* The artwork measures 555 x 523 of ink on the artboard. The files are trimmed
   to that ink, so the element box is the artwork — no transparent margin
   quietly widening every gap around it, which is what made the numbers here
   read right while the page looked wrong.

   103 above and 211 below rather than the artboard's 108 and 218: the label
   above and the date below each carry a few pixels of line box beyond their
   own ink, and it is the ink the eye lines up. */
.day__mandala {
  position: relative;
  width: min(555px, 60vw);
  margin: 0 auto 0;
}
.day__mandala > img { width: 100%; height: auto; }
.day__mandalaLabel {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font: var(--head);
  text-transform: uppercase; letter-spacing: 0.02em;
}

.day__title {
  font: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.day__hours { font: var(--copy); line-height: 1.2; margin-top: 0; }

.filters { display: flex; justify-content: center; gap: var(--s-half); flex-wrap: wrap; margin-top: var(--s2); }

.filter {
  font: var(--meta);
  letter-spacing: var(--track-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--pill-h);
  padding: 0 21.5px;             /* the pills' own measure */
  border: 1px solid var(--rule);
  border-radius: 999px;
  transition: background .25s ease, color .25s ease;
}
.filter:hover { background: rgba(255, 255, 255, .12); }
/* Dark text on the white fill: the sage read too faint against it. */
/* The chosen one is white with dark grey type — the panel green read as
   black against it (Arian, 23 Sep). */
.filter[aria-pressed="true"] { background: var(--ink); color: var(--ink-chosen); border-color: var(--ink); }

/* Columns are placed where the artboard puts them, as percentages of the
   content box: on 1600 the title starts at 31, the facilitator at 564, the time
   at 1101, the venue at 1206, and the chevron ends at 1569.
   The gutter is subtracted from each column and then given back as the gap, so
   the starts are the artboard's to the pixel and the words stop short of them.
   Setting it as a gap alone would have moved every column along by 8. */
.cols, .item__row {
  display: grid;
  grid-template-columns:
    calc(34.66% - var(--col-gutter))
    calc(34.91% - var(--col-gutter))
    calc(6.83% - var(--col-gutter))
    1fr
    34px;
  align-items: center;
  column-gap: var(--col-gutter);
  row-gap: 0;
  width: 100%;
  /* No 1600 cap: on the artboard the programme runs edge to edge, so on a large
     screen it does too (Arian, 11 Sep); the columns are percentages and scale. */
  padding: 0 var(--gutter);
}

.cols {
  font: var(--meta);
  font-style: italic;
  height: 56px;             /* page 1's header row, its words centred in it */
  align-items: center;
}

.item { border-top: 1px solid var(--rule); }
.item:last-child { border-bottom: 1px solid var(--rule); }

.item__row { text-align: left; min-height: 55px; transition: background .25s ease; }
                                    /* 55 + the 1px rule = the artboard's 56 pitch */
/* Closed rows only: an open row sits on its own photograph or colour, and a
   white wash over that reads as a band across the picture. */
/* Keyed to .is-open, not aria-expanded: the row reads as closed the moment the
   panel starts folding, but its ground stays until the fold ends — keyed to the
   attribute, the wash flashed over the picture on the way out. */
.item:not(.is-open) > button.item__row:hover,
.item:not(.is-open) > button.item__row:focus-visible { background: rgba(255, 255, 255, .07); }

/* Up and down step from row to row, and the page's focus ring is a line drawn
   round whatever has the focus — which around a row is a white box the width of
   the screen, redrawn on every press (Arian, 24 Sep). A row does not need one:
   it already has a way of saying which one you are on, and it is the wash
   above. Focus takes the same, so stepping down a day moves a highlight instead
   of dragging a box, and an open row needs nothing at all — a panel standing
   open under it is not ambiguous. That is also why the wash stops at .is-open:
   white over the photograph reads as a band across it.
   Only the row itself. Everything inside the panel — the links, Book Your Spot
   — keeps the ring, because nothing about those says where the focus is. */
.item__row:focus-visible { outline: none; }

/* The event title stays in the display face; the facilitator is set in the
   copy face and sentence case, per the spec. */
.item__what {
  font: var(--head);
  text-transform: uppercase;
  letter-spacing: 0.01em;
}
.item__with { font: var(--copy); font-size: var(--type-small); }


/* Not settled yet — present, but not claiming to be fixed. */
.item__tbc {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 8px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font: var(--meta);
  font-size: var(--type-small);
  letter-spacing: 0.08em;
  vertical-align: 0.18em;
  opacity: .7;
}

.item__time, .item__venue { font: var(--meta); }

/* A drawn chevron in a square box, so it turns about the mark itself. */
.item__toggle {
  justify-self: end;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  transform-origin: 50% 50%;
  transition: transform .35s ease;
}
.item__toggle svg {
  /* The board draws one arrow everywhere, desktop and phone alike: an L of
     5.96 turned 45°, so 8.43 across and 4.22 down, stroked 0.5 in white. The
     box keeps its own size as something to tap; only the mark is this fine.
     The viewBox carries half a unit around it so the stroke is not clipped. */
  width: 9.43px;
  height: 5.22px;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 0.5;
  stroke-linecap: butt;
  stroke-linejoin: miter;
  vector-effect: non-scaling-stroke;
}
[aria-expanded="true"] .item__toggle { transform: rotate(180deg); }

/* --- The connecting hairline --------------------------------------------- */
/* The artboard runs a 0.5pt white hairline down the centre axis between blocks.

   These are real elements in the flow, not pseudo-elements hung off a label.
   The pseudo-element version reached outside its own box, so nothing around it
   knew it was there: every time a nearby margin changed it collided with the
   ticket pills or drifted off the mandala, and it broke again on each edit
   because the two systems had to be kept in step by hand. In the flow the gaps
   are ordinary margins between ordinary boxes, and they add up on their own. */

.marker {
  display: grid;
  justify-items: center;
  text-align: center;
  margin-top: 24px;              /* clear of the ticket pills, as the artboard */
}

/* display:block and auto margins, so it draws the same wherever it is put —
   inside the marker's grid or on its own in a block. */
.marker__rule {
  display: block;
  width: 1px;
  background: var(--rule);
  margin-inline: auto;
  flex: none;
}

/* The artboard: 75 above the label, 53 below, with about 30 clear either side
   of the words. The label's box hugs its text, so 25 and 23 land it there. */
.marker__rule--lead  { height: 75px; margin-bottom: 25px; }
.marker__rule--trail { height: 53px; margin-top: 23px; }

/* The run that arrives from the mandala and stops short of the date. */
.marker__rule--day {
  height: 185px;
  justify-self: center;
  margin-bottom: 33px;
}

/* --- Expanded panel ------------------------------------------------------ */

.item__panel { overflow: hidden; }

/* Open, the ground runs up behind the row as well: the artboard sets the
   facilitator's row on the photograph, not on the sage above it. It goes when
   the panel has finished folding away, not before. */
.item.is-open.item--image {
  /* One gradient, black at 50% at the foot to nothing at the top, and the
     photograph as it was taken (Chris, 18 Sep). The picture used to be washed
     and desaturated in the file and darkened again here in two bands, which
     took the colour out of it. */
  /* The gradient carries the words: black at the foot, gone by two thirds up,
     with a breath of it over the whole picture. Patyatann's photograph is bright
     where the bio sits and the words were hard to read on it (Arian, 20 Sep);
     Bruna's and Mose's were already dark enough, and this is gentle enough not
     to flatten them. */
  background-image:
    linear-gradient(to top, rgba(0, 0, 0, 0.62) 0%, rgba(0, 0, 0, 0.46) 26%, rgba(0, 0, 0, 0) 66%),
    linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
    var(--item-image);
  background-size: cover;
  background-position: center;
}
.item.is-open.item--tone { background-color: var(--item-tone); }

/* The rule above an open headliner is white, as the board draws it. Elsewhere
   the page's hairline is white at a quarter, which reads as a hairline on sage
   — but the photograph and its gradients paint under the border as well
   (background-clip is border-box), so over a dark picture that quarter-white
   came out as a dark line across the top of the panel (Arian, 23 Sep, on a
   phone). Only the open image rows are affected; the tone panels keep the
   hairline, which reads correctly on a flat colour. */
.item.is-open.item--image { border-top-color: var(--ink); }

/* Every panel takes light type, whatever its colour: the designer set the
   colours for one colour of copy across all the facilitators (18 Sep). The
   pale ones — #95B03F, #C19100, #E87A2E — carry white at 2.5 to 2.9:1, which
   is his call to make and Arian's to relay; it is recorded here so nobody
   rediscovers it as a bug. */

/* The artboard's panel, read off the page: the label column runs to 35.3%, the
   copy from there to 87.2%, and the booking rail closes at the gutter. Set in
   percentages so it holds at any width, with the copy capped at the measure the
   design actually draws — 830 on 1600 — so it does not run long on a wide
   screen. */
.item__panelInner {
  display: flex;
  flex-direction: column;
  width: 100%;
  /* Every open item is one screen, row included, whatever it holds (Arian,
     17 Sep; page 5's "Module has viewport height"). js/site.js measures the
     closed item into --row-h as it opens — one line or two — and the panel
     makes up the rest. A bio longer than the screen still shows in full. */
  /* The nav is not held out of this any more: it slides away as the page comes
     down to an open row, so the space kept for it was an empty band at the top
     of the panel — most visible on a landscape iPad, where it took 75 of 820
     (Arian, 20 Sep). An open item is one screen, from the row down. */
  min-height: calc(100vh - var(--row-h, 56px));
  min-height: calc(100svh - var(--row-h, 56px));
}
/* The bio area takes the height to spare and sets its words at the foot of it,
   100 above and 30 below as page 4's About row does. */
.item__about {
  flex: 1;
  display: grid;
  grid-template-columns: 34.7% minmax(0, 1fr);
  align-content: end;
  padding: 100px var(--gutter) 30px;
}
.item__panel.has-image .item__about { padding-top: var(--s10); }


/* The foot is always there, links or not: page 4's white rule runs the full
   width of the page, edge to edge, over a band 105 deep (30, 45, 30). The rule
   is drawn across the foot's whole box, and the padding keeps its contents on
   the same columns as the bio above. */
.item__foot {
  position: relative;
  display: grid;
  grid-template-columns: 34.7% minmax(0, 1fr);
  align-items: center;
  min-height: 105px;
  padding: 30px var(--gutter);
}
.item__foot::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  border-top: 1px solid var(--rule);
}

/* 200 of 1600 on the artboard, and it should not shrink to nothing on the way
   down — a portrait below about 120 stops reading as one. */
.item__portrait {
  grid-column: 1;
  /* Centred between the edge of the page and the bio text (Arian, 16 Sep), and
     standing 40 above the About label and the bio's first line, as page 1 and
     the 4 Sep export both set it. The panel's screen height keeps the group at the
     foot of the bio area, where the design's bottom-aligned cells put it. */
  /* Page 4's 200 at 1600, growing in proportion with the screen past that —
     not a bigger fixed size (Arian, 18 Sep). */
  width: max(120px, 12.5vw);
  margin-left: calc((100% - var(--gutter) - max(120px, 12.5vw)) / 2);
  margin-bottom: 40px;
}
/* height: auto, or the img's width/height attributes (there so nothing jumps as
   it loads) hold it at 480 tall while the width follows the column: an oval. */
.item__portrait img { width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 50%; }

/* A session led by two: the two photographs stack, the second below the first
   with 30 between them — the page's own unit — and the bios follow in the same
   order, so the top photograph's bio is the first one read (Arian, 16 Sep; it
   hung diagonally off the first photograph before). Phones drop this one and
   show the second photograph again above the second bio instead. */
.item__portrait { position: relative; }
.item__partner { margin-top: var(--unit); }
.item__partner img { width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 50%; }

.item__rowLabel { grid-column: 1; align-self: start; }
.item__rowLabel--more { align-self: center; }

.item__bio {
  grid-column: 2;
  max-width: 830px;         /* the copy frame on the artboard */
  display: grid;
  gap: 1em;
}

.item__more {
  grid-column: 2;
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
}
.item__links { display: flex; gap: 26px; flex-wrap: wrap; }
.item__link { display: inline-flex; align-items: center; gap: 9px; font: var(--meta); }
.item__link:hover { opacity: .7; }
/* The button keeps the far end of the band whether the links are there or not. */
/* The board sets this row as: the number in its ring, "Spots left" beside it,
   then the button hard against the right margin. Measured off page 4 at the
   board's own 1600 — ring 51 x 46 with 10 to the label, 25 from the label to
   the button, and the button's right edge 30 from the edge of the page. */
.item__book { display: flex; align-items: center; gap: 25px; margin-left: auto; }
.item__spots {
  display: grid; place-items: center;
  /* Wider than it is tall, as the board draws it — not a circle. */
  width: 51px; height: 46px;
  border: 1px solid var(--rule); border-radius: 50%;
  /* The number is the condensed face, not the body Garamond the rest of this
     row is set in (page 4). Centred on its cap height rather than its line box,
     as the pills are: the two faces sit differently in their lines, and centring
     the box leaves the figure low. */
  font: var(--spots);
  text-box: trim-both cap alphabetic;
}
.item__spotsLabel { font: var(--meta); font-style: italic; }
.item__count { display: flex; align-items: center; gap: 10px; }
/* The artboard sets Book Your Spot in a dark taken from its panel. So here: the
   panel's own colour where it has one, which is dark enough for white copy and
   so dark enough on white; the house green over a photograph. */
/* Book Your Spot reads white on the panel, like every other pill, and takes the
   panel's own colour only when the fill arrives under it on hover. These two
   were written when the pill was filled white, so the dark text belonged on it
   at rest — transparent, that left dark on dark and the words vanished
   (Arian, 24 Sep). */
.item__book .item__bookLink { color: var(--ink); }
.item__book .item__bookLink:hover { color: var(--panel); }
.item--tone .item__book .item__bookLink:hover { color: var(--item-tone); }
.item__full { font: var(--meta); font-style: italic; opacity: .75; }
/* Each of these sets its own display, which would beat the hidden attribute. */
.item__count[hidden], .item__bookLink[hidden], .item__full[hidden] { display: none; }

.program__error { padding: 40px var(--gutter) 90px; font: var(--meta); }

/* --- Burger + full-screen menu ------------------------------------------- */
/* Desktop keeps the two link sets; below the breakpoint they give way to this.
   The bars become an X by rotating the outer two onto the middle's line. */

/* The menu button is the one control the whole of mobile navigation hangs on,
   and the bars alone gave it a 20px target. Padded to 44 without moving it. */
.burger {
  display: none;
  justify-self: end;
  align-items: center;
  gap: 10px;
  padding: 12px 0;
  margin: -12px 0;
  font: var(--meta);
  font-size: var(--type-small);   /* pages 5 and 6: the same 15 as the name */
}
.burger__bars { position: relative; width: 22px; height: 11px; flex: none; }
.burger__bar {
  position: absolute; left: 0; right: 0; height: 1px;
  background: currentColor;
  transition: transform .38s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
}
.burger__bar:nth-child(1) { top: 0; }
.burger__bar:nth-child(2) { top: 50%; }
.burger__bar:nth-child(3) { top: 100%; }

[aria-expanded="true"].burger .burger__bar:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
[aria-expanded="true"].burger .burger__bar:nth-child(2) { opacity: 0; }
[aria-expanded="true"].burger .burger__bar:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Page 6 (17 Sep): a plain sage screen, not a frosted one. The nav stays on top
   of it, so "Awake Festival" and Menu keep their places in the 61 band; the five
   links are centred in the space below, in GT Alpina 24 with a touch of
   tracking; the lens chain with its eyes sits at 707, and the dates and the
   place close the screen, one to each side, 785 down. The board draws the
   links at 24 though the rest of a phone's titles are 20 — this is its own
   screen, and it is set as drawn. */
.menu {
  position: fixed;
  inset: 0;
  z-index: 19;                 /* under the nav, so the burger stays reachable */
  display: grid;
  grid-template-rows: 61px minmax(0, 1fr) auto auto;
  justify-items: center;
  padding: 0 var(--gutter) 6px;
  background: var(--sage);
  opacity: 0;
  transition: opacity .38s ease;
}
.menu.is-open { opacity: 1; }
.menu[hidden] { display: none; }

.menu__list { grid-row: 2; align-self: center; display: grid; text-align: center; }
.menu__list a {
  font: 300 24px/1.2 var(--face-display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  transition: opacity .25s ease;
}
.menu__list a:hover { opacity: .6; }
.menu__eyes { grid-row: 3; width: 169px; height: auto; margin-bottom: 39px; }
.menu__foot {
  grid-row: 4;
  justify-self: stretch;
  display: flex; justify-content: space-between; align-items: center;
  min-height: 61px;
  font: var(--meta); font-size: var(--type-small);
}

/* The page behind must not scroll while the panel is up. */
body.is-menu { overflow: hidden; }

/* --- Location ------------------------------------------------------------ */
/* Page 1: a dark green band. The label and a 200 hairline; three photographs of
   the village, the middle one whole at 1333 of the 1600 and its neighbours
   looking in 30 from the edges, the name set over it; the copy, 802 wide, and
   the pill to the village's own site. Then a rule, with THE AREA and Open in
   Maps out in the margins beside the drawn map (1148 wide, its markers where
   page 1 puts them), and the key closing the band at the right. */

.location {
  min-height: 0;                 /* no longer one of the viewport sections */
  display: block;
  background: #304529;           /* page 1's band, R48 G69 B41 */
  padding: 31px 0 41px;
  text-align: center;
}
.location__label { font-size: var(--type-small); }
.location__rule { height: 200px; margin: 22px auto 28px; }

.location__stage { position: relative; }
.slides { overflow: hidden; touch-action: pan-y; outline-offset: -4px; }
.slides__track {
  display: flex;
  gap: 6.5vw;                    /* page 1: 104 between the photographs */
  transition: transform .9s cubic-bezier(.45, 0, .2, 1);
  will-change: transform;
}
.slides__track.is-jumping { transition: none; }
.slide { position: relative; flex: none; width: 83.3vw; aspect-ratio: 1333 / 750; margin: 0; }
/* A 15% black over the photographs, so the white name across the middle one sits
   on a ground that carries it (Arian, 23 Sep). Over the picture and under the
   name, which is positioned against the stage rather than the slide. */
.slide::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, .15);
  pointer-events: none;
}
.slide img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  pointer-events: none; user-select: none; -webkit-user-drag: none;
}

/* The name over the middle photograph. A soft shadow darkens only the ground
   right behind the words, as far as they need and no further. */
.location__title {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  white-space: nowrap;
  font: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-shadow: 0 0 28px rgba(0, 0, 0, 0.35), 0 0 6px rgba(0, 0, 0, 0.2);
}
.location__place {
  display: block;
  margin-top: 6px;
  font: var(--copy);
  line-height: 1.2;
  text-transform: none;
  letter-spacing: 0;
}

.location__intro { max-width: calc(min(33.42em, 100%) + 2 * var(--gutter)); margin: 0 auto; padding-inline: var(--gutter); }   /* 802 where the face is 24: 33.42 of its ems */
/* Page 4 (17 Sep): the copy frame starts straight under the photographs, 802
   wide and 310 deep, and the pill sits on the foot of that frame. */
.location__copy { font: var(--copy); line-height: 1.2; min-height: 310px; }
.location__copy p + p { margin-top: 1.2em; }
.location__links { margin-top: 1px; }

/* Page 4's area, measured from its white rule: THE AREA on the left margin at
   the rule, Open in Maps on the right margin 47 below it, the map 29 below it
   and set where the board puts it — 198 in from the left, not centred — and the
   key along the foot on the right. It had all been stacked on the centre line
   (16 Sep); the new board lays it out like this. */
.location__area {
  position: relative;
  margin-top: 73px;
  padding-top: 29px;
  border-top: 1px solid var(--rule);
}
.location__areaTitle {
  position: absolute;
  top: 2px;
  left: var(--gutter);
  margin: 0;
  font: var(--head);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.location__maps { position: absolute; top: 47px; right: var(--gutter); margin: 0; }
/* The drawing is 1148 of page 4's 1600, its centre at 48.25% — but never taller
   than the screen it is on, which is what sent it off the foot of a wide
   window (Arian, 18 Sep). */
.location__area { --map-w: min(71.75%, calc((100svh - 150px) / 0.784)); }
.location__map { width: var(--map-w); margin: 0 0 0 calc(48.25% - var(--map-w) / 2); }
/* The drawing and its markers live in one box so a phone can turn both together
   without the markers going with it — here it simply fills the frame. */
.location__mapInner { position: relative; }
.location__map img { width: 100%; height: auto; display: block; }

/* Page 1's markers, as shares of the map: each glyph's centre in its frame. */
.pin {
  position: absolute;
  transform: translate(-50%, -50%);
  font: var(--head);
}
.pin--main    { left: 28.31%; top: 27.28%; }
.pin--dome    { left: 26.74%; top: 34.28%; }
.pin--brabant { left: 49.48%; top: 40.28%; }
.pin--shala   { left: 48.00%; top: 27.72%; }
.pin--market  { left: 39.90%; top: 19.61%; }
.pin--garden  { left: 74.13%; top: 46.28%; }
.pin--ticoin  { left: 44.95%; top: 24.61%; }
.pin--embapie { left: 23.34%; top: 35.83%; }
.pin--parking { left: 75.52%; top: 65.83%; }
.pin--camp    { left: 19.77%; top: 30.72%; }
.pin--wc      { left: 57.97%; top: 32.61%; }
/* Beehives is on page 5 only. Here it keeps the place page 5 gives it relative
   to its neighbours, Garden and Parking (17 Sep) — an estimate until page 4
   draws it. */
.pin--beehives { left: 80.30%; top: 54.30%; }
/* where page 5 breaks the key into lines; desktop runs it as one */
.location__keyBreak { display: none; }

.location__key {
  display: flex; flex-wrap: wrap; justify-content: flex-end;
  gap: 0 18px;
  margin: -26px var(--gutter) 0;
  font: var(--meta);
  line-height: 40px;
}
.location__key li { display: inline-flex; align-items: baseline; gap: 9px; }
.location__glyph { font: var(--head); }

/* Phones: page 5 (17 Sep — the design over the full-bleed photographs, the turned
   map and the two-column key of 15–16 Sep). The band measured from its top: the
   label, the white hairline to 201, the photographs 343 x 550 with 15 between
   and the neighbours looking in at the edges, the name over the photograph;
   under it the copy, bottom-aligned in a frame that ends at 1016, and the pill;
   the rule 51 below; THE AREA on the left margin at the rule and Open in Maps on
   the right 37 below it; the map 209 below the rule, drawn 505 wide on the 393
   board so it runs off both edges, 19 left of centre; the key in centred lines
   80 below the map, broken after Garden and after Beehives. */
@media (max-width: 768px) {
  /* Page 5 sets the label 8 into the band and closes the band on the key; this
     had 31 above the label and 41 of green below the key. The green band is the
     module, so its foot is the module's foot. */
  .location { overflow: hidden; padding-top: 5px; padding-bottom: 0; }
  .location__rule { height: 130px; margin: 45px auto 0; }
  .slides__track { gap: 15px; }
  /* page 5 is drawn at 393; up to 768 this layout also serves a portrait tablet,
     where the full width would make a photograph taller than the screen */
  .slide { width: min(calc(100vw - 2 * var(--gutter)), 480px); aspect-ratio: 343 / 550; }
  .location__title { top: 45%; white-space: normal; width: calc(100% - 2 * var(--gutter)); }
  .br-wide { display: none; }

  .location__intro { max-width: none; margin: 0; padding-inline: var(--gutter); }
  .location__copy {
    min-height: 265px;
    display: flex; flex-direction: column; justify-content: flex-end;
  }
  .location__links { margin-top: 1px; }

  /* Page 5's map module is one screen, and everything in it is set out from the
     drawing: 505 wide on a 393 board with its centre 4.6% left of the middle,
     209 above it, the key 80 below it. Those were written as pixels, so they
     only held at 393 — on a 501-wide screen the drawing sat in an empty field
     with the key pushed off the foot (18 Sep). They are proportions of the
     drawing now, and the drawing takes the smaller of what the width and the
     height allow, so the module always closes above the fold. */
  .location__area {
    margin-top: 50px; padding-top: 0;
    min-height: 100svh;
    --map-w: min(128.4vw, calc((100svh - 150px) / 1.357));
  }
  .location__areaTitle { top: 48px; }   /* page 5: centred in its 116 frame, 58 down — Open in Maps' centre */
  .location__maps { top: 37px; }
  .location__map {
    width: var(--map-w);
    margin: calc(var(--map-w) * 0.4142) 0 0 calc((100% - var(--map-w)) / 2 - 4.63%);
  }

  .location__key {
    justify-content: center;
    gap: 0 12px;
    margin: calc(var(--map-w) * 0.176) var(--gutter) 0;   /* the key's first row at 1803 */
    line-height: 32px;
  }
  .location__keyBreak { display: block; flex-basis: 100%; height: 0; }

  /* page 5 sets its markers a little differently from page 4 */
  .pin--main     { left: 28.82%; top: 25.07%; }
  .pin--dome     { left: 28.82%; top: 33.28%; }
  .pin--brabant  { left: 50.00%; top: 41.61%; }
  .pin--shala    { left: 51.35%; top: 28.60%; }
  .pin--market   { left: 38.38%; top: 18.50%; }
  .pin--garden   { left: 79.50%; top: 51.10%; }
  .pin--ticoin   { left: 45.11%; top: 23.81%; }
  .pin--embapie  { left: 25.06%; top: 39.21%; }
  .pin--beehives { left: 85.41%; top: 56.07%; }
  .pin--parking  { left: 80.45%; top: 64.52%; }
  .pin--camp     { left: 21.07%; top: 30.17%; }
  .pin--wc       { left: 58.99%; top: 35.67%; }
}

/* --- Contact ------------------------------------------------------------- */
/* Measured off the artboard: the label, 73 to the artwork, the heading laid
   across its feet, 172 down to the two channels, 61 to the pills, 55 to the
   sponsor rule. */

/* The section fills the screen (Arian, 23 Sep). Two gaps give and take with the
   window — the one under Contact, above the monkey, and the one under "Connect
   with us", above Instagram and Facebook — and they are equal, so the heading
   holds the middle of the section however tall it is. Everything below the
   channels is fixed: 60 to the buttons, 60 from the buttons to the foot. */
.connect {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--s2) var(--gutter) 60px;
  text-align: center;
}
.connect .label { margin-bottom: 0; }

/* The free space is shared equally above and below the stage, which centres the
   monkey — but "Connect with us" hangs at the stage's foot, so the words came
   out below the middle of the section. The lift puts the words there instead.
   It is a constant per breakpoint, not a guess: the distance works out as
   A/2 - C/2 + 0.454H - 20, where A is the room above the stage, C everything
   below the channels and H the stage's height — none of which move with the
   window, so the heading stays centred at any screen height.
   tools/design/check-connect.mjs measures it. */
.connect__stage {
  position: relative;
  width: min(235px, 40vw);
  margin: auto;
  transform: translateY(calc(-1 * var(--connect-lift, 0px)));
}

/* The artwork passes over the heading, as the artboard has it — the monkey's
   legs cross the words rather than sitting behind them. */
.connect__art {
  position: relative;
  z-index: 2;
  /* the eyeball's own centre, so it turns in place while the monkey swings */
  transform-origin: 61.5% 31.8%;
  animation: swing 4.6s ease-in-out infinite;
}
.connect__eye { width: 100%; height: auto; display: block; }

/* Cut from the artwork and put back on top, so it can look about. The socket
   behind it is filled with the eye's own white. */
.connect__iris {
  position: absolute;
  left: 57.22%;
  top: 32.41%;
  width: 23.73%;
  height: auto;
  transform: translate(-50%, -50%);
  will-change: transform;
}

@keyframes swing {
  0%, 100% { transform: rotate(-3.4deg); }
  50%      { transform: rotate(3.4deg); }
}

/* Behind the artwork, and wider than it — the words run past the monkey.

   The artboard sets the baseline level with the foot of the artwork, so the
   monkey's body clears the word entirely and only the bunch of berries dips
   into it. Sitting it inside the artwork's box, as bottom:4% did, put the legs
   across CONNECT. */
.connect__heading {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: -2%;
  transform: translateX(-50%);
  white-space: nowrap;
  font: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.connect__channels {
  display: grid;
  justify-items: center;
  gap: 12px;
  margin-top: 0;
  font: 300 var(--type-title)/1.15 var(--face-display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.connect__channels a { padding: 11px 18px; transition: opacity .25s ease; }
.connect__channels a:hover { opacity: .6; }

.connect__links { display: flex; justify-content: center; gap: var(--s-half); flex-wrap: wrap; margin-top: 60px; }

/* --- The foot ------------------------------------------------------------ */
/* A band, not a section: a rule, the sponsors, a rule, the legal line. */

.foot { border-top: 1px solid var(--rule); }

.spons {
  position: relative;
  border-bottom: 1px solid var(--rule);
  padding: 37px var(--gutter) 34px;
  min-height: 108px;
  display: grid;
  place-items: center;
}

/* The label sits out at the gutter while the logos stay centred on the page,
   which is how the artboard has it — so it is taken out of the flow rather
   than allowed to push the row off centre. */
.spons__label {
  position: absolute;
  left: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  font: var(--meta);
  font-style: italic;
}

.spons__set {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(40px, 6.5vw, 100px);
  flex-wrap: wrap;
}
.spons__set li { display: grid; place-items: center; }

/* One optical height across the set, whatever shape each arrives in. */
.spons__logo { width: auto; height: 30px; }
.spons__logo--gv { height: 40px; }       /* the two-line lettering */
.spons__link { display: block; transition: opacity .3s ease; }
.spons__link:hover { opacity: .7; }
.spons__logo--aces { height: 44px; }     /* two lines, so taller for the same weight */
.spons__logo--chris { height: 54px; }    /* a tall, fine-lined monogram */

.legal {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s1);
  flex-wrap: wrap;
  padding: 30px var(--gutter) 40px;
  font: var(--meta);
}
/* Padded to a 44px target: these are the smallest type on the page and they are
   the ones a thumb has to find. */
.legal__links { display: flex; gap: var(--s-half); flex-wrap: wrap; }
.legal__links a { padding: 15px var(--s-half); margin: -15px 0; transition: opacity .25s ease; }
.legal__links a:hover { opacity: .6; }

/* --- Which layout a screen gets -------------------------------------------- */
/* The phone's rules run up to 768. Everything wider takes the board's own
   layout.

     phone    (max-width: 768px)
     board    (min-width: 769px)

   This used to run to 1024, so that an iPad in either orientation read as a
   phone (Arian, 20 Sep: "an iPad held upright is a tall screen and the wide
   board's columns do not belong on it"). Reversed 22 Sep, by Arian, after the
   landscape iPad: page 5 stacks the panel — portrait above, bio below — and
   stacked it does not fit a 768-tall screen. 41 of 51 rows ran past the fold
   there, half of Permaculture's bio and its whole Book button among them. The
   board's two columns are the shorter shape, so the board's layout is the one
   an iPad gets.

   There is no tablet design to put between them: what a 769-wide screen shows
   is the board's layout at a width it was never drawn for, and that is the
   accepted trade (Arian, 22 Sep). The board wants a thousand pixels for its
   five programme columns, so the closer a screen is to 769 the tighter those
   columns are.

   An iPad upright is 768 and still reads as a phone, which is the half of the
   20 Sep decision that stands.

   Both conditions are written out at every breakpoint rather than kept in a
   variable, because a media query cannot read one. Width alone is the test:
   it was written as a question of shape and touch for a while, but Safari
   answers the orientation question wrongly on the first load after a turn, so
   the site came up wide and only righted itself when the iPad was turned
   twice (Arian, 20 Sep).                                                     */


/* --- Mobile -------------------------------------------------------------- */

@media (max-width: 768px) {
  :root {
    /* The 393pt mobile artboard: 32 for display, 18 for copy, 15 for meta.
       The unit comes down with it, so every gap stays in the same proportion. */
    --unit: 25px;
    /* page 5: 32 / 20 / 18 / 15 — the lead folds into small on a phone */
  }

  /* The links move into the full-screen menu, and the name moves into the hero,
     so the bar carries nothing but Menu — as the mobile artboard has it. The
     mark keeps its cell so the eye still appears there once the nav sticks. */
  /* Page 5's header: "Awake Festival" on the left and Menu on the right, both
     Garamond 15, in a band 61 deep. The eye no longer takes the left cell. */
  .nav { grid-template-columns: 1fr auto; padding-top: 8px; padding-bottom: 8px; }
  .nav__name {
    display: block; grid-area: 1 / 1;
    font: var(--meta); font-size: var(--type-small);
    text-transform: none; letter-spacing: 0;   /* the desktop wordmark's caps are not page 5's */
  }
  .nav__eye, .nav.is-stuck .nav__eye { display: none; }
  .nav__set { display: none; }
  .nav__mark { justify-self: start; }
  .nav__wordmark { display: none; }
  .burger { display: flex; }

  /* Page 2 draws no bar over the top of the page — only Menu, with the name set
     small between. So the bar stays clear until the nav sticks, and only then
     takes its frosted ground. */
  .nav:not(.is-stuck) {
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* Page 2, measured on its 393 board: the name in three lines of 24, the first
     level with Menu (its frame at 3, inset 25); the artwork edge to edge; the
     dates and the pill centred underneath; the band at 749, 103 tall. The name
     takes the first row, the artwork the flexible one, the meta the last —
     named, or the name would take the flexible row and squeeze the artwork. */
  .hero {
    grid-template-rows: auto minmax(0, 1fr) auto;
    padding-top: 111px;
    padding-bottom: 24px;
  }
  /* Page 5: the AWAKE mark, 178 wide and centred, its top 111 down the board;
     the collage 33 below it. */
  .hero__mark {
    display: block;
    width: 178px;
    height: auto;
    margin: 0 auto;
  }
  /* The phone artwork is its own arrangement — page 2's collage, rebuilt from
     the file's pieces — taller than the desktop one and cut at the screen edges.
     It runs the full width of the screen, always: the board draws it edge to
     edge, and it is the first thing anyone sees.

     It used to be sized from the room left under the fold instead, so that the
     dates, the pill and a peek of the band all stayed on the first screen. That
     works on the 852 the board is drawn at, but a real phone hands back 100-300
     less once the browser's own bars are showing, and the collage paid for all
     of it: 76% of the width on an iPhone with its bars, 48% on a small one
     (Arian, 16 Sep). So the hero grows past a screen on a short phone and the
     pill goes below the fold, rather than the artwork shrinking into the page.
     The negative margins take it past the gutters to the edges. */
  .hero {
    height: auto;
    min-height: calc(100vh - var(--fold-peek, var(--fold-peek-guess)));
    min-height: calc(100svh - var(--fold-peek, var(--fold-peek-guess)));
  }
  .hero__art {
    position: relative;
    inset: auto;
    overflow: visible;
    container-type: normal;
    justify-self: center;
    width: 100vw;
    height: auto;
    aspect-ratio: 1297 / 1221;
    margin-inline: calc(var(--gutter) * -1);
    align-self: start;
    margin-top: 33px;          /* page 5: the collage 33 below the mark */
  }
  .hero__canvas { inset: 0; width: auto; height: auto; margin: 0; aspect-ratio: auto; }

  /* The flock flies on a phone too (Arian, 24 Sep). The whole layered hero would
     take the page from 245 KB of artwork to about two megabytes, which is not a
     trade worth making on mobile data — but the birds alone are 74, on their own
     sprite cut for this size, and they are the only part of the picture that
     moves. The layer is already in the markup for every screen; only the board
     had it drawn. The sheet and the beat are the board's, unchanged.
     Placed over the cloud band of page 2's arrangement — Christoph to nudge. */
  .hero__flock {
    display: block;
    /* The layers are only given a position above the breakpoint, where the rest
       of them are drawn; this one needs its own on a phone. */
    position: absolute;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    z-index: 2;
    /* In the open sky above the collage. On the board the flock flies behind the
       cloud banks, but page 2's artwork is one flat picture, so the birds can
       only sit over it — which means they belong where there is nothing to sit
       on. Christoph to nudge. */
    /* The board's sheet is 1400x244 with the birds only in x 133..1068 and
       y 21..203 of it — a quarter of the width, on the right, is empty. Centring
       that box put the birds left of centre and low, because the ink's own
       centre is at 42.6% of the sheet, not 50 (Arian, 24 Sep). The phone's sheet
       is cropped to the ink across all seventeen frames, so the box and the
       birds are now the same thing and centring one centres the other. */
    left: 10.4%; top: 3%; width: 79.2%;   /* a tenth bigger than 72, still centred (Arian, 24 Sep) */
    aspect-ratio: 935 / 182;
    overflow: hidden;
  }
  .hero__flock::before {
    content: '';
    position: absolute;
    left: 0; top: 0; right: 0;
    height: 1700%;
    background-image: url("../assets/art/hero/gulls-phone.webp?v=cec9d29e");
    background-size: 100% 100%;
    animation: hero-flap 1.2s steps(16) infinite alternate;
  }

  /* The board carries two flocks: the distant one on its sprite above, and five
     bigger gulls sitting still inside g_back, out at the edges of the picture.
     The phone had neither. The big ones are lifted out of that layer as their
     own band — five isolated shapes and nothing else, 3 KB — and the band spans
     the hero, so they fall at its outer edges the way they do on the board
     without anyone having to choose where to put them. Still, as they are. */
  .hero__gulls {
    position: absolute;
    left: 0; top: 8%; width: 100%;
    aspect-ratio: 3245 / 588;
    background-image: url("../assets/art/hero/gulls-big-phone.webp?v=7735c47e");
    background-repeat: no-repeat;
    background-size: 100% 100%;
    z-index: 2;
    pointer-events: none;
  }
  .hero__meta {
    position: static;
    display: grid;
    justify-items: center;
    justify-content: center;    /* the flex space-between would otherwise
                                   push the single column to the left */
    align-self: end;
    /* Page 5 sets the date 70 under the collage and the button 45 under the
       date — not the even pair this had, which sat the date 29 too low and
       squeezed the two together. */
    gap: 47px;
  }
  .marquee { padding: 8px 0 35px; }   /* page 5: the names at 760, 11 into the band */

  /* Contact, page 2: one screen (its note: "module has viewport height"). The
     label sits right at the top of the module — 5 into it on the board, not the
     52 this had — then the artwork with the heading at its feet, the two
     channels 26 apart, and the pills 50 above the rule. */
  .connect {
    min-height: 100vh; min-height: 100svh;
    display: flex; flex-direction: column; align-items: center;
    padding: 24px var(--gutter) 50px;
  }
  .connect .label { margin-bottom: 42px; }
  /* The monkey gives way before the gaps do (Arian, 24 Sep). Page 5 draws him
     229 across and he stayed that whatever the screen, so on a 667 phone he
     took 342 of it and left 68 to share — 12 above him, which is the squeeze.
     He is 1.4915 times as tall as he is wide, so the width that fits a given
     height is that height times 0.6705: the module's fixed parts come to 257,
     two gaps of 48 are the least that reads, and whatever is left over is his.
     Above about 850 there is room for all 229 of him and this does nothing. */
  .connect__stage { width: clamp(140px, calc((100svh - 353px) * 0.6705), 229px); }
  .connect__heading { bottom: -2px; }
  .connect__channels { gap: 0; line-height: 26px; }
  .connect__channels a { padding: 0 18px; }
  .connect__links { justify-content: center; }

  /* The foot, page 2: one screen. "Sponsored by:" upright and right under the
     rule, as the board has it; the logos stacked down the middle of what is
     left; then a rule, the © line
     on the left, the three policies on one line on the right, a last rule. */
  .foot { min-height: 100vh; min-height: 100svh; display: flex; flex-direction: column; }
  .spons {
    flex: 1;
    display: flex; flex-direction: column; align-items: center;
    padding: 2px var(--gutter) 0;
    min-height: 0;
  }
  .spons__label { position: static; transform: none; }
  .spons__set { flex: 1; flex-direction: column; justify-content: center; gap: 38px; flex-wrap: nowrap; }
  /* Page 5's foot: the rule at 732 of the module, the © line right under it,
     the policies at 774, the closing rule at 832. */
  .legal {
    display: grid; gap: 6px; text-align: left;
    padding: 2px var(--gutter) 0;
  }
  .legal__who { justify-self: start; }
  .legal__links { justify-self: end; justify-content: flex-end; flex-wrap: nowrap; gap: min(20px, 3.6vw); }
  .legal__links a { padding-inline: 0; }
  .legal::after {
    content: ''; height: 20px;
    border-top: 1px solid var(--rule);
    margin: 35px calc(var(--gutter) * -1) 0;
  }

  .about, .item__about, .item__foot { grid-template-columns: 1fr; }

  /* Page 5's own type, taken out of the file rather than assumed. The sizes it
     uses are 15, 16, 18, 20, 22 and 32; these are the ones that were not being
     set — the comment said them, the rules under it were never written (18 Sep).
       row title 20 · time and place 16 · the person 15
       DAY 1 24 · The Program 16 · Sponsored by: 16
       map key glyphs 22 with 15 labels · the markers on the drawing 22 */
  .day__mandalaLabel { font-size: var(--type-marker); }
  .marker .label, .spons__label { font-size: var(--type-label); }
  .location__glyph, .pin { font-size: var(--type-glyph); }


  /* About, spaced as page 2 spaces it. There the block sits in a tall frame with
     its copy centred, which leaves a wide margin above and below; the gaps here
     are those margins, measured off the board's ink. The copy at InDesign's auto
     leading, 120%. */
  .about { padding-top: 14px; }
  .about .label { margin-bottom: 0; }
  /* Page 5 runs this copy the full width of the screen — its frame is the page,
     with no gutter — which is why the board sets it in 9 lines and a gutter'd
     column takes 10. */
  .about__body { max-width: none; margin: 223px -20px 154px; }
  .about__lead, .about__copy { line-height: 1.2; }
  .about__heading { margin: 7px 0 0; }
  .tickets { margin-top: 45px; }   /* page 5: the label 61 above the pills */
  .br-phone { display: inline; }

  /* Pills and filters at page 2's 41–42, the filters 11 apart. */
  :root { --pill-h: 41.5px; }
  .pill, .filter { padding: 0 22px; }   /* page 5: 41.5 tall, about 22 each side */
  .filters { gap: 11px; margin-top: 68px; }

  .day__head { padding-top: 18px; padding-bottom: 45px; }   /* the wreath opens at 1850; the table 63 under the filters, page 5 */
  /* Page 5's own gaps under the wreath: the date 137 below its ink, the hours
     16 under the date, the filters 73 under the hours. */
  .day__title { margin-top: -34px; }
  .day__hours { font-size: var(--type-small); line-height: 22px; margin-top: 6px; }

  /* The mobile artboard, measured on its 393pt board: an 18 gutter, the title
     with the time at 54.5% and the venue closing at the right on the first
     line, the discipline and the chevron on the second, and a 96 row pitch. */
  .cols, .item__row {
    /* The title runs to 54.9% of the content box, where the time starts; the
       venue closes at the right. Proportional, so it holds on any handset. */
    /* The gutter comes off the title's column and is given back as the gap, so
       the time still starts at 54.9% of the board — exactly where the artboard
       puts it — and a long title stops short of it rather than arriving at it.
       "Arian Mortazavi Sarrafan" reached 09:30 with 4px to spare at 500 wide,
       which is a window narrowed on a desktop rather than any handset (Arian,
       24 Sep). */
    grid-template-columns: calc(54.9% - var(--col-gutter)) auto 1fr;
    grid-template-areas:
      "what time  venue"
      "with with  toggle";
    column-gap: var(--col-gutter);
    row-gap: 10px;
    padding-inline: 18px;      /* the board's own row gutter */
  }
  .cols {
    height: 56px;              /* page 2's header row, its words centred in it */
    padding-bottom: 0;
    align-items: center;
    grid-template-areas: "what time time";
  }
  .cols span:nth-child(2) { display: none; }
  .cols span:last-child { grid-area: time; justify-self: start; }

  /* 95 + the rule = the board's 96 pitch; the title's capitals sit 27 below
     the rule, the discipline 32 below them. */
  .item__row { min-height: 95px; padding-top: 24px; padding-bottom: 14px; }
  .item__what   { grid-area: what; }
  .item__with   { grid-area: with; opacity: .85; letter-spacing: 0.015em; line-height: 19px; }
  .item__time, .item__venue { font-size: var(--type-row); }
  .item__time   { grid-area: time; }
  .item__venue  { grid-area: venue; justify-self: end; text-align: right; }
  .item__toggle { grid-area: toggle; justify-self: end; align-self: end; width: 12px; height: 12px; }

  /* The filters run off the edge of the board rather than wrapping, so they
     scroll — bled to the screen edges so the strip reads as continuing. */
  .filters {
    flex-wrap: nowrap;
    /* Centred while they fit: on a tablet held upright the five of them leave
       room on the right, and left-aligned they read as having slipped (Arian,
       20 Sep). "safe" so that where they do not fit the strip starts at its own
       left edge instead of being cut off at both ends. */
    justify-content: flex-start;
    justify-content: safe center;
    overflow-x: auto;
    /* No scroll snapping: it aligns the first pill to the scroll origin, which
       is inside the start padding, so the strip opened already scrolled past
       its own inset and the pill sat flush on the edge. */
    /* Bled to the screen edges so the strip reads as continuing, with the
       first pill still on the gutter. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scrollbar-width: none;
  }
  .filters::-webkit-scrollbar { display: none; }
  .filter { flex: none; }

  /* Page 2 runs the flowers to within a few points of the board's edges: 386
     of 393. Wider than the gutter's column, so auto margins would collapse to
     nothing and hang it off the right; this centres it on the page instead. */
  .day__mandala {
    width: min(555px, 98.2vw);
    margin-inline: calc((100% - min(555px, 98.2vw)) / 2);
  }
  .day__head > .day__mandala:first-child { margin-top: 0; }

  /* Page 5's panels: a screen tall, row included, and everything centred. A bio
     photograph holds the top, 150 across and 54 below the row; the About label
     and the bio sit at the foot of the space; then the edge-to-edge rule with
     More and the links in a band 110 deep, and — where there is a booking — a
     second rule across the width, the count on the left and Book Your Spot on
     the right. */
  .item__panelInner {
    min-height: calc(100vh - var(--row-h, 96px) - var(--nav-h, 60px));
    min-height: calc(100svh - var(--row-h, 96px) - var(--nav-h, 60px));
  }
  .item__about,
  .item__panel.has-image .item__about {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    text-align: center;
    padding: 54px var(--gutter) 30px;
  }
  .item__portrait, .item__portrait--next {
    display: block;
    margin: 0 auto;
    width: min(38.17vw, 240px); min-width: 0; max-width: none;   /* page 5's 150 at 393, in proportion */
  }
  /* Photograph 54 under the row, words 42 under the photograph — the same on
     every panel. The photograph used to take the space below it, which pushed
     the words to the foot and left a gap under the face of anywhere from 42 to
     123 depending on how long the bio was. The spare space falls at the foot of
     the block now, where nobody reads it as padding. */
  /* Chris, 18 Sep: the words at the foot, as the board has them, and the
     portrait centred in the space between the top of the panel and the words —
     the same 40 at least above it and below it, and whatever is left shared
     equally. It had been pinned under the row with the gap under it left to
     chance, then pinned under the row with the words following it. */
  .item__about--face,
  .item__about:has(> .item__portrait:first-child) { justify-content: flex-end; padding-top: 40px; }
  .item__about > .item__portrait:first-child { margin: auto; }
  .item__about > .item__portrait:first-child + .item__rowLabel { margin-top: 40px; }
  /* A pair stands together on a phone: the two faces side by side, the label
     under them, and then the two bios one after the other with a dash between
     (Arian, 20 Sep). The one hung under the second bio is dropped. */
  .item__portrait--next { display: none; }
  .item__portrait:has(.item__partner) {
    display: flex;
    justify-content: center;
    gap: 5.09vw;                      /* page 5's own gutter between the faces */
    width: auto;
  }
  .item__portrait:has(.item__partner) > img,
  .item__partner { width: min(32vw, 200px); flex: none; }
  .item__partner { display: block; margin-top: 0; }
  /* One bio follows another on its own space, with no rule between them
     (Arian, 23 Sep). The dash had been separating a pair's two bios — and,
     where a session carries a note, separating that too, so Eve and Agena's
     panel drew two of them. */
  .item__about .item__bio ~ .item__bio { margin-top: 24px; }
  .item__rowLabel { margin-bottom: 14px; align-self: stretch; }
  .item__bio { max-width: none; line-height: 1.2; }

  .item__foot {
    display: flex; flex-direction: column; align-items: stretch;
    text-align: center;
    min-height: 110px;
    padding: 28px var(--gutter) 0;
  }
  .item__rowLabel--more { margin: 0 0 10px; }
  .item__more { flex-direction: column; align-items: stretch; gap: 0; }
  .item__links { justify-content: center; padding-bottom: 28px; }
  /* The board draws this row with the count on the left and the button on the
     right. Where there is no count — which is most of them — the button had
     nothing to balance and sat off to the right on its own; alone it belongs in
     the middle. */
  .item__book {
    justify-content: center;
    margin-inline: calc(var(--gutter) * -1);
    padding: 25px var(--gutter);
    border-top: 1px solid var(--rule);
  }
  .item__book--counted { justify-content: space-between; }
  .item__book--counted .item__bookLink { margin-left: auto; }
  /* Page 5 draws the ring a little shorter than the board does, and sets the
     label closer to it. */
  .item__spots { width: 46px; height: 41.5px; }
  .item__count { gap: 9px; }
  /* And it sets this one button tighter than the rest: 126.9 across where the
     pill's own padding gives 140.9. Buy Tickets on the same board is 112.6 and
     the pill's padding draws it at 116.5, so the padding is right in general
     and this button is the exception. Scoped to it, and to the phone. */
  .item__book .item__bookLink { padding-inline: 15px; }
  /* Where a session has no links, the More band holds nothing, and its rule and
     the booking's stood 30 apart with emptiness between them — two lines at the
     foot of some panels and one at the foot of others (Arian, 20 Sep). With
     nothing above it, the booking band is the foot's own band and takes the
     rule the foot already draws. */
  .item__more > .item__book:first-child {
    border-top: 0;
    padding-top: 0;
  }

  /* Page 2's hairline: 51 below the ticket pills, 36 above the label and 45
     below it to the mandala, then 130 from the mandala to the date. */
  .marker { margin-top: 51px; }
  /* One line on the board, 1689 to 1832, with the label lying over it at 1725:
     here it is that line in two pieces with the label between them. */
  .marker__rule--lead  { height: 36px; margin-bottom: 0; }
  .marker__rule--trail { height: 67px; margin-top: 20px; }   /* the line closes at 1832 */
  .marker__rule--day   { height: 130px; margin-bottom: 32px; }

  /* Menu with page 2's small three-bar mark, still a 44px target. */
  .burger { gap: 6px; }
}

/* Line breaks page 2 sets and the desktop artboard does not; and the second
   photograph a phone places ahead of a pair's second bio. */
@media (min-width: 769px) { .br-phone, .item__portrait--next, .hero__gulls { display: none; } }

/* No automatic hyphenation (17 Sep). The browser breaks words far more readily
   than InDesign does, and saved the About copy a line: 9 against page 5's 10,
   which lifted everything below it by 22px. Unhyphenated it wraps to the board's
   ten lines. */

/* Consecutive bios — a pair's, then a line about the session — sit a paragraph
   apart, as the paragraphs inside each one do. */
.item__bio ~ .item__bio { margin-top: 1em; }

/* --- The welcome --------------------------------------------------------- */
/* The holding page's opening, over the site: the mark draws itself, the eyes
   take the letters' places, and then the whole thing turns anticlockwise
   through half a circle while it travels down and shrinks, until its centre eye
   is the Buddha's third eye. js/welcome.js measures that landing from the two
   eyes themselves and drives the turn; these are the states it moves between.

   The page it covers is the same sage, so even if the veil paints for a frame
   before the script decides against it, nothing flashes. */

/* The radius the page opens to. Registered, or it cannot be animated. */
@property --open-r {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

.welcome {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--sage);
  /* The mark at the holding page's own size and height on the screen. */
  --logo-w: clamp(150px, calc(370.75vh - 1017px), 370px);
  --logo-y: 48.25%;
  --open-x: 50%;
  --open-y: 50%;
}

/* Nothing scrolls underneath while it plays. */
html.is-welcoming { overflow: hidden; }

/* Centred by its own properties rather than by transform, which the turn needs
   for itself. */
.welcome__stage {
  position: absolute;
  left: 50%;
  top: var(--logo-y);
  translate: -50% -50%;
  width: var(--logo-w);
  max-width: calc(100% - 2 * var(--gutter));
  will-change: transform;
}

.welcome .logo {
  display: block;
  width: 100%;
  height: auto;
  color: var(--ink);
  overflow: visible;
}

/* The landing: the mark and the four outer eyes clear over the end of the turn,
   and the centre eye is left to arrive on its own. */
.welcome--landing .logo,
.welcome--landing .eye:not(.eye--three) {
  opacity: 0;
  transition: opacity 700ms ease;
}

/* The page opens out of the eye: a hole the size of the eye itself, growing
   with a soft edge, so the mark's eye gives way to the Buddha's in the same
   place and at the same size.

   The edge is a band, not a line. A fixed 22px of feather is soft while the
   hole is the size of an eye and nothing at all once it is a thousand across —
   by then it read as a hard ring travelling outwards (Arian, 21 Sep). The band
   grows with the radius instead, and the stops inside it are spaced to give a
   curve rather than the straight ramp two stops would interpolate, so there is
   no edge anywhere in it to catch. */
.welcome--opening {
  --open-f: calc(var(--open-r) * 0.62 + 48px);
  -webkit-mask-image: radial-gradient(circle at var(--open-x) var(--open-y),
                        transparent 0,
                        transparent var(--open-r),
                        rgba(0, 0, 0, 0.18) calc(var(--open-r) + var(--open-f) * 0.22),
                        rgba(0, 0, 0, 0.55) calc(var(--open-r) + var(--open-f) * 0.48),
                        rgba(0, 0, 0, 0.86) calc(var(--open-r) + var(--open-f) * 0.74),
                        #000 calc(var(--open-r) + var(--open-f)));
  mask-image: radial-gradient(circle at var(--open-x) var(--open-y),
                transparent 0,
                transparent var(--open-r),
                rgba(0, 0, 0, 0.18) calc(var(--open-r) + var(--open-f) * 0.22),
                rgba(0, 0, 0, 0.55) calc(var(--open-r) + var(--open-f) * 0.48),
                rgba(0, 0, 0, 0.86) calc(var(--open-r) + var(--open-f) * 0.74),
                #000 calc(var(--open-r) + var(--open-f)));
}

/* --- Eyes ---------------------------------------------------------------- */
/* One per lens, layered over the mark. Every number is geometry measured off the
   artwork, expressed as a percentage so the whole set scales with the logo and
   never needs a breakpoint.

   The five lenses are the same shape at different sizes, so a single clip path
   and a single canvas ratio serve them all. Each lens box below is the arc pair
   that draws it, plus the group's translate(9,11), as a share of the 610.5×158.9
   viewBox. */

:root {
  /* Canvas width as a share of its lens. At 68.943% the sphere would meet the
     lens top and bottom exactly; 78% of that leaves the eyeball some air. */
  --eye-size: 53.78%;
}

.eyes { position: absolute; inset: 0; pointer-events: none; }
.eyes[hidden] { display: none; }

.eye {
  position: absolute;
  -webkit-clip-path: url(#lens-clip);
  clip-path: url(#lens-clip);
}

.eye--one   { left:  1.638%; top: 34.959%; width: 11.925%; height: 30.082%; --eye-h: 81.908%; }
.eye--two   { left: 13.563%; top: 24.890%; width: 19.885%; height: 50.220%; --eye-h: 81.816%; }
.eye--three { left: 33.448%; top:  8.150%; width: 33.120%; height: 83.700%; --eye-h: 81.762%; }
.eye--four  { left: 66.552%; top: 24.890%; width: 19.885%; height: 50.220%; --eye-h: 81.816%; }
.eye--five  { left: 86.437%; top: 34.959%; width: 11.925%; height: 30.082%; --eye-h: 81.908%; }

.eye__frame {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--eye-size);
  /* Squared by hand rather than with aspect-ratio, which Safari only gained in
     15 — on an older iPad the frame would collapse to nothing. Each lens has a
     very slightly different width:height, so --eye-h is per lens. */
  height: var(--eye-h);
  /* Scaled from nothing by js/eye.js. The lens clip lives on the parent, so it
     stays put while this pops. */
  transform: translate(-50%, -50%) scale(0);
}

.eye__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The lids are clipped to the sphere — centre 50.1%/50.0%, diameter 95.4% of the
   canvas. Without this a parked lid shows outside the eyeball. On a square box a
   percentage radius resolves against the width, so 47.7% is exactly half. */

.eye__lids {
  position: absolute;
  inset: 0;
  -webkit-clip-path: circle(47.7% at 50.1% 50%);
  clip-path: circle(47.7% at 50.1% 50%);
}

/* Rest positions. The lids move; their opacity is never touched. At
   translateY(0) they meet along their shared seam and close the eye. */

.eye__lid--upper { transform: translate3d(0, -100%, 0); }
.eye__lid--lower { transform: translate3d(0, 22.7%, 0); }

.eye__iris,
.eye__lid--upper,
.eye__lid--lower { will-change: transform; }

/* Each letter sits in its own wrapper so it can be faded out as its eye takes
   over, without touching the opacity its own keyframes are driving. */

.lh { opacity: 1; }

/* --- Logo animation ------------------------------------------------------ */
/* Plays once and stays. Two waves sweep in from the left — the lower one
   trailing the upper by 0.22s — so they cross and part like a pair of antiphase
   sound waves. Each letter fades up the moment its own lens closes, so the
   reveal reads left to right.

   The upper arcs draw from their path start (dashoffset +length to 0). The
   lower arcs are authored right-to-left, so they draw from their path *end*
   instead (-length to 0), which is what turns their sweep round to match.

   The stagger lives in the keyframe percentages, not in animation-delay, so
   --draw is the only speed control. Numbers are generated from a 3.821s reference
   timeline; regenerate them if the weave offset or the segments change. */

:root { --draw: 2.55s; }

.logo .sn {
  fill: none;
  stroke: currentColor;
  stroke-linecap: butt;
}

.logo .ltr { fill: currentColor; }

.logo .w2 { stroke-width: 2; }
.logo .w3 { stroke-width: 3; }
.logo .w4 { stroke-width: 4; }

.logo .sn,
.logo .ltr {
  animation-duration: var(--draw);
  animation-iteration-count: 1;
  animation-fill-mode: both;
}

.logo .sn { animation-timing-function: linear; }
.logo .ltr { animation-timing-function: ease-out; }

.logo .a0 { stroke-dasharray: 92.2; animation-name: ka0; }
.logo .a1 { stroke-dasharray: 153.8; animation-name: ka1; }
.logo .a2 { stroke-dasharray: 256.2; animation-name: ka2; }
.logo .a3 { stroke-dasharray: 153.8; animation-name: ka3; }
.logo .a4 { stroke-dasharray: 92.2; animation-name: ka4; }
.logo .b0 { stroke-dasharray: 92.2; animation-name: kb0; }
.logo .b1 { stroke-dasharray: 153.8; animation-name: kb1; }
.logo .b2 { stroke-dasharray: 256.2; animation-name: kb2; }
.logo .b3 { stroke-dasharray: 153.8; animation-name: kb3; }
.logo .b4 { stroke-dasharray: 92.2; animation-name: kb4; }

.logo .la { animation-name: kla; }
.logo .lw { animation-name: klw; }
.logo .lc { animation-name: klc; }
.logo .lk { animation-name: klk; }
.logo .le { animation-name: kle; }

@keyframes ka0 {
  0% { stroke-dashoffset: 92.2; }
  9.683%, 100% { stroke-dashoffset: 0; }
}
@keyframes ka1 {
  0%, 9.683% { stroke-dashoffset: 153.8; }
  25.831%, 100% { stroke-dashoffset: 0; }
}
@keyframes ka2 {
  0%, 25.831% { stroke-dashoffset: 256.2; }
  52.709%, 100% { stroke-dashoffset: 0; }
}
@keyframes ka3 {
  0%, 52.709% { stroke-dashoffset: 153.8; }
  68.856%, 100% { stroke-dashoffset: 0; }
}
@keyframes ka4 {
  0%, 68.856% { stroke-dashoffset: 92.2; }
  78.54%, 100% { stroke-dashoffset: 0; }
}
@keyframes kb0 {
  0%, 5.758% { stroke-dashoffset: -92.2; }
  15.441%, 100% { stroke-dashoffset: 0; }
}
@keyframes kb1 {
  0%, 15.441% { stroke-dashoffset: -153.8; }
  31.589%, 100% { stroke-dashoffset: 0; }
}
@keyframes kb2 {
  0%, 31.589% { stroke-dashoffset: -256.2; }
  58.466%, 100% { stroke-dashoffset: 0; }
}
@keyframes kb3 {
  0%, 58.466% { stroke-dashoffset: -153.8; }
  74.614%, 100% { stroke-dashoffset: 0; }
}
@keyframes kb4 {
  0%, 74.614% { stroke-dashoffset: -92.2; }
  84.297%, 100% { stroke-dashoffset: 0; }
}

@keyframes kla {
  0%, 15.441% { opacity: 0; }
  31.144%, 100% { opacity: 1; }
}
@keyframes klw {
  0%, 31.589% { opacity: 0; }
  47.291%, 100% { opacity: 1; }
}
@keyframes klc {
  0%, 58.466% { opacity: 0; }
  74.169%, 100% { opacity: 1; }
}
@keyframes klk {
  0%, 74.614% { opacity: 0; }
  90.317%, 100% { opacity: 1; }
}
@keyframes kle {
  0%, 84.297% { opacity: 0; }
  100%, 100% { opacity: 1; }
}

/* The five eyes sit inside the hero mark exactly as they do on the coming-soon
   page — same geometry, same component, driven by the same js/eye.js. When the
   hero artwork arrives it goes behind them and nothing here changes. */

/* --- Reduced motion ------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .art-iris { transition: none; }
  .connect__art { animation: none; }
  .marquee__track { animation: none; }
  .menu, .burger__bar { transition: none; }
  .item__panel { transition: none !important; }
  a, .pill, .filter, .item__row { transition: none; }
  .hero__canvas * { animation: none !important; }
}

/* Page 4's own module, measured out of the file rather than off an older board:
   the label 6 into it, the artwork at 98, the heading's ink at 393 with the
   monkey's feet across it, the two channels at 567 and 603, the pills at 727,
   and the rule at 828. What was here put the label at 64, the pills at 848 and
   ran the module to 947 — every one of them out by a hundred or so, on the
   desktop nobody was checking against page 4 (18 Sep). */
@media (min-width: 769px) {
  /* Contact sits as far under the green band as Location sits inside it — the
     file draws its frame against the band's edge, which on screen read as the
     label stuck to it (Arian, several times). The artwork does not move. */
  .connect {
    /* The heading's lift, capped at the room the screen actually has. The
       fixed content of this module comes to 638 on the board, so the free
       space is (100svh - 638) shared between the two gaps; the lift can take
       at most half of that, less 12 so the artwork never touches the label.
       Unclamped it pulled the monkey over the word Contact on a short screen
       (Arian, 24 Sep, iPhone SE). */
    --connect-lift: clamp(0px, calc((100svh - 662px) / 2), 47px);
    padding-top: 31px;
    /* The board draws this module 828 of its 1600, and it was set to that
       proportion. Arian asked for the screen instead (23 Sep), so the two
       flexible gaps take the difference and the heading holds the middle. */
    min-height: 100vh;
    min-height: 100svh;
  }
  /* No fixed gap under the label: that gap is one of the two that give. */
  .connect .label { margin-bottom: 0; }
  /* Centred on Open in Maps: page 4's title frame is 139 tall from the rule and
     its text sits at the frame's centre, 69 down — the button's own centre. */
  .location__areaTitle { top: 57px; }
  /* The copy sits in the middle of its 310 frame under the photographs, not at
     the top of it against the picture. */
  .location__copy { display: flex; flex-direction: column; justify-content: center; }
  .connect__heading { bottom: 4.6%; }
  /* Page 4 sets the two 36 apart: 24pt lines, no gap between them and no
     padding on the links inflating the pair. */
  .connect__channels { line-height: 36px; gap: 0; }
  .connect__channels a { padding: 0 18px; }
  .connect__links { margin-top: 60px; }
}

/* --- Page 4, read out of the file (18 Sep) ------------------------------- */
/* Everything below was out against page 4 and nothing said so: the desktop
   was only ever compared with its own last-approved baseline. tools/design/
   bands.py checks it against page 4 now, and these are the board's numbers. */

@media (min-width: 769px) {
  /* The nav bar and the lineup band are the page's sage at 80% laid over the
     collage, which runs on underneath both — seen, softened, not cut off. The
     artwork used to be clipped at the foot of the nav and the top of the band,
     so it read as a picture with its top and bottom sliced away. */
  /* No blur: at 80% with an 18px blur the artwork underneath turned to an
     invisible smudge, so the bars read as a hard edge slicing the palms and the
     flowers (Arian, 18 Sep, the fifth time of asking). Page 4 has the veil and
     nothing else, and that is what keeps the picture legible under it. */
  .nav:not(.is-stuck) {
    background: rgba(148, 166, 168, 0.8);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .hero__art { overflow: visible; }
  .featuring {
    position: relative;
    z-index: 1;
    background: rgba(148, 166, 168, 0.8);
  }

  /* The band is ruled top and bottom, as AF_Website.pdf draws it: two full-width
     hairlines at y 798.7 and 902.4, a band of 103.7 between them. It was set
     without them on a reading of an older board (18 Sep) and Arian asked for
     them back (23 Sep). The names still ride along the band's top and
     "Featuring" sits small and dropped below their line, where the loop comes
     round after "+ More" — the padding keeps that, less the two rules. */
  .marquee { border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); padding: 33px 0 26px; }

  /* At the top of the page the wordmark is stacked, AWAKE / FESTIVAL / '26, as
     page 4 sets it, with the links along its first line; the bar keeps its 77
     and the name runs on over the collage below it. Once the nav sticks, the
     eye takes its place and the row is centred as before. */
  .nav__wordmark { display: none; }
  .hero__wordmark {
    display: block;
    position: absolute;
    top: 20px;              /* its first line on the nav links' baseline */
    left: 50%;
    transform: translateX(-50%);
    z-index: 21;            /* over the nav bar (20) as it scrolls away */
    width: min-content;
    margin: 0;
    white-space: normal;
    text-align: center;
    font: 300 var(--type-title)/30px var(--face-display);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    pointer-events: none;
  }

  /* The key is one evenly spaced row; its two line-break markers belong to the
     phone and were taking a gap each here. */
  .location__key .location__keyBreak { display: none; }
  /* Page 4's key is one line with a tab before Parking: the numbered stops
     run from the left margin, Parking, Camp Zone and WC close at the right. */
  .location__key { justify-content: flex-start; }
  .location__key .location__keyBreak ~ .location__keyBreak ~ .location__keyBreak + li { margin-left: auto; }

  /* The board's key is one line: the numbers from the left margin, the three
     facilities closing at the right. Under about 1400 that line no longer fits,
     and pushing the facilities right left Parking alone at the end with Camp
     Zone and WC beginning the next line. Narrower than that, the key is simply
     centred and wraps where it must (20 Sep). */
  @media (max-width: 1399px) {
    .location__key { justify-content: center; }
    .location__key .location__keyBreak ~ .location__keyBreak ~ .location__keyBreak + li { margin-left: 0; }
  }

  /* A panel's portrait sits centred between the top of the panel and the
     words, which keep to the foot as the board has them (Chris, 18 Sep): the
     portrait has a row of its own that takes the space the words leave. */
  .item__about--face,
  .item__about:has(> .item__portrait:first-child) {
    grid-template-rows: minmax(0, 1fr) auto;
    align-content: stretch;
    row-gap: 40px;
    padding-top: 40px;
  }
  .item__about > .item__portrait:first-child { align-self: center; margin-bottom: 0; }

  /* The collage comes down with the wordmark, so '26 keeps page 4's distance
     from the birds and clouds under it instead of crowding them. */
  .hero__art { top: calc(var(--s-half) * 2 + 45px + 21px); }
}

/* About, from page 4's own baselines (19 Sep): the label 157 below the band,
   the lead 106 under it, MIND BODY & SOUND 69 under that, the copy 58 lower,
   then 99 to Buy Tickets and 59 to the pills. The block had each of these
   between 14 and 52 short, which read as the whole section sitting high. */
@media (min-width: 769px) {
  .about { padding-top: 141.6px; }
  .about > .label:first-child { margin-bottom: 79.2px; }
  .about__heading { margin-top: 28.8px; }
  .about__body { margin-bottom: 80.5px; }
  .about > .label:not(:first-child) { margin-bottom: 10.8px; }

  /* Location, page 4: the label 42 into the band — the hairline takes back what
     the label gives up, so the photograph does not move — and THE GREEN VILLAGE
     with its baseline on the photograph's centre line, where the board sets it.
     Centring the title and the place as one block had put the title 6 high. */
  .location { padding-top: 27.2px; }
  .location__rule { margin-top: 25.8px; }
  .location__title { transform: translate(-50%, calc(-50% + 3.75px)); }
  .location__place { margin-top: 0.9px; }
}

/* Opening a row places the page itself (js/site.js, bringIntoView). The
   browser's scroll anchoring moved the page as well, to compensate for the
   panel folding away above — so the two fought and the row landed 89 short on
   a phone (18 Sep). */
.rows { overflow-anchor: none; }

/* The line arriving at a later day's wreath, as The Program's arrives at the
   first: the same length and the same gap under it, taken out of the space
   above so the wreath does not move. */
.day + .day { padding-top: calc(var(--s4) - 90px); }
.marker__rule--arrive { height: 53px; margin-bottom: 37px; }
@media (max-width: 768px) {
  .day + .day { padding-top: calc(var(--s4) - 85px); }
  .marker__rule--arrive { height: 67px; margin-bottom: 18px; }
}

/* --- Page 5, from InDesign's own PDF (18 Sep) ----------------------------- */
/* Every number here is a baseline read out of the PDF the designer exported,
   not a frame edge out of the IDML. The export put each label at the top of its
   frame; InDesign draws them well down inside it, and the phone had nearly every
   label 17-28 too high as a result. */
@media (max-width: 768px) {
  /* The hero fills the screen down to the foot of the lineup band; the room
     around the AWAKE mark is what gives and takes — 50 above it and 33 under it
     on the board's 852, more on a taller phone, and never less than 16 and 12,
     so on a very small phone the page runs past the fold instead of the mark
     being squeezed against the nav and the artwork. */
  .hero {
    grid-template-rows: minmax(16px, 50fr) auto minmax(12px, 33fr) auto auto;
    padding-top: 61px;
    padding-bottom: 24px;
  }
  .hero__mark { grid-row: 2; }
  .hero__art { grid-row: 4; margin-top: 0; }
  .hero__meta { grid-row: 5; margin-top: 87px; gap: 27px; }

  /* The lineup: its two rules at 750 and 835, the names centred between them. */
  .featuring { height: 103px; }
  .marquee { padding: 27px 0; }

  /* About */
  .about { padding-top: 5px; }
  .about__body { margin: 149px 0 150px; }   /* within the margins: ten lines, as the PDF sets it */
  .about__heading { margin: 38px 0 29px; }
  .tickets { margin-top: 24px; }

  /* The Program lies over its line: the line stops 22 above the label and
     starts again 21 under it, as the board's knock-out has it. */
  .marker__rule--lead  { height: 36px; margin-bottom: 22px; }
  .marker__rule--trail { height: 45px; margin-top: 21px; }

  /* Under each wreath the line stops clear of the date: 24 above it. */
  .day__title { margin-top: -8px; }
  .day__hours { margin-top: 2px; }
  .filters { margin-top: 46px; }
  .day__head { padding-bottom: 63px; }

  /* Location */
  .location { padding-top: 25px; }
  .location__rule { margin-top: 26px; }
  .location__title { top: 50%; }
  .location__copy { justify-content: center; }
  .location__areaTitle { top: 45.4px; }  /* where text-box is unknown; see the end of the file */
  .location__key { margin-top: calc(var(--map-w) * 0.176 - 7px); }

  /* An open row takes the whole screen: on a phone the nav slides away as the
     page scrolls down to it, so there is no bar to leave room for. */
  .item__panelInner {
    min-height: calc(100vh - var(--row-h, 96px));
    min-height: calc(100svh - var(--row-h, 96px));
  }

  /* Contact */
  /* No lift on a phone: the gap above the monkey and the gap under the words
     are equal, and the monkey shrinks to keep them honest. The lift belongs to
     the board, where there is room to centre the words without taking it from
     one of the two gaps. */
  .connect { --connect-lift: 0px; padding-top: 27px; }
  .connect .label { margin-bottom: 0; }
  .connect__heading { bottom: -7px; }
  .connect__channels { line-height: 24px; }
  .connect__links { margin-top: 60px; }

  /* Sponsors and the foot */
  .spons { padding-top: 27px; }
  .legal { padding-top: 22px; gap: 18px; }
  /* One column the full measure: the flex foot's space-between had left the
     grid's one track only as wide as the policies, so neither line reached the
     right margin. */
  .legal { justify-content: stretch; grid-template-columns: minmax(0, 1fr); }
  .legal__who { justify-self: stretch; display: flex; flex-wrap: wrap; justify-content: space-between; column-gap: 12px; row-gap: 0; }
  .legal__links { justify-self: stretch; justify-content: space-between; }
  .legal::after { height: 20px; margin-top: 3px; }   /* the grid gap is already 18 */
}

/* --- Phones in a real browser (18 Sep) ------------------------------------ */
/* Nothing below the hero is sized by the screen's height on a phone any more.
   Safari's large viewport is ~760 on a 852 iPhone, not 852 — the status bar and
   the collapsed address bar stay — so the map shrank to fit it, and Contact and
   the foot, which shared out whatever height they were given, closed up: the
   key sat 75 higher, INSTAGRAM 91 higher and the footer 30 out (18 Sep). Every
   gap is now page 5's own, read out of the PDF, the same on every phone. */
@media (max-width: 768px) {
  /* Location's map module: the rule, THE AREA 66 below it, the drawing, the
     key, and 52 from the key's last line to the foot of the band. The drawing
     takes page 5's share of the width, up to what a 430 phone gives it. */
  .location__area {
    min-height: 0;
    padding-bottom: 35.5px;              /* the last key line's baseline 51.6 above the band's foot */
    --map-w: min(128.4vw, 552px);
  }

  /* Contact: the label 41 into the module, CONNECT WITH US 388 under it,
     INSTAGRAM 255 under that, the pills 50 above the rule. */
  .connect { padding-bottom: 60px; }
  .connect__channels { margin-top: 0; }

  /* The foot: the sponsors' band runs from the rule to the © line with the logos
     centred in it, then the policies and the closing rule. The board measures
     that band at 732 of its own 9000-tall page and it was set to exactly that —
     which on a 667 screen made the foot 873 tall, so the band could not fill
     the window, it overflowed it, and the logos sat in 545 of empty space
     (Arian, 24 Sep, iPhone SE). The band takes the room the screen has instead,
     and the space above and below the logos gives and takes with it. */
  .foot { min-height: 100vh; min-height: 100svh; }
  .spons { flex: 1; height: auto; }

  .item__panelInner {
    min-height: calc(100vh - var(--row-h, 96px));
    min-height: calc(100lvh - var(--row-h, 96px));
  }

  /* The key's labels on their glyphs' line, as InDesign sets them: they sat 4
     lower than the glyph beside them. */
  .location__glyph { position: relative; top: 4px; }
  .location__key li { position: relative; top: -4px; }

  /* Page 5's © line is one line: the copyright on the left margin, the BRN on
     the right. The company name does not fit beside them at this size, and the
     BRN already identifies it; desktop keeps it. If the line cannot fit on a
     narrow phone the BRN goes under the copyright, on the left. */
  .legal__co { display: none; }
}

/* --- THE AREA on Open in Maps' centre line (19 Sep) ------------------------ */
/* The pill sits where the boards put it, 47 under the rule on page 4 and 36 on
   page 5; the title's capitals are centred on the pill, as both boards have
   them. Trimmed to its capitals, the title's box is just their height, so the
   centring holds in Safari as in Chrome — it had been set as a pixel offset
   from a line box whose capitals sit differently in each, and ran 1-3 high. */
.location__area { --maps-top: 47px; }
@media (max-width: 768px) { .location__area { --maps-top: 36.4px; } }
.location__maps { top: var(--maps-top); }
@supports (text-box: trim-both cap alphabetic) {
  .location__areaTitle {
    text-box: trim-both cap alphabetic;
    top: calc(var(--maps-top) + (var(--pill-h) - 1cap) / 2);
  }
}

