* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  /* Matches CONFIG.plainColor. The sticky canvas and the plain section
     meet edge-to-edge as you scroll past the track, and a black page
     behind them shows any sub-pixel rounding there as a dark hairline. */
  background: #fefef7;
  width: 100%;
  height: 100%;
  scrollbar-width: none; /* Firefox: cosmetic only, does not affect scroll behavior */
  -ms-overflow-style: none; /* old Edge/IE */
}

html::-webkit-scrollbar,
body::-webkit-scrollbar {
  display: none; /* Chrome/Safari: cosmetic only */
}

#scroll-track {
  position: relative;
  width: 100%;
  /* height is set in JS based on frame count + speed config */
}

#scene {
  position: sticky;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  display: block;
  background: #fefef7;
}

#plain-section {
  position: relative;
  width: 100%;
  min-height: 100vh;
  background: #fefef7; /* uniform beige matching CONFIG.plainColor */
  color: rgba(20, 18, 14, 0.88);
  padding: 0;
  overflow: clip;
}

/* Base profile & story layout */
.profile {
  position: relative;
  z-index: 1;
  max-width: 1440px;
  margin: 0 auto;
  padding: clamp(90px, 14vh, 180px) clamp(24px, 6%, 80px);
  background: #fefef7;
}

.profile + .profile {
  border-top: 1px solid rgba(20, 18, 14, 0.1);
}

/* Two-column grid for Founder and Leadership profiles */
#founder,
#leadership {
  display: grid;
  grid-template-columns: minmax(0, 36%) minmax(0, 1fr);
  gap: clamp(36px, 6vw, 96px);
  align-items: start;
}

/* ---- Story Section ------------------------------------------------ */
#story {
  display: block;
}

.story-container {
  max-width: 1200px;
  margin: 0 auto;
}

.story-header {
  position: relative;
  text-align: center;
  max-width: 900px;
  margin: 0 auto clamp(48px, 7vh, 80px);
}

.story-header::before {
  content: "";
  position: absolute;
  top: -60px;
  left: 50%;
  width: 520px;
  height: 420px;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(199, 47, 29, 0.06), transparent 70%);
  pointer-events: none;
  z-index: -1;
}

.story-header .eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: fit-content;
  margin: 0 auto 18px;
}

.story-header .eyebrow::before,
.story-header .eyebrow::after {
  content: "";
  width: 26px;
  height: 1px;
  background: rgba(199, 47, 29, 0.45);
}

.story-title {
  font-family: var(--serif);
  font-size: clamp(34px, 4.8vw, 64px);
  font-weight: 400;
  font-style: normal;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: #14120e;
  margin-bottom: 24px;
}

.story-title-accent {
  font-style: normal;
  color: var(--brand-red);
}

.story-lead {
  font-family: var(--text);
  font-size: clamp(18px, 1.35vw, 22px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.62;
  color: rgba(20, 18, 14, 0.84);
  margin: 0 auto;
  max-width: 68ch;
}

/* Story Bento: 3 Core Pillars - strictly theme beige & ink */
.story-bento {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
  margin-bottom: clamp(48px, 7vh, 80px);
}

.bento-card {
  position: relative;
  background: #fefef7;
  border: 1px solid rgba(20, 18, 14, 0.12);
  border-radius: 20px;
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.35s ease, box-shadow 0.35s ease;
  overflow: hidden;
}

.bento-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--brand-red), rgba(199, 47, 29, 0.1));
  opacity: 0.45;
  transition: opacity 0.35s ease;
}

.bento-card:hover {
  transform: translateY(-5px);
  border-color: rgba(20, 18, 14, 0.18);
  box-shadow: 0 28px 52px -28px rgba(20, 18, 14, 0.22);
}

.bento-card:hover::before {
  opacity: 1;
}

.bento-card-inner {
  padding: clamp(28px, 3.2vw, 40px);
  display: flex;
  flex-direction: column;
  height: 100%;
}

.bento-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 22px;
}

.bento-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 12px;
  background: rgba(199, 47, 29, 0.07);
  border: 1px solid rgba(199, 47, 29, 0.18);
  color: var(--brand-red);
}

.bento-icon svg {
  width: 20px;
  height: 20px;
}

.bento-index {
  font-family: var(--serif);
  font-size: 36px;
  line-height: 1;
  font-weight: 400;
  color: rgba(20, 18, 14, 0.1);
}

.bento-tag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.5);
  margin-bottom: 10px;
}

.bento-title {
  font-family: var(--serif);
  font-size: clamp(23px, 2vw, 28px);
  font-weight: 400;
  font-style: normal;
  line-height: 1.18;
  color: #14120e;
  margin-bottom: 14px;
}

.bento-desc {
  font-family: var(--text);
  font-size: clamp(15px, 1.05vw, 17px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.68;
  color: rgba(20, 18, 14, 0.72);
  margin-bottom: 24px;
  flex-grow: 1;
}

.bento-stat-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 12px;
  padding: 8px 16px;
  background: rgba(199, 47, 29, 0.05);
  border: 1px solid rgba(199, 47, 29, 0.14);
  border-radius: 10px;
  margin-top: auto;
  align-self: flex-start;
}

.bento-stat-num {
  font-family: var(--serif);
  font-size: 26px;
  line-height: 1;
  color: var(--brand-red);
  font-weight: 400;
}

.bento-stat-lbl {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.6);
}

.origin-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: auto;
}

.origin-pill {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  padding: 6px 12px;
  background: #fefef7;
  border: 1px solid rgba(20, 18, 14, 0.1);
  border-radius: 999px;
  color: rgba(20, 18, 14, 0.75);
  transition: color 0.2s ease, border-color 0.2s ease;
}

.origin-pill:hover {
  color: var(--brand-red);
  border-color: rgba(199, 47, 29, 0.4);
  background: rgba(199, 47, 29, 0.06);
}

/* Trusted Partners Strip */
.story-partners {
  background: linear-gradient(180deg, rgba(20, 18, 14, 0.025), transparent);
  border: 1px solid rgba(20, 18, 14, 0.1);
  border-radius: 24px;
  padding: clamp(28px, 3.2vw, 44px) clamp(20px, 3vw, 40px);
  text-align: center;
  margin-bottom: clamp(40px, 6vh, 64px);
}

.partners-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.55);
  margin-bottom: 24px;
}

.partner-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0;
}

.partner-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 4px clamp(18px, 2.4vw, 34px);
  border-right: 1px solid rgba(20, 18, 14, 0.14);
}

.partner-chip:last-child {
  border-right: none;
}

.partner-bullet {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--brand-red);
  opacity: 0.65;
  flex: none;
}

.partner-name {
  font-family: var(--text);
  font-size: 15px;
  font-weight: 400;
  font-style: normal;
  letter-spacing: -0.01em;
  color: #14120e;
  transition: color 0.2s ease;
}

.partner-chip:hover .partner-name {
  color: var(--brand-red);
}

/* Pull Quote Banner */
.story-pull-banner {
  position: relative;
  background: linear-gradient(135deg, rgba(199, 47, 29, 0.05), rgba(20, 18, 14, 0.015));
  border: 1px solid rgba(20, 18, 14, 0.12);
  border-left: 4px solid var(--brand-red);
  border-radius: 20px;
  padding: clamp(40px, 5vw, 64px) clamp(24px, 4vw, 64px);
  text-align: center;
  overflow: hidden;
}

.story-pull-banner::before {
  content: "\201C";
  position: absolute;
  top: -0.2em;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--serif);
  font-size: clamp(160px, 20vw, 260px);
  line-height: 1;
  color: rgba(199, 47, 29, 0.07);
  pointer-events: none;
  z-index: 0;
}

.story-pull-banner .pull-text {
  position: relative;
  z-index: 1;
  font-family: var(--serif);
  font-style: normal;
  font-weight: 400;
  font-size: clamp(26px, 3.2vw, 44px);
  line-height: 1.18;
  color: #14120e;
  max-width: 28ch;
  margin: 0 auto 14px;
}

.story-pull-banner .pull-sub {
  position: relative;
  z-index: 1;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.55);
  margin: 0;
}

/* ---- Gallery (masonry, 3 columns) -----------------------------------
   Built as an explicit grid of 3 flex columns — the same
   grid-template-columns mechanism as .story-bento above, with the same
   gap — rather than CSS multi-column, whose column-width rounding could
   leave the rightmost card sitting a hair off the frame-r guideline. */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.5vw, 32px);
  align-items: start;
}

.gallery-column {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.5vw, 32px);
  min-width: 0;
}

.gallery-item {
  position: relative;
  display: block;
  border-radius: 20px;
  overflow: hidden;
  background: rgba(20, 18, 14, 0.03);
  box-shadow: 0 4px 20px rgba(20, 18, 14, 0.05);
  border: 1px solid rgba(20, 18, 14, 0.08);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.3s ease;
}

.gallery-item:hover {
  transform: translateY(-4px);
  box-shadow: 0 16px 36px rgba(20, 18, 14, 0.12);
  border-color: rgba(199, 47, 29, 0.25);
}

.gallery-img {
  width: 100%;
  height: auto;
  display: block;
  object-fit: cover;
  border-radius: 19px;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.gallery-item:hover .gallery-img {
  transform: scale(1.035);
}

/* ---- Profiles (Founder & Leadership) -------------------------------- */
.profile-portrait {
  position: sticky;
  top: clamp(100px, 16vh, 150px);
  margin-top: clamp(28px, 5vh, 64px);
  z-index: 0;
}

.profile-portrait::before {
  content: "";
  position: absolute;
  top: -10%;
  left: 50%;
  width: 130%;
  height: 85%;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(199, 47, 29, 0.08), transparent 72%);
  z-index: -1;
  pointer-events: none;
}

.portrait-image {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border: none;
  border-radius: 24px;
  transition: transform 0.4s ease;
}

.profile-portrait:hover .portrait-image {
  transform: translateY(-4px) scale(1.015);
}

.profile-body {
  display: flex;
  flex-direction: column;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.5);
  margin-bottom: 14px;
}

.profile-name {
  font-family: var(--serif);
  font-size: clamp(38px, 4.6vw, 64px);
  font-weight: 400;
  font-style: normal;
  line-height: 1.04;
  letter-spacing: -0.015em;
  color: var(--brand-red);
  margin-bottom: 8px;
}

.profile-role {
  font-family: var(--mono);
  font-size: clamp(14px, 1.15vw, 17px);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-red);
  margin-bottom: 26px;
}

.profile-body .lede {
  font-family: var(--text);
  font-size: clamp(18px, 1.4vw, 22px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.55;
  color: rgba(20, 18, 14, 0.95);
  margin-bottom: 26px;
  max-width: 62ch;
}

.narrative-body p {
  font-family: var(--text);
  font-size: clamp(16px, 1.15vw, 18.5px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.76;
  color: rgba(20, 18, 14, 0.82);
  margin-bottom: 1.5em;
  max-width: 62ch;
}

.stanza {
  font-family: var(--text);
  font-size: clamp(16.5px, 1.18vw, 19px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.8;
  color: rgba(20, 18, 14, 0.72);
  padding: 14px 0 14px 20px;
  border-left: 2px solid rgba(20, 18, 14, 0.18);
  margin: 24px 0;
  max-width: 58ch;
}

.pull {
  font-family: var(--serif);
  font-size: clamp(24px, 2.6vw, 36px);
  font-style: normal;
  font-weight: 400;
  line-height: 1.25;
  color: #14120e;
  padding: 16px 0 16px 22px;
  border-left: 3px solid var(--brand-red);
  margin: 28px 0 32px;
  max-width: 32ch;
}

/* Stats */
.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2.5vw, 32px);
  margin-top: 36px;
  padding-top: 28px;
  border-top: 1px solid rgba(20, 18, 14, 0.12);
  max-width: 62ch;
}

.stats dt {
  font-family: var(--serif);
  font-size: clamp(30px, 3.4vw, 44px);
  line-height: 1;
  color: var(--brand-red);
  margin-bottom: 6px;
  font-weight: 400;
  font-style: normal;
}

.stats dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(20, 18, 14, 0.6);
  line-height: 1.5;
}

/* Responsive adjustments */
@media (max-width: 1024px) {
  .story-bento {
    grid-template-columns: 1fr;
  }

  .gallery-grid {
    grid-template-columns: 1fr;
  }

  #founder,
  #leadership {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .profile-portrait {
    position: static;
    max-width: 380px;
    margin: 0 auto;
  }
}

@media (max-width: 640px) {
  .profile {
    padding: 60px 20px;
  }

  .gallery-grid {
    grid-template-columns: 1fr;
  }

  .stats {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  .partner-chips {
    flex-direction: column;
    width: 100%;
  }

  .partner-chip {
    width: 100%;
    justify-content: center;
    padding: 14px 0;
    border-right: none;
    border-bottom: 1px solid rgba(20, 18, 14, 0.14);
  }

  .partner-chip:last-child {
    border-bottom: none;
  }
}

/* ---- gallery, phone layout ------------------------------------------
   Stacked, nine photos deep, the gallery was most of the page's scroll
   on a phone. It becomes a swipeable rail instead: one photo at a time,
   snapping, bled to the screen edges so it reads as something to push
   sideways rather than a column that ran out of room.

   The three column groups are lifted out with display:contents so the
   photos themselves become the slides — otherwise each "slide" would be
   a stack of three. The masonry aspect ratios are dropped with them: a
   row of mixed heights staggers, where the vertical layout wanted the
   variety.

   After the breakpoints above, so these win on the same specificity. */
@media (max-width: 768px) {
  .gallery-grid {
    display: flex;
    grid-template-columns: none;
    gap: 14px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    /* Full-bleed: the rail runs to both screen edges while the section
       keeps its 20px gutter, so a photo is always half-visible at the
       edge and the swipe is discoverable. */
    margin-left: -20px;
    margin-right: -20px;
    padding: 0 20px 4px;
    scrollbar-width: none;
  }

  .gallery-grid::-webkit-scrollbar {
    display: none;
  }

  .gallery-column {
    display: contents;
  }

  /* Flattening the columns leaves the photos in column order — the whole
     of column one, then column two — so the rail would swipe out of the
     sequence the desktop grid reads in. These put them back into row
     order, left to right: column n's nth photo is (row - 1) * 3 + n.
     Covers six rows, which is the deepest column. */
  .gallery-column:nth-child(1) .gallery-item:nth-child(1) { order: 1; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(1) { order: 2; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(1) { order: 3; }
  .gallery-column:nth-child(1) .gallery-item:nth-child(2) { order: 4; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(2) { order: 5; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(2) { order: 6; }
  .gallery-column:nth-child(1) .gallery-item:nth-child(3) { order: 7; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(3) { order: 8; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(3) { order: 9; }
  .gallery-column:nth-child(1) .gallery-item:nth-child(4) { order: 10; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(4) { order: 11; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(4) { order: 12; }
  .gallery-column:nth-child(1) .gallery-item:nth-child(5) { order: 13; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(5) { order: 14; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(5) { order: 15; }
  .gallery-column:nth-child(1) .gallery-item:nth-child(6) { order: 16; }
  .gallery-column:nth-child(2) .gallery-item:nth-child(6) { order: 17; }
  .gallery-column:nth-child(3) .gallery-item:nth-child(6) { order: 18; }

  .gallery-item {
    flex: 0 0 auto;
    width: 76vw;
    max-width: 320px;
    scroll-snap-align: center;
  }

  /* One band height across the rail. The photos run from 2.15 to 0.46 in
     aspect, and left to their own ratios a horizontal strip staggers
     badly — the variety is what the vertical masonry wants, not this.
     Cropping to fill keeps the row even. */
  .gallery-item .gallery-img {
    height: 64vw;
    max-height: 270px;
    object-fit: cover;
  }
}

/* ---- looping hero video -------------------------------------------
   Sits over the frame canvas at the top of the page and fades out as
   you scroll, so the sequence underneath is revealed rather than cut
   to. Opacity is driven per-frame from script.js. */
#hero-loop {
  position: fixed;
  inset: 0;
  z-index: 65; /* over the canvas, under the grid (70) and logo (80) */
  background: #fefef7; /* CONFIG.plainColor, so no dark flash before it paints */
  overflow: hidden;
  pointer-events: none;
  will-change: opacity;
}

#loop-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- navigation grid ------------------------------------------------
   Tune the whole layout from these four values; every line, marker and
   label is positioned off them. */
:root {
  /* Resting position of the frame. Script.js slides these to follow the
     fruit, so keep them in step with FRAME_BASE there. */
  --frame-l: 6%;
  --frame-r: 94%;
  --frame-t: 11%;
  --frame-b: 71%;

  --ink: rgba(20, 18, 14, 0.28);
  --ink-strong: rgba(20, 18, 14, 0.6);

  /* Display only — names, pull quotes, figures, the scroll copy.
     Instrument Serif is a high-contrast display serif: it is drawn to be
     set large, and thins out badly in a paragraph. */
  --serif: "Instrument Serif", Georgia, "Times New Roman", serif;

  /* Running text. Newsreader is a reading face — bigger x-height, much
     less stroke contrast, spaced for continuous prose — so the founder
     stories hold up at 16px in a way the display serif never could.
     Georgia leads the fallbacks for the same reason. */
  --text: "Newsreader", Georgia, "Times New Roman", serif;

  /* JetBrains Mono rather than the system stack: ui-monospace resolves to
     Consolas on Windows, which reads as code next to this type. */
  --mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* Sampled from the wordmark in logo.png — its dominant fully-opaque red. */
  --brand-red: #c72f1d;

  /* Orange-cultivar accent, used only for the orange rail link's hover. */
  --brand-orange: #e2740f;

  /* Dragonfruit-cultivar accent, used for the dragonfruit rail link's hover. */
  --brand-dragonfruit: #e11d74;
}

#grid-overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
}

.grid-line {
  position: absolute;
  background: var(--ink);
}

.grid-line--v {
  top: 0;
  bottom: 0;
  width: 1px;
}

.grid-line--h {
  left: 0;
  right: 0;
  height: 1px;
}

.frame-l { left: var(--frame-l); }
.frame-r { left: var(--frame-r); }
.frame-t { top: var(--frame-t); }
.frame-b { top: var(--frame-b); }

/* four-pointed stars at the corners of the frame */
.marker {
  position: absolute;
  width: 13px;
  height: 13px;
  transform: translate(-50%, -50%);
  background: var(--ink-strong);
  clip-path: polygon(
    50% 0%, 58% 42%, 100% 50%, 58% 58%,
    50% 100%, 42% 58%, 0% 50%, 42% 42%
  );
}

.marker--tl { left: var(--frame-l); top: var(--frame-t); }
.marker--tr { left: var(--frame-r); top: var(--frame-t); }
.marker--bl { left: var(--frame-l); top: var(--frame-b); }
.marker--br { left: var(--frame-r); top: var(--frame-b); }

/* --- rules draw themselves in ---------------------------------------
   Each rule runs along its own length rather than fading up, so the
   frame assembles itself a line at a time. Held collapsed until the
   loading screen lifts (#grid-overlay.is-ready), otherwise the whole
   thing would play out behind the loader and never be seen.

   Scale rather than width/height: it runs on the compositor, so the
   draw costs nothing while the frame sequence is decoding. */
@keyframes rule-draw-v {
  to { transform: scaleY(1); }
}

@keyframes rule-draw-h {
  to { transform: scaleX(1); }
}

.grid-line--v {
  transform: scaleY(0);
  transform-origin: top;
}

.grid-line--h {
  transform: scaleX(0);
  transform-origin: left;
}

/* 1.15s on a steep ease-out: quick off the mark, long settle. */
#grid-overlay.is-ready .grid-line--v {
  animation: rule-draw-v 1.15s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

#grid-overlay.is-ready .grid-line--h {
  animation: rule-draw-h 1.15s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

/* Staggered so the four arrive in sequence instead of together. */
#grid-overlay.is-ready .frame-l { animation-delay: 0.2s; }
#grid-overlay.is-ready .frame-t { animation-delay: 0.25s; }
#grid-overlay.is-ready .frame-r { animation-delay: 0.3s; }
#grid-overlay.is-ready .frame-b { animation-delay: 0.4s; }

/* Stars land once the rules that cross beneath them have arrived. A
   transition, not an animation, so the in-story rules below can still
   take the bottom pair back out. */
.marker {
  opacity: 0;
}

#grid-overlay.is-ready .marker {
  opacity: 1;
  transition: opacity 0.5s ease 0.95s;
}

/* Anyone who asks for less motion gets the frame already drawn. */
@media (prefers-reduced-motion: reduce) {
  .grid-line--v { transform: scaleY(1); }
  .grid-line--h { transform: scaleX(1); }

  #grid-overlay.is-ready .grid-line--v,
  #grid-overlay.is-ready .grid-line--h {
    animation: none;
  }

  #grid-overlay.is-ready .marker {
    transition: none;
  }
}

/* Only opacity is transitioned — the line positions are already eased
   in JS, and a CSS transition on top of that would fight it. */
.grid-line,
.marker,
.nav-mask {
  transition: opacity 0.55s ease;
}

/* --- how the grid stands once the story section has settled --------- */

/* Hides the band above the top rule, so paragraphs scrolling up vanish
   at that line rather than sliding behind the logo. Sits first in the
   overlay so the rules themselves still draw over it. */
.nav-mask {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--frame-t);
  background: #fefef7; /* CONFIG.plainColor */
  opacity: 0;
}

#grid-overlay.in-story .nav-mask {
  opacity: 1;
}

/* The story layout keeps only the clean rules: open at the bottom, and
   with no tick marks crossing them. */
#grid-overlay.in-story .frame-b,
#grid-overlay.in-story .marker--bl,
#grid-overlay.in-story .marker--br {
  opacity: 0;
}

/* Story section only: the right vertical starts at the top rule, so its
   stub no longer runs through the navigation bar. During the frame
   scroll it spans the full height, like the left one. The line itself
   stays — only that top segment goes. */
#grid-overlay.in-story .frame-r {
  top: var(--frame-t);
}





.readout {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-strong);
}



.readout {
  position: absolute;
  left: calc(var(--frame-l) + 10px);
  bottom: 6vh;
  font-style: italic;
  letter-spacing: 0.08em;
  text-transform: none;
}

#brand-logo {
  position: fixed;
  left: 50%;
  top: 18px;
  transform: translateX(-50%);
  z-index: 80;
  width: clamp(62px, 9vw, 123px);
  height: auto;
  pointer-events: none;
}

/* ---- scroll copy ---------------------------------------------------
   Stacked in a single grid cell so every line shares one origin and they
   can cross-fade without nudging the layout. Opacity and the small rise
   are written per-frame by script.js; only the resting state lives here,
   so the lines stay invisible if the script never runs. */
#scroll-copy {
  position: fixed;
  /* Hung off the frame's bottom-left corner and travelling with it, so
     the copy always stands on the horizontal rule. The two offsets are
     the relationship pear.no uses at a 1280 viewport: 28px in from the
     vertical rule, 42px of air above the horizontal one. */
  left: calc(var(--frame-l) + 28px);
  top: var(--frame-b);
  right: auto;
  bottom: auto;
  transform: translateY(calc(-100% - 42px));
  /* The frame closes in as you scroll, carrying this corner toward the
     middle — without a cap a beat could run off the right edge. */
  max-width: calc(100vw - var(--frame-l) - 56px);
  z-index: 66; /* over the frames and the intro video, under the grid (70) */
  display: grid;
  justify-items: start;
  /* Stacked beats would otherwise stretch to the tallest of them, so a
     two-line beat would sit a line higher than a three-line one. Pinned
     to the bottom they all stand on the rule and grow upward. */
  align-items: end;
  pointer-events: none;
}

.copy-line {
  grid-area: 1 / 1;
  margin: 0;
  /* Narrow measure, so each beat breaks into a short stack rather than
     running along the rule. */
  max-width: 12ch;
  font-family: var(--serif);
  font-size: clamp(1.9rem, 3.3vw, 3.3rem);
  font-weight: 400;
  /* Leading tighter than the type size, as pear sets it — the lines close
     up into a single block instead of reading as separate sentences. */
  line-height: 0.98;
  letter-spacing: -0.01em;
  text-align: left;
  /* Adaptive, like the guide grid — the sequence runs from bright sky to
     dark foliage, and fixed ink would vanish against one or the other. */
  color: var(--ink-strong);
  opacity: 0;
}

/* On a narrow screen the frame closes in far enough to carry its left
   rule most of the way across, which would push the copy off the right
   edge. Below this width it keeps the vertical relationship — still
   standing on the horizontal rule — but holds the page margin instead of
   chasing the vertical one sideways. */
@media (max-width: 768px) {
  #scroll-copy {
    left: 6%;
    max-width: 88vw;
  }
}

#loader-bar {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  width: 160px;
  height: 2px;
  background: rgba(20, 18, 14, 0.15);
  overflow: hidden;
  z-index: 60;
  transition: opacity 0.5s ease;
}

#loader-bar.hidden {
  opacity: 0;
  pointer-events: none;
}

#loader-fill {
  width: 0%;
  height: 100%;
  background: rgba(20, 18, 14, 0.6);
  transition: width 0.15s ease;
}

/* ---- left rail navigation -------------------------------------------
   Sits under the 2x2 mark, outside #grid-overlay so it can take clicks. */
#rail-nav {
  position: fixed;
  left: 2.6%;
  /* Deliberately not tied to --frame-t: script.js animates that variable
     as the guide frame closes in and tracks the fruit, which dragged the
     rail around with it. This is the frame's resting top (11%) baked in,
     so the icons hold one position. */
  top: calc(11% + 44px);
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* Centered, not flex-start: "Orange" and "Dragon" are different label
     widths, and left-aligning the whole link block let each icon drift
     to a different x depending on its own label's width. Centering the
     blocks keeps every icon on the same vertical line. */
  align-items: center;
}

.rail-link {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* Adaptive: script.js flips --ink-strong to white over dark frames.
     A fixed dark colour disappeared entirely against the forest shots. */
  color: var(--ink-strong);
  opacity: 0.72;
  text-decoration: none;
  /* negative margin keeps the glyphs visually spaced while the padding
     gives each one a comfortable hit area */
  padding: 8px;
  margin: -8px;
  transition: color 0.25s ease, opacity 0.25s ease;
}

.rail-label {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: inherit;
}

/* The left guide rule sits at 6% of the viewport — under ~30px on a
   phone screen, narrower than any label. Icon-only there, same as this
   rail always was before labels were added. */
@media (max-width: 640px) {
  .rail-label {
    display: none;
  }
}

.rail-link:focus-visible {
  outline: 1px solid var(--brand-red);
  outline-offset: 2px;
}

.rail-link:hover,
.rail-link:focus-visible {
  color: var(--brand-red);
  opacity: 1;
}

.rail-link--orange:hover,
.rail-link--orange:focus-visible {
  color: var(--brand-orange);
}

.rail-link--dragonfruit:hover,
.rail-link--dragonfruit:focus-visible {
  color: var(--brand-dragonfruit);
}

.rail-link.is-current {
  opacity: 1;
}

/* Drawn marks rather than plain dots: the 2x2 grid glyph for home, and
   fruit silhouettes for the cultivars. All inherit currentColor, so they
   follow the link's ink and turn brand red on hover with the label. */
.rail-glyph {
  width: 24px;
  height: 24px;
  flex: none;
  display: block;
}

.rail-glyph svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: currentColor;
}

/* ---- mobile navigation ----------------------------------------------
   Hidden on desktop, where the left rail does this job. Below 768px the
   rail goes and these take over: the button sits opposite the logo, and
   the panel is the one dark surface on the page besides the footer, so
   it reads as an overlay rather than another section. */
.nav-toggle {
  display: none;
  position: fixed;
  /* Sits on the logo's line rather than under it. The logo is 18px down
     and its height tracks its clamped width, so at phone sizes its
     centre lands ~32px in; 10px puts this 44px hit area's centre on the
     same line. Was calc(11% + 30px) — the rail's top, borrowed when this
     replaced the rail, which left it 113px adrift of the mark. */
  top: 10px;
  right: 5%;
  z-index: 95;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--ink-strong);
  -webkit-tap-highlight-color: transparent;
}

.nav-toggle-bars {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 22px;
}

.nav-toggle-bars span {
  display: block;
  height: 1.5px;
  width: 100%;
  background: currentColor;
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}

/* Bars cross into an X while the panel is open. */
.nav-toggle[aria-expanded='true'] {
  color: #fefef7;
}

.nav-toggle[aria-expanded='true'] .nav-toggle-bars span:first-child {
  transform: translateY(3.25px) rotate(45deg);
}

.nav-toggle[aria-expanded='true'] .nav-toggle-bars span:last-child {
  transform: translateY(-3.25px) rotate(-45deg);
}

/* Always in the DOM, held back by visibility rather than `display` or
   `hidden`. A display swap needs a layout frame before the panel can
   animate, which means an rAF hop — and if frames are throttled (back-
   grounded tab, a hidden pane) the panel opens without ever sliding in.
   visibility transitions honestly and costs nothing while closed. */
.nav-drawer {
  position: fixed;
  inset: 0;
  z-index: 92;
  visibility: hidden;
  pointer-events: none;
  /* Visibility waits out the close animation, then flips. */
  transition: visibility 0s linear 0.45s;
}

.nav-drawer.is-open {
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

.nav-drawer-scrim {
  position: absolute;
  inset: 0;
  background: rgba(20, 18, 14, 0.4);
  opacity: 0;
  transition: opacity 0.35s ease;
}

.nav-drawer-panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding: 0 clamp(28px, 9vw, 56px);
  background: #14120e;
  color: #fefef7;
  transform: translateY(-100%);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-drawer.is-open .nav-drawer-scrim {
  opacity: 1;
}

.nav-drawer.is-open .nav-drawer-panel {
  transform: translateY(0);
}

.nav-drawer-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--brand-red);
  margin-bottom: 22px;
}

.nav-drawer-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-drawer-list li + li {
  border-top: 1px solid rgba(254, 254, 247, 0.12);
}

.nav-drawer-list a {
  display: block;
  padding: 18px 0;
  font-family: var(--serif);
  font-size: clamp(30px, 9vw, 40px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: #fefef7;
  text-decoration: none;
  transition: color 0.25s ease;
}

.nav-drawer-list a:hover,
.nav-drawer-list a:focus-visible {
  color: var(--brand-red);
}

.nav-drawer-foot {
  margin-top: 28px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(254, 254, 247, 0.45);
}

/* Locks the page behind the open panel, so a swipe scrolls the menu
   rather than the story underneath it. */
body.nav-open {
  overflow: hidden;
}

/* ---- phone layout ---------------------------------------------------
   The desktop chrome all assumes margins either side of the column: the
   rail sits in the left margin, the guide frame rules the page off at
   6%/94%, and the scroll readout hangs beside them. At this width there
   is no margin left to sit in — every one of them lands on top of the
   text. So the rail becomes the menu button, and the frame furniture is
   dropped once the story starts.

   Placed after the rules it overrides rather than up with the other
   breakpoints: these are all single-class/id selectors, so the later
   declaration is the one that wins. */
@media (max-width: 768px) {
  #rail-nav {
    display: none;
  }

  .nav-toggle {
    display: flex;
  }

  /* The frame still draws over the hero sequence, where it is the whole
     point — it just does not follow the story down the page. */
  #grid-overlay.in-story .grid-line,
  #grid-overlay.in-story .marker,
  #grid-overlay.in-story .readout {
    opacity: 0;
  }
}

/* ---- loading screen -------------------------------------------------
   Covers everything (above the logo at z 80) until the frame sequence has
   preloaded, then fades away. */
#loading-screen {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: #fefef7; /* CONFIG.plainColor, so no flash before the video paints */
  overflow: hidden;
  transition: opacity 0.5s ease, background-color 0.3s ease;
}

/* During the handoff the backdrop clears so the page shows through and
   the mark travels over it — fading the whole screen instead would fade
   the mark out mid-flight. */
#loading-screen.is-handoff {
  background-color: transparent;
}

#loading-screen.is-done {
  opacity: 0;
  pointer-events: none;
}

#loading-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- loading -> home logo handoff ----------------------------------
   The clip ends on the logo; this picks it up and shrinks it into the
   header logo's exact position, so the two read as one continuous mark
   rather than a cut. */
#loading-logo {
  position: absolute;
  /* left/top/width are set from JS to sit exactly where the clip's own
     logo ends up, allowing for the video's cover-crop. A centred default
     would jump, because the mark sits above centre in the frame. */
  left: 0;
  top: 0;
  width: 0;
  height: auto;
  transform-origin: center center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}

#loading-screen.is-handoff #loading-logo {
  opacity: 1;
}

/* The video steps aside so the mark is alone for the shrink. */
#loading-screen.is-handoff #loading-video,
#loading-screen.is-handoff #loader-bar {
  opacity: 0;
  transition: opacity 0.25s ease;
}

/* The real header logo stays out of sight until the shrink lands on it,
   otherwise both would be visible at once during the move. */
#brand-logo.is-waiting {
  opacity: 0;
}

/* ---- loading screen, phone layout -----------------------------------
   The clip is 3840x2160. Cover-fitting 16:9 into a portrait phone scales
   it to ~1500px wide inside a 390px screen, so roughly a quarter of the
   frame was on show — a giant cropped apple, and a wordmark running off
   both edges. contain shows the whole thing at a size you can read.

   script.js repeats this fit when it lines its overlay mark up with the
   clip's own; it reads the computed object-fit, so the two stay in step
   from here rather than needing the same change made twice. */
@media (max-width: 768px) {
  #loading-video {
    object-fit: contain;
  }

  /* Containing the clip leaves bars above and below it. Sampled from the
     clip's own corners (#fffff9) rather than assuming the page cream
     (#fefef7): one value apart per channel, which is invisible on its
     own but reads as a distinct panel edge against a flat field. */
  #loading-screen {
    background: #fffff9;
  }
}

/* ---- footer ---------------------------------------------------------
   The one dark band on the page. Everything above it is sand, so ending
   on warm ink reads as a base the site rests on rather than one more
   section — and it lets the beige act as an accent instead of being the
   ground it already is everywhere else.

   The ink is warmed to the same hue family as the sand rather than a
   neutral black, so the two read as one palette. */
#site-footer {
  --beige: #fefef7;
  --beige-dim: rgba(254, 254, 247, 0.62);
  --beige-faint: rgba(254, 254, 247, 0.16);

  position: relative;
  /* Above the fixed guide overlay (70), the rail and the header logo (80).
     Those are all pinned to the viewport, so without this they carry on
     painting over the footer — the sand nav-mask in particular kept a
     beige band across the top of it. Clearing them lets the footer read
     as a full panel the page ends on, and it brings its own logo and
     links, so nothing is actually lost by covering them. */
  z-index: 85;
  background: #17150f;
  color: var(--beige-dim);
  padding: clamp(64px, 10vh, 120px) 6% clamp(28px, 4vh, 44px);

  /* Fills the screen, so scrolling to the end lands on the footer alone
     rather than on a strip of it under the previous section.
     vh deliberately, not svh: svh is the viewport with the mobile browser
     chrome *showing*, which left a band of the section above still
     visible once that chrome retracted. Being a little taller than the
     visible area costs nothing on a footer; being shorter defeats it. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* Spreads the three bands over whatever height is going: brand at the
     top, figures through the middle, legal line on the floor. */
  justify-content: space-between;
}

.footer-main {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.6fr);
  gap: clamp(48px, 7vw, 120px);
  /* Flex items with auto side margins do not stretch — the auto margins
     swallow the free space instead, and each band shrank to its own
     content. This keeps them filling the footer, with max-width still
     capping and the auto margins still centring. */
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding-bottom: clamp(48px, 7vh, 88px);
}

/* --- brand column --- */
.footer-logo {
  display: block;
  width: clamp(96px, 11vw, 132px);
  height: auto;
  margin-bottom: clamp(24px, 3.5vh, 40px);
}

.footer-tagline {
  font-family: var(--serif);
  font-size: clamp(28px, 3.4vw, 44px);
  line-height: 1.04;
  letter-spacing: -0.01em;
  color: var(--beige);
  margin-bottom: 0.7em;
}

/* The italic cut is already loaded for the wordmark's voice. */
.footer-tagline em {
  font-style: italic;
  color: var(--brand-red);
}

.footer-blurb {
  font-family: var(--text);
  font-size: clamp(14px, 1.05vw, 16.5px);
  line-height: 1.7;
  max-width: 42ch;
}

/* --- link columns --- */
.footer-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: clamp(32px, 4vw, 64px);
}

.footer-heading {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--brand-red);
  margin-bottom: 1.9em;
}

.footer-col ul {
  list-style: none;
  display: grid;
  gap: 0.95em;
}

.footer-col a {
  font-family: var(--text);
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.35;
  color: var(--beige);
  text-decoration: none;
  transition: color 0.25s ease;
}

.footer-col a:hover,
.footer-col a:focus-visible {
  color: var(--brand-red);
}

.footer-col a:focus-visible {
  outline: 1px solid var(--brand-red);
  outline-offset: 3px;
}

/* Long address, narrow phones: break rather than force the page wide. */
.footer-email {
  word-break: break-word;
}

.footer-social {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
}

/* Stroked, not filled — an outline sits better than a solid mark at this
   size against the hairlines around it. */
.footer-social-glyph {
  width: 16px;
  height: 16px;
  flex: none;
}

.footer-social-glyph svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}

.footer-social-glyph .is-dot {
  fill: currentColor;
  stroke: none;
}

.footer-place {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(222, 220, 210, 0.62);
  line-height: 1.7;
  padding-top: 0.4em;
}

/* --- figures --- */
.footer-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: clamp(20px, 3vw, 56px);
  /* Flex items with auto side margins do not stretch — the auto margins
     swallow the free space instead, and each band shrank to its own
     content. This keeps them filling the footer, with max-width still
     capping and the auto margins still centring. */
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding: clamp(32px, 4.5vh, 52px) 0;
  border-top: 1px solid var(--beige-faint);
  border-bottom: 1px solid var(--beige-faint);
}

.footer-figures dt {
  font-family: var(--serif);
  font-size: clamp(30px, 3.6vw, 48px);
  line-height: 1;
  color: var(--beige);
  margin-bottom: 0.3em;
}

.footer-figures dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(222, 220, 210, 0.62);
  line-height: 1.6;
}

/* --- base bar --- */
.footer-base {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  /* Flex items with auto side margins do not stretch — the auto margins
     swallow the free space instead, and each band shrank to its own
     content. This keeps them filling the footer, with max-width still
     capping and the auto margins still centring. */
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding-top: clamp(22px, 3vh, 32px);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* Kept above 4.5:1 against the ink ground. These labels are 10px, and
     small type needs more contrast than large, not less. */
  color: rgba(222, 220, 210, 0.55);
}

/* Same breakpoint the profiles collapse at, so the page reflows on one
   line rather than two. */
@media (max-width: 880px) {
  .footer-main {
    grid-template-columns: 1fr;
    gap: clamp(44px, 8vw, 64px);
  }
}
