/*
 * B2B Lunch Club — 2026
 * Design system + components. No framework, no build step.
 */

/* ------------------------------------------------------------------ *
 * 1. Fonts
 * ------------------------------------------------------------------ */

@font-face {
  font-family: 'Manrope';
  src: url('/wp-content/themes/b2b26/assets/fonts/manrope.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'InterVar';
  src: url('/wp-content/themes/b2b26/assets/fonts/inter.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------------ *
 * 2. Tokens
 * ------------------------------------------------------------------ */

:root {
  /* Brand — sampled from the logo artwork itself, not the old Elementor
     globals. Those had #020266 / #32CCCC, which are hand-typed approximations:
     #020266 is a near-pure blue that vibrates against white and reads cheap at
     large sizes. The mark is actually #242961 — same navy family, more red in
     it, far calmer as a big flat fill. */
  --navy: #242961;
  --navy-deep: #1a1d4d;    /* large dark sections */
  --navy-darkest: #12142f; /* footer */
  --navy-soft: #363c82;    /* hover */
  --teal: #2bbfcb;
  --teal-bright: #48d8e3;
  --teal-ink: #0e6e77;     /* the teal darkened until it passes contrast on cream */

  /* Neutrals — warm, because this is a lunch club, not a bank */
  --cream: #fbf7f0;
  --cream-deep: #f3ece1;
  --white: #ffffff;
  --ink: #1b1d40;
  --muted: #5a5c7d;
  --line: rgba(36, 41, 97, 0.12);
  --line-dark: rgba(255, 255, 255, 0.16);

  /* Type */
  --font-display: 'Manrope', 'Segoe UI', system-ui, sans-serif;
  --font-body: 'InterVar', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

  /* The floor is 14px, not 13.3px. This step carries most of the small print
     on the site — card summaries, the timeline labels, the whole footer — and
     below about 755px the floor is what is actually in use, which is to say
     on every phone. The ceiling and the slope are untouched, so nothing above
     that width moves. */
  --step--1: clamp(0.875rem, 0.79rem + 0.18vw, 0.94rem);
  --step-0: clamp(1rem, 0.95rem + 0.24vw, 1.13rem);
  --step-1: clamp(1.2rem, 1.11rem + 0.44vw, 1.5rem);
  --step-2: clamp(1.44rem, 1.28rem + 0.78vw, 2rem);
  --step-3: clamp(1.73rem, 1.46rem + 1.32vw, 2.66rem);
  --step-4: clamp(2.07rem, 1.63rem + 2.14vw, 3.55rem);
  --step-5: clamp(2.49rem, 1.9rem + 2.6vw, 4.15rem);

  /* Space */
  --gap: clamp(1rem, 0.8rem + 1vw, 1.75rem);
  --section-y: clamp(3.5rem, 2rem + 6vw, 7rem);
  --wrap: 1180px;
  --wrap-narrow: 780px;

  /* Shape */
  --r-sm: 10px;
  --r-md: 18px;
  --r-lg: 28px;
  --r-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(36, 41, 97, 0.06), 0 2px 8px rgba(36, 41, 97, 0.05);
  --shadow-md: 0 2px 4px rgba(36, 41, 97, 0.05), 0 12px 32px -8px rgba(36, 41, 97, 0.14);
  --shadow-lg: 0 4px 8px rgba(36, 41, 97, 0.06), 0 28px 60px -16px rgba(36, 41, 97, 0.22);

  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ------------------------------------------------------------------ *
 * 3. Reset / base
 * ------------------------------------------------------------------ */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

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

/*
 * A film of grain over everything. Large flat areas of a single colour are
 * what make a page read as cheap on a good screen; a few percent of noise
 * gives the cream some tooth. Generated, so it costs about 300 bytes.
 */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.022em;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

h1 { font-size: var(--step-5); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

/*
 * The accent. There was an italic serif here — one phrase of the headline set
 * in a Georgia-ish italic a size up, to give the line a hinge to turn on. It
 * worked against Bricolage, which is wonky enough to take it. Against Manrope
 * it read as another site's type wandering in: the whole reason for choosing
 * Manrope is that it recedes, and a large serif italic in the middle of it does
 * exactly the opposite.
 *
 * One typeface now, and the emphasis comes from weight and colour instead —
 * the phrase drops from 700 to 300 and takes the teal that carries every other
 * accent on the site. Same hinge, no second voice.
 *
 * Any <em> inside a heading gets it, so the accent is still reachable from the
 * Customizer by italicising a phrase — no class to remember.
 */
.accent,
h1 em, h2 em, h3 em {
  font-style: normal;
  font-weight: 300;
  color: var(--teal-ink);
  letter-spacing: -0.02em;
}

/* The ink teal is mixed to pass contrast on cream; on a navy panel it is the
   bright one that has to do the job. */
.section--navy .accent,
.section--navy h1 em,
.section--navy h2 em,
.section--navy h3 em,
.site-footer .accent { color: var(--teal); }

p { margin: 0 0 1.1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: var(--teal-ink); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--navy); }

img, video { max-width: 100%; height: auto; display: block; }

/* Icons are inline SVG with no intrinsic size — give them a sane default so a
   missing width rule can never blow one up to fill the viewport. */
svg { max-width: 100%; }
svg:not([width]):not([height]) { width: 1.15em; height: 1.15em; flex: none; }

ul, ol { margin: 0 0 1.1em; padding-left: 1.25em; }
li { margin-bottom: 0.35em; }

/* Navy, not teal: a teal ring on a teal button is nearly invisible, which is
   precisely the control a keyboard user most needs to find. */
:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 3px;
  border-radius: 4px;
}
.section--navy :focus-visible,
.signup :focus-visible,
.site-footer :focus-visible,
.lunch__card :focus-visible { outline-color: var(--teal-bright); }

/* ------------------------------------------------------------------ *
 * Calendar / directions row
 * ------------------------------------------------------------------ */

.ev-utils {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  margin: 1.1rem 0 0;
  font-size: var(--step--1);
}
.ev-utils a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.ev-utils svg { width: 1rem; height: 1rem; }
.ev-utils--dark a { color: var(--muted); }
.ev-utils--dark a:hover { color: var(--navy); border-bottom-color: var(--navy); }
.ev-utils--dark svg { color: var(--teal-ink); }
.ev-utils--light a { color: rgba(255, 255, 255, 0.72); }
.ev-utils--light a:hover { color: var(--white); border-bottom-color: var(--teal); }
.ev-utils--light svg { color: var(--teal); }

.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: 1rem; top: -100px;
  z-index: 200;
  background: var(--navy);
  color: var(--white);
  padding: 0.7rem 1.1rem;
  border-radius: var(--r-sm);
  transition: top 0.2s var(--ease);
}
.skip-link:focus { top: 1rem; color: var(--white); }

/* ------------------------------------------------------------------ *
 * 4. Layout helpers
 * ------------------------------------------------------------------ */

.wrap {
  width: min(100% - 2.5rem, var(--wrap));
  margin-inline: auto;
}
.wrap--narrow { width: min(100% - 2.5rem, var(--wrap-narrow)); }

.section { padding-block: var(--section-y); }
.section--navy {
  /* Two soft light sources over the gradient rather than one flat fill. A
     large block of a single saturated blue is what made the old palette
     harsh, and a straight linear gradient still reads as a backdrop; the
     off-axis radials give it somewhere to come from. */
  background:
    radial-gradient(60% 80% at 10% 0%, rgba(43, 191, 203, 0.16), transparent 60%),
    radial-gradient(55% 75% at 95% 100%, rgba(96, 76, 210, 0.26), transparent 62%),
    linear-gradient(168deg, var(--navy) 0%, var(--navy-deep) 100%);
  color: rgba(255, 255, 255, 0.86);
}
.section--navy h2,
.section--navy h3 { color: var(--white); }
.section--white { background: var(--white); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--teal-ink);
  margin-bottom: 0.9rem;
}
.eyebrow::before {
  content: '';
  width: 1.75rem; height: 2px;
  background: var(--teal);
  border-radius: 2px;
}
.section--navy .eyebrow { color: var(--teal); }

.lede {
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--muted);
}
.section--navy .lede { color: rgba(255, 255, 255, 0.78); }
.section--navy .lede svg { color: var(--teal); margin-top: 0.25em; }

.section-head { max-width: 46rem; margin-bottom: clamp(2rem, 1rem + 3vw, 3.5rem); }

/* ------------------------------------------------------------------ *
 * 5. Buttons
 * ------------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1;
  padding: 0.95rem 1.6rem;
  border: 1.5px solid transparent;
  border-radius: var(--r-pill);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.18s var(--ease-out), background-color 0.18s var(--ease),
              border-color 0.18s var(--ease), color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--teal);
  border-color: var(--teal);
  color: var(--navy-deep);
  box-shadow: 0 6px 18px -6px rgba(43, 191, 203, 0.7);
}
.btn--primary:hover {
  background: var(--teal-bright);
  border-color: var(--teal-bright);
  color: var(--navy-deep);
  box-shadow: 0 10px 26px -8px rgba(43, 191, 203, 0.85);
}

.btn--navy { background: var(--navy); border-color: var(--navy); color: var(--white); }
.btn--navy:hover { background: var(--navy-soft); border-color: var(--navy-soft); color: var(--white); }

.btn--ghost { background: transparent; border-color: var(--line); color: var(--navy); }
.btn--ghost:hover { border-color: var(--navy); background: rgba(36, 41, 97, 0.04); color: var(--navy); }

/* Ghost buttons default to navy-on-light; anywhere they sit on a dark panel
   they have to flip, or they vanish into the background. */
.section--navy .btn--ghost,
.lunch__card .btn--ghost,
.signup .btn--ghost { border-color: var(--line-dark); color: var(--white); }
.section--navy .btn--ghost:hover,
.lunch__card .btn--ghost:hover,
.signup .btn--ghost:hover { border-color: var(--teal); background: rgba(43, 191, 203, 0.12); color: var(--white); }

.btn--sm { padding: 0.65rem 1.15rem; font-size: var(--step--1); }
.btn--lg { padding: 1.1rem 2rem; font-size: var(--step-0); }

/* ------------------------------------------------------------------ *
 * 6. Header
 * ------------------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(251, 247, 240, 0.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.site-header.is-stuck {
  border-bottom-color: var(--line);
  box-shadow: 0 4px 20px -12px rgba(36, 41, 97, 0.4);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--gap);
  min-height: 4.75rem;
}

/* flex: none on both — the logo and the toggle are the two items in this row
   with a fixed shape, and a flex item's default is to shrink. Without it the
   lockup squashes horizontally and the square button becomes a rectangle. */
.site-brand { display: flex; align-items: center; margin-right: auto; flex: none; }
.site-brand img { width: auto; height: clamp(2.65rem, 2.2rem + 1.7vw, 3.5rem); }

.nav-toggle {
  display: none;
  flex: none;
  width: 2.9rem; height: 2.9rem;
  align-items: center; justify-content: center;
  background: transparent;
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--navy);
}
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 18px; height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 0.25s var(--ease-out), opacity 0.15s var(--ease);
}
.nav-toggle__bars::before,
.nav-toggle__bars::after { content: ''; position: absolute; }
.nav-toggle__bars::before { transform: translateY(-6px); }
.nav-toggle__bars::after { transform: translateY(6px); }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::before { transform: rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::after { transform: rotate(-45deg); }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 0.4rem + 1.6vw, 2.1rem);
  margin: 0; padding: 0;
  list-style: none;
}
.site-nav li { margin: 0; }
.site-nav a {
  position: relative;
  font-size: var(--step--1);
  font-weight: 550;
  letter-spacing: 0.005em;
  color: var(--navy);
  text-decoration: none;
  padding-block: 0.4rem;
}
.site-nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 2px;
  background: var(--teal);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s var(--ease-out);
}
.site-nav a:hover::after,
.site-nav a:focus-visible::after { transform: scaleX(1); }

@media (max-width: 900px) {
  /*
   * The menu drops to its own row underneath the logo. That only happens if
   * the row is allowed to wrap — flex-basis:100% on .site-nav below is a
   * request for a full row, and in a nowrap container it is instead read as
   * "this item wants 100% and may shrink", which squeezed the logo to under
   * half its width, flattened the square toggle into a rectangle, and left
   * the panel opening as a 156px sliver over the top of the Book button.
   */
  .site-header__inner {
    flex-wrap: wrap;
    /* The gap token is a column gap here. As a row gap it would push the open
       menu away from the bar and leave the first link's rule floating. */
    row-gap: 0;
    align-content: center;
  }

  .nav-toggle { display: inline-flex; position: relative; order: 3; }
  .site-nav {
    order: 4;
    flex-basis: 100%;
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.3s var(--ease-out);
  }
  .site-nav[data-open='true'] { grid-template-rows: 1fr; }
  .site-nav > ul {
    overflow: hidden;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    min-height: 0;
  }
  .site-nav a { display: block; padding: 0.85rem 0; border-top: 1px solid var(--line); }
  .site-nav a::after { display: none; }

  /* Kept on mobile, just shortened. This is a site whose entire purpose is
     booking a place; hiding the only button that does it behind a hamburger
     was the wrong trade. */
  .site-header__cta {
    order: 2;
    /* Short label, but still the button the whole site is pointing at — it
       gets a thumb-sized target rather than the 34px it used to be. */
    padding: 0.85rem 1.1rem;
    font-size: 0.85rem;
  }
  .site-header__cta .full { display: none; }
}
@media (min-width: 901px) {
  .site-header__cta .short { display: none; }
}

/* ------------------------------------------------------------------ *
 * 7. Hero
 * ------------------------------------------------------------------ */

.hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(2rem, 1rem + 4vw, 4rem) 0;
}
.hero::before {
  content: '';
  position: absolute;
  inset: -20% 30% auto -25%;
  height: 110%;
  background: radial-gradient(ellipse 55% 55% at 35% 40%, rgba(43, 191, 203, 0.2), transparent 68%);
  pointer-events: none;
}

.hero__inner {
  position: relative;
  width: min(100% - 2.5rem, var(--wrap));
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 43%);
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: center;
  min-height: min(76vh, 720px);
  padding-bottom: clamp(3rem, 2rem + 4vw, 5rem);
}

.hero h1 {
  color: var(--navy);
  margin-bottom: 0.38em;
  letter-spacing: -0.032em;
  line-height: 1.02;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 2.25rem;
}

.hero__trust {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-top: 2.25rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
  font-size: var(--step--1);
  color: var(--muted);
}
.hero__trust span { display: inline-flex; align-items: center; gap: 0.45rem; }
.hero__trust svg { width: 1.05rem; height: 1.05rem; color: var(--teal-ink); flex: none; }

/* The photograph is a panel, not a card: it runs off the right edge of the
   viewport and the full height of the hero, with square corners.
   It was previously rounded on the left and cut off square on the right,
   which read as a card that had failed to fit rather than as a window.
   Either both sides are contained or neither is; this is neither.

   The negative block margins cancel the hero's own padding exactly, so the
   panel reaches the top of the section and the bottom of the band below. Keep
   them in step with .hero padding-block and .hero__inner padding-bottom. */
.hero__media {
  position: relative;
  /* Out to the right edge of the viewport exactly. The usual `50% - 50vw`
     idiom assumes the element spans the centred wrapper; this one is a 43%
     grid column, so 50% is half the column and the panel overshot by a third
     of its width — all of it clipped, which pushed the crop of the photograph
     well left of where object-position said it was. Measure from the wrapper
     instead. The vw units include the scrollbar, so this overshoots by a few
     pixels rather than leaving a hairline gap; .hero clips the difference. */
  margin-right: calc(min(100vw - 2.5rem, var(--wrap)) / 2 - 50vw);
  margin-top: calc(-1 * clamp(2rem, 1rem + 4vw, 4rem));
  margin-bottom: calc(-1 * clamp(3rem, 2rem + 4vw, 5rem));
  align-self: stretch;
  display: flex;
  align-items: stretch;
}
.hero__frame {
  position: relative;
  width: 100%;
  min-height: min(66vh, 620px);
  margin: 0;
  overflow: hidden;
  border-radius: 0;
}
/* The photographs are stacked on top of each other and cross-faded, so they
   come out of flow. The frame keeps its own height from min-height above and
   from .hero__media stretching it, never from the picture, so nothing here
   collapses when they all go absolute.

   The extra 72px and the -36px offset are headroom for the parallax below: the
   picture is taller than its frame so it can drift without showing an edge. */
.hero__shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: calc(100% + 72px);
  margin-top: -36px;
  object-fit: cover;
  object-position: center 45%;
  opacity: 0;
}
.hero__shot.is-on { opacity: 1; }

/* Only fade once the script is driving it. Without this the whole set is
   invisible to anyone with JavaScript off — the first frame has .is-on from
   PHP, and the rest sit at opacity 0 where they belong. */
.hero__frame.is-cycling .hero__shot {
  transition: opacity 1.4s var(--ease-out);
}

/* A slow drift into the picture while it is the one showing. `scale` rather
   than a transform, because the parallax below owns `transform` and the two
   would otherwise overwrite each other — they compose as separate properties.
   1.06 over the five seconds it is up: enough to feel alive, not enough to
   read as a zoom. */
@media (prefers-reduced-motion: no-preference) {
  .hero__frame.is-cycling .hero__shot {
    transition: opacity 1.4s var(--ease-out), scale 6s linear;
    scale: 1;
  }
  .hero__frame.is-cycling .hero__shot.is-on { scale: 1.06; }
}

/* Real parallax, no JavaScript. Falls back to a static image where the
   scroll-driven timeline is unsupported. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero__shot {
      animation: hero-drift linear both;
      animation-timeline: scroll(root);
      animation-range: 0 700px;
    }
    @keyframes hero-drift {
      from { transform: translateY(-36px); }
      to { transform: translateY(36px); }
    }
  }
}

.hero__stamp {
  position: absolute;
  left: 0;
  bottom: clamp(1.25rem, 1rem + 1vw, 2rem);
  transform: translateX(-40%);
  z-index: 2;
  display: grid;
  place-items: center;
  width: clamp(6rem, 5rem + 3vw, 8rem);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--teal);
  color: var(--navy-deep);
  text-align: center;
  line-height: 1.1;
  box-shadow: 0 12px 32px -10px rgba(43, 191, 203, 0.9);
}
.hero__stamp b {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.03em;
}
.hero__stamp span {
  display: block;
  font-size: 0.62rem;
  font-weight: 650;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.75;
}

@media (max-width: 940px) {
  .hero__inner {
    grid-template-columns: 1fr;
    min-height: 0;
    gap: 2.5rem;
    /* Flush against the next-lunch band — a strip of cream between the two
       leaves the photograph looking like it is floating. */
    padding-bottom: 0;
  }
  /* Stacked: the panel is a full-width band, so the vertical bleed that
     squares it off against the hero padding on desktop has nothing to cancel. */
  .hero__media {
    margin-inline: calc(50% - 50vw);
    margin-block: 0;
  }
  .hero__frame { min-height: 0; height: min(48vh, 380px); }
}

/* ------------------------------------------------------------------ *
 * 8. Booking card on a lunch page
 * ------------------------------------------------------------------ */

/* What is left of a much larger block. The home page carried a navy "next
   event" card beside the hero, and later a full-width band under it, and both
   are gone: parts/events.php is now the only place a lunch appears on that
   page. These rules survive because single-b2b_lunch.php still uses them for
   the booking card in its sidebar. */

.next-event__meta {
  position: relative;
  display: grid;
  gap: 0.7rem;
  margin-bottom: 1.6rem;
  padding-block: 1.35rem;
  border-block: 1px solid var(--line-dark);
  font-size: var(--step--1);
}
.next-event__meta > div { display: flex; gap: 0.7rem; align-items: flex-start; }
.next-event__meta svg { width: 1.1rem; height: 1.1rem; color: var(--teal); flex: none; margin-top: 0.15rem; }
.next-event__meta strong { color: var(--white); font-weight: 600; display: block; }

.next-event__actions { position: relative; display: flex; flex-wrap: wrap; gap: 0.7rem; }
.next-event__actions .btn { flex: 1 1 auto; }

/* The live dot on an eyebrow. Small enough to read as punctuation rather than
   as a notification, which is the point — it is saying "these are on sale
   now", not demanding anything. */
.eyebrow .dot {
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 0.15rem;
  border-radius: 50%;
  background: var(--teal-ink);
  animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ------------------------------------------------------------------ *
 * 8c. Stats
 * ------------------------------------------------------------------ */

.stats { padding-block: clamp(2.5rem, 1.5rem + 3vw, 4rem); }
.stats__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
  gap: clamp(1.5rem, 1rem + 2vw, 3rem);
}
.stat { text-align: center; position: relative; }
.stat + .stat::before {
  content: '';
  position: absolute;
  left: calc(-1 * clamp(0.75rem, 0.5rem + 1vw, 1.5rem));
  top: 12%; bottom: 12%;
  width: 1px;
  background: var(--line);
}
.stat__num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 1.8rem + 2.4vw, 3.75rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.stat__num .accent { color: var(--teal-ink); }
.stat__label {
  display: block;
  margin-top: 0.6rem;
  font-size: var(--step--1);
  color: var(--muted);
}

@media (max-width: 560px) {
  .stat + .stat::before { display: none; }

  /* The 10rem minimum above is about 9px too wide to make two columns at
     375px, so four short facts became four full-width rows and 400px of
     scrolling. Two up, which is what the numbers were sized for. */
  .stats__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2rem 1.25rem;
  }
}

/* ------------------------------------------------------------------ *
 * 9. Event list
 * ------------------------------------------------------------------ */

/* One card shape for every lunch on sale, however many there are. Two dates at
   once is the normal state of this box office, so the grid is built around
   two: a card will not go below 26rem, so below roughly 56rem of room the row
   becomes a column, and a third or fourth date wraps underneath rather than
   squeezing the row thinner. Cards stretch to a common height so two dates
   with unequal amounts written about them still line up along the bottom. */
.events__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: clamp(1.25rem, 1rem + 1.5vw, 2rem);
}
.events__grid--spaced { margin-bottom: clamp(3rem, 2rem + 3vw, 4.5rem); }

/* A lone date is not a narrow card in the left third of an empty row. */
.events__grid[data-count='1'] { grid-template-columns: minmax(0, 1fr); }

.event-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  box-shadow: var(--shadow-sm);
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s var(--ease-out);
}
.event-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }

/* The venue photograph, bled to the card edge by cancelling the card's own
   padding. Doing it this way rather than with a padded body means a card with
   no media at all — page-area.php builds a few — still sits correctly. */
.event-card__media {
  position: relative;
  margin: calc(-1 * clamp(1.25rem, 1rem + 1vw, 1.75rem));
  margin-bottom: 1.4rem;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--navy);
  border-radius: calc(var(--r-md) - 1px) calc(var(--r-md) - 1px) 0 0;
}
.event-card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out);
}
.event-card:hover .event-card__media img { transform: scale(1.04); }
/* Enough shading at the foot for the date to hold against a bright frontage. */
.event-card__media::after {
  content: '';
  position: absolute;
  inset: 42% 0 0 0;
  background: linear-gradient(to bottom, transparent, rgba(18, 20, 47, 0.72));
  pointer-events: none;
}

.event-card__stamp {
  position: absolute;
  z-index: 1;
  left: 1rem; bottom: 1rem;
  margin: 0;
  display: grid;
  place-items: center;
  width: 4.1rem;
  padding: 0.5rem 0 0.55rem;
  border-radius: var(--r-sm);
  background: var(--teal);
  color: var(--navy-deep);
  line-height: 1;
}
.event-card__stamp .d {
  font-family: var(--font-display);
  font-size: 1.65rem;
  font-weight: 800;
  letter-spacing: -0.03em;
}
.event-card__stamp .m {
  margin-top: 0.22rem;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.event-card__badge { position: absolute; z-index: 2; right: 1rem; top: 1rem; }
/* The badges are pale by design and are sitting on a photograph here, so they
   need their own ground rather than the tint that works on cream. */
.event-card__media .badge--ok { background: rgba(255, 255, 255, 0.94); color: var(--teal-ink); }
.event-card__media .badge--out { background: rgba(255, 255, 255, 0.92); color: var(--navy); }

/* No header image on the Ticket Tailor event and nothing to inherit from a
   previous lunch at the same venue — the first visit to a new room. The panel
   becomes navy with the date large in it, which reads as a decision rather
   than as a photograph that failed to load. Put a header image on the event in
   Ticket Tailor and this goes away by itself. */
.event-card:not(.event-card--media) .event-card__media {
  aspect-ratio: 16 / 10;
  background: linear-gradient(150deg, #2c327a 0%, var(--navy) 55%, var(--navy-deep) 100%);
  display: grid;
  place-items: center;
}
.event-card:not(.event-card--media) .event-card__media::after { display: none; }
.event-card:not(.event-card--media) .event-card__stamp {
  position: static;
  width: auto;
  padding: 0;
  background: none;
  color: var(--white);
  grid-auto-flow: column;
  align-items: baseline;
  gap: 0.5rem;
}
.event-card:not(.event-card--media) .event-card__stamp .d { font-size: clamp(2.4rem, 2rem + 2vw, 3.4rem); }
.event-card:not(.event-card--media) .event-card__stamp .m { margin-top: 0; color: var(--teal); font-size: 0.8rem; }

.event-card__body { display: flex; flex-direction: column; flex: 1 1 auto; }

.event-card__title {
  font-size: var(--step-2);
  color: var(--navy);
  margin: 0 0 0.95rem;
  line-height: 1.16;
}
.event-card__title a { color: inherit; text-decoration: none; }
.event-card__title a:hover { color: var(--teal-ink); }
/* The whole card is the link to book. Everything with its own destination —
   the buttons, the calendar row — is lifted above it. */
.event-card__title a::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}
.event-card__foot .btn,
.event-card .ev-utils a { position: relative; z-index: 2; }

.event-card__facts {
  list-style: none;
  margin: 0 0 1.15rem;
  padding: 0;
  display: grid;
  gap: 0.55rem;
  font-size: var(--step--1);
  color: var(--muted);
}
.event-card__facts li { display: flex; gap: 0.65rem; align-items: flex-start; }
.event-card__facts svg { width: 1.05rem; height: 1.05rem; color: var(--teal-ink); flex: none; margin-top: 0.15rem; }

.event-card__summary {
  margin: 0 0 1.3rem;
  font-size: var(--step--1);
  color: var(--muted);
}

/* The speaker. Tinted rather than ruled off, because it is a different kind of
   fact from the date and the venue — those are about getting there, this is
   the reason for going. */
.event-card__speaker {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
  margin: 0 0 1.4rem;
  padding: 1.1rem;
  border-radius: var(--r-sm);
  background: var(--cream);
}
.event-card__headshot {
  width: 4.4rem; height: 4.4rem;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 25%;
}
.event-card__speaker-copy > * { margin: 0; }
.event-card__speaker-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.4rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--teal-ink);
}
.event-card__speaker-label svg { width: 0.95rem; height: 0.95rem; }
.event-card__speaker-name {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--navy);
}
.event-card__speaker-role { margin-top: 0.1rem; font-size: var(--step--1); color: var(--muted); }
.event-card__speaker-bio { margin-top: 0.5rem; font-size: var(--step--1); color: var(--muted); }

.event-card__foot {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}
.event-card .ev-utils { margin-bottom: 0; padding-top: 1rem; }

/* One date on sale: the photograph moves beside the detail rather than above
   it, so a single card fills the row instead of being a very tall column. */
@media (min-width: 860px) {
  .event-card--wide {
    display: grid;
    grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
    padding: 0;
  }
  .event-card--wide .event-card__media {
    margin: 0;
    aspect-ratio: auto;
    min-height: 24rem;
    border-radius: calc(var(--r-md) - 1px) 0 0 calc(var(--r-md) - 1px);
  }
  .event-card--wide .event-card__media::after {
    inset: 0 0 0 45%;
    background: linear-gradient(to right, transparent, rgba(18, 20, 47, 0.5));
  }
  .event-card--wide .event-card__body { padding: clamp(1.5rem, 1rem + 2vw, 2.5rem); }
  .event-card--wide .event-card__title { font-size: var(--step-3); }
  .event-card--wide .event-card__stamp { left: auto; right: 1.25rem; top: 1.25rem; bottom: auto; }
  .event-card--wide .event-card__badge { right: auto; left: 1.25rem; top: auto; bottom: 1.25rem; }

  /* A wide card with no photograph — one date on sale and no header image on
     it in Ticket Tailor, which is the state the site is in more often than not
     between rounds of dates.

     The no-media rule above sets aspect-ratio 16/10 and outranks the wide rule
     beside it (:not() carries the specificity of its argument), so the ratio
     survives into this layout. Paired with the min-height here, the panel then
     sizes its WIDTH from its height — 24rem x 1.6 = 614px — and a 614px panel
     in a 40% column overflows a third of its width across the title, the date
     and the venue. It reads as a navy block dumped on top of the text, and it
     only appears when both conditions are true, which is why it survived.

     Stated explicitly rather than by reordering: the two rules would otherwise
     go on quietly depending on each other's position in this file. */
  .event-card--wide:not(.event-card--media) .event-card__media { aspect-ratio: auto; }
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.32rem 0.7rem;
  border-radius: var(--r-pill);
}
.badge--ok { background: rgba(43, 191, 203, 0.16); color: var(--teal-ink); }
.badge--low { background: #fff1e0; color: #9a5b00; }
.badge--out { background: rgba(36, 41, 97, 0.08); color: var(--muted); }

/* The one-date state. A full white panel for a single sentence reads as an
   error box; a rule and a line of type reads as an editor's note. */
.events__single {
  border-top: 2px solid var(--navy);
  border-bottom: 1px solid var(--line);
  padding-block: clamp(1.5rem, 1rem + 1.5vw, 2.25rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.25rem, 1rem + 2vw, 3rem);
  align-items: center;
}
.events__single-lead {
  margin: 0;
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--navy);
  max-width: 40rem;
}
.events__single-lead em {
  font-style: normal;
  font-weight: 300;
  color: var(--teal-ink);
}
.events__single-lead a { color: var(--teal-ink); text-underline-offset: 0.22em; }
.events__single-actions {
  margin: 0;
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}
/* With a photograph the note becomes picture, sentence, buttons. The rule
   above and below stays, so it still reads as an editor's note, not a card. */
.events__single--media { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr) auto; }
.events__single-media { position: relative; margin: 0; border-radius: var(--r-md); overflow: hidden; }
.events__single-media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}
.events__single-media figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1.75rem 0.85rem 0.6rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--white);
  background: linear-gradient(to top, rgba(18, 20, 47, 0.8), rgba(18, 20, 47, 0));
}

@media (max-width: 1000px) {
  .events__single--media { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); }
  .events__single--media .events__single-actions { grid-column: 2; }
}
@media (max-width: 760px) {
  .events__single,
  .events__single--media { grid-template-columns: 1fr; }
  .events__single--media .events__single-actions { grid-column: auto; }
}

.events__empty {
  text-align: center;
  padding: clamp(2.5rem, 1.5rem + 4vw, 4.5rem) 1.5rem;
  background: var(--white);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
}
.events__empty h3 { color: var(--navy); }

.events__note {
  margin-top: 1.75rem;
  font-size: var(--step--1);
  color: var(--muted);
}
.events__note svg {
  width: 1.05rem; height: 1.05rem;
  color: var(--teal-ink);
  vertical-align: -0.2em;
  margin-right: 0.2rem;
}

/* Loading skeleton while the live feed refreshes */
.event-card--skeleton { pointer-events: none; }
.event-card--skeleton .sk {
  display: block;
  background: linear-gradient(90deg, var(--cream-deep) 25%, #ffffff 50%, var(--cream-deep) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: 6px;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ------------------------------------------------------------------ *
 * 10. Running order
 * ------------------------------------------------------------------ */

/* The section has to be the containing block for its backdrop, which is
   absolute with a negative z-index — an unpositioned section lets it escape to
   the page and paint a full-viewport photograph behind everything from the top
   down. Named on the first rule and matched structurally on the second, so
   another section carrying a backdrop cannot repeat it. Separate rules
   deliberately: :has() sharing a comma list would take the whole thing down in
   a browser without it, which is the bug it exists to prevent. */
/*
   clip, not hidden. The backdrop below is sticky with margin-bottom: -100vh,
   and a negative bottom margin expands a sticky element's constraint
   rectangle — so it carries on sticking a full screen past the end of its own
   section and paints the room behind whatever follows, which here is the FAQ.
   overflow:hidden would fix that and break the pinned card row at the same
   time, because it makes a scroll container and position:sticky inside one
   sticks to that container rather than to the viewport. clip crops without
   creating one. */
.rundown { position: relative; isolation: isolate; overflow: clip; }
.section:has(> .format__backdrop) { position: relative; isolation: isolate; }

/*
 * A photograph of the room behind the navy. Sticky and one viewport tall
 * rather than absolute and inset:0 — this section is several thousand pixels
 * deep, and stretched over that, object-fit:cover zooms the picture until
 * about a tenth of it fills the band, which reads as a flat field of blue
 * rather than as a room. Held at a screen, it stays a photograph while the
 * cards travel across it.
 *
 * The negative margin keeps it out of the flow: a sticky element still takes
 * its space, and without this it would push the section down a screen.
 */
.format__backdrop {
  position: sticky;
  top: 0;
  height: 100vh;
  margin-bottom: -100vh;
  z-index: -1;
  overflow: hidden;
}
.format__backdrop img {
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.34;
  /* Blurred deliberately: unblurred at this opacity the room competes with the
     copy set over it, and the job of this layer is depth, not detail. */
  filter: grayscale(0.25) contrast(1.05) blur(3px);
  /* Blur samples past the edges and leaves a soft rim on a full-bleed image;
     overscanning hides it. */
  scale: 1.03;
}
/* Three layers, topmost first.

   A fine dot grid over everything: it is the only thing in the section with a
   regular rhythm, and against a blurred photograph and a row of cards that are
   all different, a little order underneath reads as design rather than as
   pattern for its own sake. It rides on the held backdrop, so it stays put
   while the cards travel across it — a texture on the glass rather than on the
   scenery.

   Then the scrim: heaviest where the copy runs, lightest across the middle
   where a card sits over it anyway, so the room shows in the gaps rather than
   behind the type. */
.format__backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1.3px) 0 0 / 24px 24px,
    linear-gradient(to right, rgba(26, 29, 77, 0.72) 0%, rgba(26, 29, 77, 0.42) 30%, rgba(26, 29, 77, 0.78) 62%, rgba(26, 29, 77, 0.88) 100%),
    linear-gradient(to bottom, var(--navy-deep) 0%, rgba(26, 29, 77, 0) 20%, rgba(26, 29, 77, 0) 80%, var(--navy-deep) 100%);
}

.format-note {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem);
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.78);
}
.format-note svg { color: var(--teal); margin-top: 0.25em; }

/* --- The cards ------------------------------------------------------ */

/* Six moments running left to right, moved along by scrolling down. See
   parts/format.php for why this axis.

   The base state — no pin, no translation — is a plain horizontal scroller
   with snap points. That is what a phone gets, and what any browser without
   scroll-driven animations gets, and it is a component people already know how
   to use rather than a broken effect. The pinning is added on top of it in the
   @supports block, and only there. */

.rundown .tl-track { position: relative; }

.tl-rail {
  list-style: none;
  margin: 0;
  padding: 0 max(1.25rem, calc(50vw - var(--wrap) / 2));
  display: flex;
  gap: clamp(1rem, 0.5rem + 1.5vw, 2rem);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}

.tl-card {
  margin: 0;
  flex: 0 0 auto;
  width: min(84vw, 30rem);
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
}

.tl-card__figure {
  margin: 0 0 1.25rem;
  overflow: hidden;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.05);
  aspect-ratio: 4 / 3;
}
.tl-card__figure img { width: 100%; height: 100%; object-fit: cover; }

.tl-card__time {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  font-family: var(--font-display);
  font-size: clamp(2rem, 1.6rem + 1.6vw, 3rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--white);
  font-variant-numeric: tabular-nums;
  margin-bottom: 0.5rem;
}
.tl-card__time span {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.3);
}

.tl-card__title {
  font-size: var(--step-2);
  color: var(--teal-bright);
  margin-bottom: 0.45rem;
}
.tl-card__text {
  font-size: var(--step-0);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
  margin: 0;
}

/* The line it all runs along. Static until there is a timeline to drive it. */
.tl-axis {
  position: relative;
  margin: 2rem max(1.25rem, calc(50vw - var(--wrap) / 2)) 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.16);
}
.tl-axis__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  transform-origin: 0 50%;
  background: var(--teal);
}
/* One per moment, placed by elapsed time — see the 'at' figure in
   b2b26_format_steps(). Three of the six fall inside the first hour, so they
   bunch at the left and open out across lunch, which is the shape of the
   afternoon and not something that had to be drawn. */
.tl-axis__tick {
  position: absolute;
  top: -4px;
  width: 1px;
  height: 9px;
  background: rgba(255, 255, 255, 0.38);
}
/* :first-of-type would have matched the fill bar, which is also a span —
   the ends are marked from the template instead. */
.tl-axis__tick--end { background: var(--teal); height: 13px; top: -6px; }

.tl-axis__from,
.tl-axis__to {
  position: absolute;
  top: 0.75rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.4);
  font-variant-numeric: tabular-nums;
}
.tl-axis__from { left: 0; }
.tl-axis__to { right: 0; }

/* --- Pinned, where the browser can drive it from scroll position ---- */

@supports (animation-timeline: view()) {
  @media (min-width: 900px) and (prefers-reduced-motion: no-preference) {

    /*
     * The track is the runway. Its height is what the horizontal travel is
     * mapped from, so it sets the gearing: 360vh gives 260vh of scroll to
     * cover roughly 250vw of rail, which is close enough to one-to-one that
     * the row appears to move at the speed of the wheel rather than being
     * flung along by it.
     */
    .rundown .tl-track {
      height: 360vh;
      view-timeline-name: --tl;
      view-timeline-axis: block;
    }
    .rundown { timeline-scope: --tl; }

    .rundown .tl-track__pin {
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: center;
      overflow: hidden;
    }

    /* No longer a scroller — the page's own scroll position moves it. */
    .rundown .tl-rail {
      overflow: visible;
      scroll-snap-type: none;
      /*
       * Load-bearing. A flex row in a block container takes the container's
       * width, and a percentage in translateX() resolves against the element's
       * own border box — so -100% would have been 1,265px here rather than the
       * 3,258px the row actually spans, and it would have shuffled sideways by
       * fifteen pixels and stopped. max-content makes the box the size of the
       * content, which is what the keyframe below is written against.
       */
      width: max-content;
      animation: tl-slide linear both;
      animation-timeline: --tl;
      animation-range: contain 0% contain 100%;
    }
    .rundown .tl-card { width: min(44vw, 32rem); }

    /*
     * -100% is the rail's own width, so this ends with its right edge at the
     * right of the viewport whatever the cards add up to. No JavaScript has to
     * measure anything, and adding a seventh step would need no arithmetic.
     */
    @keyframes tl-slide {
      from { transform: translateX(0); }
      to   { transform: translateX(calc(100vw - 100%)); }
    }

    .tl-axis__fill {
      animation: tl-fill linear both;
      animation-timeline: --tl;
      animation-range: contain 0% contain 100%;
    }
    @keyframes tl-fill {
      from { scale: 0 1; }
      to   { scale: 1 1; }
    }
  }
}

/* Nothing to fill against when the rail is a plain scroller: a bar sitting at
   100% for ever reads as a progress indicator that is stuck. */
@supports not (animation-timeline: view()) {
  .tl-axis__fill { display: none; }
}
@media (max-width: 899px) {
  .tl-axis__fill { display: none; }
}

/* ------------------------------------------------------------------ *
 * 11. Value props
 * ------------------------------------------------------------------ */

/* Sits directly under the events grid, so it does not need a full section's
   worth of breathing room above it as well. */
.why { padding-top: clamp(2rem, 1rem + 3vw, 3.5rem); }

.why__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 1rem + 5vw, 5rem);
  align-items: center;
}

@media (max-width: 900px) {
  .why__inner { grid-template-columns: 1fr; gap: clamp(2.5rem, 2rem + 3vw, 3.5rem); }
}

/* Main photo with a smaller one lapping its bottom-right corner. */
.why__media { position: relative; padding-bottom: 3.5rem; padding-right: 2rem; }
.why__img { margin: 0; }
.why__img img { border-radius: var(--r-md); box-shadow: var(--shadow-md); }
.why__img--a img { aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }
.why__img--b {
  position: absolute;
  right: 0; bottom: 0;
  width: 46%;
  border: 6px solid var(--cream);
  border-radius: calc(var(--r-md) + 6px);
}
.why__img--b img { aspect-ratio: 1 / 1; object-fit: cover; width: 100%; box-shadow: none; }

@media (max-width: 520px) {
  .why__media { padding-bottom: 2.5rem; padding-right: 0; }
  .why__img--b { width: 52%; right: -0.5rem; }
}

.prop-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }
.prop-list li { margin: 0; display: flex; gap: 1.1rem; align-items: flex-start; }

.prop__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 3rem; height: 3rem;
  border-radius: var(--r-sm);
  background: rgba(43, 191, 203, 0.14);
  color: var(--teal-ink);
}
.prop__icon svg { width: 1.5rem; height: 1.5rem; }
.prop h3 { font-size: var(--step-1); color: var(--navy); margin-bottom: 0.3rem; }
.prop p { font-size: var(--step--1); color: var(--muted); }

/* ------------------------------------------------------------------ *
 * 11b. Photo gallery
 * ------------------------------------------------------------------ */

.gallery__grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(0.6rem, 0.4rem + 0.6vw, 1rem);
}
.gallery__item { margin: 0; overflow: hidden; border-radius: var(--r-md); grid-column: span 2; }
.gallery__item img {
  width: 100%; height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out);
}
.gallery__item:hover img { transform: scale(1.05); }

/* First photo runs large; the rest tile beside and beneath it. */
.gallery__item:first-child { grid-column: span 4; grid-row: span 2; }
.gallery__item:first-child img { aspect-ratio: 3 / 2; }

/* Where and when, over the bottom of the photograph. A full room is a better
   argument when it is dated and placed than when it is floating in a grid. */
.gallery__item { position: relative; }
.gallery__item figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 2rem 0.9rem 0.7rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--white);
  background: linear-gradient(to top, rgba(18, 20, 47, 0.78), rgba(18, 20, 47, 0));
  pointer-events: none;
}

.gallery__note {
  margin: 1.5rem 0 0;
  font-size: var(--step--1);
  color: var(--muted);
}

/* The fade needs room to fade in, but on a phone that head of padding is a
   third of the tile. Shorten it there rather than everywhere. */
@media (max-width: 720px) {
  .gallery__item figcaption,
  .area-photos figcaption,
  .events__single-media figcaption { padding-top: 1.2rem; padding-bottom: 0.55rem; }
}

/* One photograph is not a grid. Full width, and a shape that suits a single
   image rather than two-thirds of a row with a gap beside it. */
.gallery__grid[data-count='1'] .gallery__item { grid-column: span 6; grid-row: auto; }
.gallery__grid[data-count='1'] .gallery__item img { aspect-ratio: 2 / 1; }

/* Two, three and four photographs cannot make the asymmetric grid — the tall
   first tile leaves a hole underneath the last one. Even rows instead. */
.gallery__grid[data-count='2'] .gallery__item,
.gallery__grid[data-count='4'] .gallery__item {
  grid-column: span 3;
  grid-row: auto;
}
.gallery__grid[data-count='3'] .gallery__item { grid-column: span 2; grid-row: auto; }
.gallery__grid[data-count='2'] .gallery__item img,
.gallery__grid[data-count='3'] .gallery__item img,
.gallery__grid[data-count='4'] .gallery__item img { aspect-ratio: 4 / 3; }

/* Two columns on a phone, and the lead photograph across both. The selectors
   carry [data-count] deliberately: without it the count rules above out-weigh
   these and the lead tile stays half-width, which at 375px is 163px of a room
   full of people — too small to read as anything. */
@media (max-width: 720px) {
  .gallery__grid { grid-template-columns: repeat(2, 1fr); }
  .gallery__grid[data-count] .gallery__item { grid-column: span 1; grid-row: auto; }
  .gallery__grid[data-count] .gallery__item:first-child { grid-column: span 2; }
  .gallery__grid[data-count] .gallery__item img { aspect-ratio: 1 / 1; }
  .gallery__grid[data-count] .gallery__item:first-child img { aspect-ratio: 3 / 2; }

  /* Exactly two: stack them. Odd counts leave the last tile half-width with a
     gap beside it, which is fine in a run of five but is half the section when
     there are only two. */
  .gallery__grid[data-count='2'] .gallery__item { grid-column: span 2; }
  .gallery__grid[data-count='2'] .gallery__item img { aspect-ratio: 3 / 2; }
}

/* ------------------------------------------------------------------ *
 * 11c. Speaker spotlight
 * ------------------------------------------------------------------ */

/* The homepage had a site-wide "speaking next" section, which could only ever
   describe one lunch. It is gone; the speaker is now part of the event card and
   these rules dress the same block on a single lunch page. */
.speaker__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  align-items: center;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(1.75rem, 1rem + 3vw, 3rem);
  box-shadow: var(--shadow-sm);
}
.speaker__inner--nophoto { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 860px) { .speaker__inner { grid-template-columns: 1fr; } }

.speaker__photo { margin: 0; }
.speaker__photo img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--r-md);
}
.speaker__copy h2 { font-size: var(--step-3); color: var(--navy); margin-bottom: 0.2rem; }
.speaker__role {
  font-weight: 600;
  color: var(--teal-ink);
  margin-bottom: 1.1rem;
}
.speaker__bio { color: var(--muted); font-size: var(--step--1); }

/* On a lunch page it follows the body copy rather than standing on its own. */
.lunch__speaker { margin-top: clamp(2rem, 1.5rem + 2vw, 3rem); }
.lunch__speaker .speaker__photo img { aspect-ratio: 1 / 1; }
.lunch__speaker .speaker__copy h2 { font-size: var(--step-2); }

/* ------------------------------------------------------------------ *
 * 12. Sponsors marquee
 * ------------------------------------------------------------------ */

.sponsors { padding-block: clamp(2.5rem, 1.5rem + 3vw, 4rem); }
/* Specificity has to beat the base `p:last-child { margin-bottom: 0 }` — this
   label is the only child of its wrapper. */
.sponsors p.sponsors__label {
  text-align: center;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2rem;
}

/* A wide fade, so a logo is already invisible by the time the edge cuts it.
   At 8% the slice landed mid-word and read as a broken layout. */
.marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 18%, #000 82%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 18%, #000 82%, transparent 100%);
}
.marquee__track {
  display: flex;
  align-items: center;
  gap: clamp(2.5rem, 1rem + 5vw, 5rem);
  width: max-content;
  animation: scroll-x 38s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes scroll-x { to { transform: translateX(-50%); } }

.marquee img {
  height: clamp(2rem, 1.6rem + 1.4vw, 2.9rem);
  width: auto;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.62;
  transition: filter 0.3s var(--ease), opacity 0.3s var(--ease);
}
.marquee img:hover { filter: grayscale(0); opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .marquee { mask-image: none; -webkit-mask-image: none; overflow-x: auto; }
  .marquee__track { animation: none; flex-wrap: wrap; justify-content: center; width: 100%; }
}

/* ------------------------------------------------------------------ *
 * 13. Details / policy
 * ------------------------------------------------------------------ */

/* Without a photograph this is the centred, narrow column it has always been.
   With one it becomes two columns and the heading stops being centred, because
   a centred heading over a left-aligned pair of columns looks like a mistake. */
.details__inner {
  width: min(100% - 2.5rem, 46rem);
  margin-inline: auto;
}
.details__inner .section-head { text-align: center; margin-inline: auto; }
.details__inner .eyebrow { justify-content: center; }

.details--media .details__inner {
  width: min(100% - 2.5rem, var(--wrap));
  display: grid;
  grid-template-columns: minmax(0, 0.72fr) minmax(0, 1fr);
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: start;
}
.details--media .section-head { text-align: left; margin-inline: 0; }
.details--media .eyebrow { justify-content: flex-start; }

/* Sticky: the accordion grows as answers open, and a photograph that scrolls
   away leaves the column empty for the rest of the section. */
.details__media {
  position: sticky;
  top: 6rem;
  margin: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}
.details__media img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.details__media figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 2.5rem 1.2rem 1rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--white);
  background: linear-gradient(to top, rgba(18, 20, 47, 0.82), rgba(18, 20, 47, 0));
}

@media (max-width: 900px) {
  .details--media .details__inner {
    grid-template-columns: 1fr;
    width: min(100% - 2.5rem, 46rem);
  }
  /* Below the questions, not above them: on a phone a portrait photograph at
     the top pushes the whole section off the first screen.

     relative, not static. The figcaption inside is absolutely positioned to
     the foot of the photograph, and a static parent is not a containing block
     for it — it would resolve against the initial containing block instead and
     land at the foot of the first screen, captioning the hero. Nothing showed
     it while the reveal above was stuck mid-animation, because a transform
     makes a containing block too; fixing that one uncovered this one. */
  /* top:auto matters. The desktop rule is `position: sticky; top: 6rem`, and
     only the position was being overridden here — harmless while this was
     static, because a static box ignores `top`, but a relative one honours
     it and the photograph dropped 96px out of the bottom of its own
     section and under the mailing-list panel. */
  .details--media .details__media { position: relative; top: auto; order: 2; }
  .details--media .details__media img { aspect-ratio: 3 / 2; }
  .details--media .details__body { order: 1; }
}

.faq { display: grid; gap: 0.75rem; }

.faq details {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.15rem 1.4rem;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--navy);
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '';
  flex: none;
  width: 0.7rem; height: 0.7rem;
  border-right: 2px solid var(--teal-ink);
  border-bottom: 2px solid var(--teal-ink);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.25s var(--ease-out);
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq details > div { padding: 0 1.4rem 1.35rem; color: var(--muted); font-size: var(--step--1); }

/* ------------------------------------------------------------------ *
 * 14. Signup
 * ------------------------------------------------------------------ */

.signup {
  position: relative;
  background:
    radial-gradient(60% 90% at 8% 0%, rgba(43, 191, 203, 0.18), transparent 60%),
    radial-gradient(50% 70% at 95% 100%, rgba(96, 76, 210, 0.3), transparent 60%),
    linear-gradient(155deg, var(--navy) 0%, var(--navy-deep) 100%);
  border-radius: var(--r-lg);
  padding: clamp(2rem, 1.2rem + 3.5vw, 3.75rem);
  overflow: hidden;
  color: rgba(255, 255, 255, 0.84);
}
.signup::before {
  content: '';
  position: absolute;
  inset: auto -10% -60% -10%;
  height: 120%;
  background: radial-gradient(ellipse 40% 60% at 50% 100%, rgba(43, 191, 203, 0.28), transparent 70%);
  pointer-events: none;
}
.signup__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3vw, 3.5rem);
  align-items: center;
}
@media (max-width: 860px) { .signup__inner { grid-template-columns: 1fr; } }
.signup h2 { font-size: var(--step-3); }

.signup .lede { color: rgba(255, 255, 255, 0.78); }
.signup__note { font-size: var(--step--1); color: rgba(255, 255, 255, 0.6); margin-top: 0.9rem; }

/* ---- ActiveDEMAND form ------------------------------------------- *
 * The embed renders its own markup and ships a scoped <style> block.
 * Two things to know before editing these rules:
 *  - its text inputs carry no `type` attribute at all, so `input[type=text]`
 *    never matches them. Match by exclusion instead.
 *  - it renders its own H3 + intro paragraph, which duplicate the heading in
 *    the left-hand column, so those are hidden here.
 * ------------------------------------------------------------------ */

.signup__form,
.signup__form * { font-family: var(--font-body) !important; }

.signup__form .cta_text h3,
.signup__form .cta_text h3 + p { display: none; }

/* ActiveDEMAND scopes its rules by element id, which outranks any class
   selector we can write here. Inside this one container, !important is the
   only thing that reaches. */
.signup__form input:not([type='checkbox']):not([type='radio']):not([type='submit']):not([type='button']):not([type='hidden']),
.signup__form select,
.signup__form textarea {
  width: 100% !important;
  height: auto !important;
  font-size: var(--step-0) !important;
  line-height: 1.4 !important;
  color: var(--ink) !important;
  background: var(--white) !important;
  border: 1.5px solid transparent !important;
  border-radius: var(--r-sm) !important;
  padding: 0.8rem 1rem !important;
  box-shadow: none;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.signup__form input:focus,
.signup__form select:focus,
.signup__form textarea:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 4px rgba(43, 191, 203, 0.25);
}

.signup__form label,
.signup__form .form-group > label,
.signup__form p,
.signup__form span {
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.82);
}
.signup__form .form-group { margin-bottom: 0.9rem; }

.signup__form input[type='checkbox'],
.signup__form input[type='radio'] {
  accent-color: var(--teal);
  width: 1.05rem; height: 1.05rem;
  margin-right: 0.4rem;
  vertical-align: -2px;
}

.signup__form button,
.signup__form input[type='submit'] {
  min-width: 12rem;
  margin-top: 0.5rem;
  font-size: var(--step-0) !important;
  font-weight: 600 !important;
  color: var(--navy-deep) !important;
  background: var(--teal) !important;
  border: none !important;
  border-radius: var(--r-pill) !important;
  padding: 0.9rem 1.8rem !important;
  cursor: pointer;
  transition: background-color 0.2s var(--ease), transform 0.18s var(--ease-out);
}
.signup__form button:hover,
.signup__form input[type='submit']:hover { background: var(--teal-bright) !important; transform: translateY(-2px); }

/* ------------------------------------------------------------------ *
 * 15. Footer
 * ------------------------------------------------------------------ */

.site-footer {
  background: var(--navy-darkest);
  color: rgba(255, 255, 255, 0.66);
  padding-block: clamp(3rem, 2rem + 3vw, 4.5rem) 2rem;
  font-size: var(--step--1);
}
.site-footer a { color: rgba(255, 255, 255, 0.82); text-decoration: none; }
.site-footer a:hover { color: var(--teal); text-decoration: underline; }

.site-footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: clamp(2rem, 1rem + 3vw, 3.5rem);
  padding-bottom: 2.5rem;
  border-bottom: 1px solid var(--line-dark);
}
.site-footer__brand img { height: 5rem; width: auto; margin-bottom: 1.25rem; }
.site-footer h4 {
  font-size: 0.78rem;
  font-weight: 650;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--teal);
  margin-bottom: 1rem;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 0.6rem; }

.socials { display: flex; gap: 0.6rem; margin-top: 1.25rem; }
.socials a {
  display: grid;
  place-items: center;
  width: 2.4rem; height: 2.4rem;
  border: 1px solid var(--line-dark);
  border-radius: var(--r-sm);
  color: rgba(255, 255, 255, 0.8);
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.socials a:hover { background: var(--teal); border-color: var(--teal); color: var(--navy-deep); }
.socials svg { width: 1.15rem; height: 1.15rem; }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  align-items: center;
  padding-top: 1.75rem;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.45);
}

/*
 * The 1.4fr brand column can shrink to nothing, and once the auto-fit tracks
 * cannot make their 11rem minimum that is exactly what happens: the brand was
 * handed 68px at 753, 127px at 375 and 72px at 320, which squashes the lockup
 * out of shape and breaks "Networking in Great Yarmouth" over four lines.
 * Below desktop the brand takes a full row of its own and the link lists
 * share the row underneath.
 */
@media (max-width: 900px) {
  .site-footer__grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  }
  .site-footer__brand { grid-column: 1 / -1; }
}
@media (max-width: 720px) {
  .site-footer__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2rem 1.5rem;
  }
}
/* Below this the two columns are about 130px each, which puts "Networking in
   Great Yarmouth" on three lines. One column is taller but it is not ragged. */
@media (max-width: 360px) {
  .site-footer__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------------ *
 * 16. Motion on scroll
 * ------------------------------------------------------------------ */

/* Three things move as the page scrolls, and all three are off entirely under
   prefers-reduced-motion.

   1. Blocks arrive. .reveal fades and lifts as it crosses into view, staggered
      within its own group — main.js sets --i per group rather than counting
      across the whole page, so six timeline steps run one after another
      instead of continuing a sequence begun by the event cards.
   2. Photographs drift. A slow scale as each one passes, driven by the scroll
      position rather than by a timer, so it tracks the scrollbar exactly and
      costs no JavaScript. Animated on `scale` and not `transform`, so the
      hover lift on a card is free to use transform without the two fighting.
   3. The numbers count. See countStats() in main.js — the whole argument of
      that band is the figures, and a number that lands is read. */

/* Scoped to .b2b-motion, which the inline script in wp_head adds and takes
   away again if main.js never arrives. Without that scope a missing script
   leaves the whole page below the hero at opacity 0. */
@media (prefers-reduced-motion: no-preference) {
  .b2b-motion .reveal {
    opacity: 0;
    transform: translateY(22px);
    transition:
      opacity 0.7s var(--ease-out) calc(var(--i, 0) * 80ms),
      transform 0.7s var(--ease-out) calc(var(--i, 0) * 80ms);
  }
  .b2b-motion .reveal.is-in { opacity: 1; transform: none; }

  /* A card is a bigger object than a line of type and wants a longer travel
     and a touch of scale, or it reads as text sliding rather than a card
     arriving.

     :not(.is-in) is load-bearing. This is the same specificity as the
     .reveal.is-in rule above it and comes later, so without the guard it won
     the tie and these three never arrived: the event cards, the Why photo and
     the FAQ photo faded in but stayed 30px low and 1.5% small, on every page,
     for good. Stating "this is the state before it arrives" fixes it without
     depending on where the two rules sit in this file. */
  .b2b-motion .event-card.reveal:not(.is-in),
  .b2b-motion .why__media.reveal:not(.is-in),
  .b2b-motion .details__media.reveal:not(.is-in) {
    transform: translateY(30px) scale(0.985);
    transform-origin: 50% 100%;
  }

  /* The hero is above the fold, so it has nothing to scroll into — it runs
     once on load instead, in the reading order of the block. */
  .b2b-motion .hero__copy > * {
    animation: rise 0.8s var(--ease-out) both;
  }
  .b2b-motion .hero__copy > :nth-child(1) { animation-delay: 0.05s; }
  .b2b-motion .hero__copy > :nth-child(2) { animation-delay: 0.14s; }
  .b2b-motion .hero__copy > :nth-child(3) { animation-delay: 0.23s; }
  .b2b-motion .hero__copy > :nth-child(4) { animation-delay: 0.32s; }
  .b2b-motion .hero__copy > :nth-child(5) { animation-delay: 0.41s; }
  .b2b-motion .hero__frame { animation: fade 1s var(--ease-out) 0.1s both; }

  @keyframes rise {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes fade {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}

/* Nothing scrolls on paper, so nothing would ever be uncovered. */
@media print {
  .b2b-motion .reveal { opacity: 1 !important; transform: none !important; }
}

/* Scroll-linked drift. Chrome, Edge and Safari 26 run this natively; anywhere
   else the images simply sit still, which is the correct fallback. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Not the running-order cards: those are pinned and travelling
       sideways already, and a second scroll-driven transform on top of that
       reads as a wobble rather than as depth. */
    .why__img img,
    .details__media img,
    .event-card__media img {
      animation: shot-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes shot-drift {
      from { scale: 1.001; }
      to   { scale: 1.075; }
    }

  }
}

/* ------------------------------------------------------------------ *
 * 17. Editorial content (privacy policy etc.)
 * ------------------------------------------------------------------ */

.prose { max-width: 44rem; }
.prose h2 { font-size: var(--step-2); margin-top: 2em; }
.prose h3 { font-size: var(--step-1); margin-top: 1.6em; }
.prose ul { padding-left: 1.15em; }
.prose a { color: var(--teal-ink); }

/* ------------------------------------------------------------------ *
 * 18. Touch targets
 *
 * Standalone controls that are comfortably clickable with a cursor and are
 * not with a thumb. Everything here is a link that sits on its own line or in
 * a list — links inside a sentence are left alone, because padding them opens
 * gaps in the line box and the surrounding words already give them room.
 *
 * Measured at 375px before changing anything: the footer's link lists were
 * 16px tall, the calendar and directions row 24px, and the social buttons
 * 38px square.
 *
 * 900px because that is where this theme already decides a visitor is on a
 * touch device — it is the width the hamburger appears at. A tablet in
 * portrait is 768px and has the same thumbs as a phone.
 * ------------------------------------------------------------------ */

@media (max-width: 900px) {
  .site-footer ul a,
  .event-card__detail a,
  .gallery__note a,
  .lunch__back a {
    display: inline-block;
    padding-block: 0.55rem;
  }

  .ev-utils {
    gap: 0.2rem 1.4rem;
  }
  .ev-utils a {
    padding-block: 0.5rem;
  }

  .socials a { width: 2.75rem; height: 2.75rem; }
}

/* The default is an opaque grey box the shape of the element, which on the
   pill buttons and the photo tiles flashes as a hard rectangle over rounded
   artwork. A tint of the brand teal instead. */
a, button, summary, [role='button'] {
  -webkit-tap-highlight-color: rgba(43, 191, 203, 0.24);
}

/* ------------------------------------------------------------------ *
 * 19. Phone action rows
 *
 * These rows are `flex-wrap: wrap` and were sized for a line that has room
 * for two buttons. On a phone they do wrap, but into a ragged left-aligned
 * stack — a 218px button above a 142px one — which reads as a wrapping
 * accident rather than a decision. Full width, in the order they were
 * written, and the primary action is the width of the thumb reaching it.
 *
 * Scoped to phones so the tablet arrangement, which does fit two across,
 * is left as it is.
 * ------------------------------------------------------------------ */

@media (max-width: 560px) {
  .hero__actions,
  .event-card__foot,
  .events__single-actions {
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
  }
}
