/* ===========================================================================
   CONTENT PAGES
   ---------------------------------------------------------------------------
   Loaded after style.css, which supplies the palette, the type and the whole
   header/menu/footer. Nothing here restates any of that — the chrome must be
   identical to the landing page, and the only way to guarantee that is to use
   the same rules rather than a copy of them.
   ======================================================================== */

.page {
  background: var(--surface, var(--purple));
  color: var(--cream);
  min-height: 100svh;
}
/* Never white, per the art direction — every page picks from the palette.
   Declared on <html> as well as <body> so --surface exists at :root, which is
   where the page transition's backdrop has to read it from. */
.page--purple  { --surface: var(--purple); }
.page--sage    { --surface: var(--sage); }
.page--magenta { --surface: var(--magenta); }
.page--coral   { --surface: var(--coral); }
.page--orange  { --surface: var(--orange); }
.page--sky     { --surface: var(--sky); }
.page--pink    { --surface: var(--pink); }
/* Ink is set on the body copy only — putting it on <html> too would tint the
   transition backdrop's inherited colour for no reason. */
body.page--sage { color: var(--forest); }
body.page--sky,
body.page--pink { color: var(--purple-deep); }

.page__main {
  max-width: 1180px;
  margin: 0 auto;
  padding: calc(var(--header-h) + 26px) 20px 72px;
  display: grid;
  gap: 26px;
}

.page__head { display: grid; gap: 12px; }

/* Every page opens on its title, centred in the viewport. Beyond the look, it
   is what makes the headline hand-off work: the word always flies to the same
   place, so the motion reads as one word travelling rather than as each page
   catching it somewhere different. Only the FIRST head gets this — pages with
   a second one further down keep it inline. */
.page__main > .page__head:first-child {
  min-height: calc(100svh - var(--header-h) - 26px);
  align-content: center;
  justify-items: center;
  text-align: center;
}
.page__main > .page__head:first-child .page__lede { margin-inline: auto; }
.page__main > .page__head:first-child .page__actions { justify-content: center; }
.page__title {
  font-family: var(--display);
  text-transform: uppercase;
  /* dual-axis like the hero: the vw term wins on a phone, the svh cap keeps
     it off the fold on a short laptop */
  font-size: clamp(2.4rem, min(13vw, 8svh), 5.4rem);
  line-height: 1.02;
  letter-spacing: 0.01em;
}
.page__title--sm { font-size: clamp(1.8rem, min(8vw, 5svh), 3rem); }
.page__lede { font-size: clamp(1.05rem, 4.2vw, 1.3rem); max-width: 62ch; line-height: 1.5; }
.page__note { font-size: 1rem; opacity: 0.86; max-width: 68ch; }
.muted { opacity: 0.72; }

.page__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }

.btn {
  display: inline-block;
  padding: 13px 20px;
  border-radius: 999px;
  background: rgba(255, 244, 228, 0.16);
  color: inherit;
  font-weight: 700;
  font-size: 1rem;
  transition: transform 180ms var(--ease-swirl), background 180ms var(--ease-out);
}
.btn:hover { transform: scale(1.03); background: rgba(255, 244, 228, 0.28); }
.btn--go { background: var(--forest); color: var(--cream); }
.btn--go:hover { background: var(--forest); filter: brightness(1.12); }
.btn--small { padding: 10px 16px; font-size: 0.94rem; }
.page--sage .btn { background: rgba(25, 94, 28, 0.14); }
.page--sage .btn:hover { background: rgba(25, 94, 28, 0.22); }
/* The surface override out-specifies .btn--go on its own, which quietly
   stripped the primary action of its solid fill and left "Get directions"
   looking like every other button on the page. */
.page--sage .btn--go { background: var(--forest); color: var(--cream); }
.page--sage .btn--go:hover { background: var(--forest); filter: brightness(1.12); }

/* --- the scroll cue -------------------------------------------------------
   Every page opens on a title block that fills the first screen, which leaves
   the rest below the fold with nothing to indicate it is there.

   Fixed rather than pinned to the bottom of that block. It reads as a control
   rather than as part of the title now that it is this size, and since it
   retires at the first flick of the wheel there is no chance of it trailing
   the reader down the page. Sits clear of the very bottom edge: hard against
   it, it collides with a phone's home indicator and with the browser chrome
   that slides in and out on scroll. */
.page__main > .page__head:first-child { position: relative; }

.cue {
  position: fixed;
  bottom: clamp(26px, 7svh, 72px);
  left: 50%;
  margin-left: -39px;
  width: 78px;
  height: 78px;
  z-index: 3;
  display: grid;
  place-content: center;
  border-radius: 50%;
  color: inherit;
  background: rgba(255, 244, 228, 0.16);
  transition: opacity 400ms var(--ease-out), background 200ms var(--ease-out);
}
.page--sage .cue { background: rgba(25, 94, 28, 0.16); }
.cue svg { width: 39px; height: 39px; }
.cue:hover { background: rgba(255, 244, 228, 0.3); }
/* Nothing to scroll to — withdrawn by js/site.js. Kept as a class rather than
   simply not rendering it, because whether a page scrolls depends on the
   viewport and can change with a rotation or a resize. */
.cue.is-idle { opacity: 0; pointer-events: none; }

/* Two motions rather than one: the ring breathes and the arrow travels. A
   single pulse reads as decoration; something moving DOWNWARD reads as an
   instruction. */
@media (prefers-reduced-motion: no-preference) {
  .cue { animation: cue-breathe 2.6s ease-in-out infinite; }
  .cue svg { animation: cue-fall 2.6s ease-in-out infinite; }
}
@keyframes cue-breathe {
  0%, 100% { opacity: 0.72; }
  50%      { opacity: 1; }
}
@keyframes cue-fall {
  0%, 100% { transform: translateY(-4.5px); }
  50%      { transform: translateY(6px); }
}

/* Once the reader has started scrolling it has done its job and would just be
   nagging. The animation has to be cancelled, not just overridden: a running
   animation sets opacity every frame and outranks a plain declaration, so on
   its own `opacity: 0` here does nothing at all. Same for the idle state. */
.is-scrolled .cue,
.is-scrolled .cue svg,
.cue.is-idle,
.cue.is-idle svg { animation: none; }
.is-scrolled .cue { opacity: 0; pointer-events: none; }

/* --- open / closed banner -------------------------------------------------
   Ships as "Today's hours" and is upgraded by js/site.js. It must never
   render a guess: an OPEN badge on a locked door is worse than no badge. */
.status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  align-self: start;
  padding: 10px 18px;
  border-radius: 999px;
  background: rgba(255, 244, 228, 0.16);
  font-weight: 700;
  font-size: 1rem;
}
.page--sage .status { background: rgba(25, 94, 28, 0.14); }
.status__dot {
  width: 11px; height: 11px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
}
.status.is-open  { background: var(--forest); color: var(--cream); }
.status.is-open .status__dot { background: var(--sage); opacity: 1; }
.status.is-shut  { background: rgba(24, 8, 48, 0.26); color: var(--cream); }
.status.is-shut .status__dot { background: var(--coral); opacity: 1; }
.status__detail { font-weight: 400; opacity: 0.86; }

@media (prefers-reduced-motion: no-preference) {
  .status.is-open .status__dot { animation: pulse 2.4s ease-in-out infinite; }
}
@keyframes pulse { 50% { opacity: 0.35; transform: scale(0.82); } }

/* --- footage as the page ---------------------------------------------------
   The shop itself, full viewport, with the header and the page riding over
   it. Fixed rather than scrolling: the film is the room you are standing in,
   and the content moves through it. */
.filmset {
  position: fixed;
  inset: 0 0 auto 0;
  /* Sized to the LARGE viewport, not the live one. `inset: 0` on iPhone
     Safari tracks the toolbar: as it collapses on scroll the box grows and
     the cover-fitted film re-scales under the page. At 100lvh the box is
     already the size it will be with the toolbar gone, so the only thing
     the collapse reveals is more of the same frame. */
  height: 100vh;
  height: 100lvh;
  z-index: 0;
  pointer-events: none;
}
/* --- the arrival ----------------------------------------------------------
   Vector globe, a flight into it, and a landing on the shop. Every layer is
   driven by CSS animations off one shared start, with the same delays, so the
   pieces cannot drift apart the way two independent clocks would.

     0.00  globe turns          1.50  shop arrives, still blurred
     1.00  flight begins        2.00  blur clears, loop starts
                                3.50  wash and page fade up            */
:root {
  --arr-turn: 1s;      /* the rotation */
  --arr-fly: 0.5s;     /* the flight into the earth */
  --arr-land: 0.5s;    /* the shop resolving out of the same blur */
  --arr-loop: 5s;      /* the living loop, once settled */
}

/* The page itself is transparent so the sequence can show through, which
   leaves nothing painting the ground — the arrival has to supply its own sky
   or the globe floats on white. */
.arrival {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--purple-deep);
}

.earth,
.place { position: absolute; inset: 0; width: 100%; height: 100%; }

/* `meet`, not `slice`: the globe is a subject and wants air around it. Slice
   scales the square viewBox to COVER a portrait screen, which pushes the
   sphere past every edge and loses the planet. */
.earth { transform-origin: 50% 50%; }

/* RADIAL BLUR. CSS has no such filter and SVG's is gaussian, so the artwork is
   stamped several times at increasing scale and falling opacity — smeared
   outward from the centre, which is what a zoom blur is. The stack is inert
   until the flight: every ghost but the first is transparent while the globe
   simply turns. */
.earth__ghost,
.place__ghost {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  /* Promote every layer up front. Without this the compositor creates them
     mid-flight, and on a large display that hand-off lands as a one-frame
     flash — the bigger the surface, the more visible. Declaring the intent
     ahead of time means the layers already exist when the animation starts.
     `backface-visibility` is here for the same reason on WebKit. */
  will-change: transform, opacity;
  backface-visibility: hidden;
}
.earth__ghost:first-of-type { opacity: 1; }

/* A CENTRED SQUARE, not a stretched sheet. This is what makes the flight aim
   true: with the element square, the 400x400 viewBox maps onto it 1:1 and a
   percentage origin means the same thing on every screen. Previously the svg
   filled the viewport and the viewBox was letterboxed inside it, so a fixed
   point in viewBox units landed somewhere different depending on which axis
   the letterboxing ate — and the camera flew off in a different direction on
   every window size. */
.earth__ghost {
  /* `inset` first: it is the shorthand, and setting it after left/top would
     reset them straight back to auto. */
  inset: auto;
  left: 50%;
  top: 50%;
  width: min(100vw, 100svh);
  height: min(100vw, 100svh);
  margin-left: calc(min(100vw, 100svh) / -2);
  margin-top: calc(min(100vw, 100svh) / -2);
  transform-origin: var(--fly-origin, 50% 50%);
}

/* Half size on a desktop window. The globe is a subject, and at full width on
   a wide screen it stops reading as a planet seen from space and starts
   reading as a wall. Composes with the flight's own scale rather than
   replacing it: `scale` and `transform` multiply. */
@media (min-width: 900px) {
  .earth__ghost { scale: 0.5; }
}

/* The photo expands from the matching point on screen, so the cut does not
   jump sideways as one hands over to the other. */
.place__ghost {
  object-fit: cover;
  transform-origin: 50% 44%;
}
.place__ghost:first-of-type { opacity: 1; }

/* --- 0.00s: the globe turns ---------------------------------------------
   The map strip is twice its window and slides one half-width left, which
   wraps seamlessly and lands with North America's west under the camera. */
.earth__map {
  animation: earth-turn var(--arr-turn) cubic-bezier(0.34, 0, 0.2, 1) both;
}
/* Rightward, which is counter-clockwise seen from over the north pole — the
   way the planet turns. It opens on Europe and Africa and carries North
   America in from the left. The end value is set by where Canada sits in the
   traced artwork; site/scene.mjs computes both, so the two cannot drift. */
@keyframes earth-turn {
  from { transform: translateX(var(--turn-from)); }
  to   { transform: translateX(var(--turn-to)); }
}

/* The destination announces itself only at the end of the turn, so the globe
   reads as a globe first and a map pin second. */
.earth__pin {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: pin-in 0.34s var(--ease-swirl) calc(var(--arr-turn) * 0.62) both;
}
@keyframes pin-in {
  from { opacity: 0; scale: 0.2; }
  to   { opacity: 1; scale: 1; }
}

/* --- 1.00s: the flight ---------------------------------------------------
   Each ghost scales harder than the one before it, so the stack pulls apart
   into a streak as the globe rushes past the camera. */
.earth__ghost {
  animation: earth-fly var(--arr-fly) cubic-bezier(0.6, 0, 0.85, 0.3) var(--arr-turn) both;
}
/* 4.6, not 9. On a 1920px screen a 9x scale asks the compositor for a layer
   around 22,000px across — far past its raster budget, and it answers with
   blank tiles for a frame or two. That is what the flashes were. The flight
   reads just as fast at 4.6 because the ghost trail is doing the work, and
   the layer stays inside what can actually be rastered. */
@keyframes earth-fly {
  from { transform: scale(1); opacity: var(--go); }
  70%  { opacity: var(--go); }
  to   { transform: scale(calc(4.6 * var(--gs))); opacity: 0; }
}
/* the leading copy is the sharp one and has to stay opaque longest */
.earth__ghost:first-of-type { animation-name: earth-fly-lead; }
@keyframes earth-fly-lead {
  from { transform: scale(1); opacity: 1; }
  78%  { opacity: 1; }
  to   { transform: scale(4.6); opacity: 0; }
}

/* --- 1.50s: the shop arrives --------------------------------------------
   Out of the same blur, running the other way: the ghosts converge and fade
   out, leaving one sharp photograph. This is the join, and it works because
   both sides of it are built the same way. */
/* Overlaps the flight by 120ms rather than cutting in at the end of it. A
   hard switch at exactly the same instant the globe reaches zero leaves one
   frame where NEITHER is painted — measured, one frame at 1496ms — and one
   frame is all a flash is. Starting early guarantees something is always on
   screen, and the two are both blurred to nothing at that moment anyway. */
.place {
  opacity: 0;
  animation: place-in 0.2s linear calc(var(--arr-turn) + var(--arr-fly) - 0.12s) both;
}
@keyframes place-in { to { opacity: 1; } }

.place__ghost {
  animation: place-land var(--arr-land) cubic-bezier(0.2, 0.8, 0.3, 1)
             calc(var(--arr-turn) + var(--arr-fly)) both;
}
@keyframes place-land {
  from { transform: scale(calc(1 + (var(--gs) - 1) * 6)); opacity: var(--go); }
  to   { transform: scale(1); opacity: 0; }
}
.place__ghost:first-of-type { animation-name: place-land-lead; }
@keyframes place-land-lead {
  from { transform: scale(1.42); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}

/* --- 2.00s: the loop -----------------------------------------------------
   The generated clip takes over from the still. It sits exactly on top of it
   and its first frame IS that still, so nothing is seen to change — the page
   simply starts moving. js/site.js presses play once the blur has cleared;
   until then the video holds frame 0 and the two are indistinguishable. */
.place__film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 44%;
  opacity: 0;
  animation: place-reveal 0.01s linear
             calc(var(--arr-turn) + var(--arr-fly) + var(--arr-land)) both;
}
@keyframes place-reveal { to { opacity: 1; } }

/* Reduced motion gets the destination and none of the journey. */
@media (prefers-reduced-motion: reduce) {
  .earth { display: none; }
  .place,
  .place__ghost:first-of-type { animation: none; opacity: 1; transform: none; }
  .place__ghost { display: none; }
  .place__ghost:first-of-type { display: block; }
  .place__film { display: none; }
}
/* Legibility is not optional over footage. A moving image will pass through
   every value there is, so the copy cannot rely on the frame staying dark —
   the scrim guarantees the contrast rather than hoping for it. Purple rather
   than black, because a neutral wash over a warm shop reads as a fault. */
/* Fully up by default and taken DOWN by js/site.js, which then raises it
   again — along with the page itself — from the fade mark onward. The page
   content works the same way: visible in the stylesheet, faded out only once
   the script has taken charge of it. That way the failure modes — no
   JavaScript, a refused autoplay, a video that never loads — all land on a
   readable page rather than on a blank one. */
.filmset__scrim {
  position: absolute;
  inset: 0;
  /* Lightened a stop: the wash was carrying more than it needed to. The gradient
     still does the work where the work is — behind the title at the top and the
     footer at the bottom — and thins through the middle, where the shop is worth
     looking at and nothing but the address sits over it. */
  background:
    linear-gradient(to bottom,
      rgba(24, 8, 48, 0.66) 0%,
      rgba(24, 8, 48, 0.40) 26%,
      rgba(24, 8, 48, 0.45) 60%,
      rgba(24, 8, 48, 0.72) 100%),
    rgba(59, 23, 105, 0.24);
}
/* The page sits above the film, and its own surface must not paint over it.
   This only works because .filmset is a SIBLING of <main> rather than a child
   of it — see the note in build-site.mjs. Put it inside, and .page__main's own
   z-index makes it a stacking context, the fixed film paints above every
   non-positioned block in the page, and the only things left visible are the
   handful of elements carrying opacity < 1, which form stacking contexts of
   their own. It looks exactly like a compositing bug and is not one. */
.page--film { background: transparent; }
.page--film .page__main,
.page--film .footer { position: relative; z-index: 1; }
/* Panels need more body over footage than over flat colour, or the text sits
   on whatever happens to be moving underneath. */
.page--film .panel { background: rgba(24, 8, 48, 0.62); }
.page--film .footer { background: var(--forest-deep); }

@media (prefers-reduced-motion: reduce) {
  /* held on its final frame instead of playing — see js/site.js */
  .filmset__video { animation: none; }
}

/* --- photo + map ---------------------------------------------------------- */
.shot { margin: 0; border-radius: var(--r-soft); overflow: hidden; }
.shot img { display: block; width: 100%; height: auto; }

.map { display: grid; gap: 10px; }
.map iframe {
  width: 100%;
  height: clamp(240px, 42svh, 420px);
  border: 0;
  border-radius: var(--r-soft);
  /* the embed paints white; a frame keeps it from reading as a hole */
  outline: 3px solid rgba(255, 244, 228, 0.22);
  outline-offset: -3px;
}
.map__link { font-weight: 700; justify-self: start; }
.map__link:hover { text-decoration: underline; text-underline-offset: 3px; }

/* --- panels --------------------------------------------------------------- */
.cols {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}
@media (min-width: 720px) { .cols { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .cols { grid-template-columns: repeat(3, 1fr); } }

.panel {
  background: rgba(255, 244, 228, 0.12);
  border-radius: var(--r-soft);
  padding: 22px;
  display: grid;
  gap: 10px;
  align-content: start;
}
.page--sage .panel { background: rgba(255, 244, 228, 0.42); }
.panel--wide { grid-column: 1 / -1; }
.panel__title {
  font-family: var(--display);
  text-transform: uppercase;
  font-size: clamp(1.3rem, 5vw, 1.7rem);
  line-height: 1.1;
}
.panel p { line-height: 1.5; }
.panel a { text-decoration: underline; text-underline-offset: 3px; }
.panel__note { font-size: 0.95rem; opacity: 0.8; }
.price { font-weight: 700; }

.hours { display: grid; gap: 2px; margin: 0; }
.hours__row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 7px 10px;
  border-radius: 10px;
}
.hours__row dt, .hours__row dd { margin: 0; }
.hours__row.is-today { background: rgba(25, 94, 28, 0.9); color: var(--cream); font-weight: 700; }

/* --- prose ---------------------------------------------------------------- */
.prose {
  max-width: 72ch;
  display: grid;
  gap: 14px;
  font-size: clamp(1rem, 3.8vw, 1.1rem);
  line-height: 1.6;
}
.prose h2 {
  font-family: var(--display);
  text-transform: uppercase;
  font-size: clamp(1.4rem, 5.5vw, 1.9rem);
  line-height: 1.1;
  margin-top: 12px;
}
.prose ul { margin: 0; padding-left: 1.2em; display: grid; gap: 6px; }
.prose a { text-decoration: underline; text-underline-offset: 3px; }

/* --- link grids ----------------------------------------------------------- */
.grid-links { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 680px) { .grid-links { grid-template-columns: repeat(2, 1fr); } }

.card-link {
  display: block;
  background: rgba(255, 244, 228, 0.12);
  border-radius: var(--r-soft);
  padding: 24px;
  color: inherit;
  transition: transform 200ms var(--ease-swirl), background 200ms var(--ease-out);
}
a.card-link:hover { transform: translateY(-3px); background: rgba(255, 244, 228, 0.22); }
.card-link__title {
  font-family: var(--display);
  text-transform: uppercase;
  font-size: clamp(1.5rem, 6vw, 2.1rem);
  line-height: 1.05;
  margin-bottom: 6px;
}

.tops { display: grid; gap: 6px; }
.tops__title {
  font-family: var(--display);
  text-transform: uppercase;
  font-size: clamp(1.15rem, 4.6vw, 1.45rem);
}
.tops__list { line-height: 1.6; opacity: 0.92; max-width: 90ch; }

/* The menus themselves live in style.css with the rest of the chrome, so the
   landing page and every content page share one definition of them. */

/* ===========================================================================
   THE FLAVOUR BOARD
   ---------------------------------------------------------------------------
   Two cards side by side on a desktop, one on a phone, and on a wide display
   the board breaks OUT of the page's 1180px measure — the labels are the
   content here, and a column width chosen for paragraphs is not the right
   width for them.
   ======================================================================== */
.board { display: grid; gap: 22px; }
.board__head { display: grid; gap: 6px; justify-items: center; text-align: center; }
.board__updated { margin: 0; font-size: 1rem; opacity: 0.8; }

/* Wider than the page it sits in. The negative margin is capped so it can
   never exceed the space actually available, which is what stops it forcing a
   horizontal scrollbar on a mid-size window. */
.board {
  /* Half what it was. At full width the labels were competing with the page
     rather than sitting in it — a flavour label is a thing you glance across,
     not a poster. */
  --board-max: 780px;
  width: min(var(--board-max), calc(100vw - 40px));
  justify-self: center;
}

/* Exactly two on a desktop and one on a phone — stated, not left to auto-fit.
   `auto-fit` on a board this wide happily produced three, which makes the
   labels smaller the bigger the display gets: the opposite of the point. */
.flavours {
  display: grid;
  gap: clamp(14px, 2.2vw, 30px);
  grid-template-columns: 1fr;
}
/* Two up on anything wider than a phone held upright. 30rem is 480px — above
   every common phone portrait width and below every tablet, so a phone gets
   one big label and everything else gets the pair. */
@media (min-width: 30rem) {
  .flavours { grid-template-columns: repeat(2, 1fr); }
}

/* --- the card -------------------------------------------------------------
   THREE ELEMENTS, NOT TWO, and the reason is worth writing down: `overflow`
   on a preserve-3d element forces its children FLAT, which makes
   backface-visibility do nothing at all — the first build of this showed a
   mirrored front face where the back should have been. So the card holds the
   aspect and the perspective, an inner wrapper does the rotating, and each
   face clips itself. */
.flav {
  position: relative;
  /* the plate as cut: 2642 x 2434 */
  aspect-ratio: 2642 / 2434;
  perspective: 1600px;
  /* 10% off, so the board reads as a set of labels rather than as a wall */
  width: 90%;
  scroll-margin-top: calc(var(--header-h) + 18px);
}

/* Staggered right-left down the board. Done by ROW rather than by card: in a
   two-column grid every even card is in the left column, so alternating per
   card just pushes each column to its outer edge — a spread, not a stagger.
   The nth-child cycle is four, which is one row of two either way.

   The offset is the 10% the card gives up by being 90% wide, so nothing
   overlaps and nothing leaves the board. */
.flav { justify-self: center; }

@media (min-width: 30rem) {
  .flavours > :nth-child(4n+1),
  .flavours > :nth-child(4n+2) { justify-self: end; }
  .flavours > :nth-child(4n+3),
  .flavours > :nth-child(4n+4) { justify-self: start; }
}
@media (max-width: 29.99rem) {
  .flavours > :nth-child(odd)  { justify-self: end; }
  .flavours > :nth-child(even) { justify-self: start; }
}

.flav__inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 620ms var(--ease-out);
  /* set by js/site.js as the page scrolls — the labels bob gently */
  translate: 0 var(--bob, 0px);
}

.flav__face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

/* The ART is clipped to the printed plate's own corner: 250px on a 2642px
   plate, which as a proportion is 9.46% across by 10.27% down. Stated that way
   it stays a true circular corner at every card size and trims the white the
   label leaves outside its border.

   The INFORMATION side gets a fixed radius instead. Percentages resolve
   against the element's own box, so once a card zooms to fill the viewport
   the same rule gave a 103x78px corner — a stretched ellipse that grew and
   changed shape with the window. A panel of text is not the label artwork and
   should not inherit its geometry. */
.flav__front { border-radius: 9.46% / 10.27%; }
.flav__back { border-radius: var(--r-soft); }
.flav__front {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: inherit;
}
.flav__art { display: block; width: 100%; height: 100%; object-fit: cover; }

.flav__hint {
  position: absolute;
  left: 50%; bottom: 5%;
  transform: translateX(-50%);
  padding: 8px 16px;
  border-radius: 999px;
  background: rgba(24, 8, 48, 0.72);
  color: var(--cream);
  font-size: 0.9rem;
  font-weight: 700;
  /* --pill-o is written by js/site.js from the card's place on screen: 1
     above the centreline, fading to 0 below it. No script, no pill — hover
     and focus still bring it up. */
  opacity: var(--pill-o, 0);
  transition: opacity 220ms var(--ease-out);
}
.flav__front:hover .flav__hint,
.flav__front:focus-visible .flav__hint { opacity: 1; }

/* --- the back ------------------------------------------------------------
   Bright green ground with dark ink, per the brand's own rule that light
   green is a GROUND for dark text and never the text itself. */
.flav__back {
  transform: rotateY(180deg);
  background: var(--sage);
  color: var(--forest);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 16px 20px 22px;
  text-align: left;
}
.flav__close {
  position: sticky;
  top: 0;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding: 12px 22px 12px 14px;
  border: 0;
  border-radius: 999px;
  background: var(--forest);
  color: var(--cream);
  font: inherit;
  font-weight: 700;
  font-size: 1.02rem;
  cursor: pointer;
  box-shadow: 0 6px 18px -8px rgba(0, 0, 0, 0.5);
}
.flav__close svg { width: 20px; height: 20px; }
.flav__close:hover { filter: brightness(1.15); }

.flav__panel h4 {
  margin: 16px 0 4px;
  font-size: 0.76rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.75;
}
.flav__name { font-family: var(--display); font-size: clamp(1.5rem, 3.4vw, 2.2rem); line-height: 1.1; margin: 0; }
.flav__base { margin: 2px 0 12px; opacity: 0.8; font-size: 0.98rem; }
.flav__per { margin: 6px 0 0; font-size: 0.86rem; opacity: 0.75; }

.flav__nutrition {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 0;
  padding: 14px;
  border-radius: var(--r-soft);
  background: rgba(25, 94, 28, 0.14);
  text-align: center;
}
.flav__nutrition dt { font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.8; }
.flav__nutrition dd { margin: 2px 0 0; font-size: 1.2rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.flav__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
/* The dot IS the answer, so colour alone will not do — each carries a mark as
   well, and the text says what it means. */
.dot { position: relative; padding-left: 26px; font-size: 1rem; }
.dot::before {
  content: "";
  position: absolute;
  left: 0; top: 0.4em;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: currentColor;
}
.dot--yes { color: #157a1a; }
.dot--no { color: #b8210a; }
.dot--caution { color: #8a6a00; }
.dot--no::after {
  content: "";
  position: absolute;
  left: 3.5px; top: calc(0.4em + 5.5px);
  width: 6px; height: 2px;
  background: var(--sage);
}
.dot--caution::after {
  content: "";
  position: absolute;
  left: 5.5px; top: calc(0.4em + 3px);
  width: 2px; height: 7px;
  background: var(--sage);
}
.flav__note { margin: 16px 0 0; font-size: 0.86rem; opacity: 0.8; line-height: 1.45; }
.flav__note a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* The blurb is the only text left on this card, and it sits over the brightest
   artwork in the set — a full-bleed rainbow. The wash therefore has to be
   heavier and reach higher than the old one did: it was transparent at the top
   of the box, which put the first line of type over raw yellow. Solid where the
   words are, clearing well above them. */
.flav--trial .flav__meta {
  position: absolute;
  inset: auto 0 0 0;
  padding: 60px 18px 18px;
  background: linear-gradient(to top,
    rgba(24, 8, 48, 0.97) 0%,
    rgba(24, 8, 48, 0.94) 46%,
    rgba(24, 8, 48, 0.72) 72%,
    rgba(24, 8, 48, 0) 100%);
  color: var(--cream);
  border-radius: 0 0 9.46% 9.46% / 0 0 10.27% 10.27%;
}
.flav--trial .flav__blurb { margin: 4px 0 0; font-size: 0.94rem; }

/* --- open: zoom to full viewport -----------------------------------------
   The card leaves the grid and takes the screen. js/site.js measures where it
   was and sets the offset, so the movement starts from the label the reader
   actually tapped rather than from nowhere.

   Not the bounce curve: an overshoot on a rotation ending at exactly 180deg
   sails past it and flashes the hidden side of the front face. */
.flav.is-open {
  position: fixed;
  z-index: 60;
  left: var(--from-x); top: var(--from-y);
  width: var(--from-w); height: var(--from-h);
  margin: 0;
  aspect-ratio: auto;
  transition: left 620ms var(--ease-out), top 620ms var(--ease-out),
              width 620ms var(--ease-out), height 620ms var(--ease-out);
}
.flav.is-open .flav__inner { transform: rotateY(180deg); translate: none; }
.flav.is-zoomed {
  left: max(12px, calc(50vw - 34rem));
  top: calc(var(--header-h) + 10px);
  width: min(68rem, calc(100vw - 24px));
  height: calc(100svh - var(--header-h) - 22px);
}
.flav__scrim {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgba(24, 8, 48, 0.72);
  opacity: 0;
  transition: opacity 420ms var(--ease-out);
}
.flav__scrim.is-in { opacity: 1; }
.flav__spacer { display: block; }

/* --- filters --------------------------------------------------------------- */
.filters { display: grid; gap: 10px; justify-items: start; }
.filters__row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }

.drop { position: relative; }
.drop__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 10px 18px;
  border-radius: 999px;
  background: rgba(255, 244, 228, 0.16);
  font-weight: 700;
  font-size: 0.98rem;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.drop__btn::-webkit-details-marker { display: none; }
.drop__btn:hover { background: rgba(255, 244, 228, 0.28); }
.page--sage .drop__btn { background: rgba(25, 94, 28, 0.14); }
.drop[open] .drop__btn { background: var(--forest); color: var(--cream); }
.drop__caret { width: 13px; height: 8px; transition: transform 200ms var(--ease-out); }
.drop[open] .drop__caret { transform: rotate(180deg); }
.drop__n {
  min-width: 22px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--sage);
  color: var(--forest);
  font-size: 0.82rem;
  text-align: center;
}

.drop__menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 0;
  min-width: 15rem;
  display: grid;
  gap: 2px;
  padding: 8px;
  border-radius: var(--r-soft);
  background: var(--purple-deep);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.7);
}
.opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  color: var(--cream);
  font-size: 0.98rem;
  cursor: pointer;
}
.opt:hover { background: rgba(255, 244, 228, 0.12); }
.opt input { width: 18px; height: 18px; accent-color: var(--sage); }

.chip--clear {
  min-height: 44px;
  padding: 10px 18px;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 244, 228, 0.16);
  color: inherit;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.chip--clear:hover { background: rgba(255, 244, 228, 0.28); }

.filters__count { margin: 0; font-size: 0.98rem; font-weight: 700; }
.filters__warn { margin: 0; font-size: 0.92rem; opacity: 0.9; }

.flav[hidden] { display: none; }
.board__empty { text-align: center; font-size: 1.05rem; padding: 20px; }
.board__smallprint { font-size: 0.88rem; opacity: 0.78; line-height: 1.55; max-width: 72ch; }
.board__smallprint a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* --- entrance -------------------------------------------------------------
   Cards rise and settle as they come into view, on the brand's overshoot
   curve, staggered down the grid. The entrance is on the CARD and the flip is
   on the inner wrapper — keeping them on different elements is what stops one
   transform overwriting the other. */
@media (prefers-reduced-motion: no-preference) {
  .flav { opacity: 0; transform: translateY(26px) scale(0.97); }
  .flav.is-in { opacity: 1; transform: none; transition: opacity 420ms var(--ease-out), transform 620ms var(--ease-swirl); }
  .flav.is-open { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .flav__inner { transition: none; translate: none; }
  .flav.is-open { transition: none; }
}

/* The machine number. Small, in the corner, because it is a fact about where
   the flavour is rather than part of its name — and because it is what makes
   the board's ordering legible: the cards run up the wall in Regina and down
   it in the other two, which is invisible without it. */
.flav__machine {
  position: absolute;
  top: 5%;
  left: 5%;
  z-index: 2;
  display: grid;
  place-items: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.45rem;
  border-radius: 999px;
  background: rgba(24, 8, 48, 0.72);
  color: var(--cream);
  font-family: var(--display);
  font-size: 1rem;
  line-height: 1;
  letter-spacing: 0.02em;
  /* WebKit does not reliably carry the face's backface-visibility down to a
     positioned, z-indexed child: the badge showed through the back as a
     mirrored number. Say it on the badge itself, and take it out entirely
     while the card is turned. */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.flav.is-open .flav__machine,
.flav.is-open .flav__hint { visibility: hidden; }

/* A trial is not a thing you open — there is no spec behind it to turn over
   to — so it must not offer the cursor or the hover lift that say "tap me". */
.flav--trial .flav__front { cursor: default; }
.flav--trial .flav__inner { transition: none; }

/* --- fundraising: steps, profit slider, sticky ORDER NOW ------------------ */
/* The three steps come straight from the poster; the slider is the page's
   one interactive moment and doubles as the price explainer. */

.steps {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr;
}
@media (min-width: 720px) { .steps { grid-template-columns: repeat(3, 1fr); } }
.steps__item {
  background: rgba(255, 244, 228, 0.42);
  border-radius: var(--r-soft);
  padding: 14px 18px;
  display: grid;
  grid-template-columns: 56px 1fr;
  align-items: center;
  gap: 14px;
}
.steps__n {
  font-size: 3.4rem;
  line-height: 0.9;
  color: var(--forest);
  text-align: center;
}
.steps__body { display: grid; gap: 3px; min-width: 0; }
.steps__body b {
  font-size: clamp(0.92rem, 3.6vw, 1.05rem);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: var(--forest);
}
.steps__body p { margin: 0; line-height: 1.4; font-size: 0.95rem; opacity: 0.92; }

.profit {
  margin: 30px 0 8px;
  background: var(--forest);
  color: var(--cream);
  border-radius: var(--r-card);
  padding: clamp(18px, 3.5vw, 26px);
  display: grid;
  gap: 14px;
}
.profit__title {
  font-size: clamp(1.6rem, 6vw, 2.4rem);
  line-height: 1.05;
  text-transform: uppercase;
}
.profit__row { display: grid; gap: 8px; }
.profit__label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: 700;
  font-size: 1.02rem;
}
.profit__label output {
  font-family: var(--display);
  font-size: 1.35rem;
  letter-spacing: 0.02em;
  color: var(--sage);
  font-variant-numeric: tabular-nums;
}
.profit__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 10px;
  border-radius: 999px;
  background: rgba(255, 244, 228, 0.25);
  outline-offset: 4px;
  cursor: pointer;
}
.profit__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--sage);
  border: 4px solid var(--cream);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.profit__slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--sage);
  border: 4px solid var(--cream);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.profit__sum {
  display: grid;
  gap: 3px;
  font-size: clamp(1.02rem, 3.6vw, 1.2rem);
  line-height: 1.5;
  margin: 2px 0 0;
}
.profit__part { white-space: nowrap; }
.profit__arrow { display: none; }
.profit__sum b { font-variant-numeric: tabular-nums; }
.profit__part--keep b {
  font-family: var(--display);
  font-size: 1.5em;
  color: var(--sage);
  letter-spacing: 0.02em;
}
.profit__arrow { opacity: 0.6; }
.profit__fine { font-size: 0.88rem; opacity: 0.75; line-height: 1.5; margin: 0; max-width: 60ch; }

.guarantee {
  margin: 20px 0 26px;
  font-size: clamp(1.05rem, 4vw, 1.25rem);
  line-height: 1.5;
  max-width: 62ch;
  display: flex;
  align-items: center;
  gap: 16px;
}
.guarantee__seal { flex: none; height: 4.5em; width: auto; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.25)); }
.guarantee strong { color: var(--sage); text-transform: uppercase; }
.page--sage .guarantee strong { color: var(--forest); }

.btn--order {
  background: var(--forest);
  color: var(--cream);
  font-size: 1.1rem;
  padding: 15px 30px;
}

/* ORDER NOW: pinned beneath the logo in the fixed header. Always visible,
   links straight to the order form. */
.ordernow {
  position: fixed;
  left: 50%;
  top: calc(var(--header-h) + 14px);
  transform: translateX(-50%);
  z-index: 60;
  font-family: var(--display);
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  padding: 12px 30px 9px;
  border-radius: 999px;
  background: var(--forest);
  color: var(--cream);
  box-shadow: 0 6px 24px rgba(14, 58, 16, 0.5);
  white-space: nowrap;
}
.ordernow:hover { background: var(--forest-deep); }
.ordernow { transition: opacity 160ms var(--ease-out); }
.ordernow.is-hidden { opacity: 0; pointer-events: none; }

/* --- fundraising hero: edge-to-edge title, photo, audience carousel ------- */
/* One viewport tall. The title lines are sized by js/fundraise.js so
   FUNDRAISE runs edge-to-edge (capped by the container on desktop) and
   WITH SWIRL WORLD matches its width exactly. */

.fhero {
  position: relative;
  container-type: inline-size;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  /* reclaim .page__main's top padding so the hero starts at the viewport top */
  margin-top: calc(-1 * (var(--header-h) + 26px));
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

.fhero__title {
  margin: 0 auto;
  position: relative;
  z-index: 2;
  padding: calc(var(--header-h) + 66px) 14px 10px;
  width: 100%;
  max-width: 960px;
  text-align: center;
  text-transform: uppercase;
  font-weight: 400;
  display: grid;
  line-height: 0.92;
  /* A transform, not padding: the title alone drops onto the photo's gradient
     while the photo, cue and strip keep their places (Tim, Aug 2026). */
  transform: translateY(clamp(16px, 4svh, 40px));
}
.fhero__line {
  display: inline-block;
  justify-self: center;
  white-space: nowrap;
  visibility: hidden;             /* until the display font is in — no FOUC */
  transform-origin: 50% 80%;
  /* Deterministic size, identical on every load: the text never changes, so
     its width is a fixed multiple of font-size in Luckiest Guy —
     FUNDRAISE ×5.0256, WITH SWIRL WORLD ×8.8748 (measured once). Sized from
     the container, capped by the 960px title box on desktop. */
  font-size: min(calc((100cqw - 28px) / 5.0256), 185.4px);
}
.fhero__line--2 {
  font-size: min(calc((100cqw - 28px) / 8.8748), 105px);
}
.fhero.is-ready .fhero__line {
  visibility: visible;
  animation: fhero-balloon 700ms var(--ease-swirl) both;
}
.fhero.is-ready .fhero__line--2 { animation-delay: 210ms; }
@keyframes fhero-balloon {
  from { transform: scale(0); opacity: 0; }
  55%  { opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .fhero.is-ready .fhero__line { animation: none; }
}

.fhero__photo {
  flex: 1;
  min-height: 180px;
  position: relative;
  /* the photo slides up under the title so the headline sits partially over
     the gradient — the masked top 20% is what it overlaps */
  margin-top: clamp(-84px, -8svh, -36px);
}
.fhero__photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 54% 50%;
  display: block;
  /* top 20% fades out so the photo hands off to the title area */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 20%);
}

/* The scroll cue lives just above the strip, inside the hero. */
.fhero .cue {
  position: absolute;
  bottom: 66px;
  left: 50%;
  margin-left: -39px;
  /* over the photo the translucent wash disappears — solid backing here */
  background: rgba(14, 58, 16, 0.6);
  color: var(--cream);
}
.fhero .cue:hover { background: rgba(14, 58, 16, 0.85); }

/* Audience carousel — a hero-green strip across the bottom of the viewport,
   one bold line moving right to left forever. */
.fhero__strip {
  background: var(--purple);
  color: #D9C2FF;
  overflow: hidden;
  padding: 13px 0;
}
.fhero__track {
  display: flex;
  width: max-content;
  animation: fhero-marquee 26s linear infinite;
}
.fhero__seq {
  white-space: nowrap;
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  padding-right: 0.4em;
}
@keyframes fhero-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .fhero__track { animation: none; }
}

.page__head--intro { text-align: center; justify-items: center; }
.page__head--intro .page__lede { margin-inline: auto; }

/* --- viewport sections + section arrows ----------------------------------- */
/* Each vsect fits between the fixed ORDER NOW pill and its own arrow. The
   arrows are NOT .cue — site.js retires .cue on scroll, and these must stay. */
.vsect {
  min-height: calc(100svh - 200px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.vsect__lede { text-align: center; margin-inline: auto; }
.vsect .steps { margin-top: 4px; width: 100%; }
.vsect .profit { width: 100%; margin-bottom: 0; }
.vsect .guarantee { margin: 0; }

.varrow {
  margin-top: auto;
  width: 58px;
  height: 58px;
  border: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(25, 94, 28, 0.16);
  color: inherit;
  cursor: pointer;
}
.varrow svg { width: 32px; height: 32px; }
.varrow:hover { background: rgba(25, 94, 28, 0.3); }
/* Was `display: block`, which threw away the grid centring above and left the
   arrow sitting high-left inside its circle. Margin only now. */
.varrow--deck { margin: 22px auto 0; }
.varrow--up { margin-top: 8px; }

/* --- the six S's as a deck of cards --------------------------------------- */
.deckzone {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 40px 0;
}
#s-order { margin-top: 14svh; }
.deck {
  position: relative;
  max-width: 30rem;
  margin: 0 auto;
  height: clamp(340px, 58svh, 470px);
  touch-action: pan-y;
}
.deck__card,
.page--sage .deck__card {
  position: absolute;
  inset: 0;
  /* gradeschool homework: ruled lines every 30px, red margin line at 44px */
  background:
    linear-gradient(to right, transparent 42px, rgba(226, 88, 88, 0.55) 42px, rgba(226, 88, 88, 0.55) 44px, transparent 44px),
    repeating-linear-gradient(to bottom, transparent 0 29px, rgba(112, 156, 210, 0.35) 29px 30px),
    #FFFDF6;
  color: var(--ink);
  border-radius: 8px;
  box-shadow: 0 10px 30px rgba(14, 58, 16, 0.28);
  transform: rotate(var(--rot, 0deg));
  overflow-y: auto;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  padding: 24px 20px 20px 58px;
}
.deck__card .panel__title { color: var(--forest); }
.deck__card p { line-height: 30px; font-size: 0.98rem; margin-bottom: 0; }  /* sit the writing on the ruled lines */
.deck__grade {
  position: absolute;
  top: 10px;
  right: 14px;
  font-family: var(--display);
  font-size: 1.7rem;
  color: #D02818;
  transform: rotate(9deg);
  border: 3px solid #D02818;
  border-radius: 50% / 42%;
  padding: 6px 10px 1px;
  line-height: 1;
  pointer-events: none;
}
.deck__card.is-dragging { cursor: grabbing; transition: none !important; }
.deck__card { transition: transform 380ms var(--ease-swirl), opacity 300ms var(--ease-out); }
.deck__stage { position: relative; width: min(100%, 30rem); margin: 0 auto; }
.deck__stage .deck { max-width: none; }
.deck__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 20;
  width: 52px;
  height: 52px;
  border: 0;
  border-radius: 50%;
  background: rgba(14, 58, 16, 0.55);
  color: var(--cream);
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1;
}
.deck__btn--prev { left: -10px; }
.deck__btn--next { right: -10px; }
.deck__btn:hover { background: rgba(14, 58, 16, 0.8); }

/* --- feedback form --------------------------------------------------------- */
.fb { display: grid; gap: 16px; max-width: 44rem; }
.fb__pills { display: flex; flex-wrap: wrap; gap: 8px; }
.fb__pill {
  border: 2px solid rgba(25, 94, 28, 0.4);
  background: rgba(255, 244, 228, 0.5);
  color: inherit;
  border-radius: 999px;
  padding: 10px 18px;
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
}
.fb__pill.is-on { background: var(--forest); border-color: var(--forest); color: var(--cream); }
.fb__text {
  width: 100%;
  border: 2px solid rgba(25, 94, 28, 0.35);
  border-radius: var(--r-soft);
  padding: 14px;
  font: inherit;
  background: rgba(255, 255, 255, 0.85);
  color: var(--ink);
}
.fb__check { display: flex; gap: 10px; align-items: center; font-weight: 700; margin: 14px 0 6px; cursor: pointer; }
.fb__check input { width: 22px; height: 22px; accent-color: var(--forest); }
.fb__contact { display: grid; gap: 10px; margin: 6px 0 4px; }
.fb__field { display: grid; gap: 4px; font-size: 0.92rem; font-weight: 700; }
.fb__field input {
  border: 2px solid rgba(25, 94, 28, 0.35);
  border-radius: 12px;
  padding: 11px 13px;
  font: inherit;
  background: rgba(255, 255, 255, 0.85);
  color: var(--ink);
}
.fb__note { font-size: 0.9rem; opacity: 0.8; margin: 4px 0 12px; }
.fb__send { background: var(--forest); color: var(--cream); font-size: 1.05rem; padding: 14px 30px; }
.fb__send:disabled { opacity: 0.6; }
.fb__err { color: #8f1d12; font-weight: 700; font-size: 0.92rem; margin: 8px 0 0; }
.fb__done { max-width: 44rem; }

/* Short viewports (small phones, squat laptop windows): compact the offer
   section so lede + 1-2-3 + arrow always fit between ORDER NOW and the fold. */
@media (max-height: 720px) {
  .vsect { gap: 10px; min-height: calc(100svh - 180px); }
  .vsect__lede { font-size: 1rem; line-height: 1.35; }
  .steps { gap: 8px; }
  .steps__item { padding: 10px 14px; grid-template-columns: 44px 1fr; gap: 10px; }
  .steps__n { font-size: 2.6rem; }
  .steps__body p { font-size: 0.88rem; }
  .varrow { width: 48px; height: 48px; margin-bottom: 2px; }
  .varrow svg { width: 26px; height: 26px; }
  .profit { padding: 16px 18px; gap: 10px; }
  .guarantee__seal { height: 3.6em; }
}

/* feedback, minimal: discreet side-by-side optional dropdowns, attach link */
.fb--one { max-width: 34rem; }
.fb__row { display: flex; gap: 8px; margin: 10px 0 4px; }
.fb__select {
  flex: 1;
  min-width: 0;
  border: 0;
  border-bottom: 2px solid rgba(25, 94, 28, 0.3);
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.9rem;
  padding: 7px 2px;
}
.fb__attach {
  display: inline-block;
  margin: 8px 0 0;
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--forest);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* pages whose content must share the first viewport with the title */
.page__main > .page__head--tight:first-child { min-height: 0; align-content: start; }

/* site dialogs (feedback confirmation, robot check) */
.sdlg {
  border: 0;
  border-radius: var(--r-soft);
  padding: 0;
  max-width: 26rem;
  width: calc(100% - 40px);
  background: var(--cream);
  color: var(--ink);
}
.sdlg::backdrop { background: rgba(35, 16, 63, 0.5); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.sdlg__in { padding: 24px; text-align: center; display: grid; gap: 10px; justify-items: center; }
.sdlg__in .panel__title { color: var(--forest); }

/* sky-page variants for the shared fundraising/catering components */
.page--sky .panel { background: rgba(255, 255, 255, 0.5); }
.page--sky .steps__item { background: rgba(255, 255, 255, 0.5); }
.page--sky .steps__n, .page--sky .steps__body b { color: var(--purple); }
.page--sky .varrow { background: rgba(59, 23, 105, 0.18); }
.page--sky .varrow:hover { background: rgba(59, 23, 105, 0.32); }
.page--sky .guarantee strong { color: var(--purple); }
.page--sky .cue { background: rgba(59, 23, 105, 0.16); }
/* Catering: the two solid blocks go dark blue, not forest — the sky page's own
   deep tone (Tim, Aug 2026). Cream text on #1D4E89 is 7.9:1. */
.page--sky { --deep-blue: #1D4E89; --deep-blue-hover: #153A66; }
.page--sky .ordernow { background: var(--deep-blue); box-shadow: 0 6px 24px rgba(21, 58, 102, 0.5); }
.page--sky .ordernow:hover { background: var(--deep-blue-hover); }
.page--sky .profit { background: var(--deep-blue); }

/* ==========================================================================
   TREATS — full-bleed themed bands with wavy joins, per the reference set.
   ========================================================================= */
.tzmain { gap: 0; padding-left: 0; padding-right: 0; padding-bottom: 0; max-width: none; }
.tzmain .tz-hero { padding-left: 20px; padding-right: 20px; }
.tzsec {
  position: relative;
  width: 100%;
  min-height: 88svh;
  display: flex;
  flex-direction: column;
}
.tz-wave { display: block; width: 100%; height: clamp(38px, 7vw, 90px); }
.varrow--band {
  margin: auto auto 26px;
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
}
.varrow--band:hover { background: rgba(255, 255, 255, 0.38); }

/* hero confetti */
.tz-hero { position: relative; overflow: hidden; }
.tz-hero > * { position: relative; z-index: 1; }
.tz-confetti { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.tz-sprinkle {
  position: absolute;
  width: 18px; height: 6px; border-radius: 4px;
  opacity: 0.85;
  animation: tz-drift 6s ease-in-out infinite alternate;
}
.tz-sprinkle--dot { width: 10px; height: 10px; border-radius: 50%; }
@keyframes tz-drift {
  from { translate: 0 -14px; rotate: 0deg; }
  to   { translate: 0 14px;  rotate: 40deg; }
}

/* B - gift boxes over the chocolate table */
.tz-sb1 {
  background: #17100C url('../assets/treats/sb-bg.webp') center / cover no-repeat;
  min-height: 100svh;
}
.tz-sb1__top { flex: 1; display: grid; justify-items: center; align-content: start; padding: calc(var(--header-h) + 20px) 18px 0; }
.tz-sb1__title { font-size: clamp(2.6rem, 9vw, 4.6rem); color: var(--coral); line-height: 1; text-transform: lowercase; }
.tz-sb1__sub { color: var(--coral); font-weight: 700; font-size: clamp(1rem, 3.6vw, 1.3rem); margin: 4px 0 18px; text-align: center; }
.tz-sb1__boxes { position: relative; width: min(86vw, 560px); aspect-ratio: 4 / 3.4; }
.tz-sb1__box { position: absolute; width: 88%; height: auto; filter: drop-shadow(0 16px 30px rgba(0, 0, 0, 0.55)); border-radius: 8px; }
.tz-sb1__box--back { top: 0; right: 0; transform: rotate(6deg); }
.tz-sb1__box--front { bottom: 0; left: 0; transform: rotate(-6deg); }
.tz-sb1__band { margin-top: auto; }
.tz-wave--band { margin-bottom: -1px; }
.tz-price { background: #3B2314; padding: 4px 20px 54px; }
.tz-price__table { width: min(100%, 34rem); margin: 0 auto; border-collapse: collapse; font-size: clamp(1.05rem, 4vw, 1.4rem); }
.tz-price__table th { font-weight: 400; color: rgba(255, 255, 255, 0.9); text-align: left; padding: 6px 10px 12px; border-bottom: 2px solid rgba(255, 244, 228, 0.5); }
.tz-price__table td { padding: 12px 10px; }
.tz-price__classic { color: #fff; }
.tz-price__artisan { color: #F5C518; }
.tz-sb1 .varrow--band { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); margin: 0 0 10px; }

/* C - three swirlberry bands */
.tz-band { position: relative; color: #fff; }
.tz-band--lilac { background: #B57FE0; }
.tz-band--grape { background: #7B2FB5; }
.tz-band--noir  { background: #111; }
.tz-band__kicker { text-align: center; font-weight: 700; padding: 18px 20px 0; margin: 0; font-size: clamp(0.95rem, 3.4vw, 1.15rem); }
.tz-band__row {
  width: min(100%, 56rem);
  margin: 0 auto;
  padding: 26px 22px 40px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 20px;
  align-items: center;
}
.tz-band__row--flip { grid-template-columns: auto 1fr; }
.tz-band__copy h3 { font-weight: 400; font-size: clamp(1.9rem, 6.5vw, 2.8rem); text-transform: uppercase; margin: 0 0 6px; }
.tz-band__lead { font-weight: 700; margin: 0 0 8px; }
.tz-band__copy p { margin: 0 0 6px; line-height: 1.5; max-width: 46ch; }
.tz-band__art { width: clamp(110px, 22vw, 220px); height: auto; }
.tz-gold { color: #F5C518; }
@media (max-width: 560px) {
  .tz-band__row, .tz-band__row--flip { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .tz-band__row--flip img { order: 2; }
}

/* D - pints on sky */
.tz-pints { background: var(--sky); color: var(--purple-deep); }
.tz-pints .tz-wave { margin-bottom: 8px; }
.tz-pints__row {
  width: min(100%, 60rem);
  margin: auto;
  padding: 20px 22px 30px;
  display: grid;
  gap: 30px;
  align-items: center;
}
@media (min-width: 760px) { .tz-pints__row { grid-template-columns: 1.1fr 1fr; } }
.tz-pints__copy h2 { font-weight: 400; font-size: clamp(2.2rem, 7vw, 3.4rem); text-transform: uppercase; margin: 0 0 4px; }
.tz-pints__copy p { line-height: 1.55; max-width: 50ch; }
.tz-pints__tip { font-size: 0.92rem; opacity: 0.8; }
.tz-pints__shot { margin: 0; transform: rotate(3deg); }
.tz-pints__shot img {
  width: 100%; height: auto; border-radius: 18px;
  border: 8px solid #fff; box-shadow: 0 18px 40px rgba(59, 23, 105, 0.35);
}
.tz-pints .varrow--band { background: rgba(59, 23, 105, 0.2); color: var(--purple-deep); }
.tz-pints .varrow--band:hover { background: rgba(59, 23, 105, 0.34); }

/* E - poppers */
.tz-pop { background: #C8283C; }
.tz-pop__hero {
  background: linear-gradient(120deg, #E4322B, #B32BB8 40%, #F5A623 80%, #FFE04A);
  text-align: center;
  padding: 26px 20px 40px;
}
.tz-pop > .tz-wave:first-child { background: linear-gradient(120deg, #E4322B, #B32BB8 40%, #F5A623 80%, #FFE04A); }
.tz-pop__logo { width: min(84vw, 520px); height: auto; filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.35)); }
.tz-pop__price { color: #fff; font-size: clamp(1.5rem, 5.4vw, 2.2rem); margin: 10px 0 0; text-shadow: 0 3px 8px rgba(0, 0, 0, 0.35); }
.tz-pop__band { text-align: center; color: #fff; padding: 0 0 40px; }
.tz-pop__band h3 { font-size: clamp(1.15rem, 4.4vw, 1.6rem); text-transform: uppercase; letter-spacing: 0.04em; margin: 26px 0 4px; font-weight: 400; }
.tz-pop__band h3 b { font-weight: 800; }
.tz-pop__band p { margin: 0; opacity: 0.95; }
.tz-pop__band--red { background: #C8283C; }
.tz-pop__band--purple { background: #8B27B8; }
.tz-pop__band--blue { background: #1F8FE0; }
.tz-pop__band--green { background: #2FA43C; }
.tz-pop .varrow--band { margin-top: 4px; }

/* F - boba on orange */
.tz-boba { background: var(--orange); color: #4A1D06; }
.tz-boba__in { width: min(100%, 44rem); margin: auto; padding: 16px 22px 34px; text-align: center; }
.tz-boba__in h2 { font-weight: 400; font-size: clamp(2.1rem, 7vw, 3.2rem); text-transform: uppercase; margin: 0 0 4px; }
.tz-boba__in p { line-height: 1.55; }
.tz-boba__chips { display: flex; gap: 10px; justify-content: center; margin-top: 14px; }
.tz-boba__chips span { background: #fff; color: #B03A2E; border-radius: 999px; padding: 8px 20px; font-weight: 700; }
.tz-boba .varrow--band { background: rgba(74, 29, 6, 0.18); color: #4A1D06; }
.tz-boba .varrow--band:hover { background: rgba(74, 29, 6, 0.32); }

/* G - order on sage */
.tz-order { background: var(--sage); color: var(--forest); min-height: 70svh; }
.tz-order__in { margin: auto; text-align: center; padding: 12px 22px 40px; display: grid; justify-items: center; gap: 8px; }
.tz-order .varrow--up { background: rgba(25, 94, 28, 0.18); color: var(--forest); margin-top: 16px; }

/* the fixed up-navigator, standing where ORDER NOW stands elsewhere */
.upnav {
  position: fixed;
  top: calc(var(--header-h) + 14px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  width: 54px; height: 54px;
  border: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 244, 228, 0.24);
  color: var(--cream);
  cursor: pointer;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.upnav:hover { background: rgba(255, 244, 228, 0.4); }
.upnav svg { width: 28px; height: 28px; }
.upnav[hidden] { display: none; }
body.is-locked .upnav { opacity: 0; pointer-events: none; }

/* --- treats, refined: full-viewport hero, bigger waves as the only break,
       one-viewport sections C and E, centred pints ---------------------- */

/* A: the hero owns its whole viewport; its arrow sits where every section
   arrow sits — the bottom edge. */
.tz-hero { min-height: calc(100svh - var(--header-h) - 26px); }
.tz-hero .varrow { margin-bottom: 26px; }

/* the wave is the boundary — taller, longer, no strip remnants */
.tz-wave { height: clamp(64px, 12vw, 170px); margin: 0; }
.tz-pints .tz-wave { margin-bottom: 0; }
.tz-pop > .tz-wave:first-child { background: none; }
.tz-wave--inhero { height: clamp(50px, 9vw, 130px); margin: 0 -20px; width: calc(100% + 40px); }
.tz-pop__hero { padding-top: 0; }

/* B: header, subheader and boxes ride lower, clear of the fixed up-arrow */
.tz-sb1__top { padding-top: calc(var(--header-h) + 96px); }

/* C: one viewport, art alternating beside the text */
.tz-sb2 { min-height: 100svh; }
.tz-sb2 .tz-band { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.tz-sb2 .tz-band .tz-wave { flex: none; }
.tz-band__kicker { padding-top: 10px; }
.tz-band__row { padding: 8px 22px 16px; gap: 16px; }
.tz-band__copy h3 { font-size: clamp(1.5rem, 5vw, 2.2rem); }
.tz-band__copy p { font-size: 0.95rem; line-height: 1.45; }
.tz-band__art { width: clamp(84px, 15vw, 150px); }
.tz-sb2 .varrow--band { margin-bottom: 14px; }

/* D: pints, centre-aligned */
.tz-pints__copy { text-align: center; }
.tz-pints__copy p { margin-inline: auto; }

/* E: poppers in one viewport, logo clear under the site header */
.tz-pop { min-height: 100svh; }
.tz-pop__hero { padding-bottom: 18px; }
.tz-pop__logo { width: min(62vw, 340px); }
.tz-pop__price { font-size: clamp(1.2rem, 4.2vw, 1.7rem); margin-top: 6px; }
.tz-pop__band { flex: 1; display: flex; flex-direction: column; justify-content: center; padding-bottom: 14px; }
.tz-pop__band .tz-wave { flex: none; height: clamp(28px, 5vw, 64px); }
.tz-pop__band h3 { margin: 8px 0 2px; font-size: clamp(1rem, 3.6vw, 1.35rem); }
.tz-pop__band p { font-size: 0.9rem; }
.tz-pop .varrow--band { margin-top: 6px; margin-bottom: 16px; }

/* --- round-20 verification fixes ------------------------------------- */

/* C: waves pinned to the band top (no self-coloured sliver above them);
   copy centres in the space that remains. Kicker rides below the fixed
   up-arrow. */
.tz-sb2 .tz-band { justify-content: flex-start; }
.tz-sb2 .tz-band__row { margin-block: auto; }
.tz-band__kicker { padding-top: 48px; }

/* E: the poppers logo is display:block, so centre it with margins and
   drop it below the site logo + up-arrow; band waves pinned to the top
   with the flavour copy centred beneath. */
.tz-pop__logo { margin: 78px auto 0; }
.tz-pop__band { justify-content: flex-start; }
.tz-pop__band h3 { margin: auto 0 2px; }
.tz-pop__band p { margin: 0 0 auto; }

/* C: reclaim the kicker padding so the section stays one viewport */
.tz-band__kicker { padding-top: 36px; margin-block: 0; }
.tz-band__row { padding: 4px 22px 8px; }

/* --- Aug 2026 iPhone review (Tim) ------------------------------------- */

/* The three "stripes" behind section arrows were the SECTION's own
   background showing under its arrow, which sits below the last band:
   tz-sb2 had none (page purple), tz-pop was the first band's red, and the
   fundraising deck's `#s-order { margin-top: 14svh }` leaked a purple gap
   above Treat yourself. Each section now ends in its last band's colour. */
.tz-sb2 { background: #111; }
.tz-pop { background: #2FA43C; }
.tzmain #s-order { margin-top: 0; }

/* The page ends at the footer: the root canvas is what iOS shows when the
   page rubber-bands past the bottom, so it takes the footer's green rather
   than the body purple. */
html:has(.tzmain) { background: var(--forest); }

/* B: Swirlberries title and sub-line bigger, with a shadow for the photo */
.tz-sb1__title { font-size: clamp(3rem, 10.6vw, 5.6rem); text-shadow: 0 3px 10px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.5); }
.tz-sb1__sub { font-size: clamp(1.15rem, 4.4vw, 1.55rem); text-shadow: 0 2px 8px rgba(0, 0, 0, 0.65); }
/* boxes reversed: the 12-pack (box1) now sits behind, a touch larger; the
   six (box2) in front */
.tz-sb1__box--back { width: 94%; }
.tz-sb1__box--front { width: 82%; }

/* A: hero copy centred in the viewport, with the Tuesday topping arcs above
   and (mirrored) below it. Same icon pool, same 2 s bounce-and-swap. */
.tz-hero { justify-content: center; }
.tz-hero__core { display: grid; justify-items: center; gap: 12px; margin: auto 0; width: 100%; }
.tz-hero__core .page__title, .tz-hero__core .page__lede, .tz-hero__core .page__actions { margin: 0; }
.tz-hero__core .page__lede { margin-inline: auto; }
/* The arcs ARE the Tuesday page's .tiut__arc / .tiut__arc-slot / .tiutk__fl
   (same CSS, same kin-bounce) — only the lower one is flipped. The slots'
   --ay is mirrored in the markup, so the arc opens downward as a reflection
   rather than as the same arc moved. */
.tz-hero .tiut__arc { width: min(100%, 420px); }
.tz-hero .tiut__arc-slot { width: calc(clamp(46px, 13vw, 72px) * var(--s, 1)); }
.tz-hero .tiut__arc-slot img { filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.35)); }

/* F: the boba product shot, framed like the pints photo, on the orange band */
/* a cut-out, so it floats on the orange like the poppers logo does */
.tz-boba__shot { margin: 8px auto 0; width: min(60vw, 340px); }
/* one viewport, copy + photo centred under the wave and clear of the header */
.tz-boba { min-height: 100svh; }
.tz-boba__in p { margin-block: 0 8px; }
.tz-boba__shot img {
  width: 100%; height: auto; display: block;
  filter: drop-shadow(0 16px 28px rgba(74, 29, 6, 0.4));
}
.tz-boba__in { padding-top: 110px; padding-bottom: 18px; }   /* clears the fixed up-arrow when the arrow scroll lands here */

/* --- about: founder story -------------------------------------------------
   The About page is one long first-person read, so it needs three things the
   rest of the site does not: a lead paragraph that sits above body size, the
   family snapshot as a physical object rather than a flush photo, and two
   card lists (flavour provenance, ownership promises) that break the column
   before it gets tiring. */
.prose__lede {
  font-size: clamp(1.12rem, 4.2vw, 1.3rem);
  line-height: 1.45;
}

/* The snapshot is tilted and matted like a print left on the counter. It is a
   family photo on a page about a family business; a flush-cropped rectangle
   would read as stock. */
.snap {
  margin: 8px auto;
  max-width: 21rem;
  background: var(--card);
  padding: 12px 12px 0;
  border-radius: 6px;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.55);
  transform: rotate(-2.5deg);
  transition: transform 400ms var(--ease-swirl);
}
.snap:hover, .snap:focus-within { transform: rotate(1.5deg) scale(1.02); }
.snap img { display: block; width: 100%; height: auto; border-radius: 3px; }
.snap figcaption {
  font-family: var(--display);
  color: var(--purple);
  font-size: 1.05rem;
  line-height: 1.25;
  text-align: center;
  padding: 12px 4px 15px;
}
.snap figcaption small {
  display: block;
  font-family: var(--text);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(35, 16, 63, 0.62);
  margin-top: 6px;
}

/* Flavour provenance. The top rule is the only colour each card carries, so
   four of them together read as a set rather than a paint chart. */
.passports {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr;
}
@media (min-width: 680px) { .passports { grid-template-columns: repeat(2, 1fr); } }
.passport {
  background: rgba(255, 244, 228, 0.12);
  border-radius: var(--r-soft);
  border-top: 5px solid var(--accent, var(--coral));
  padding: 18px 20px 20px;
  display: grid;
  gap: 6px;
}
.passport--coral   { --accent: var(--coral); }
.passport--magenta { --accent: var(--magenta); }
.passport--sage    { --accent: var(--sage); }
.passport--sky     { --accent: var(--sky); }
.passport h3 {
  font-family: var(--display);
  font-size: 1.2rem;
  line-height: 1.1;
  margin: 0;
}
.passport p { margin: 0; font-size: 0.97rem; line-height: 1.55; }

/* The four ownership promises. Heading and body stack rather than sitting on
   a shared baseline, so cards of unequal length still align down the left. */
.promises {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
  max-width: 72ch;
}
.promises li {
  background: rgba(255, 244, 228, 0.1);
  border: 1px solid rgba(146, 214, 111, 0.35);
  border-radius: var(--r-soft);
  padding: 16px 22px 18px;
  display: grid;
  gap: 4px;
}
.promises b {
  font-family: var(--display);
  font-weight: 400;
  color: var(--sage);
  font-size: 1.15rem;
  line-height: 1.15;
}
.promises span { font-size: 0.97rem; line-height: 1.55; }

.sig {
  font-family: var(--display);
  font-size: 1.45rem;
  color: var(--coral);
  margin-top: 8px;
}
.sig span {
  display: block;
  font-family: var(--text);
  font-size: 0.76rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  opacity: 0.72;
  margin-top: 5px;
}

/* --- about: FAQ accordion --------------------------------------------------
   Ten questions stacked as open prose buried the sign-off above them, so each
   one is a <details>. Native disclosure, so it works with no JS, is keyboard
   operable, and stays findable: browser in-page search opens a closed
   <details> in every current engine. */
.faq {
  display: grid;
  gap: 8px;
  max-width: 72ch;
}
.faq__item {
  background: rgba(255, 244, 228, 0.1);
  border-radius: var(--r-soft);
  overflow: hidden;
}
.faq__q {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 15px 20px;
  font-weight: 700;
  font-size: 1.02rem;
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
  transition: background 200ms var(--ease-out);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { background: rgba(255, 244, 228, 0.14); }
.faq__q:focus-visible { outline: 3px solid var(--sage); outline-offset: -3px; }
/* The caret is drawn from the summary itself rather than the default marker,
   which cannot be positioned consistently across engines. */
.faq__q::after {
  content: '';
  margin-left: auto;
  flex: none;
  width: 11px;
  height: 11px;
  border-right: 2.6px solid var(--sage);
  border-bottom: 2.6px solid var(--sage);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 220ms var(--ease-out);
}
.faq__item[open] .faq__q { background: rgba(255, 244, 228, 0.16); }
.faq__item[open] .faq__q::after { transform: rotate(225deg) translate(-3px, -3px); }
.faq__a { padding: 0 20px 18px; }
.faq__a p { margin: 0; font-size: 0.99rem; line-height: 1.6; }
.faq__a a { text-decoration: underline; text-underline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .snap, .faq__q, .faq__q::after { transition: none; }
  .snap:hover, .snap:focus-within { transform: rotate(-2.5deg); }
}

/* The About lede is a two-line hook and its own answer; the second line is the
   line, so it takes the accent rather than the .muted grey used elsewhere. */
.lede__pop {
  font-family: var(--display);
  color: var(--sage);
  font-size: 1.35em;
  line-height: 1.15;
  display: inline-block;
  margin-top: 6px;
}

/* ===========================================================================
   ABOUT — THE STORY BANDS  (body.page--story)
   ---------------------------------------------------------------------------
   The About page is the one long first-person read on the site, and it is
   carried by full-bleed alternating bands rather than the shared purple
   surface: cream and lilac for the story, purple for the two blocks that make
   a claim (the promises, the questions). The rest of the site keeps its own
   treatment — every rule here is scoped to .page--story.

   .page__main is un-boxed for the same reason: its 1180px column and side
   padding would inset the bands, and a band that does not reach the edge is
   just a card. The measure moves inside, onto .prose and .wrap.
   ======================================================================== */
.page--story {
  /* Near-black plum rather than grey: the ground is the brand purple taken
     down until type sits comfortably on it, so the purple claim bands read as
     a step up out of the same family rather than a different page. */
  --story-ground: #1B1026;
  --story-alt:    #241536;
  --story-ink:    #F4ECFA;
  --story-soft:   #BBA6D2;
}
.page--story .page__main {
  max-width: none;
  padding: calc(var(--header-h) + 26px) 0 0;
  gap: 0;
}
/* The hero keeps the shared full-screen head, so the headline hand-off and the
   scroll cue behave exactly as they do everywhere else. */
.page--story .page__main > .page__head:first-child { padding-inline: 20px; }
/* The cue scrolls the first band to the top of the viewport, where the fixed
   header sat on its opening line. Stop short by the header's height. */
.page--story .page__main > .page__head:first-child + * {
  scroll-margin-top: calc(var(--header-h) + 12px);
}

.page--story .band {
  padding: clamp(52px, 9vw, 104px) 20px;
  background: var(--story-ground);
  color: var(--story-ink);
}
.page--story .band--alt { background: var(--story-alt); }
.page--story .band--purple { background: var(--purple); color: var(--cream); }

.page--story .band > .prose,
.page--story .band > .wrap,
.page--story .band > .promises,
.page--story .band > .page__head { margin-inline: auto; }
.page--story .band > .wrap { max-width: 72ch; display: grid; gap: 22px; }
.page--story .band > .wrap--wide { max-width: 980px; }

/* Body copy sits at the artifact's reading size, a step above the site's
   utility pages, because this one is read start to finish. */
.page--story .prose {
  font-size: clamp(1.02rem, 3.6vw, 1.14rem);
  line-height: 1.68;
  gap: 18px;
}
/* Every band is dark now, so the headings take sage throughout — the mid
   purple they used on cream has nowhere near the contrast it needs here. */
.page--story .prose h2 {
  color: var(--sage);
  font-size: clamp(1.55rem, 5.4vw, 2.4rem);
  margin-top: 6px;
}
.page--story .prose__lede { color: var(--story-ink); }
.page--story .prose a { color: inherit; }

/* The snapshot keeps its white mat — it is a print, and the caption under it
   is the one place on the page that stays dark-on-light. */
.page--story .snap figcaption { color: var(--purple); }
.page--story .snap figcaption small { color: rgba(35, 16, 63, 0.62); }
.page--story .sig span { color: var(--story-soft); opacity: 1; }

/* The promises keep the purple treatment they were designed for. */
.page--story .promises { max-width: 72ch; }

/* Fair questions + Find your store share the closing purple band, so the
   second head inside it must not re-centre itself as a page opener. */
.page--story .band--purple .page__head { max-width: 72ch; }
.page--story .band--purple .page__head + .faq { margin: 22px auto 0; }
.page--story .band--purple .faq + .page__head { margin-top: 56px; }

/* --- the hook -------------------------------------------------------------
   The title block is the artifact's hero: three lines, oversized, with the
   answer in green on its own line. It replaces the page name, so it takes the
   page__title scale and then some. */
/* Three lines, and they stay three lines at every width: each is its own
   block and none of them may wrap. The size is driven off vw so the longest
   line ("froyo was dead.") sets the scale — a clamp on its own would break a
   line into an orphan word on a narrow phone, which is the one thing this
   headline cannot do. The svh term keeps all three off the fold on a short
   laptop window. */
.story__hook {
  text-transform: none;
  line-height: 1.02;
  letter-spacing: 0.005em;
  max-width: none;
}
.story__hook span {
  display: block;
  white-space: nowrap;
  font-size: min(9.4vw, 7.2svh, 5rem);
}
.story__pop { color: var(--sage); margin-top: 0.1em; }

/* .btn's translucent cream reads on all three grounds here; it only needs
   its underline suppressed, since .prose underlines every link inside it. */
.page--story .band .btn { text-decoration: none; }

/* Section eyebrows. The sitewide .eyebrow is sized for a hero band; inside the
   story's reading column it steps down and takes the soft ink, so it labels
   the section without competing with the heading under it. Dates and section
   names both live here — the eyebrow says WHERE you are in the story. */
.page--story .prose .eyebrow,
.page--story .page__head--story .eyebrow {
  font-size: 0.8rem;
  letter-spacing: 0.16em;
  color: var(--story-soft);
  margin-bottom: -8px;
}
.page--story .band--purple .prose .eyebrow { color: rgba(255, 244, 228, 0.72); }

/* The family photograph is served small and made awkward to lift: no drag, no
   selection, no long-press save sheet on iOS, and a transparent layer over the
   image so a right-click lands on the figure (which cancels the menu) rather
   than on the <img>. None of this is real protection — anything on screen can
   be screenshotted, and the file is still a URL — it only raises the effort
   above idle curiosity. The real safeguards are the reduced resolution and
   noimageindex. */
.page--story .snap { position: relative; }
.page--story .snap img {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  pointer-events: none;
}
.page--story .snap::after {
  content: '';
  position: absolute;
  inset: 0;
}

/* ===========================================================================
   TOP-IT-UP TUESDAY — the deal's page. Sign-styled: the loudest page on the
   site, on the brand purple, with the Big Cup Rule as a till-sign card.
   ========================================================================= */
/* The main is un-boxed so the video sections run edge to edge (the treats
   page's .tzmain move); the text sections get the column back individually. */
.page--tiut .page__main {
  text-align: center;
  max-width: none;
  padding-left: 0;
  padding-right: 0;
  /* the flash belt's max-content track would otherwise blow the grid's auto
     column out to the belt's full width and shove the centred copy off
     screen — clamp the track so intrinsic size can never win. minmax(0, 1fr)
     rather than minmax(0, 100%): the percentage let the track shrink-wrap
     its widest child, which is why the FAQ list used to grow as an answer
     opened. */
  grid-template-columns: minmax(0, 1fr);
}
.tiut-a, .tiut-b { min-width: 0; }
.page--tiut .tiut__head,
.page--tiut .tiut__ways,
.page--tiut .bigcup,
.page--tiut .tiut__faq,
.page--tiut .page__note {
  max-width: 1180px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 20px;
  padding-right: 20px;
  box-sizing: border-box;
}

/* The backdrop film: fixed, so the page scrolls over a still-standing loop,
   under a plum screen that keeps the type legible. Sibling of <main> (see
   the .page--film note above); main and footer lift above it. */
/* 100lvh for the same reason as .filmset: no re-scale as Safari's toolbar goes */
.tiut-bg { position: fixed; inset: 0 0 auto 0; height: 100vh; height: 100lvh; pointer-events: none; }
.tiut-bg video { width: 100%; height: 100%; object-fit: cover; }
.tiut-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(42, 18, 63, 0.82);
}
.page--tiut .page__main,
.page--tiut .footer { position: relative; z-index: 1; }

/* the sprinkle field — fixed above the film and its screen, so the icons
   keep floating over the footage */
.tiut__confetti { position: fixed; inset: 0; z-index: -1; }

/* --- the flash reel: an endless right-to-left belt of 4s clips,
       filling the lower half of the landing screen ------------------------- */
/* Height is the clip width in disguise (9:16): on a phone the 71vw term binds
   and gives a 40vw clip, so two and a half of them show across the screen
   rather than two; on anything wider the half-viewport height still wins. */
.tflash { overflow: hidden; height: min(50svh, 71vw); min-height: 220px; }
.tflash__track {
  display: flex;
  height: 100%;
  width: max-content;
  animation: tflash-roll 60s linear infinite;
  animation-play-state: paused;
}
.tflash.is-on .tflash__track { animation-play-state: running; }
.tflash__group { display: flex; height: 100%; gap: 12px; padding-right: 12px; }
.tflash video {
  display: block;
  height: 100%;
  width: auto;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  border: 1px solid #000;
  background: #000;
}
@keyframes tflash-roll {
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .tflash { overflow-x: auto; }
  .tflash__track { animation: none; }
}

/* --- the 1.5s curtain -------------------------------------------------------
   With JS (html.tiut-js, stamped before first paint) the plum screen and the
   whole page start transparent; the timer stamps is-up and both fade in
   together while the scramble arms. No JS: no class, nothing hidden. */
/* The headline leads by a second (is-live at 1.0s, is-up at 2.0s), so the
   fade is applied to everything AROUND the .kin rather than to the main —
   a child can never be more opaque than its parent. The letters themselves
   are held at zero until the scramble arms them (kin-pop fills backwards). */
.tiut-js .page--tiut .tiut__head > :not(.kin),
.tiut-js .page--tiut .page__main > :not(.tiut-a),
.tiut-js .page--tiut .tflash,
.tiut-js .tiut-bg::after { opacity: 0; transition: opacity 700ms ease-out; }
.tiut-js.is-up .page--tiut .tiut__head > :not(.kin),
.tiut-js.is-up .page--tiut .page__main > :not(.tiut-a),
.tiut-js.is-up .page--tiut .tflash,
.tiut-js.is-up .tiut-bg::after { opacity: 1; }
.tiut-js .page--tiut .kin:not(.is-live) b { opacity: 0; }

/* the anchor hop to the rule card lands softly, clear of the fixed header */
html:has(.page--tiut) { scroll-behavior: smooth; }
.bigcup { scroll-margin-top: calc(var(--header-h) + 16px); }

/* --- viewport A: the landing screen ---------------------------------------
   Copy centred in the upper half, the flash reel filling the lower half. */
/* The main's top padding is pulled back to the header's own height so this
   screen starts at the logo's lower edge; the head then centres its copy in
   the band between the logo and the reel — equal air above the arc and below
   the rule link (Tim, Aug 2026: the block sat hard against the reel). */
.page--tiut .page__main { padding-top: var(--header-h); }
.tiut-a {
  min-height: calc(100svh - var(--header-h));
  display: flex;
  flex-direction: column;
}
.tiut-a .tiut__head {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding-top: 0;
  /* the rule link keeps a little more daylight from the reel than the arc
     keeps from the logo */
  padding-bottom: clamp(8px, 1.4svh, 16px);
}

/* --- the topping arc -------------------------------------------------------
   Four of the homepage TIUT icons arched over the headline: outer slots low,
   inner slots high, each tilted along the curve. The slot places and carries
   the entry pop (same spring as the homepage's .tiutk__illo); the inner
   .tiutk__fl — the homepage class — bobs forever. The pop keys off the same
   is-up that lifts the curtain, so the icons spring in as the page fades up;
   without JS the tiut-js class never lands and they are simply there. */
.tiut__arc {
  position: relative;
  width: min(100%, 740px);
  height: clamp(64px, 10svh, 118px);
  margin: 0 auto;
  pointer-events: none;
}
.tiut__arc-slot {
  position: absolute;
  left: var(--ax);
  top: var(--ay);
  width: calc(clamp(46px, 7vw, 88px) * var(--s, 1));
  transform: translate(-50%, -50%) scale(0) rotate(var(--r, 0deg));
  transition: transform 700ms cubic-bezier(0.34, 1.7, 0.64, 1);
  transition-delay: calc(var(--i) * 90ms);
}
.tiut__arc-slot img { display: block; width: 100%; }
/* the wave's hit — kin-bounce is the homepage section's keyframe (style.css) */
.tiut__arc-slot img.is-hit { animation: kin-bounce 0.28s cubic-bezier(0.3, 1.4, 0.5, 1); }
html:not(.tiut-js) .tiut__arc-slot,
.tiut-js.is-up .tiut__arc-slot {
  transform: translate(-50%, -50%) scale(1) rotate(var(--r, 0deg));
}
@media (prefers-reduced-motion: reduce) {
  .tiut__arc-slot {
    transition: none;
    transform: translate(-50%, -50%) scale(1) rotate(var(--r, 0deg));
  }
  .tiut__arc-slot img.is-hit { animation: none; }
}
/* the scramble also bows to viewport HEIGHT here, so headline + copy + reel
   genuinely compose into one screen on landscape displays; on phones the
   vw term still binds and nothing changes */
.page--tiut .kin__word { font-size: clamp(2.6rem, min(19.8vw, 11.8svh), 16rem); }
.page--tiut .kin__word + .kin__word { font-size: clamp(2.9rem, min(23.5vw, 14.5svh), 18.5rem); }

.tiut__one {
  font-size: clamp(1.3rem, 3.6vw, 2.1rem);
  color: var(--sage);
  margin: clamp(8px, 1.6vh, 16px) 0 0;
}
.tiut__rulelink {
  display: inline-block;
  margin: 8px auto 0;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  color: var(--cream);
  opacity: 0.82;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.tiut__rulelink:hover { opacity: 1; }

/* --- viewport B: the deal, then the shops in motion ------------------------ */
.tiut-b {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
.tiut-b .tiut__ways {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.tiut__least {
  white-space: nowrap;
  font-size: clamp(1.75rem, min(7.9vw, 6.4svh), 3.5rem);
  margin: 0;
}
.way__price {
  margin: 10px 0 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.02rem, 2.2vw, 1.2rem);
  line-height: 1.5;
}
.way__price small { font-size: 0.72em; font-weight: 600; opacity: 0.75; }
/* the pair, framed into the bottom third: each clip covers its half */
/* a touch taller than the bottom third (Tim, Aug 2026): the pair reads
   as two portrait phones rather than two squares */
.tiut-b .tiutv { height: 38svh; min-height: 205px; }
.tiut-b .tiutv__pair { height: 100%; }
.tiut-b .tiutv__vid { height: 100%; width: 50%; aspect-ratio: auto; }
.ways {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  max-width: 1040px;
  margin: clamp(22px, 4.4vh, 44px) auto 0;
}
.way {
  background: rgba(255, 244, 228, 0.1);
  border-radius: 20px;
  padding: 30px 20px 26px;
}
.way__k { font-size: clamp(1.5rem, 3vw, 2.05rem); margin: 0 0 9px; }
.way:nth-child(1) .way__k { color: var(--sky); }
.way:nth-child(2) .way__k { color: var(--sage); }
.way:nth-child(3) .way__k { color: var(--coral); }
.way p { margin: 0; font-size: clamp(1.08rem, 2.3vw, 1.2rem); }
@media (max-width: 640px) {
  .ways { grid-template-columns: 1fr; max-width: 470px; }
  .way { padding: 22px 20px 20px; }
}

/* --- the Big Cup Rule: a till sign, not fine print ------------------------- */
.bigcup { margin-top: clamp(56px, 9vh, 110px); }
.bigcup__card {
  background: var(--cream);
  color: var(--ink);
  max-width: 600px;
  margin: 0 auto;
  padding: clamp(28px, 5vw, 44px) clamp(22px, 4.5vw, 44px);
  border-radius: 26px;
  transform: rotate(-1.2deg);
  box-shadow: 0 14px 34px rgba(20, 8, 40, 0.35);
}
.bigcup__title { color: var(--purple); font-size: clamp(1.9rem, 5.4vw, 3rem); margin: 0 0 14px; }
.bigcup__body { font-size: clamp(1.05rem, 2.4vw, 1.25rem); margin: 0 0 12px; }
.bigcup__body strong { color: var(--purple); }
.bigcup__why { font-size: 0.95rem; font-style: italic; opacity: 0.75; margin: 0; }

/* --- faq + fine print ------------------------------------------------------ */
.tiut__faq { margin-top: clamp(56px, 9vh, 110px); }
/* Sized by the column, never by its contents: the list used to shrink-wrap
   the longest closed question and then grow to the answer on open, so every
   item jumped wider as one expanded. */
.page--tiut .tiut__faq { width: 100%; }   /* a grid item with auto margins shrink-wraps */
.page--tiut .faq { text-align: left; width: min(100%, 72ch); margin-inline: auto; }
.page--tiut .page__note { margin-top: clamp(40px, 7vh, 80px); }

/* --- the sound toggle ------------------------------------------------------ */
.sndbtn {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 40;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 2px solid rgba(255, 244, 228, 0.45);
  background: rgba(43, 18, 63, 0.55);
  color: var(--cream);
  cursor: pointer;
  display: grid;
  place-items: center;
  backdrop-filter: blur(6px);
  transition: transform 160ms var(--ease-swirl), background 160ms ease-out;
}
.sndbtn:hover { transform: scale(1.08); }
.sndbtn:focus-visible { outline: 3px solid var(--sage); outline-offset: 2px; }
.sndbtn svg { width: 26px; height: 26px; }
.sndbtn .sndbtn__slash { display: none; }
.sndbtn.is-off .sndbtn__slash { display: block; }
.sndbtn.is-off .sndbtn__wave { display: none; }
.sndbtn.is-off { opacity: 0.72; }
