@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/fonts/space-grotesk-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/space-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/space-mono-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bone: #FAF9F5;
  --ink: #15160F;
  --lime: #CFE800;
  --grey-1: #EDECE5;
  --grey-2: #8A8B7F;
  --grey-3: #5B5C52;
  /* A secondary text colour for ink grounds. grey-2 is 5.27:1 on flat ink but
     only 4.31:1 once a translucent panel lifts the ground, which is below AA at
     the size these labels are actually set. */
  --grey-on-ink: #B9BAAE;
  --line: #DEDDD3;

  --ground: var(--bone);
  --surface: #FFFFFF;
  /* Legacy aliases, so a renderer that has not been rewritten yet still reads a
     real token rather than a colour that no longer exists. */
  --muted: var(--grey-3);
  --faint: var(--grey-3);
  --sunk: var(--grey-1);
  --line-soft: var(--line);

  /* Links are ink, underlined. Lime cannot be a foreground on bone at 1.31:1,
     and colour alone was never a sufficient affordance anyway. */
  --link: var(--ink);
  --accent: var(--lime);
  --accent-ink: var(--ink);
  --accent-soft: #F3F7CC;
  --on-accent: var(--ink);

  /* Semantic states. Checked too: 6.02:1 and 7.78:1 on bone. */
  --ok: #2F6B45;
  --ok-soft: #EDF4EF;
  --no: #8C3111;
  --no-soft: #F8EDE9;

  --font: "Space Grotesk", "Helvetica Neue", Arial, system-ui, sans-serif;
  --mono: "Space Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Tiling noise, generated once by feTurbulence and inlined so it costs no
     request. Two octaves at a high base frequency: tooth, not texture. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");

  --text-xs: .75rem;
  --text-sm: .875rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.75rem;
  --text-2xl: 2.5rem;
  --text-hero: clamp(2.5rem, 6vw, 4.25rem);

  --radius-card: 8px;
  --radius-pill: 999px;
  --radius-stamp: 6px;

  /* 4 8 12 16 24 32 48 64 96 */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;
}

* { box-sizing: border-box; }

/* Full-bleed bands are laid out with a viewport-width escape, and `vw` counts
   the scrollbar gutter — so without this a band overhangs by the width of the
   scrollbar on any platform that shows one. `clip` rather than `hidden`
   because `hidden` makes the root a scroll container and breaks the sticky
   header. */
html { overflow-x: clip; }

/* The hidden attribute is a UA style at the lowest specificity, so any class
   that sets display beats it. The season view sets display:grid and was
   therefore never hidden at all: the catalogue shipped the card grid and the
   chronological list stacked one above the other, and the "Grid / Follow the
   season" toggle appeared to do nothing because both were always on. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
}

a { color: var(--link); text-underline-offset: 2px; }

/* ---- Rhythm --------------------------------------------------------------
   The site alternates ink and bone bands down the page. Without this it reads
   as one long pale column whatever the content is: a page with no dark section
   has no rhythm to it, and nothing on it can feel like an arrival.

   A band escapes its column and spans the viewport, then pads itself back in,
   so the text still lines up with everything above and below it. */
.band {
  margin-inline: calc(50% - 50vw);
  padding-inline: calc(50vw - 50% + var(--s5));
  padding-block: var(--s7);
}
.band > *:not(.photo-bg) { max-width: 1120px; }
.band-ink { background: var(--ink); color: var(--bone); }

/* Inside a dark block the tokens themselves change meaning, rather than every
   rule that uses them being rewritten with a darker-ground twin. grey-3 is a
   secondary text colour on bone and 2.69:1 on ink — so a paragraph that was
   correct on a light page became unreadable the moment a band went dark behind
   it. Redefining the custom property fixes every descendant at once, whatever
   its selector's specificity, which the alternative could never do. */
.band-ink, .ink-block {
  --grey-3: var(--grey-on-ink);
  --muted: var(--grey-on-ink);
  --faint: var(--grey-on-ink);
  /* --ink is deliberately NOT remapped. It is a background as often as it is
     a foreground — the lime highlight paints ink text on lime, a sport chip
     paints bone on ink, a fresh stamp is an ink block — and flipping it turned
     all three into one colour on itself. */
  --link: var(--lime);
  --line: rgba(250, 249, 245, .18);
  --line-soft: rgba(250, 249, 245, .12);
  --surface: rgba(250, 249, 245, .07);
  --grey-1: rgba(250, 249, 245, .07);
  --sunk: rgba(250, 249, 245, .07);
}
/* Text that took its colour from --ink needs the inversion the token no
   longer provides, so the few places that do get it explicitly. */
.band-ink, .ink-block { color: #FAF9F5; }
.band-ink h1, .band-ink h2, .band-ink h3, .band-ink h4,
.ink-block h1, .ink-block h2, .ink-block h3 { color: #FAF9F5; }
.band-ink strong, .ink-block strong { color: #FAF9F5; }
.band-ink h1, .band-ink h2, .band-ink h3 { color: var(--bone); }
.band-ink p { color: var(--grey-on-ink); }
.band-ink a:not(.cta) { color: var(--lime); }
.band-grey { background: var(--grey-1); }

/* ---- Grain ----------------------------------------------------------------
   One tiling noise layer. It is most of the difference between "designed" and
   "template" at a two-colour palette: flat ink over a whole band reads as a
   fill, and the same ink with a little tooth reads as a surface. */
.band-ink, .ink-block, .img-duotone-wrap { position: relative; isolation: isolate; }
.band-ink::after, .ink-block::after, .img-duotone-wrap::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background-image: var(--grain);
  background-size: 180px 180px;
  opacity: .06;
}
.band-ink > *:not(.photo-bg), .ink-block > *:not(.photo-bg) { position: relative; z-index: 2; }

/* Prose keeps a readable measure whatever the container is doing. */
p, li { max-width: 68ch; }

/* Never a fixed pixel width on an image, and never one without a ratio: a box
   that reserves its space cannot shift the layout when its contents load. */
img, svg, video { max-width: 100%; height: auto; }

/* ---- Type -----------------------------------------------------------------
   The scale was timid: a 2rem H1 on a page whose whole job is to feel like a
   season. Headings now carry the page and the body stays quiet under them. */
h1 {
  font-size: var(--text-hero); line-height: 1.05; letter-spacing: -.02em;
  font-weight: 700; margin: 0 0 var(--s4);
}
h2 {
  font-size: clamp(1.75rem, 3.4vw, 2.5rem); line-height: 1.1; letter-spacing: -.015em;
  font-weight: 700; margin: var(--s7) 0 var(--s4);
}
h3 { font-size: var(--text-lg); line-height: 1.25; letter-spacing: -.01em; }
.lede { font-size: clamp(1.0625rem, 1.6vw, 1.25rem); line-height: 1.5; color: var(--grey-3); }

/* The brand's highlight device: a lime block behind two to four words, ink text
   on top. Lime cannot be a foreground on bone at 1.31:1, but ink on lime is
   13.17:1 — so the accent carries emphasis by becoming the ground, never the
   letter. One phrase per heading, and no more: a page where everything is
   highlighted has nothing highlighted. */
.mark {
  background: var(--lime); color: var(--ink);
  padding: 0 .18em .06em; margin: 0 -.06em;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}

/* Machine truth. Anything set in mono came out of the catalogue. */
.mono, .price, time, .stamp-date { font-family: var(--mono); font-variant-ligatures: none; }

/* Focus has to be visible on both grounds, and lime on bone is not visible as a
   hairline — so the ring takes the colour that contrasts with what it sits on. */
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.on-ink :focus-visible, .ink-block :focus-visible { outline-color: var(--lime); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- Buttons -------------------------------------------------------------
   One appearance for a primary action, whether it submits a form or navigates.
   A <button> submits; an <a> navigates. Swapping either for the other breaks
   keyboard behaviour, middle-click, and what a screen reader announces, so the
   element follows the job and only the styling is shared. */
.cta,
button[type="submit"],
.waitlist-form button {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s2); font: inherit; font-size: 15px; font-weight: 600; line-height: 1.2;
  padding: 12px 20px; border: 1px solid transparent; border-radius: 4px;
  background: var(--lime); color: var(--on-accent); text-decoration: none;
  cursor: pointer; text-align: center;
  /* Comfortably past the 44px target a thumb needs. */
  min-height: 46px;
}
.cta:hover,
button[type="submit"]:hover,
.waitlist-form button:hover { filter: brightness(0.94); }

.cta:active,
button[type="submit"]:active { transform: translateY(1px); }

/* The quiet twin: same shape, ink outline, for a secondary destination. */
.cta-quiet {
  background: transparent; color: var(--ink); border-color: var(--ink);
}
.cta-quiet:hover { background: var(--grey-1); filter: none; }

@media (max-width: 480px) {
  /* Full width when there is no room beside it, which is most phones. */
  .cta, .cta-quiet, .waitlist-form button { width: 100%; }
}

/* ---- Ink blocks ----------------------------------------------------------
   A section on ink. Lime becomes a legal foreground inside one, at 13.17:1. */
.ink-block {
  background: var(--ink); color: var(--bone);
  border-radius: var(--radius-card); padding: var(--s5);
}
.ink-block a { color: var(--lime); }
/* A CTA inside an ink block is still ink on lime. Without this it inherits the
   block's lime link colour and becomes lime on lime: a button with no words. */
.ink-block a.cta { color: var(--on-accent); }
.ink-block a.cta-quiet { color: var(--bone); border-color: var(--grey-2); }
.ink-block .grey { color: var(--grey-2); }

/* ---- Photography ----------------------------------------------------------
   A photo layer that costs nothing until there is a photo.

   The image comes from a custom property, so with --photo unset the value is
   none, the browser makes no request, and --photo-on holds the whole layer at
   zero opacity. Switching one element on is then two declarations and no code:

     style="--photo: url('/assets/photos/photo-openwater-pack.jpg'); --photo-on: 1"

   The scrim is stacked in the same background so it always travels with the
   image it is darkening. Two gradients: one across, so text on the left stays
   on near-solid ink whatever the crop does, and one down, so the artwork and
   the stats at the foot sit on ink rather than on somebody's shoulder. */
.photo-bg {
  position: absolute; inset: 0; z-index: -2; pointer-events: none;
  overflow: hidden;
}
/* The photograph, held well back. The scrim is a separate layer at full
   strength: stacking both in one background meant one opacity governed both,
   so dimming the picture dimmed the thing that makes the words readable. */
.photo-bg::before {
  content: ""; position: absolute; inset: 0;
  background-image: var(--photo, none);
  background-size: cover;
  background-position: var(--photo-position, center 35%);
  background-repeat: no-repeat;
  opacity: calc(var(--photo-on, 0) * var(--photo-opacity, .38));
}
/* Two gradients. One across, so the left column where the words start stays on
   near-solid ink whatever the crop does, and one down, so the drawing and the
   figures at the foot sit on ink rather than on somebody's shoulder.
   The values are stronger than the brief's because the brief's did not hold:
   these photographs are duotoned onto an ink-to-bone ramp, so their highlights
   land at bone, and measuring the rendered pixels behind the headline against a
   bone ground gave 1.25:1. Contrast was the point of the two-gradient scrim; a
   scrim that looks right and fails AA is not a scrim. */
.photo-bg::after {
  content: ""; position: absolute; inset: 0;
  opacity: var(--photo-on, 0);
  background-image:
    linear-gradient(transparent 50%, rgba(21, 22, 15, .90) 95%),
    linear-gradient(100deg, rgba(21, 22, 15, .94) 0%, rgba(21, 22, 15, .72) 55%, rgba(21, 22, 15, .45) 100%);
}

/* ---- Duotone --------------------------------------------------------------
   For operator photography when any exists. Never stock: a stock image of a
   place nobody verified is a claim about a real place. */
.img-duotone {
  filter: grayscale(1) contrast(1.08);
  mix-blend-mode: multiply;
  background: var(--ink);
}

.icon { width: 20px; height: 20px; flex: none; vertical-align: -.2em; }
.icon-sm { width: 16px; height: 16px; }
.icon-lg { width: 24px; height: 24px; }
.icon-xl { width: 32px; height: 32px; }

.elevation { display: block; width: 100%; height: auto; background: var(--ink); }
.elevation-card { aspect-ratio: 16 / 9; }
.elevation-hero-wrap { border-radius: var(--radius-card); overflow: hidden; }
.elevation-caption {
  font-family: var(--mono); font-size: 15px; letter-spacing: .08em;
}
.elevation-hero .elevation-caption { font-size: 17px; }

/* The one non-interaction animation on the site: the homepage ridge draws
   itself once, on load. Everything else on this site moves only when the reader
   moves it. */
@keyframes elevation-draw {
  from { stroke-dashoffset: 2400; }
  to   { stroke-dashoffset: 0; }
}
.elevation-draw {
  stroke-dasharray: 2400;
  animation: elevation-draw 900ms cubic-bezier(.22,.61,.36,1) both;
}
@media (prefers-reduced-motion: reduce) {
  .elevation-draw { animation: none; stroke-dasharray: none; }
}

.stamp {
  display: inline-flex; align-items: flex-start; gap: var(--s2);
  margin: 0; border-radius: var(--radius-stamp); font-size: var(--text-xs);
}
.stamp-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.stamp-label { font-weight: 600; letter-spacing: .01em; }
.stamp-meta { font-family: var(--mono); font-size: 11px; line-height: 1.45; }
.stamp .icon { margin-top: 1px; }

/* fresh — a solid ink block. The only state that shouts, and it has earned it. */
.stamp-fresh { background: var(--ink); color: var(--bone); padding: 10px 12px; }
.stamp-fresh .icon { color: var(--lime); }
.stamp-fresh .stamp-meta { color: var(--lime); }

/* aging — still true, no longer shouting. */
.stamp-aging { border: 1.5px solid var(--ink); color: var(--ink); padding: 8.5px 10.5px; }
.stamp-aging .icon { color: var(--ink); }
.stamp-aging .stamp-meta { color: var(--grey-3); }

/* stale — says so itself. Dashed, because a dashed border reads as provisional
   before a single word has been read. */
.stamp-stale { border: 1.5px dashed var(--grey-2); color: var(--grey-3); padding: 8.5px 10.5px; }
.stamp-stale .stamp-label { color: var(--ink); }

/* The three states were drawn for a light page. On a dark one the outlined
   states keep their shape and invert their ink: a dashed grey box saying
   "last checked over 90 days ago" in ink-on-ink says nothing at all. */
.band-ink .stamp-aging, .band-ink .stamp-stale, .band-ink .stamp-never,
.ink-block .stamp-aging, .ink-block .stamp-stale, .ink-block .stamp-never {
  border-color: var(--grey-2); color: var(--grey-on-ink);
}
.band-ink .stamp-aging .stamp-label, .band-ink .stamp-stale .stamp-label,
.band-ink .stamp-never .stamp-label, .ink-block .stamp-stale .stamp-label { color: var(--bone); }
.band-ink .stamp-aging .icon, .ink-block .stamp-aging .icon { color: var(--bone); }
/* And the fresh state, which is an ink block itself, needs an edge to sit on. */
.band-ink .stamp-fresh, .ink-block .stamp-fresh { outline: 1px solid var(--grey-2); }
.stamp-stale .icon { color: var(--grey-2); }

.stamp-never { border: 1.5px dashed var(--grey-2); color: var(--grey-3); padding: 8.5px 10.5px; }
.stamp-never .icon { color: var(--grey-2); }

.stamp-compact { padding: 8px 10px; }
.stamp-compact .stamp-label { font-size: 11.5px; }
.stamp-compact.stamp-aging, .stamp-compact.stamp-stale { padding: 6.5px 8.5px; }

.stamp-mini { padding: 0; background: none; border: 0; color: inherit; gap: 6px; align-items: center; }
.stamp-mini .stamp-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.stamp-mini .stamp-meta { color: inherit; }
.stamp-mini .icon { color: inherit; }

.stamp-states { list-style: none; margin: var(--s5) 0 0; padding: 0; display: grid; gap: var(--s3); }
@media (min-width: 640px) { .stamp-states { grid-template-columns: repeat(3, 1fr); align-items: start; } }
.stamp-states .stamp { width: 100%; }

/* ---- Cards --------------------------------------------------------------- */
.camp-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s4); }
@media (min-width: 960px) { .camp-grid { grid-template-columns: repeat(2, 1fr); } }
.camp-item { min-width: 0; }
.camp-item[hidden] { display: none; }

.card {
  display: flex; flex-direction: column; height: 100%;
  background: var(--grey-1); border: 1px solid transparent;
  border-radius: var(--radius-card); overflow: hidden;
  transition: transform 120ms ease, border-color 120ms ease;
}
.card:hover, .card:focus-within { transform: translateY(-2px); border-color: var(--ink); }
/* The drawing rests at 85% and comes up to full on hover. It is a small thing
   and it is what makes a grid of cards feel alive rather than printed. */
.card .elevation-hero { opacity: .85; transition: opacity 140ms ease; }
.card:hover .elevation-hero, .card:focus-within .elevation-hero { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
  .card:hover { transform: none; }
  .card .elevation-hero { transition: none; }
}

.card-art { position: relative; }
.card-art-top {
  position: absolute; inset: var(--s3) 0 auto 0;
  display: flex; align-items: flex-start; gap: var(--s2);
}
/* The sport sits at the foot of the art rather than beside the sample tab: two
   chips in one corner meant the tab covered the sport on every sample card. */
.card-art-foot {
  position: absolute; inset: auto var(--s3) var(--s3) auto;
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px;
}

.card-body { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s4); flex: 1; }
.card-title { margin: 0; font-size: var(--text-lg); font-weight: 600; line-height: 1.25; }
.card-title a { color: var(--ink); text-decoration: none; }
.card-title a::after { content: ""; position: absolute; inset: 0; }
.card { position: relative; }
.card-title a:hover { text-decoration: underline; }

.card-where {
  margin: 0; display: flex; align-items: center; gap: 6px;
  font-size: var(--text-sm); color: var(--grey-3);
}
.card-line {
  margin: 0; font-size: var(--text-sm); color: var(--grey-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.card-facts {
  margin: var(--s1) 0 0; display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  border-top: 1px solid var(--line); padding-top: var(--s3);
}
/* A min-width of 0 here let a date that does not wrap run straight through the
   column beside it, so "18 - 22 Mar 2027" and the departure count printed on top
   of each other. Each fact now keeps its own width and the row wraps instead. */
.card-facts > div { display: flex; flex-direction: column; gap: 2px; flex: 0 0 auto; }
.card-facts dd time { white-space: nowrap; }
.card-facts dt {
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--grey-3);
}
.card-facts dd { margin: 0; font-family: var(--mono); font-size: var(--text-sm); }
.card-facts .price { font-size: var(--text-xl); line-height: 1.1; font-weight: 700; }

/* Below 380px the price keeps its own line and leads, because it is the figure
   that decides whether the rest is worth reading. */
@media (max-width: 380px) {
  .card-facts { gap: var(--s2) var(--s4); }
  .card-facts > div:first-child { flex-basis: 100%; }
}

.card-sample { border-style: dashed; border-color: var(--grey-2); }
.sample-tab {
  position: absolute; top: 0; right: 0; z-index: 1;
  background: var(--lime); color: var(--ink);
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 10px; border-bottom-left-radius: var(--radius-stamp);
}
.sample-note {
  margin: var(--s3) 0 0; font-size: var(--text-sm); color: var(--grey-3);
  border-left: 3px solid var(--lime); padding-left: var(--s3);
}

/* ---- Chips and pills ----------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: var(--radius-pill); font-size: var(--text-xs); line-height: 1;
  padding: 6px 10px; white-space: nowrap;
}
.chip-sport { background: var(--ink); color: var(--bone); font-weight: 500; }
.chip-sport .icon { color: var(--lime); }

.chip-departure {
  background: var(--ink); color: var(--bone);
  font-family: var(--mono); font-size: 11.5px;
  border-left: 3px solid var(--grey-2);
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
.chip-departure.freshness-fresh { border-left-color: var(--lime); }
.chip-departure.freshness-aging { border-left-color: var(--bone); }
.chip-departure.freshness-stale,
.chip-departure.freshness-never { border-left-color: var(--grey-2); }

.pills { display: flex; flex-wrap: wrap; gap: 6px; margin: var(--s1) 0 0; }
.pill {
  border: 1px solid var(--grey-2); border-radius: var(--radius-pill);
  color: var(--ink); font-size: var(--text-xs); line-height: 1; padding: 5px 10px;
}

/* ---- HeroStats ----------------------------------------------------------- */
.hero-stats {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--s2);
}
.hero-stats li {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--grey-1); border-radius: var(--radius-pill); padding: 8px 14px;
  font-size: var(--text-sm);
}
.hero-stats .icon { color: var(--grey-3); }
.stat-value { font-weight: 700; }
.stat-label { color: var(--grey-3); }
.stat-fresh { font-family: var(--mono); font-size: var(--text-xs); }

/* ---- BrowseBySport tiles ------------------------------------------------- */
/* Two up on a phone, three on a tablet, four on a wide page. Five across a
   820px column gave each tile 132px, which is not enough for "Swimming and open
   water" to sit beside anything. */
.tiles { list-style: none; margin: var(--s5) 0 0; padding: 0; display: grid; gap: var(--s3); }
@media (min-width: 640px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .tiles { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 639px) {
  .tiles {
    grid-auto-flow: column; grid-auto-columns: 62%; overflow-x: auto;
    scroll-snap-type: x mandatory; padding-bottom: var(--s2);
  }
  .tiles > li { scroll-snap-align: start; }
}
.tile {
  display: flex; flex-direction: column; border-radius: var(--radius-card);
  overflow: hidden; text-decoration: none; color: var(--bone); background: var(--ink);
  height: 100%;
}
.tile-art { position: relative; display: block; }
.tile .elevation.elevation-card { aspect-ratio: 16 / 9; height: auto; }
.tile-art .icon { position: absolute; top: var(--s3); left: var(--s3); color: var(--lime); }
/* The label sits on flat ink rather than on the tail of a gradient over a
   drawing. A gradient is a promise about contrast that depends on what is
   underneath it; a solid ground is a fact. */
.tile-face {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--s3); background: var(--ink); flex: 1;
}
.tile:hover .tile-name { text-decoration: underline; }
.tile-name { font-weight: 600; font-size: var(--text-sm); line-height: 1.25; }
.tile-count { font-size: var(--text-xs); color: var(--grey-on-ink); }

/* ---- FilterBar ----------------------------------------------------------- */
.filter-bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  background: var(--bone); border-bottom: 1px solid var(--line);
  padding: var(--s3) 0; margin: var(--s5) 0 var(--s5);
}
/* min-width:0 on both the group and the scroller, or a nowrap row of month chips
   sets the flex item's basis to its content width and pushes the whole page
   sideways instead of scrolling inside itself. */
.filter-group { display: flex; align-items: center; gap: var(--s2); min-width: 0; max-width: 100%; }
.filter-label {
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--grey-3);
}
.filter-options { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.filter-scroll {
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  min-width: 0; max-width: 100%; padding-bottom: 2px;
  scrollbar-width: thin;
}
.filter-scroll > * { flex: none; }
.chip-filter {
  position: relative;
  border: 1px solid var(--line); background: var(--surface); cursor: pointer;
  padding: 7px 12px; min-height: 34px;
}
/* position:relative on the label is load-bearing. Without it these absolutely
   positioned inputs resolve against the page, land wherever, and stretch the
   document sideways on a phone — a horizontal scrollbar with no visible cause. */
.chip-filter input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.chip-filter:has(input:checked) { background: var(--ink); color: var(--bone); border-color: var(--ink); }
.chip-filter:has(input:checked) .icon { color: var(--lime); }
.chip-filter:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }
.filter-foot {
  display: flex; align-items: center; gap: var(--s3); margin: 0;
  font-size: var(--text-xs); color: var(--grey-3);
}
.filter-clear {
  font: inherit; font-size: var(--text-xs); background: none; border: 0;
  color: var(--ink); text-decoration: underline; cursor: pointer; padding: 4px;
}
@media (max-width: 639px) {
  .filter-bar { gap: var(--s3); }
  /* Each control gets its own row on a phone; the month scroller then has a
     real width to scroll within. */
  .filter-group { flex-basis: 100%; flex-wrap: wrap; }
  .filter-group .filter-options { flex: 1 1 100%; }
}

.empty, .empty-filtered { margin: var(--s6) 0; color: var(--grey-3); }

/* ---- Season view --------------------------------------------------------- */
.season { display: grid; gap: var(--s5); }
.season-month h2, .season-month h3 {
  margin: 0 0 var(--s2); font-size: var(--text-sm); letter-spacing: .08em;
  text-transform: uppercase; color: var(--grey-3);
  border-bottom: 1px solid var(--line); padding-bottom: 6px;
}
.season-month ul { list-style: none; margin: 0; padding: 0; }
.season-row {
  display: grid; grid-template-columns: auto 1fr auto; align-items: baseline;
  gap: 4px var(--s3); padding: var(--s3) 0; border-bottom: 1px solid var(--line);
  font-size: var(--text-sm);
}
.season-row a { font-weight: 600; }
.season-where { color: var(--grey-3); grid-column: 2; }
.season-row time { grid-column: 2; color: var(--grey-3); font-size: var(--text-xs); }
.season-row .price { grid-column: 3; grid-row: 1 / span 3; font-weight: 700; }
@media (min-width: 640px) {
  .season-row { grid-template-columns: auto 2fr 1.2fr auto; align-items: center; }
  .season-where, .season-row time, .season-row .price { grid-column: auto; grid-row: auto; }
}

/* ---- DataTable ----------------------------------------------------------- */
.data-table { margin: var(--s5) 0 0; display: grid; gap: 0; }
.data-table > div {
  display: grid; grid-template-columns: minmax(0, 11rem) 1fr; gap: var(--s3);
  padding: var(--s3) 0; border-bottom: 1px solid var(--line);
}
.data-table dt {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--text-sm); font-weight: 600;
}
.data-table dt .icon { color: var(--grey-3); }
.data-table dd { margin: 0; font-size: var(--text-sm); }
.data-table .row-accent {
  background: var(--ink); color: var(--bone); border-bottom-color: var(--ink);
  padding-inline: var(--s3); margin-inline: calc(var(--s3) * -1);
  border-radius: var(--radius-stamp);
}
.data-table .row-accent dt { color: var(--bone); }
.data-table .row-accent dt .icon { color: var(--lime); }
@media (max-width: 520px) {
  .data-table > div { grid-template-columns: minmax(0, 7.5rem) 1fr; gap: var(--s2); padding: 10px 0; }
  .data-table dt { font-size: var(--text-xs); }
}

/* ---- Voices -------------------------------------------------------------- */
.voices-interim {
  background: rgba(250,249,245,.07); border-radius: var(--radius-card); padding: var(--s5);
  max-width: 46rem;
}
.voices .voices-interim p { color: var(--bone); }
.voices .voices-interim a { color: var(--lime); }
.voices-interim p { margin: 0 0 var(--s2); }
.voices-interim p:last-child { margin: 0; }

/* Wraps into as many columns as fit, so fifteen facilities stack into a block
   rather than one long column, and three do not stretch across the page. */
.facilities {
  list-style: none; margin: var(--s3) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  gap: var(--s2) var(--s5);
}
.facilities li {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: var(--text-sm); color: var(--grey-3); min-width: 0;
}
.facilities .icon { color: var(--lime); flex: none; margin-top: 3px; }
/* Long feature names break rather than push the grid wider than the page. */
.facilities span { min-width: 0; overflow-wrap: anywhere; }

/* The five groups. Two columns where there is room, one on a phone: the groups
   are short, and a single column of four short tables is a lot of scrolling
   for eleven facts. */
.fact-groups {
  display: grid; gap: var(--s5) var(--s7);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  margin-top: var(--s4);
}
/* Facilities can run long, so it takes the full width rather than sitting in a
   narrow column with fifteen items stacked in it. */
.fact-group-wide { grid-column: 1 / -1; }
/* Logistics runs long and the other two are short. Left to the grid they sat on
   separate rows and left a column-high hole beside the price. Pairing the two
   short groups into one column closes it without asking for masonry. */
.fact-column { display: flex; flex-direction: column; gap: var(--s5); min-width: 0; }
.fact-group h3 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--grey-3); margin: 0 0 var(--s2); font-weight: 600;
}
/* The offer group carries the accent the price row already had. */
.fact-group-offer h3 { color: var(--ink); }

/* The differentiator, out of the table and onto ink. */
.different-band .different-line {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 var(--s3); max-width: 46rem;
}
.different-band .different-line:last-child { margin-bottom: 0; }
.different-band .icon { color: var(--lime); flex: none; margin-top: 4px; }
.different-band strong { color: var(--bone); }

/* The prose now leads, so it is set larger than body copy and narrower than
   the page: it is the paragraph that has to do the persuading. */
.camp-prose-band { margin: var(--s6) 0 var(--s7); }
.camp-prose { font-size: var(--text-lg, 19px); line-height: 1.55; max-width: 42rem; color: var(--ink); }

ol.steps { counter-reset: step; list-style: none; padding: 0; margin: var(--s5) 0 0; }
ol.steps > li {
  counter-increment: step; position: relative;
  padding: 0 0 var(--s5) 46px; border-left: 1px solid var(--line); margin-left: 13px;
}
ol.steps > li:last-child { border-left-color: transparent; padding-bottom: 0; }
ol.steps > li::before {
  content: counter(step); position: absolute; left: -13px; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--ink); color: var(--lime);
  font-family: var(--mono); font-size: 13px;
  display: grid; place-items: center;
}
ol.steps h3 { margin: 0 0 4px; font-size: var(--text-base); }
ol.steps p { margin: 0; color: var(--grey-3); font-size: var(--text-sm); }

.founder p { max-width: 42rem; }
.signature { font-family: var(--mono); font-size: var(--text-sm); color: var(--grey-3); }

ul.never { list-style: none; margin: var(--s4) 0 0; padding: 0; }
ul.never li { padding: 10px 0; border-bottom: 1px solid var(--line); color: var(--grey-3); }
ul.never s { text-decoration-thickness: 1.5px; }

ul.contacts { list-style: none; margin: var(--s4) 0 0; padding: 0; font-size: var(--text-sm); }
ul.contacts li { margin: 0 0 6px; }

ul.methods { list-style: none; margin: var(--s5) 0 0; padding: 0; display: grid; gap: var(--s4); }
/* min-width on a grid item defaults to auto, which means "never narrower than
   your longest unbreakable thing". The endpoint below is one 45-character URL
   with nothing to break on, so each row grew to fit it and pushed 7px of the
   page off the right-hand edge of a phone — a horizontal scrollbar earned by a
   link nobody could read anyway. The URL is allowed to wrap mid-string, and
   the row is allowed to be as narrow as its track. */
ul.methods > li { border-top: 1px solid var(--line); padding-top: var(--s4); min-width: 0; }
ul.methods a.mono { overflow-wrap: anywhere; }
ul.methods h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: var(--text-base); }
ul.methods p { margin: 0 0 6px; color: var(--grey-3); font-size: var(--text-sm); max-width: 44rem; }
ul.methods a { font-size: 13px; }

ul.query { list-style: none; margin: var(--s4) 0 0; padding: 0; }
ul.query li {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
  font-size: var(--text-sm); color: var(--grey-3);
}
ul.query .icon { color: var(--grey-3); margin-top: 2px; }

.honesty { margin: var(--s6) 0 0; font-size: var(--text-sm); }
.steps-out { margin: var(--s6) 0 0; font-size: var(--text-sm); }

.operator-head .lede { display: flex; align-items: center; gap: 8px; }
.operator-head .lede .icon { color: var(--grey-3); }
.operator-about { max-width: 44rem; color: var(--grey-3); font-size: var(--text-sm); }

/* The link is text first and a button second, so the page still does its job
   with the script off. */
.share {
  margin: var(--s5) 0 0; padding: var(--s4) 0 0; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
}
.share-url { margin: 0; font-size: 13px; flex: 1 1 20rem; min-width: 0; overflow-wrap: anywhere; }
.share-status { font-size: var(--text-xs); color: var(--grey-3); }
@media (max-width: 480px) { .share .cta-quiet { width: auto; } }

ul.cross-links { list-style: none; margin: var(--s4) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
ul.cross-links a {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 8px 14px; text-decoration: none; font-size: var(--text-sm);
}
ul.cross-links a:hover { border-color: var(--ink); }

.not-found { text-align: center; display: grid; justify-items: center; gap: var(--s3); padding: var(--s9) var(--s5); }
.not-found .icon { color: var(--lime); }
.not-found h1 { margin: 0; max-width: 18ch; }
.not-found p { margin: 0; color: var(--grey-2); }
.not-found-links { display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: center; margin-top: var(--s3); }
.not-found .cta-quiet { color: var(--bone); border-color: var(--grey-2); }
.not-found .cta-quiet:hover { background: rgba(250,249,245,.08); }

/* ---- Operator identity ---------------------------------------------------
   The name leads and the mark sits beside it, so the page reads as theirs
   rather than as a row in a directory with the name substituted in. */
/* Top-aligned, not centred: a long name wraps to three lines on a phone and a
   centred mark then floats beside the middle one, attached to nothing. */
.operator-identity { display: flex; align-items: flex-start; gap: var(--s4); margin: 0 0 var(--s3); }
.operator-names { min-width: 0; }
.operator-names h1 { margin: 0; }
/* The placeholder until a real logo arrives: their initial, in the accent, on
   ink. Reserved at a fixed size so dropping an image in later changes nothing
   about the layout around it. */
.operator-mark {
  flex: none; width: 64px; height: 64px; margin: 0;
  display: grid; place-items: center;
  background: var(--ink); color: var(--lime);
  font-family: var(--font); font-weight: 700; font-size: 30px; line-height: 1;
  border-radius: 4px;
}
.operator-sub { margin: 6px 0 0; color: var(--grey-3); font-size: var(--text-sm); }
.operator-langs {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 var(--s4); font-size: var(--text-sm); color: var(--grey-3); max-width: 46rem;
}
.operator-langs .icon { color: var(--grey-3); flex: none; margin-top: 3px; }
.operator-langs strong { color: var(--ink); }
.operator-site { display: flex; align-items: center; gap: 8px; margin: 0 0 var(--s4); font-size: var(--text-sm); }
.operator-site .icon { color: var(--grey-3); flex: none; }

@media (max-width: 640px) {
  /* At 375px a 64px mark beside a long name leaves the name three words wide. */
  .operator-identity { gap: var(--s3); }
  .operator-mark { width: 48px; height: 48px; font-size: 23px; }
}

.credit-table-wrap { overflow-x: auto; margin: var(--s5) 0; }
.credit-table { border-collapse: collapse; font-size: var(--text-sm); min-width: 44rem; }
.credit-table th, .credit-table td {
  text-align: left; vertical-align: top; padding: 10px var(--s4) 10px 0;
  border-bottom: 1px solid var(--line);
}
.credit-table th {
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: var(--grey-3);
}
.credit-table td { color: var(--grey-3); }
.credit-table td:first-child { font-family: var(--mono); font-size: 12.5px; color: var(--ink); }
.credit-table a { overflow-wrap: anywhere; font-size: 12.5px; }
ul.credit-notes { margin: var(--s4) 0 0; padding-left: var(--s5); }
ul.credit-notes li { margin: 0 0 var(--s2); color: var(--grey-3); font-size: var(--text-sm); }

/* The header sits inside .wrap, so its background was constrained to the
   wrapper and stopped short of the viewport edge — a strip of page ground
   showing down one side of what reads as a bar. It escapes and pads itself
   back in, the same device the ink bands use, which is why this is not solved
   by moving the element out of .wrap instead: the inline padding is computed
   from the container's real width, so the wordmark keeps its position under
   every page family's wrapper (1080, 820, 780 and 680) without any of them
   having to name its own width twice.

   padding is written long-hand on purpose. The shorthand would reset
   padding-inline to zero and pull the wordmark to the viewport edge. */
/* Not sticky, at any width. A bar pinned to the top of the screen spends a
   permanent slice of every phone on two links and a language toggle, and on
   this site it rode over the dark bands as you scrolled — a pale strip sliding
   across a photograph. The header is read once, at the top, and the footer
   carries the same destinations for anyone who reaches the bottom. */
.site-head {
  border-top: 3px solid var(--lime);
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  margin-inline: calc(50% - 50vw);
  padding-block: 14px; padding-inline: calc(50vw - 50%);
  margin-bottom: var(--s5);
  background: var(--bone); border-bottom: 1px solid var(--line);
}
.wordmark { text-decoration: none; color: var(--ink); flex: 0 0 auto; display: inline-flex; align-items: center; gap: 7px; }
.wordmark-name {
  font-family: var(--font);
  font-weight: 700; font-size: 19px; letter-spacing: -.035em; white-space: nowrap;
}
/* The lime dot: the one place the accent appears in the chrome, and the only
   thing that would survive being scaled down to a favicon. */
.wordmark-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--lime); flex: none; }
.wordmark:hover .wordmark-name { text-decoration: underline; }

.site-nav { display: flex; gap: var(--s4); flex: 1 1 auto; min-width: 0; }
.site-nav a {
  font-size: var(--text-sm); color: var(--grey-3); text-decoration: none;
  padding: 6px 0; border-bottom: 2px solid transparent; white-space: nowrap;
}
.site-nav a:hover { color: var(--ink); }
.site-nav a[aria-current="page"] { color: var(--ink); font-weight: 600; border-bottom-color: var(--lime); }

.site-tagline {
  font-size: var(--text-sm); color: var(--grey-3); margin: 0; min-width: 0;
}
/* The tagline is not in the header. It is a positioning line rather than a
   destination, it pushed the language switcher onto a second row at every width
   the wrapper was narrow, and the footer already carries it. */

.site-langs { display: flex; gap: var(--s2); flex: 0 0 auto; }
.site-langs a {
  font-size: 13px; color: var(--grey-3);
  text-decoration: none; border: 1px solid var(--line); border-radius: 3px;
  padding: 5px 10px; background: var(--surface);
}
.site-langs a:hover { color: var(--ink); border-color: var(--ink); }

/* On a phone the header also wrapped to two rows, because the nav could not
   share a line with the wordmark and the language toggle.

   So on a phone it stops being navigation and becomes a masthead: one 56px
   row, the wordmark and the language toggle. The two nav links are dropped
   rather than folded into a menu — the hero's own buttons lead to both
   destinations, and so does the footer, so a hamburger would be a third route
   to the same two pages and a permanent piece of chrome to maintain. */
@media (max-width: 640px) {
  .site-head {
    flex-wrap: nowrap;
    gap: var(--s3);
    min-height: 56px;
    padding-block: 0;
  }
  /* Hidden, not merely unstyled: it must leave the row and the reading order
     together, or a screen reader would announce two links nothing shows. */
  .site-nav { display: none; }
  /* The nav was what pushed the toggle right. With it gone, the margin does. */
  .site-langs { margin-left: auto; }
}

.site-foot {
  margin-top: var(--s9); padding: var(--s6) 0 var(--s2); border-top: 1px solid var(--line);
  font-size: 14.5px; color: var(--grey-3);
}
.site-foot-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s5); }
.site-foot h2 {
  font-size: 11.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--grey-3); margin: 0 0 9px; font-weight: 600;
}
.site-foot ul { list-style: none; margin: 0; padding: 0; }
.site-foot li { margin: 0 0 6px; line-height: 1.45; }
.site-foot a { color: var(--grey-3); text-decoration: none; border-bottom: 1px solid var(--line); }
.site-foot a:hover { color: var(--ink); border-color: var(--ink); }
/* The machine-readable links are set in mono, like every other machine fact. */
.site-foot-agents a { font-family: var(--mono); font-size: 13px; }
.site-foot-end {
  display: flex; justify-content: space-between; gap: var(--s4); flex-wrap: wrap;
  margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--line);
  font-size: 13.5px; color: var(--grey-3);
}
.site-foot-end p { margin: 0; }
@media (max-width: 620px) { .site-foot-cols { grid-template-columns: 1fr; gap: var(--s5); } }

/* Photographs present in site/assets/photos/, and only those. */
[data-photo="photo-cycling-volta"] {
  --photo: url("/assets/photos/photo-cycling-volta.jpg");
  --photo-on: 1;
}
[data-photo="photo-effort-closeup"] {
  --photo: url("/assets/photos/photo-effort-closeup.jpg");
  --photo-on: 1;
  --photo-position: center 30%;
}
[data-photo="photo-hyrox-lunges"] {
  --photo: url("/assets/photos/photo-hyrox-lunges.jpg");
  --photo-on: 1;
  --photo-position: center 40%;
}
[data-photo="photo-hyrox-sled"] {
  --photo: url("/assets/photos/photo-hyrox-sled.jpg");
  --photo-on: 1;
  --photo-position: center 45%;
}
[data-photo="photo-openwater-pack"] {
  --photo: url("/assets/photos/photo-openwater-pack.jpg");
  --photo-on: 1;
  --photo-position: center 35%;
}
[data-photo="photo-openwater-stroke"] {
  --photo: url("/assets/photos/photo-openwater-stroke.jpg");
  --photo-on: 1;
}
[data-photo="photo-trail-singletrack"] {
  --photo: url("/assets/photos/photo-trail-singletrack.jpg");
  --photo-on: 1;
}
[data-photo="photo-trail-ultra-start"] {
  --photo: url("/assets/photos/photo-trail-ultra-start.jpg");
  --photo-on: 1;
  --photo-position: center 45%;
}
[data-photo="photo-triathlon-transition"] {
  --photo: url("/assets/photos/photo-triathlon-transition.jpg");
  --photo-on: 1;
}