/* ==========================================================================
   DEMO 3 — "Banner"

   Demo 2's stylesheet with a full-bleed banner hero in place of the boxed
   one, and photograph-led blocks in place of the text-led ones. The bands
   demo 3 imports from demo 2 (topbar, header, stats, method steps, marquee,
   private label, export, story, certifications, footer) keep their rules
   unchanged, which is why this file starts as a copy: the two demos must be
   able to diverge without one breaking the other.

   The demo-2 hero rules below (.hero-bg, .hero-curve, .art-*) are unused
   here — .banner replaces them — but .hero-cta is not, so the block stays
   rather than being half-stripped.

   Original header follows.
   --------------------------------------------------------------------------
   DEMO 2 — "Thali"
   The Ramdev direction: saffron-led, one confident hero instead of a
   carousel, a white stat strip that overlaps the hero edge, and a tabbed
   range explorer rather than a scroller.

   Structural differences from Demo 1 are deliberate — this page has a
   single vertical spine of alternating cream / saffron / ink bands, and
   nothing on it moves on a timer except the claim marquee.
   ========================================================================== */

:root {
  --saffron: #efa818;
  --saffron-dp: #d98f06;
  --amber-tx: #7a5405;      /* saffron dark enough for body text on cream */
  --brick: #c13b1b;
  --brick-dk: #9c2f14;
  --leaf: #3f7d45;
  --leaf-dk: #2d5f33;
  --clay-tx: #7d4526;
  --cream: #fffbf3;
  --cream-2: #fbf0dc;
  --cream-3: #f6e6ca;
  --ink: #24180f;
  --ink-2: #5d4634;
  --ink-3: #7f6a56;
  --line: #ecdcc0;
  --white: #fff;

  --display: "Outfit", ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  --body: "Mulish", "Noto Sans Gujarati", ui-sans-serif, system-ui, sans-serif;

  --s-1: 0.85rem;
  --s0: clamp(1rem, 0.97rem + 0.16vw, 1.06rem);
  --s1: clamp(1.12rem, 1.06rem + 0.28vw, 1.3rem);
  --s2: clamp(1.35rem, 1.24rem + 0.55vw, 1.75rem);
  --s3: clamp(1.7rem, 1.45rem + 1.15vw, 2.6rem);
  --s4: clamp(2.3rem, 1.6rem + 3.1vw, 4.2rem);

  --sp1: 0.25rem;
  --sp2: 0.5rem;
  --sp3: 0.75rem;
  --sp4: 1rem;
  --sp5: 1.5rem;
  --sp6: 2rem;
  --sp7: 3rem;
  --sp8: 4.5rem;
  --sec: clamp(3rem, 2.2rem + 4vw, 5.5rem);

  --r: 14px;
  --r-lg: 28px;
  --r-pill: 999px;

  --sh: 0 2px 6px rgba(36, 24, 15, 0.06), 0 12px 30px rgba(36, 24, 15, 0.08);
  --sh-lg: 0 8px 18px rgba(36, 24, 15, 0.09), 0 30px 60px rgba(36, 24, 15, 0.14);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* No `scroll-behavior: smooth` here. ScrollTrigger measures the scroller
   directly, and a CSS-smoothed root makes every measurement it takes during
   a refresh land mid-animation — the pinned card stack ends up with a
   negative start and never pins. Anchor jumps are instant instead. */
html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--body);
  font-size: var(--s0);
  line-height: 1.68;
  color: var(--ink);
  background: var(--cream);
  -webkit-font-smoothing: antialiased;
  /* `clip`, not `hidden`: overflow-x:hidden turns <body> into a scroll
     container, which breaks ScrollTrigger's pinning. */
  overflow-x: hidden;
}

@supports (overflow: clip) { body { overflow-x: clip; } }

img, svg, picture { display: block; max-width: 100%; }
img { height: auto; }
ul, ol { padding: 0; list-style: none; }
button { font: inherit; color: inherit; }

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

a { color: var(--brick); }
:focus-visible { outline: 3px solid var(--brick); outline-offset: 3px; }

.wrap {
  width: 100%; max-width: 80rem;
  margin-inline: auto; padding-inline: var(--sp4);
}
@media (min-width: 40em) { .wrap { padding-inline: var(--sp6); } }

.skip-link {
  position: absolute; top: 0; left: var(--sp4); z-index: 300;
  padding: var(--sp3) var(--sp5);
  background: var(--brick); color: #fff; font-weight: 700;
  text-decoration: none; border-radius: 0 0 var(--r) var(--r);
  transform: translateY(-120%); transition: transform .18s var(--ease);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ------------------------------------------------------------ headings -- */
.kicker {
  font-family: var(--display); font-size: var(--s-1); font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--amber-tx); margin-block-end: var(--sp3);
}
.kicker--light { color: var(--saffron); }
.kicker--centre { text-align: center; }

.sec-title { font-size: var(--s3); }
.sec-title--centre { text-align: center; }
.sec-sub { color: var(--ink-2); font-size: var(--s1); max-width: 48ch; }
.sec-sub--centre { margin-inline: auto; text-align: center; }

.sec-head {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: var(--sp5);
  margin-block-end: var(--sp7);
}
.sec-head .kicker { margin-block-end: var(--sp2); }

.link-arrow {
  display: inline-flex; align-items: center; gap: var(--sp2);
  min-height: 2.75rem;
  font-family: var(--display); font-weight: 700; font-size: 0.95rem;
  color: var(--brick); text-decoration: none;
}
.link-arrow span { transition: transform .2s var(--ease); }
.link-arrow:hover { text-decoration: underline; }
.link-arrow:hover span { transform: translateX(4px); }

/* ------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 3.1rem; padding: var(--sp3) var(--sp6);
  font-family: var(--display); font-size: var(--s0); font-weight: 600;
  text-decoration: none; border: 2px solid transparent;
  border-radius: var(--r-pill); cursor: pointer;
  transition: transform .18s var(--ease), background-color .18s var(--ease),
              border-color .18s var(--ease), color .18s var(--ease);
}
.btn:hover { transform: translateY(-2px); }

.btn--solid { background: var(--brick); border-color: var(--brick); color: #fff; box-shadow: var(--sh); }
.btn--solid:hover { background: var(--brick-dk); border-color: var(--brick-dk); color: #fff; }

.btn--gold { background: var(--saffron); border-color: var(--saffron); color: #3a2803; box-shadow: var(--sh); }
.btn--gold:hover { background: #ffc043; border-color: #ffc043; color: #3a2803; }

.btn--line { background: transparent; border-color: var(--clay-tx); color: var(--ink); }
.btn--line:hover { border-color: var(--brick); color: var(--brick); background: rgba(193,59,27,.06); }

.btn--ghost { background: transparent; border-color: rgba(255,255,255,.55); color: #fff; }
.btn--ghost:hover { background: rgba(255,255,255,.15); border-color: #fff; color: #fff; }

/* Inside inverted bands, the band link colour must not beat the button. */
.pl a:not(.btn), .cta-strip a:not(.btn), .ftr a:not(.btn) { color: inherit; }

/* ---------------------------------------------------- topbar + header -- */
.topbar { background: var(--ink); color: #e9dcc9; font-size: 0.8rem; }
.topbar-in {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp4); min-height: 2.5rem;
}
.topbar-contact, .topbar-social { display: flex; align-items: center; gap: var(--sp5); }
/* min-height, not padding: the bar is only 2.5rem tall and these links
   would otherwise be 22px targets, under the 24px minimum. */
.topbar a {
  display: inline-flex; align-items: center; gap: var(--sp2);
  min-height: 1.75rem; color: #e9dcc9; text-decoration: none;
}
.topbar a:hover { color: var(--saffron); }
.topbar svg { width: 0.95rem; height: 0.95rem; flex: none; }
.topbar-social a { min-width: 2rem; min-height: 2rem; justify-content: center; }
.topbar-social svg { width: 1.05rem; height: 1.05rem; }
.topbar-note { display: none; color: #bda98f; }

@media (min-width: 62em) { .topbar-note { display: block; } }
@media (max-width: 40em) { .topbar-contact li:nth-child(2) { display: none; } }

.hdr {
  position: sticky; top: 0; z-index: 200;
  background: var(--white);
  border-block-end: 1px solid var(--line);
  box-shadow: 0 2px 14px rgba(36,24,15,.05);
}
.hdr-in { display: flex; align-items: center; gap: var(--sp4); min-height: 4.75rem; }
.logo img { height: 3.1rem; width: auto; }

.nav { margin-inline-start: auto; }
.nav ul { display: flex; align-items: center; gap: var(--sp1); }
.nav a {
  display: block; padding: var(--sp2) var(--sp3);
  font-family: var(--display); font-size: 0.92rem; font-weight: 600;
  color: var(--ink); text-decoration: none; white-space: nowrap;
  border-radius: var(--r-pill);
  transition: background-color .18s var(--ease), color .18s var(--ease);
}
.nav a:hover { background: var(--cream-2); color: var(--brick); }

.hdr-cta {
  display: none; align-items: center; min-height: 2.9rem;
  padding: var(--sp2) var(--sp5);
  background: var(--saffron); color: #3a2803;
  font-family: var(--display); font-weight: 700; font-size: 0.9rem;
  text-decoration: none; border-radius: var(--r-pill); white-space: nowrap;
}
.hdr-cta:hover { background: #ffc043; }

.burger {
  display: inline-flex; align-items: center; gap: var(--sp2);
  margin-inline-start: auto; min-height: 2.75rem; padding: var(--sp2) var(--sp4);
  background: var(--cream-2); border: 0; border-radius: var(--r-pill);
  color: var(--ink); font-family: var(--display); font-weight: 700; font-size: 0.85rem;
  cursor: pointer;
}
.burger span[aria-hidden] { position: relative; width: 1.05rem; height: 2px; background: var(--ink); }
.burger span[aria-hidden]::before,
.burger span[aria-hidden]::after {
  content: ""; position: absolute; left: 0; width: 100%; height: 2px;
  background: var(--ink); transition: transform .24s var(--ease);
}
.burger span[aria-hidden]::before { transform: translateY(-6px); }
.burger span[aria-hidden]::after { transform: translateY(6px); }
.burger[aria-expanded="true"] span[aria-hidden] { background: transparent; }
.burger[aria-expanded="true"] span[aria-hidden]::before { transform: rotate(45deg); }
.burger[aria-expanded="true"] span[aria-hidden]::after { transform: rotate(-45deg); }

@media (max-width: 63.99em) {
  .nav {
    position: fixed; inset: 7.25rem 0 auto 0; z-index: 190;
    max-height: calc(100dvh - 7.25rem); overflow-y: auto;
    padding: var(--sp3) var(--sp4) var(--sp7);
    background: var(--white); border-block-end: 1px solid var(--line);
    clip-path: inset(0 0 100% 0); transition: clip-path .4s var(--ease);
  }
  .nav[data-open="true"] { clip-path: inset(0 0 0 0); }
  .nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .nav a { padding: var(--sp4) var(--sp2); font-size: 1.05rem; border-radius: 0;
           border-block-end: 1px solid var(--line); }
}
@media (min-width: 64em) {
  .burger { display: none; }
  .hdr-cta { display: inline-flex; }
}

/* ---------------------------------------------------------------- hero -- */
.hero { position: relative; isolation: isolate; padding-block: var(--sp8) calc(var(--sp8) + 3rem); }

/* Saffron field with a soft radial lift, sitting behind everything. */
.hero-bg {
  position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(90rem 40rem at 78% 8%, rgba(255,255,255,.55), transparent 60%),
    radial-gradient(60rem 40rem at 5% 100%, rgba(193,59,27,.16), transparent 62%),
    linear-gradient(168deg, #ffd371 0%, var(--saffron) 46%, #e79a10 100%);
}

/* A faint repeat of the pack's floral line art, masked so it never fights
   the headline. */
.hero-bg::after {
  content: ""; position: absolute; inset: 0;
  background: url("../../assets/brand/pack-pattern.webp") repeat;
  background-size: 300px;
  opacity: .16;
  mask-image: linear-gradient(to bottom, transparent, #000 40%, #000 70%, transparent);
}

.hero-in { display: grid; gap: var(--sp7); align-items: center; }

.hero h1 { font-size: var(--s4); color: #2a1a06; }

/* The highlighted phrase gets a hand-drawn underline rather than a box, so
   it reads as emphasis and not as a second button. */
.hero h1 .mark {
  position: relative; display: inline-block; white-space: nowrap;
  color: var(--brick-dk);
}
.hero h1 .mark::after {
  content: ""; position: absolute; left: -.06em; right: -.06em; bottom: .02em;
  height: .18em; border-radius: var(--r-pill);
  background: rgba(255,255,255,.75);
  z-index: -1;
}

.hero .lede {
  margin-block-start: var(--sp5);
  font-size: var(--s1); color: #4a3308; max-width: 48ch;
}

.hero-cta { display: flex; flex-wrap: wrap; gap: var(--sp4); margin-block-start: var(--sp6); }

.hero-chips {
  display: flex; flex-wrap: wrap; gap: var(--sp3);
  margin-block-start: var(--sp6);
}
.hero-chips li {
  display: inline-flex; align-items: center; gap: var(--sp2);
  padding: var(--sp2) var(--sp4);
  background: rgba(255,255,255,.72); color: #3d2a06;
  border-radius: var(--r-pill);
  font-family: var(--display); font-size: 0.85rem; font-weight: 700;
}
.hero-chips svg { width: 1rem; height: 1rem; color: var(--leaf-dk); }

/* --- hero art ------------------------------------------------------- */
.hero-art { position: relative; }

.art-main img {
  width: 100%; aspect-ratio: 16 / 11; object-fit: cover;
  border-radius: var(--r-lg);
  border: 8px solid rgba(255,255,255,.85);
  box-shadow: var(--sh-lg);
}

.art-sub { display: none; }

@media (min-width: 48em) {
  .art-sub {
    display: block; position: absolute; right: -1.5rem; bottom: -2.5rem;
    width: 11rem;
  }
  .art-sub img {
    width: 100%; aspect-ratio: 1; object-fit: cover;
    border-radius: var(--r-lg);
    border: 6px solid var(--white);
    box-shadow: var(--sh-lg);
  }
}

/* Seal: fixed square so the logomark cannot stretch to the frame width. */
.art-seal {
  position: absolute; left: -1rem; top: -1.75rem;
  display: grid; place-items: center;
  width: 6.5rem; height: 6.5rem;
  background: var(--white); border-radius: 50%;
  box-shadow: var(--sh);
}
.art-seal svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  animation: spin 22s linear infinite;
}
.art-seal text {
  font-family: var(--display); font-size: 12px; font-weight: 700;
  letter-spacing: .1em; fill: var(--clay-tx);
}
.art-seal img { width: 2.2rem; height: auto; }
@keyframes spin { to { transform: rotate(360deg); } }

.hero-curve {
  position: absolute; left: 0; right: 0; bottom: -1px;
  width: 100%; height: clamp(40px, 5vw, 80px);
}
.hero-curve path { fill: var(--cream); }

@media (min-width: 62em) {
  .hero-in { grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr); gap: var(--sp8); }
}

/* --------------------------------------------------------------- stats -- */
/* Pulled up over the hero curve so the two bands interlock. */
.stats { position: relative; z-index: 2; margin-block-start: -3.25rem; }

.stat-row {
  display: grid; gap: var(--sp2);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  padding: var(--sp5);
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--sh-lg);
}
.stat-row li {
  display: grid; gap: var(--sp1); padding: var(--sp4) var(--sp5);
  border-radius: var(--r);
}
.stat-row li + li { border-inline-start: 1px solid var(--line); }
@media (max-width: 55.99em) { .stat-row li + li { border-inline-start: 0; border-block-start: 1px solid var(--line); } }

.stat-ic {
  display: grid; place-items: center; width: 2.6rem; height: 2.6rem;
  background: var(--cream-2); border-radius: 12px; color: var(--brick);
  margin-block-end: var(--sp2);
}
.stat-ic svg { width: 1.3rem; height: 1.3rem; }
.stat-row b { font-family: var(--display); font-size: var(--s1); }
.stat-row li > span:last-child { color: var(--ink-2); font-size: 0.9rem; line-height: 1.5; }

/* --------------------------------------------------------------- tabs -- */
.range { padding-block: var(--sec); }

.tab-strip {
  display: flex; gap: var(--sp3); flex-wrap: wrap;
  padding-block-end: var(--sp5); margin-block-end: var(--sp6);
  border-block-end: 2px solid var(--line);
}
.tab-strip button {
  display: inline-flex; align-items: center; gap: var(--sp2);
  min-height: 3rem; padding: var(--sp2) var(--sp5);
  background: var(--cream-2); border: 2px solid transparent;
  border-radius: var(--r-pill); cursor: pointer;
  font-family: var(--display); font-weight: 700; font-size: 0.98rem;
  transition: background-color .2s var(--ease), color .2s var(--ease),
              border-color .2s var(--ease);
}
.tab-strip button span {
  display: grid; place-items: center; min-width: 1.6rem; height: 1.6rem;
  padding-inline: .35rem; border-radius: var(--r-pill);
  background: rgba(36,24,15,.1); font-size: .75rem;
}
.tab-strip button:hover { background: var(--cream-3); }
.tab-strip button[aria-selected="true"] {
  background: var(--brick); border-color: var(--brick); color: #fff;
}
.tab-strip button[aria-selected="true"] span { background: rgba(0,0,0,.3); }

.tab-panel[hidden] { display: none; }
.js .tab-panel:not([hidden]) { animation: fade .45s var(--ease) both; }
@keyframes fade { from { opacity: 0; transform: translateY(14px); } }

.tab-intro {
  display: grid; gap: var(--sp5); align-items: center;
  padding: var(--sp5); margin-block-end: var(--sp6);
  background: var(--cream-2); border-radius: var(--r-lg);
}
.tab-plate img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--r); }
.tab-intro h3 { font-size: var(--s2); margin-block-end: var(--sp2); }
.tab-intro p { color: var(--ink-2); }

@media (min-width: 48em) { .tab-intro { grid-template-columns: 13rem 1fr; } }

.tcards {
  display: grid; gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}
.tcards a {
  display: grid; height: 100%;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden; text-decoration: none;
  color: inherit;
  transition: transform .24s var(--ease), box-shadow .24s var(--ease),
              border-color .24s var(--ease);
}
.tcards a:hover { transform: translateY(-4px); box-shadow: var(--sh); border-color: var(--saffron); }
.tcard-img img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.tcard-body { display: grid; gap: var(--sp2); align-content: start; padding: var(--sp5); }
.tcard-body b { font-family: var(--display); font-size: 1.05rem; line-height: 1.25; }
.tcard-meta { color: var(--ink-3); font-size: 0.85rem; }
.tcard-tags { display: flex; flex-wrap: wrap; gap: var(--sp2); margin-block-start: var(--sp1); }

.tag { padding: .15em .6em; border-radius: var(--r-pill); font-size: .7rem;
       font-weight: 700; border: 1px solid;
       color: var(--leaf-dk); border-color: rgba(63,125,69,.4); background: rgba(63,125,69,.09); }
.tag--jain { color: var(--amber-tx); border-color: rgba(239,168,24,.55); background: rgba(239,168,24,.15); }
.tag--farali { color: var(--clay-tx); border-color: rgba(155,90,51,.4); background: rgba(155,90,51,.09); }

/* ------------------------------------------------------------- marquee -- */
.marq { background: var(--ink); color: var(--cream); overflow: hidden; }
.marq-track { display: flex; width: max-content; animation: scroll 34s linear infinite; }
.marq ul { display: flex; align-items: center; }
.marq li {
  padding: var(--sp3) var(--sp6);
  font-family: var(--display); font-size: .85rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; white-space: nowrap;
}
.marq li::after { content: "\2022"; margin-inline-start: var(--sp6); color: var(--saffron); }
.marq:hover .marq-track { animation-play-state: paused; }
@keyframes scroll { to { transform: translateX(-50%); } }

/* --------------------------------------------------------------- steps -- */
.steps { padding-block: var(--sec); background: var(--cream-2); }
.steps .sec-title { margin-block-end: var(--sp7); }

.step-row {
  display: grid; gap: var(--sp6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.step-row li { position: relative; text-align: center; }

/* Dashed connector between the three steps, drawn only where there is a
   next step to connect to. */
@media (min-width: 58em) {
  .step-row li:not(:last-child)::after {
    content: ""; position: absolute; top: 5.5rem; left: calc(50% + 6rem);
    width: calc(100% - 12rem); border-block-start: 2px dashed rgba(193,59,27,.35);
  }
}

.step-n {
  display: grid; place-items: center;
  width: 3rem; height: 3rem; margin-inline: auto;
  background: var(--brick); color: #fff; border-radius: 50%;
  font-family: var(--display); font-weight: 700; font-size: 1.05rem;
  box-shadow: var(--sh);
}
.step-art { width: 11rem; margin: calc(var(--sp5) * -1 + -.5rem) auto var(--sp5); }
.step-art img {
  width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 50%;
  border: 6px solid var(--white); box-shadow: var(--sh);
}
.step-row h3 { font-size: var(--s2); margin-block-end: var(--sp3); }
.step-row p { color: var(--ink-2); max-width: 34ch; margin-inline: auto; }

/* Keep the numbered disc above the round photo it overlaps. */
.step-n { position: relative; z-index: 1; }

/* ------------------------------------------------------- private label -- */
.pl { padding-block: var(--sec); background: var(--ink); color: #f3e7d4; }
.pl-in { display: grid; gap: var(--sp7); align-items: center; }
.pl h2 { font-size: var(--s3); color: #fff; margin-block-end: var(--sp4); }
.pl p { color: #d9c8b1; }
.pl-art img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--r-lg); box-shadow: var(--sh-lg);
}
.pl-list { display: grid; gap: var(--sp3); margin-block: var(--sp5); }
.pl-list li { display: flex; align-items: flex-start; gap: var(--sp3); }
.pl-list svg { width: 1.2rem; height: 1.2rem; flex: none; margin-block-start: .3rem; color: var(--saffron); }

@media (min-width: 58em) { .pl-in { grid-template-columns: minmax(0,1fr) minmax(0,1.05fr); } }

/* -------------------------------------------------------------- export -- */
.exp { padding-block: var(--sec); }
.exp-in { display: grid; gap: var(--sp7); align-items: center; }
.exp h2 { font-size: var(--s3); margin-block-end: var(--sp4); }
.exp p { color: var(--ink-2); }

.exp-chips { display: flex; flex-wrap: wrap; gap: var(--sp3); margin-block: var(--sp5); }
.exp-chips li {
  padding: var(--sp2) var(--sp4);
  background: var(--cream-2); border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-family: var(--display); font-weight: 600; font-size: .9rem;
}

.exp-note {
  padding: var(--sp4) var(--sp5);
  background: rgba(239,168,24,.12); border: 1px solid rgba(239,168,24,.45);
  border-radius: var(--r); font-size: .9rem; color: var(--amber-tx);
}

/* 4:3, not demo 2's 16:9. The band held a map plate there; it holds a pack
   render here, and 16:9 both cropped the top and bottom off a 4:3 source and
   left the image half the height of the copy beside it. */
.exp-art img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--sh);
}

/* Give the image column the wider share now that it is the taller element,
   and let the copy sit against it rather than floating in the middle. */
@media (min-width: 58em) {
  .exp-in { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: center; }
}

/* ------------------------------------------------------------- recipes -- */
.recs { padding-block: var(--sec); background: var(--cream-2); }
.rec-grid { display: grid; gap: var(--sp5);
            grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.rcard {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg);
  transition: transform .24s var(--ease), box-shadow .24s var(--ease);
}
.rcard:hover { transform: translateY(-4px); box-shadow: var(--sh); }
.rcard-img { overflow: hidden; }
.rcard-img img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform .5s var(--ease); }
.rcard:hover .rcard-img img { transform: scale(1.05); }
.rcard-body { display: grid; gap: var(--sp3); align-content: start; padding: var(--sp5); }
.rcard-steps {
  justify-self: start; padding: .15em .7em; border-radius: var(--r-pill);
  background: rgba(193,59,27,.1); color: var(--brick);
  font-family: var(--display); font-weight: 700; font-size: .75rem;
}
.rcard h3 { font-size: var(--s2); }
.rcard p { color: var(--ink-2); font-size: .95rem; }

/* --------------------------------------------------------------- story -- */
.story { padding-block: var(--sec); }
.story-in { display: grid; gap: var(--sp7); align-items: center; }
.story h2 { font-size: var(--s3); margin-block-end: var(--sp4); }
.story p + p { margin-block-start: var(--sp4); }
.story p { color: var(--ink-2); }

.story-art { position: relative; }
.story-art-main img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--r-lg); box-shadow: var(--sh-lg);
}
.story-art-sub { display: none; }
@media (min-width: 48em) {
  /* The art sits on the LEFT in demo 3, so the inset photo hangs off its
     right edge — off the left it would sit in the page gutter. */
  .story-art-sub { display: block; position: absolute; right: -2rem; bottom: -2.5rem; width: 10rem; }
  .story-art-sub img {
    width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--r-lg);
    border: 6px solid var(--cream); box-shadow: var(--sh);
  }
}
@media (min-width: 58em) { .story-in { grid-template-columns: minmax(0,.95fr) minmax(0,1fr); } }

/* --------------------------------------------------------------- certs -- */
.certs { padding-block: var(--sec); background: var(--cream-3); }
.certs .sec-sub { margin-block: var(--sp3) var(--sp7); }
.cert-row {
  display: grid; gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}
.cert-row li {
  display: grid; gap: var(--sp2); justify-items: center; text-align: center;
  padding: var(--sp6) var(--sp5);
  background: var(--white); border-radius: var(--r-lg); border: 1px solid var(--line);
}
.cert-row li > span {
  display: grid; place-items: center; width: 3rem; height: 3rem;
  background: var(--cream-2); border-radius: 50%; color: var(--brick);
}
.cert-row svg { width: 1.4rem; height: 1.4rem; }
.cert-row b { font-family: var(--display); font-size: var(--s1); }
.cert-row code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
                 font-size: .82rem; color: var(--ink-2); word-break: break-all; }
.certs-note {
  margin: var(--sp6) auto 0; max-width: 52ch; text-align: center;
  font-size: .9rem; color: var(--clay-tx);
}

/* ----------------------------------------------------------- CTA strip -- */
.cta-strip { background: var(--brick); color: #fff; }
.cta-in {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp5); padding-block: var(--sp7);
}
.cta-strip h2 { font-size: var(--s3); }
.cta-strip p { color: rgba(255,255,255,.93); }
.cta-strip .hero-cta { margin-block-start: 0; }

/* -------------------------------------------------------------- footer -- */
.ftr { background: var(--ink); color: #cdbca7; padding-block: var(--sp8) var(--sp6); }
.ftr-top {
  display: grid; gap: var(--sp7);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  padding-block-end: var(--sp7); border-block-end: 1px solid rgba(255,255,255,.12);
}
.ftr-brand img { height: 3.4rem; width: auto; margin-block-end: var(--sp4); }
.ftr-brand p { font-size: .92rem; max-width: 30ch; }
.ftr-social { display: flex; gap: var(--sp3); margin-block-start: var(--sp5); }
.ftr-social a {
  display: grid; place-items: center; width: 2.6rem; height: 2.6rem;
  background: rgba(255,255,255,.08); border-radius: 50%; color: #fff;
}
.ftr-social a:hover { background: var(--saffron); color: #3a2803; }
.ftr-social svg { width: 1.2rem; height: 1.2rem; }

.ftr-col h2 {
  font-size: .82rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--saffron); margin-block-end: var(--sp4);
}
.ftr-col ul { display: grid; gap: var(--sp1); }
.ftr-col a { display: inline-block; padding-block: var(--sp2); color: #cdbca7; text-decoration: none; font-size: .95rem; }
.ftr-col a:hover { color: #fff; text-decoration: underline; }

.ftr-contact li { display: flex; align-items: flex-start; gap: var(--sp3); }
.ftr-contact svg { width: 1.1rem; height: 1.1rem; flex: none; margin-block-start: .75rem; color: var(--saffron); }
.ftr-contact address { font-style: normal; padding-block: var(--sp2); font-size: .95rem; }

.ftr-legal {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp4); padding-block-start: var(--sp5); font-size: .85rem; color: #a08f7c;
}
.ftr-legal ul { display: flex; gap: var(--sp4); }
.ftr-legal a { display: inline-block; padding-block: var(--sp2); color: #a08f7c; }

/* ---------------------------------------------------------- animation -- */
.js [data-reveal] { opacity: 0; transform: translateY(22px); }
.js [data-reveal][data-shown] { animation: rise .6s var(--ease) both;
                                animation-delay: calc(var(--i, 0) * 80ms); }
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }

/* --------------------------------------------------------- demo chrome -- */
.demo-back {
  position: fixed; left: var(--sp4); bottom: var(--sp4); z-index: 250;
  padding: var(--sp3) var(--sp5);
  background: var(--ink); color: #fff; border-radius: var(--r-pill);
  font-family: var(--display); font-size: .8rem; font-weight: 700;
  text-decoration: none; box-shadow: var(--sh);
}
.demo-back:hover { background: var(--brick); color: #fff; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .marq-track { animation: none; width: auto; }
  .marq-track ul:nth-child(2) { display: none; }
  .marq ul { flex-wrap: wrap; }
  .art-seal svg { animation: none; }
  .btn:hover, .rcard:hover, .tcards a:hover { transform: none; }
  .js [data-reveal] { opacity: 1; transform: none; animation: none; }
}

/* ==========================================================================
   PINNED CARD STACK  (shared block — identical in demo-1.css and demo-2.css)

   Ships as an ordinary vertical list of cards. /assets/js/card-stack.js adds
   data-pinned="true" once GSAP is up, and only then does the section become
   the pinned stack. That ordering matters: the pinned mode makes the cards
   `position: absolute` inside a frame with a percentage height, so if it were
   the default and the script never ran, the section would collapse to an
   empty box.
   ========================================================================== */

.stack { padding-block: var(--sec) 0; background: var(--ink); color: #f3e7d4; }
/* The colour stays only as a fallback under the photograph: if the image
   ever fails to load the heading must not land on white. With the picture
   in place it is never seen. */
/* Tighter: the card already sits 80px below this once the band pins, and
   the two gaps together left the card stranded low in the section. */
.stack-head { text-align: center; margin-block-end: var(--sp5); }
.stack-head .kicker { color: var(--saffron); }
.stack-head .sec-title { color: #fff; }
.stack-head .sec-sub { margin-inline: auto; color: #d4c2ab; }

/* --- default: a readable list ---------------------------------------- */
.stack-sticky { padding-block-end: var(--sec); }

.stack-frame {
  display: grid; gap: var(--sp5);
  width: 100%; max-width: 48rem; margin-inline: auto;
  padding-inline: var(--sp4);
}

.stack-card {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--ink);
  box-shadow: var(--sh-lg);
}
.stack-card picture { display: block; }
/* 4:3, the renders' own ratio — this is what mobile sees now that it
   never pins, and a 16:9 crop cut the top and bottom off every card. */
.stack-card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: #eceaea; }

.stack-card figcaption {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: var(--sp8) var(--sp5) var(--sp5);
  color: #fff;
  /* Deep enough at the foot that the caption stays legible over a bright
     photograph, and long enough at the top that the fade is not a visible
     band across the image. */
  background: linear-gradient(
    to top,
    rgba(12, 7, 3, 0.94) 0%,
    rgba(12, 7, 3, 0.86) 34%,
    rgba(12, 7, 3, 0.45) 68%,
    rgba(12, 7, 3, 0) 100%
  );
}
/* A pill, not bare text: the number sits at the top of the gradient where
   a light photograph still shows through, and saffron on cream fails
   contrast badly. The pill carries its own ground. */
.stack-n {
  display: inline-block; margin-block-end: var(--sp3);
  padding: 0.3rem 0.7rem; border-radius: var(--r-pill);
  background: rgba(12, 7, 3, 0.8);
  font-family: var(--display); font-size: 0.85rem; font-weight: 700;
  line-height: 1.2; letter-spacing: 0.22em; color: var(--saffron);
}
.stack-card h3 { font-size: var(--s2); color: #fff; }
.stack-card p {
  margin-block-start: var(--sp2); max-width: 52ch;
  font-size: 0.95rem; color: #e2d4c1;
}

.stack-count { display: none; }

/* --- pinned mode (added by card-stack.js) ---------------------------- */
.stack[data-pinned="true"] .stack-sticky {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100vh; height: 100dvh;
  /* Top padding clears the sticky header, which would otherwise sit over
     the top of the frame for the whole pinned run. */
  /* The side padding matches .wrap, so the card's edges line up with the
     stats card above it rather than running past it to the screen edge. */
  padding: clamp(4.75rem, 7.6vh, 6rem) var(--sp6) 0;
  overflow: hidden;
}

.stack[data-pinned="true"] .stack-frame {
  position: relative; display: block;
  width: 100%; height: 90%; max-width: 24rem;
  padding-inline: 0;
  border-radius: var(--r-lg); overflow: hidden;
}

/* Responsive caps, small through large, so the frame never outgrows the
   1600px plate it is filled with. */
@media (min-width: 30em) { .stack[data-pinned="true"] .stack-frame { max-width: 30rem; } }
@media (min-width: 48em) { .stack[data-pinned="true"] .stack-frame { max-width: 38rem; } }
@media (min-width: 62em) { .stack[data-pinned="true"] .stack-frame { max-width: 48rem; } }

.stack[data-pinned="true"] .stack-card {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  will-change: transform;
}
.stack[data-pinned="true"] .stack-card picture { width: 100%; height: 100%; }
.stack[data-pinned="true"] .stack-card img {
  width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover;
}

/* Counter, fed by --card which the ScrollTrigger onUpdate keeps current. */
.stack[data-pinned="true"] .stack-count {
  /* A pill, and sat in the gutter beside the frame. The taller card now
     reaches close enough to the foot that bare text here landed on its
     bottom edge; the pill means it stays legible even on the narrow
     viewports where there is no gutter left to sit in. */
  display: block; position: absolute; right: var(--sp5); bottom: var(--sp5);
  padding: .3rem .8rem; border-radius: var(--r-pill);
  background: rgba(12, 7, 3, .72);
  counter-reset: card var(--card, 1);
  font-family: var(--display); font-size: 0.9rem; font-weight: 600;
  color: #b9a894;
}
.stack-count b { color: var(--saffron); font-size: 1.15rem; }
.stack-count b span::after { content: counter(card); }

/* Demo 2 only: the stack lands directly above the private-label band, which
   is also ink. A deeper brick ground keeps the two readable as two sections
   instead of one long dark run. */
/*
 * Brand motif, not a photograph.
 *
 * The stock kitchen shot was fighting the cards: the cards carry a warm,
 * sunlit domestic kitchen and the background was a cold steel commercial one,
 * so the section read as two kitchens stacked on each other. It also said
 * nothing -- it was stock, and not Raecha's.
 *
 * This is the treatment from the reference instead: something large and
 * barely there. It belongs to the brand, it has no colour temperature to
 * clash with, and it cannot compete with the cards because there is almost
 * nothing to compete with.
 */
/*
 * NO overflow on this section. `overflow: clip` was tried here to contain the
 * motif and it broke the pin outright: the frame scrolled away over 4,200px
 * instead of holding, and --card stayed at 1 through the whole run. `hidden`
 * would be worse. The motif is kept inside by its own box instead, below.
 */
.stack { background-color: var(--ink); }

/*
 * The pack tile, repeated.
 *
 * White line art with a mean alpha of 4.7/255 -- 0.07% of its pixels are more
 * than half opaque. It only ever reads on a dark ground, which is why it works
 * in this section and did nothing at all on the cream page headers. .5 looks
 * like a lot and is not: at that alpha it is a whisper.
 */
.stack::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: url("../../assets/brand/pack-pattern.webp") repeat;
  background-size: 190px;
  opacity: .5;
}

/*
 * The logomark, large and bleeding off the right edge -- the peacock's job in
 * the reference. 4% on this ground is visible as a shape and unreadable as a
 * logo, which is the point of a watermark.
 */
.stack::after {
  content: "";
  position: absolute;
  z-index: 0;
  /*
   * Flush to the right edge and NARROWER than the artwork drawn inside it.
   *
   * A background image is clipped to its own element's box, so a 44rem mark
   * anchored left inside a 26rem box loses its right-hand side at the section
   * edge -- the bleed the reference has, with nothing sticking out. The
   * earlier version used right: -10rem and put 160px of horizontal scroll on
   * the page.
   */
  right: 0;
  top: 6rem;
  width: 26rem;
  height: 44rem;
  pointer-events: none;
  opacity: .04;
  background: url("../../assets/brand/raechas-logomark.svg") no-repeat left center;
  background-size: 44rem 44rem;
}

@media (max-width: 61.99em) {
  .stack::after {
    top: 3rem;
    width: 13rem;
    height: 24rem;
    opacity: .05;
    background-size: 24rem 24rem;
  }
}
.stack-head .sec-sub { color: #e0c8b6; }

/* ==========================================================================
   DEMO 3 additions — banner hero, photo-led grids, video band
   ========================================================================== */

/* --------------------------------------------------------------- banner -- */
/* Full-bleed: this is the one block that ignores .wrap entirely. */
.banner { position: relative; overflow: hidden; background: var(--cream-2); }

/* Slides share one grid cell, so the band is as tall as its tallest slide
   and nothing jumps on change. */
.bslides { display: grid; }

.bslide {
  grid-area: 1 / 1;
  position: relative;
  display: grid; align-items: center;
  min-height: clamp(26rem, 46vw, 40rem);
  opacity: 0; visibility: hidden;
  transition: opacity .7s var(--ease), visibility .7s var(--ease);
}
.bslide[data-active="true"] { opacity: 1; visibility: visible; }

/* On mobile the photograph fills the band and the plane sits over it at 93%,
   so the copy reads ON the image. From 56em it is confined to the left of
   the band instead: stretched across the full 1385px the 4:3 render crops to
   a letterbox strip and the pouch falls outside it entirely. */
.bslide-art { position: absolute; inset: 0; z-index: 0; }
.bslide-art picture { height: 100%; }
.bslide-art img {
  width: 100%; height: 100%; object-fit: cover;
  object-position: 42% 50%;
}
@media (min-width: 56em) {
  /* The photo takes the left 56%, and the crop is biased left so the pouch
     and the bowl both clear the plane's leading edge — at 42% the plane cut
     straight across the dish. */
  .bslide-art { right: 44%; }
  .bslide-art img { object-position: 40% 50%; }
}

/* The angled plane: a clip-path on a straight box, not a rotated element,
   so the copy sitting on it is never on a slant. */
.bslide-plane {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(118deg, #ffc954 0%, var(--saffron) 52%, #e79a10 100%);
  opacity: .93;
}
.bslide--b .bslide-plane { background: linear-gradient(118deg, #f7f0e0 0%, #f3e3c3 60%, #eed9b2 100%); }
.bslide--c .bslide-plane { background: linear-gradient(118deg, #d7532f 0%, var(--brick) 55%, #9c2f14 100%); }

.bslide-copy {
  position: relative; z-index: 2;
  width: 100%; max-width: 80rem; margin-inline: auto;
  padding: clamp(2.5rem, 5vw, 4.5rem) var(--sp6);
}

.bslide-kicker {
  font-family: var(--display); font-size: var(--s-1); font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: #5b3d04; margin-block-end: var(--sp3);
}
.bslide--c .bslide-kicker { color: #ffe0b3; }

.bslide-title { margin-block-end: var(--sp5); }
.bslide-title .line-1 {
  display: block;
  font-size: clamp(1.5rem, 3.2vw, 2.5rem); font-weight: 600;
  color: #3a2803;
}
.bslide-title .line-2 {
  display: block;
  font-size: clamp(2.4rem, 6.4vw, 5rem); font-weight: 800;
  line-height: .98; letter-spacing: -.03em; text-transform: uppercase;
  color: var(--brick-dk);
}
.bslide--c .bslide-title .line-1 { color: #ffe9cf; }
.bslide--c .bslide-title .line-2 { color: #fff; }

/* The ribbon, as on the reference banners: a solid pill, so the strapline
   holds its own contrast whatever the photograph behind it is doing. */
.bslide-ribbon {
  display: inline-block; padding: var(--sp2) var(--sp5);
  background: var(--brick); color: #fff; border-radius: var(--r-pill);
  font-family: var(--display); font-size: .88rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
}
.bslide--c .bslide-ribbon { background: #fff; color: var(--brick-dk); }

.bslide-cta { margin-block-start: var(--sp6); }
.bslide--c .btn--solid { background: #fff; border-color: #fff; color: var(--brick-dk); }
.bslide--c .btn--solid:hover { background: var(--cream); border-color: var(--cream); color: var(--brick-dk); }

@media (min-width: 56em) {
  /* Copy occupies the right half; the plane's angled edge slices the
     photograph diagonally behind it. */
  /* The copy starts just inside the plane's leading edge. It used to start
     at 50% while the plane began at 38%, which left a band of flat saffron
     a tenth of the screen wide doing nothing between the photo and the
     headline. */
  .bslide-copy { padding-inline-start: 58%; }
  .bslide-plane { clip-path: polygon(56% 0, 100% 0, 100% 100%, 48% 100%); opacity: 1; }
}

/* Below the split breakpoint the band stacks: photograph first at its own
   4:3 crop, copy underneath on the colour.

   It used to keep the desktop composition — photo behind, plane over it at
   93% — and at phone width that left the pouch as a ghost under the orange,
   effectively invisible. Legibility of the product beats keeping the copy
   on the artwork at this size, and it is what both reference sites do too. */
@media (max-width: 55.99em) {
  .bslide { min-height: 0; grid-template-rows: auto auto; }

  .bslide-art { position: relative; inset: auto; }
  .bslide-art picture { height: auto; }
  .bslide-art img {
    width: 100%; height: auto; aspect-ratio: 4 / 3;
    object-fit: cover; object-position: 50% 50%;
  }

  /* The plane becomes the copy block's own ground, so it covers exactly the
     copy and never the photograph. */
  .bslide-plane { display: none; }
  .bslide-copy {
    background: linear-gradient(150deg, #ffc954 0%, var(--saffron) 52%, #e79a10 100%);
  }
  .bslide--b .bslide-copy {
    background: linear-gradient(150deg, #f7f0e0 0%, #f3e3c3 60%, #eed9b2 100%);
  }
  .bslide--c .bslide-copy {
    background: linear-gradient(150deg, #d7532f 0%, var(--brick) 55%, #9c2f14 100%);
  }
}

/* --- banner nav --- */
.banner-nav {
  position: absolute; z-index: 3; left: 0; right: 0; bottom: var(--sp5);
  display: flex; align-items: center; justify-content: center; gap: var(--sp4);
}
.banner-arrow {
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem;
  background: rgba(255,255,255,.9); border: 0; border-radius: 50%;
  color: var(--ink); font-size: 1.4rem; line-height: 1; cursor: pointer;
  box-shadow: var(--sh);
}
.banner-arrow:hover { background: #fff; }

/* The dots sit on a cream slide, a saffron one and a brick one, so they get
   their own ground rather than a colour that has to work on all three. The
   inactive dots were rgba(ink, .3) and disappeared completely on the brick
   slide. */
.dots {
  display: flex; gap: var(--sp2); align-items: center;
  padding: .25rem .5rem;
  background: rgba(12, 7, 3, .3);
  border-radius: var(--r-pill);
}

/* 24x24 hit area, 12px dot — the dot is drawn as ::before so the two sizes
   can differ. */
.dots button {
  position: relative; overflow: hidden;
  width: 1.5rem; height: 1.5rem; padding: 0; border: 0;
  background: none; cursor: pointer;
  transition: width .45s var(--ease);
}
.dots button::before {
  content: ""; position: absolute; inset: .375rem;
  border-radius: var(--r-pill); background: rgba(255, 255, 255, .6);
}
.dots button span {
  position: absolute; top: .375rem; bottom: .375rem; left: .375rem;
  width: 0; border-radius: var(--r-pill); background: var(--brick);
}
.dots button[aria-current="true"] { width: 4rem; }
.dots button[aria-current="true"]::before { background: #fff; }
.dots button[aria-current="true"] span { animation: dot-fill var(--slide-ms, 6500ms) linear both; }

@keyframes dot-fill { from { width: 0; } to { width: calc(100% - .75rem); } }

/* ---------------------------------------------------- bestseller grid -- */
.best { padding-block: var(--sec); }
.best .sec-head { align-items: center; }

/* Fixed column counts rather than auto-fill: auto-fill fitted five columns
   at desktop, which stranded the sixth card alone on a second row and made
   every image smaller than the brief asked for. Four across at ~300px is
   both tidier and bigger. */
.bgrid { display: grid; gap: var(--sp6) var(--sp5); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 40em) { .bgrid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 62em) { .bgrid { grid-template-columns: repeat(4, 1fr); } }
.bgrid a {
  display: grid; align-content: start; gap: var(--sp1);
  text-decoration: none; color: inherit;
}
.bcard-img {
  display: block; overflow: hidden;
  background: var(--cream-2); border-radius: var(--r-lg);
  margin-block-end: var(--sp4);
}
.bcard-img img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  transition: transform .5s var(--ease);
}
.bgrid a:hover .bcard-img img { transform: scale(1.06); }
.bcard-cat {
  font-family: var(--display); font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.bcard-name { font-family: var(--display); font-size: 1.12rem; font-weight: 700; line-height: 1.25; }
.bgrid a:hover .bcard-name { color: var(--brick); }
.bcard-meta { font-size: .85rem; color: var(--ink-3); }

/* ------------------------------------------------------ category tiles -- */
.cstrip { padding-block: var(--sec); background: var(--cream-2); }
.cstrip .sec-title { margin-block-end: var(--sp7); }

.ctiles {
  display: grid; gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}
.ctile {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--r-lg); text-decoration: none;
  box-shadow: var(--sh);
}
.ctile img {
  width: 100%; aspect-ratio: 3 / 4; object-fit: cover;
  transition: transform .5s var(--ease);
}
.ctile:hover img { transform: scale(1.05); }
.ctile-body {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: var(--sp8) var(--sp5) var(--sp5);
  color: #fff;
  background: linear-gradient(
    to top,
    rgba(12,7,3,.94) 0%, rgba(12,7,3,.86) 34%,
    rgba(12,7,3,.45) 68%, rgba(12,7,3,0) 100%
  );
}
.ctile-body b { display: block; font-family: var(--display); font-size: 1.2rem; line-height: 1.2; }
.ctile-body span { font-size: .85rem; color: #e0cfba; }

/* --------------------------------------------------------------- video -- */
.vids { padding-block: var(--sec); background: var(--ink); color: #e7d9c6; }
.vids .sec-title { color: #fff; }
.sec-head--light .kicker { color: var(--saffron); }

.vtiles {
  display: grid; gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}
.vtile {
  position: relative; display: block; overflow: hidden;
  background: #2f2016; border-radius: var(--r-lg);
  text-decoration: none; color: inherit;
  transition: transform .24s var(--ease);
}
.vtile:hover { transform: translateY(-4px); }
.vtile-img { display: block; }
.vtile-img img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }

/* Centred on the image, not on the whole tile: a top-margin percentage
   resolves against the container's WIDTH, so 28.125% is exactly half the
   height of a 16:9 image. */
.vtile-play {
  position: absolute; left: 50%; top: 0;
  margin-block-start: 28.125%;
  transform: translate(-50%, -50%);
  display: grid; place-items: center;
  width: 3.75rem; height: 3.75rem;
  background: var(--brick); color: #fff; border-radius: 50%;
  box-shadow: var(--sh-lg);
  transition: transform .24s var(--ease), background-color .24s var(--ease);
}
.vtile:hover .vtile-play { transform: translate(-50%, -50%) scale(1.08); background: var(--brick-dk); }
.vtile-play svg { width: 1.5rem; height: 1.5rem; margin-inline-start: .15rem; }

.vtile-body { display: block; padding: var(--sp5); }
.vtile-body b { display: block; font-family: var(--display); font-size: 1.05rem; line-height: 1.3; color: #fff; }
.vtile-body span { display: block; margin-block-start: var(--sp2); font-size: .85rem; color: #c1ae98; }

.vids-note {
  margin-block-start: var(--sp6); max-width: 60ch;
  font-size: .9rem; color: #c1ae98;
}

@media (prefers-reduced-motion: reduce) {
  .bslide { transition: none; }
  .bslide:not([data-active="true"]) { opacity: 0; visibility: hidden; }
  .dots button[aria-current="true"] span { animation: none; width: calc(100% - .75rem); }
  .vtile:hover, .bgrid a:hover .bcard-img img, .ctile:hover img { transform: none; }
}

/* Demo 2 pulls this strip up over the hero's curved base. Demo 3's banner
   ends on its carousel controls, which the overlap would sit on top of. */
.stats { margin-block-start: 0; padding-block-start: var(--sec); }

/* Mobile banner controls.

   This has to live AFTER the base .banner-nav rule, not with the rest of the
   stacked-banner overrides higher up: same specificity, so source order
   decides, and declared earlier it lost to `position: absolute` — the dots
   stayed pinned to the band's foot and sat on top of the CTA button. */
@media (max-width: 55.99em) {
  .banner-nav {
    position: static;
    padding-block: var(--sp5);
    background: var(--cream-2);
  }
  /* Two lines of ribbon next to a real button reads as two buttons. */
  .bslide-ribbon { font-size: .78rem; padding-inline: var(--sp4); }
}

/* ==========================================================================
   DEMO 3 — sliders, reel wall, Instagram embeds
   Overrides the grid versions above; keep this block last.
   ========================================================================== */

/* --------------------------------------------------------------- shell -- */
.slider { position: relative; }

.slider-track {
  display: grid; grid-auto-flow: column;
  gap: var(--sp5);
  overflow-x: auto; overscroll-behavior-inline: contain;
  /* `proximity`, not `mandatory`: a mandatory snap re-targets a programmatic
     smooth scroll while it is still running, which is one of the ways an
     arrow button ends up appearing to do nothing. Proximity still lands
     cards on the edge after a swipe without fighting the arrows. */
  scroll-snap-type: x proximity;
  padding-block-end: var(--sp2);
  /* Hidden scrollbar: the arrows and the snap are the affordance. The track
     stays swipeable and keyboard-scrollable either way. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.slider-track::-webkit-scrollbar { display: none; }
.slider-track > * { scroll-snap-align: start; }

/* Exact column widths, never `minmax(0, …)`: a minmax floor of 0 lets the
   columns shrink to fit, the track stops overflowing, and the arrows go dead
   because there is nothing left to scroll. */
.slider--products .slider-track,
.slider--cats .slider-track { grid-auto-columns: calc((100% - var(--sp5)) / 1.6); }
.slider--reels .slider-track { grid-auto-columns: calc((100% - var(--sp5)) / 1.7); }

@media (min-width: 40em) {
  .slider--products .slider-track,
  .slider--cats .slider-track { grid-auto-columns: calc((100% - var(--sp5) * 2) / 2.4); }
  .slider--reels .slider-track { grid-auto-columns: calc((100% - var(--sp5) * 2) / 2.6); }
}
@media (min-width: 62em) {
  .slider--products .slider-track { grid-auto-columns: calc((100% - var(--sp5) * 3) / 4); }
  .slider--cats .slider-track { grid-auto-columns: calc((100% - var(--sp5) * 3) / 4); }
  .slider--reels .slider-track { grid-auto-columns: calc((100% - var(--sp5) * 3) / 4); }
}

/* --- arrows --- */
.slider-arrow {
  position: absolute; top: 40%; z-index: 4;
  display: grid; place-items: center;
  width: 3rem; height: 3rem;
  background: var(--white); border: 1px solid var(--line); border-radius: 50%;
  color: var(--ink); font-size: 1.5rem; line-height: 1; cursor: pointer;
  box-shadow: var(--sh);
  transition: background-color .2s var(--ease), opacity .2s var(--ease);
}
.slider-arrow--prev { left: 0; transform: translate(-45%, -50%); }
.slider-arrow--next { right: 0; transform: translate(45%, -50%); }
.slider-arrow:hover:not(:disabled) { background: var(--cream-2); }
.slider-arrow:disabled { opacity: .35; cursor: default; }

/* On a phone the arrows move to a centred pair beneath the track. They used
   to be hidden entirely on the reasoning that the track is swipeable — but
   an invisible affordance reads as a broken slider, and swipe does not exist
   at all for someone testing in a desktop browser's device mode. Hanging
   them over the gutter is what does not work at this width, not showing
   them. */
@media (max-width: 47.99em) {
  .slider { padding-block-end: 3.75rem; }
  .slider-arrow { top: auto; bottom: 0; transform: none; }
  .slider-arrow--prev { left: calc(50% - 3.25rem); right: auto; }
  .slider-arrow--next { right: calc(50% - 3.25rem); left: auto; }
}

.vids .slider-arrow {
  background: #40291b; border-color: #58402e; color: #fff;
}
.vids .slider-arrow:hover:not(:disabled) { background: var(--brick); border-color: var(--brick); }

.sec-head--centre { justify-content: center; text-align: center; }

/* ------------------------------------------------------------- cards -- */
.bcard a {
  display: grid; align-content: start; gap: var(--sp1); height: 100%;
  text-decoration: none; color: inherit;
}

/* Tiles keep their own height inside a column track. */
.slider--cats .ctile { height: 100%; }

/* --------------------------------------------------------------- reels -- */
.reel {
  position: relative; display: block; height: 100%;
  overflow: hidden; border-radius: var(--r-lg);
  background: #2f2016; text-decoration: none; color: inherit;
  transition: transform .24s var(--ease);
}
.reel:hover { transform: translateY(-4px); }

.reel-media { display: block; }
/* The poster and the video share one rule: the tile must reserve its 9:16
   box before the file loads, or the slider re-measures mid-scroll and the
   arrows step the wrong distance. */
.reel-media img,
.reel-media video {
  display: block; width: 100%; aspect-ratio: 9 / 16; object-fit: cover;
  background: #2f2016;
  transition: transform .5s var(--ease);
}
.reel:hover .reel-media img,
.reel:hover .reel-media video { transform: scale(1.04); }

/* Marks the loop as a stand-in. It has to be legible over a moving image,
   so it carries its own ground rather than relying on the frame behind it. */
.reel-tag {
  position: absolute; top: var(--sp4); right: var(--sp4);
  padding: .15rem .55rem; border-radius: var(--r-pill);
  background: rgba(12, 7, 3, .75); color: #fff;
  font-family: var(--display); font-size: .72rem; font-weight: 700;
}

/* The product strip along the foot, as on the reference: a thumbnail, the
   reel title and the product it is about. No price — none exists. */
.reel-strip {
  position: absolute; inset-inline: var(--sp3); bottom: var(--sp3);
  display: grid; grid-template-columns: 3.25rem 1fr auto; gap: var(--sp3);
  align-items: center;
  padding: var(--sp3);
  background: rgba(12, 7, 3, .82);
  backdrop-filter: blur(6px);
  border-radius: var(--r);
}
.reel-thumb img {
  width: 3.25rem; height: 3.25rem; object-fit: cover;
  border-radius: 10px; background: var(--cream-2);
}
.reel-meta { min-width: 0; }
.reel-meta b {
  display: block; color: #fff;
  font-family: var(--display); font-size: .88rem; font-weight: 700; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.reel-meta > span {
  display: block; margin-block-start: .1rem;
  font-size: .76rem; color: #cbb8a2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ------------------------------------------------ Instagram reel tiles -- */
/*
 * A real Instagram embed is not the same object as the placeholder loop it
 * replaced, so it does not inherit that treatment.
 *
 * The loop was a bare 9:16 video, so the caption strip could float over its
 * lower third. An embed is a white card with Instagram's own header, caption
 * and action row around the video -- floating anything over it would cover
 * their chrome, and the strip would be white-on-white anyway. The strip moves
 * below the frame and the tile becomes two rows.
 */
.reel--ig {
  display: grid;
  grid-template-rows: 1fr auto;
  background: #fff;
}
/* The iframe swallows its own clicks, so the tile cannot lift on hover the
   way an anchor does -- there is no reliable hover to respond to. */
.reel--ig:hover { transform: none; }

.reel--ig .reel-media { position: relative; display: block; }
/*
 * Sizing the frame.
 *
 * Measured off the embed page itself, not guessed from 9:16. Instagram does
 * NOT show a reel at 9:16 in an embed -- it fits the video into a 4:5 box and
 * wraps it in about 210px of its own chrome: the account header at the top,
 * then the action row, like count and caption underneath.
 *
 * At a 980px layout that came out at 980 wide by 1433 tall, of which the
 * video was 980x1225. The chrome does not shrink with the column, so the
 * whole thing gets proportionally taller as the tile gets narrower -- which
 * is why the two widths below carry different ratios.
 *
 * Both err a few per cent TALL on purpose. `scrolling="no"` means an
 * undersized box silently clips, and the first thing to go would be the
 * caption and like count; an oversized one just leaves a sliver of the
 * iframe's own white showing under a white card, which nobody can see.
 *
 * The ratio has to be declared here rather than left to the iframe, or the
 * tile has no height until the embed loads and the slider measures its
 * columns against an empty box -- which makes the arrows step the wrong
 * distance.
 */
.reel--ig .reel-media { aspect-ratio: 1 / 1.62; }
@media (min-width: 48em) {
  .reel--ig .reel-media { aspect-ratio: 1 / 1.5; }
}
@media (min-width: 62em) {
  .reel--ig .reel-media { aspect-ratio: 1 / 1.45; }
}
.reel--ig .reel-media iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; display: block; background: #fff;
}

/* Back into normal flow, on its own dark ground under the white card. */
.reel-strip--ig {
  position: static; inset: auto;
  grid-template-columns: 1fr auto;
  background: #2f2016;
  backdrop-filter: none;
  border-radius: 0;
  padding: var(--sp4);
}
/* The handle line wraps to two lines on the collab tiles, so it cannot be
   the single clipped line the product strip used. */
.reel-strip--ig .reel-meta > span { white-space: normal; overflow: visible; }

.reel-go:focus-visible {
  outline: 2px solid var(--saffron);
  outline-offset: 3px;
  border-radius: 4px;
}

/*
 * Embeds need room, so the column count is not the loops'.
 *
 * The loops ran 1.7 across on a phone (about 190px) and four across at
 * desktop (about 286px). Instagram's embed will not lay out under 326px --
 * below that it starts squeezing its own header -- so both of those are out.
 *
 * On a phone that leaves no room for a peeking next card at all: a 390px
 * screen gives about 358px inside the gutters, and 358 is already close to
 * the floor. One full-width card it is; the arrows and the snap still say
 * there is more.
 */
.vids--ig .slider--reels .slider-track { grid-auto-columns: 100%; }
@media (min-width: 48em) {
  .vids--ig .slider--reels .slider-track {
    grid-auto-columns: calc((100% - var(--sp5)) / 1.9);
  }
}
/* Three across on a small laptop: going straight from 1.9-up to 4-up at
   62em would drop the tile to about 220px the instant the breakpoint fires,
   which is narrower than anything else in this section ever gets. */
@media (min-width: 62em) {
  .vids--ig .slider--reels .slider-track {
    grid-auto-columns: calc((100% - var(--sp5) * 2) / 3);
  }
}
@media (min-width: 75em) {
  .vids--ig .slider--reels .slider-track {
    grid-auto-columns: calc((100% - var(--sp5) * 3) / 4);
  }
}

/* --------------------------------------------------- Instagram embeds -- */
.ig-h {
  margin-block: var(--sp8) var(--sp5);
  font-size: var(--s2); color: #fff;
}

/*
 * Three columns where three real embeds fit, fewer where they do not.
 *
 * The minimum used to be 19rem. That was set when these frames were dashed
 * placeholders, which will lay out at any width at all; a real Instagram
 * embed will not go under about 326px without squeezing its own header. So
 * the floor is now 20.5rem -- 328px -- and a track only appears when
 * something can actually render in it.
 *
 * The max-width query catches phones narrower than the minimum itself, where
 * one 328px track would overflow a 320px column.
 */
.ig-row {
  display: grid; gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(20.5rem, 1fr));
}
@media (max-width: 24em) {
  .ig-row { grid-template-columns: 1fr; }
}

.ig-frame {
  position: relative; overflow: hidden;
  border-radius: var(--r-lg); background: #2f2016;
  /* Instagram's own embed is about 4:5 plus its chrome. Fixing the ratio
     here means the real embed drops in without the section resizing. */
  aspect-ratio: 4 / 5;
}
.ig-frame iframe { width: 100%; height: 100%; border: 0; display: block; background: #fff; }

.ig-frame--empty {
  display: grid; place-items: center; text-align: center;
  border: 2px dashed #6a4f3a; background: #33231a;
}
.ig-frame--empty span { display: grid; justify-items: center; gap: var(--sp2); padding: var(--sp5); }
.ig-frame--empty svg { width: 2.25rem; height: 2.25rem; color: var(--saffron); }
.ig-frame--empty b { font-family: var(--display); font-size: 1rem; color: #fff; }
.ig-frame--empty em {
  font-style: normal; font-size: .85rem; color: #c1ae98; max-width: 24ch; line-height: 1.5;
}

/* --------------------------------------------------------------- steps -- */
/* Real photography in the discs now, so the round crop needs a ground: the
   pack renders sit on a light grey that would otherwise cut off hard. */
.step-art img { background: #eceaea; }

@media (prefers-reduced-motion: reduce) {
  .reel:hover { transform: none; }
  .reel:hover .reel-media img { transform: none; }
  .slider-track { scroll-behavior: auto; }
}

/* The arrow at the end of the reel strip, standing in for the play badge the
   autoplaying tiles no longer need. */
.reel-go {
  color: var(--saffron); font-size: 1.1rem; line-height: 1;
  transition: transform .2s var(--ease);
}
.reel:hover .reel-go { transform: translateX(3px); }

/* Photography in the bands that used to carry plates. */
.pl-art img, .exp-art img, .story-art-main img { background: #eceaea; }

/* --------------------------------------------------------- travel band -- */
/* Full-bleed statement band: one photograph, a hard scrim, one headline. */
.travel {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; align-items: center;
  min-height: clamp(24rem, 40vw, 34rem);
  background: var(--ink);
}

.travel-art { position: absolute; inset: 0; z-index: 0; }
.travel-art picture { height: 100%; }
.travel-art img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%; }

/* Darkest on the left, where the copy sits. A flat overlay dark enough for
   white text at the right-hand edge would flatten the photograph entirely. */
.travel-scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(90deg, rgba(12,7,3,.92) 0%, rgba(12,7,3,.86) 34%,
                            rgba(12,7,3,.62) 62%, rgba(12,7,3,.35) 100%),
    linear-gradient(0deg, rgba(12,7,3,.35), rgba(12,7,3,.35));
}

.travel-in {
  position: relative; z-index: 2;
  padding-block: var(--sp8);
  /* Capped so the text never reaches the light end of the scrim. */
  max-width: 80rem;
}
.travel-in > * { max-width: 34rem; }

.travel-kicker {
  font-family: var(--display); font-size: var(--s-1); font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--saffron); margin-block-end: var(--sp4);
}

.travel-title {
  font-size: clamp(2.1rem, 1.2rem + 3.6vw, 4rem);
  line-height: 1.02; letter-spacing: -.025em; color: #fff;
}

.travel-body {
  margin-block-start: var(--sp5);
  font-size: var(--s1); color: #e5d7c6;
}

.travel-cta { display: flex; flex-wrap: wrap; gap: var(--sp4); margin-block-start: var(--sp6); }

/* Outline-on-photograph: a solid fill would read as a third colour block. */
.btn--outline { background: transparent; border-color: #fff; color: #fff; }
.btn--outline:hover { background: #fff; border-color: #fff; color: var(--ink); }

/* Bigger step discs: at 11rem the pack inside a circular crop was unreadable.
   The connector line between them is positioned from the disc's top, so it
   moves with the size. */
.step-art { width: clamp(12rem, 20vw, 17rem); }
@media (min-width: 58em) {
  .step-row li:not(:last-child)::after { top: 9.5rem; }
}

/* ------------------------------------------------- banner text effect -- */
/*
 * The headline wipes up and settles, one element after another, each time a
 * slide becomes active.
 *
 * It re-fires on every rotation for free: the animation is scoped to
 * [data-active="true"], and demo.js removes that attribute from the outgoing
 * slide and adds it to the incoming one. Removing it tears the animation
 * down, adding it starts a fresh one — no reflow hack needed.
 *
 * `clip-path` rather than an overflow-hidden wrapper, so the effect needs no
 * extra markup, and the copy stays one selectable block of text.
 */
@keyframes line-rise {
  from { clip-path: inset(0 0 105% 0); transform: translateY(.28em); opacity: 0; }
  to   { clip-path: inset(0 0 0 0);    transform: none;              opacity: 1; }
}

/*
 * Gated behind .js, like every other reveal on these pages. The fill mode is
 * `both`, so the element sits at the hidden keyframe through its delay — and
 * an animation that never advances would leave the page's most important line
 * invisible. Requiring the class means a visitor with no JavaScript, or a
 * failed script, gets the headline immediately and unanimated.
 */
.js .bslide[data-active="true"] .bslide-kicker { animation: line-rise .55s var(--ease) .05s both; }
.js .bslide[data-active="true"] .line-1        { animation: line-rise .7s  var(--ease) .16s both; }
.js .bslide[data-active="true"] .line-2        { animation: line-rise .8s  var(--ease) .30s both; }
.js .bslide[data-active="true"] .bslide-ribbon { animation: line-rise .6s  var(--ease) .52s both; }
.js .bslide[data-active="true"] .bslide-cta    { animation: line-rise .6s  var(--ease) .64s both; }

/* A little weight under the big line so it sits on the plane rather than
   floating on it. Tinted per slide — a dark shadow under white type on the
   brick slide would read as a smudge. */
.line-2 { text-shadow: 0 .04em 0 rgba(255, 255, 255, .45); }
.bslide--b .line-2 { text-shadow: 0 .04em 0 rgba(255, 255, 255, .7); }
.bslide--c .line-2 { text-shadow: 0 .04em 0 rgba(0, 0, 0, .18); }

@media (prefers-reduced-motion: reduce) {
  .js .bslide[data-active="true"] .bslide-kicker,
  .js .bslide[data-active="true"] .line-1,
  .js .bslide[data-active="true"] .line-2,
  .js .bslide[data-active="true"] .bslide-ribbon,
  .js .bslide[data-active="true"] .bslide-cta {
    animation: none; clip-path: none; transform: none; opacity: 1;
  }
}

/* ------------------------------------------------------- inner pages -- */
/* The homepage banner, cut down. An inner page should read as the same site
   without repeating the homepage's full-height statement. */
.banner--page { min-height: clamp(17rem, 26vw, 23rem); align-items: stretch; }
/* stretch, not the homepage's `center`: with a single short slide the grid
   centred it and left a band of the section's own background showing below
   the artwork. */
.banner--page .bslides { min-height: inherit; }
.banner--page .bslide { min-height: inherit; }
.banner--page .bslide-copy { padding-block: var(--sp7); }
.banner--page .bslide-title .line-1 { font-size: clamp(1.2rem, 2.2vw, 1.7rem); }
.banner--page .bslide-title .line-2 { font-size: clamp(2rem, 4.6vw, 3.4rem); }
.banner--page .bslide-ribbon { margin-block-start: var(--sp4); }

/* Below the split breakpoint the page banner keeps the copy on the artwork
   rather than stacking: there is no CTA under it to push off the screen, and
   a stacked half-height band reads as an accident. */
@media (max-width: 55.99em) {
  .banner--page .bslide-art { position: absolute; inset: 0; }
  .banner--page .bslide-art img { height: 100%; aspect-ratio: auto; }
  .banner--page .bslide-plane { display: block; opacity: .93; }
  .banner--page .bslide-copy { background: none; }
}

/* The same note, on a light band instead of the dark video one. */
.vids-note--light { color: var(--clay-tx); }

/* ==========================================================================
   DEMO 3 — brand band, tile row, about band, full-image steps
   ========================================================================== */

/* ----------------------------------------------------- mirrored band -- */
/* The second full-bleed band runs the other way, so the two read as a pair
   rather than as the same slide used twice. The scrim has to flip with the
   copy or the text lands on the light end of it. */
.travel--right .travel-scrim {
  background:
    linear-gradient(270deg, rgba(12,7,3,.92) 0%, rgba(12,7,3,.86) 34%,
                             rgba(12,7,3,.62) 62%, rgba(12,7,3,.35) 100%),
    linear-gradient(0deg, rgba(12,7,3,.35), rgba(12,7,3,.35));
}
.travel--right .travel-art img { object-position: 30% 45%; }

@media (min-width: 56em) {
  .travel--right .travel-in > * { margin-inline-start: auto; }
  .travel--right .travel-in { text-align: right; }
  .travel--right .travel-cta { justify-content: flex-end; }
}

/* ------------------------------------------------------- about band -- */
.about { padding-block: var(--sec); }
.about-in { display: grid; gap: var(--sp7); align-items: center; }

.about-art img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  background: #eceaea;
  border-radius: var(--r-lg); box-shadow: var(--sh-lg);
}
.about-copy > p { color: var(--ink-2); }
.about-copy .sec-title { margin-block-end: var(--sp4); }

.about-facts {
  display: grid; gap: var(--sp4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  margin-block: var(--sp6);
  padding-block: var(--sp5);
  border-block: 1px solid var(--line);
}
.about-facts li { display: grid; gap: .15rem; }
.about-facts span {
  font-family: var(--display); font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.about-facts b {
  font-family: var(--display); font-size: 1.02rem; line-height: 1.25;
  word-break: break-word;
}

@media (min-width: 58em) {
  .about-in { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); }
}

/* --------------------------------------------- steps as full images -- */
/* Replaces demo 2's round discs. A pack render inside a circle loses most of
   the pouch to the crop; these are full-width 4:3 cards instead. */
.step-row--full { display: grid; gap: var(--sp6) var(--sp5); grid-template-columns: 1fr; }
@media (min-width: 48em) { .step-row--full { grid-template-columns: repeat(3, 1fr); } }

.step-row--full li { text-align: left; }
.step-row--full li::after { content: none; }   /* the dashed connector goes */

.step-row--full .step-art {
  position: relative; width: 100%; margin: 0 0 var(--sp5);
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--sh);
}
.step-row--full .step-art img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border: 0; border-radius: 0; background: #eceaea;
  transition: transform .5s var(--ease);
}
.step-row--full li:hover .step-art img { transform: scale(1.04); }

/* The number sits on the image now rather than above a circle. */
.step-row--full .step-n {
  position: absolute; top: var(--sp4); left: var(--sp4);
  width: 3rem; height: 3rem; margin: 0;
  display: grid; place-items: center;
  background: var(--brick); color: #fff; border-radius: 50%;
  font-family: var(--display); font-weight: 700; font-size: 1.05rem;
  box-shadow: var(--sh);
}
.step-row--full h3 { font-size: var(--s2); margin-block-end: var(--sp3); }
.step-row--full p { max-width: none; margin-inline: 0; color: var(--ink-2); }

/* ------------------------------------------------ card stack, bigger -- */
/* Full-width frame. The homepage stack was capped at 48rem, which left it
   reading as a panel on a 1400px screen rather than as the section. */
/* The frame carries the source's own 4:3, so `object-fit: cover` has nothing
   to crop. It used to be 90% of the viewport height at up to 72rem wide —
   about 1.54:1 — which cut a band off the top and bottom of every render.
   `contain` was the other option and is worse here: the renders' corners run
   from rgb(183) to rgb(213) with a vignette, so no flat backdrop matches and
   the letterbox shows as a seam.

   max-height stays as a guard for short windows. If it ever bites, the crop
   is symmetric and small rather than the 13% it was. */
/* FULL BLEED, edge to edge, on the client's instruction that the crop is
   acceptable until new photography arrives.

   Both sides are definite — 100% of a sticky that is itself 100dvh — which
   is what keeps ScrollTrigger's measurement honest. `width: 100%` paired
   with `height: auto` is the combination that collapsed this section once:
   it makes the flex parent's size depend on its own content, and the pin was
   measured and baked at 48x112.

   The renders are 4:3 and the band is about 1.58:1, so each one loses
   roughly 8% off the top and the bottom. Measured subject boxes say the
   safe limit was 3.2% per end, so the dish is clipped on some cards — that
   is the accepted trade for full width. Replacement photography should be
   shot at, or croppable to, about 16:9. */
.stack[data-pinned="true"] .stack-frame {
  /* Fixed 16:9, the same at every window size.

     `height: 100%` let the ratio float with the window: 1.39 at 1400x950 but
     1.218 at 1920x1080, where the frame goes TALLER than the renders and the
     crop flips from vertical to horizontal and starts eating the pouch's
     edge. A frame whose shape depends on the visitor's window cannot be
     specced for a photographer either.

     Height is the definite side (a min() of two definite values) and the
     width derives from it — the same arrangement that keeps ScrollTrigger's
     measurement honest. 76rem x 9/16 = 42.75rem, so above that height the
     card is exactly 76rem wide and lines up with the stats strip. */
  height: min(100%, 42.75rem);
  width: auto;
  aspect-ratio: 16 / 9;
  max-width: 76rem;
  margin-inline: auto;
  border-radius: var(--r-lg);
}
.stack[data-pinned="true"] .stack-card { border-radius: var(--r-lg); }
.stack[data-pinned="true"] .stack-card figcaption {
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}


/* --------------------------------------------- mobile: card captions -- */
/*
 * On a phone the caption comes OFF the photograph.
 *
 * The overlay gradient was written for the pinned desktop card: 16:9, most of
 * the viewport, one short line of copy over the bottom eighth of a big image.
 * Mobile never pins, so the card is 4:3 and much smaller, and the same
 * caption -- a number pill, a heading and a sentence -- needs roughly half the
 * card's height. The gradient therefore ran up over half the photograph and
 * read as a black wash sitting on the food.
 *
 * Below 62em the caption is a solid band under the picture instead. The
 * photograph is then completely unobscured, which is the point of shooting
 * it, and the text sits on a ground built for text rather than fighting
 * whatever happens to be behind it.
 *
 * The card gets taller. That is the trade, and on a scrolling list it costs
 * nothing -- these cards are not competing for a fixed viewport down here.
 */
@media (max-width: 61.99em) {
  .stack-card figcaption {
    position: static;
    background: #0c0703;
    padding: var(--sp5);
  }

  /* The pill existed to carry its own ground where a bright photo showed
     through the top of the gradient. On a solid band there is nothing to
     show through, so it is just saffron text. */
  .stack-n {
    padding: 0;
    background: none;
    margin-block-end: var(--sp2);
  }
}

/* ---------------------------------------------- mobile: banner crop -- */
/*
 * STOPGAP, until the portrait artwork lands.
 *
 * The banners are 2000x750 with the headline, the claim icons and the button
 * PAINTED INTO the image. On a 375px phone that renders 375x141, where the
 * claim text lands at about 3px tall -- unreadable, and the same headline
 * then repeats underneath as real HTML, so the message appears twice.
 *
 * So on a phone the art becomes a square window onto the FOOD, and the baked
 * text is cropped out. The HTML copy below carries the words, which it was
 * already doing.
 *
 * The geometry is exact rather than eyeballed. A 2000x750 source in a square
 * box covers at scale W/750, so the visible window is always 750 source px
 * wide and its left edge sits at (position% x 1250):
 *
 *   slide 0  22%  -> 275..1025   packs and bowls; headline starts at 1200
 *   slide 1  72%  -> 900..1650   pack, bowl, model; headline ends at 703
 *   slide 2  12%  -> 150..900    pack and bowl; headline starts at 914
 *
 * Vertical position is irrelevant here: at 1:1 the scaled height equals the
 * box height exactly, so there is no vertical overflow to position.
 *
 * These are keyed to slide ORDER. Reorder BANNERS in build/demos/demo4-art.js
 * and these three values must be re-derived.
 */
@media (max-width: 47.99em) {
  .banner--art:not(.banner--mobile-art) .bnr-art {
    display: block; aspect-ratio: 1 / 1; overflow: hidden;
  }
  .banner--art:not(.banner--mobile-art) .bnr-art picture {
    display: block; height: 100%;
  }
  .banner--art:not(.banner--mobile-art) .bnr-art img {
    width: 100%; height: 100%;
    aspect-ratio: auto;
    object-fit: cover;
  }

  .banner--art:not(.banner--mobile-art) .bnr[data-slide="0"] .bnr-art img { object-position: 22% 50%; }
  .banner--art:not(.banner--mobile-art) .bnr[data-slide="1"] .bnr-art img { object-position: 72% 50%; }
  .banner--art:not(.banner--mobile-art) .bnr[data-slide="2"] .bnr-art img { object-position: 12% 50%; }
}

/* ------------------------------------ mobile: once the portrait art is in -- */
/*
 * The finished state. `banner--mobile-art` is added by the build only when
 * every banner has its portrait counterpart, so these rules cannot switch on
 * halfway through a partial delivery.
 *
 * Two things happen. The artwork is shown WHOLE -- no crop, no object-fit,
 * because a designed 4:5 banner is composed to be seen entire and any crop
 * would cut the very headline it was drawn to carry. And the copy block goes,
 * because the portrait artwork now carries the headline, the sub-line and the
 * button itself, and showing both is the duplication that started all this.
 *
 * Removing .bnr-copy costs no function and no accessibility:
 *   - .bnr-art is wrapped in an <a>, so the whole banner stays tappable and
 *     goes exactly where the painted button points;
 *   - .bnr-h is already a visually-hidden heading, so the document outline is
 *     untouched;
 *   - the words in the artwork are carried in the image's alt text.
 */
@media (max-width: 47.99em) {
  .banner--mobile-art .bnr-copy { display: none; }

  .banner--mobile-art .bnr-art,
  .banner--mobile-art .bnr-art picture { display: block; }
  .banner--mobile-art .bnr-art img {
    width: 100%; height: auto;
    object-fit: fill;      /* whole image, never cropped */
  }

  /*
   * The three portrait banners are not all the same shape: two are 2:3 and
   * the Authentic one is 4:5. That matters less than it sounds, because the
   * slides are stacked in one grid cell (grid-area: 1 / 1) rather than laid
   * out in sequence -- so the carousel is always as tall as its tallest
   * slide and the height never changes as it advances. Nothing jumps.
   *
   * What it does leave is about 94px of spare cell on the 4:5 slide. Left
   * alone the slide stretches and the art sits against the top of it, which
   * reads as the image having slipped. Centring puts the slack above and
   * below instead, where it looks like margin.
   *
   * Cropping the odd slide to match was the obvious alternative and is the
   * wrong one: going from 4:5 to 2:3 at a fixed height means taking 8% off
   * each side, which cuts into both the claim row and the headline.
   */
  .banner--mobile-art .bnr { align-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .step-row--full li:hover .step-art img { transform: none; }
}

/* ------------------------------------------------------- artwork band -- */
/* A finished banner, used whole. No crop, no scrim, no overlaid copy — the
   headline is painted into the image, and any crop would cut it. */
.artband { position: relative; background: var(--cream-2); }

.artband-link { display: block; }
.artband-link img {
  display: block; width: 100%; height: auto;   /* never `cover`: see above */
}
.artband-link:focus-visible { outline: 3px solid var(--brick); outline-offset: -3px; }

/* The real heading. The artwork shows it, so it is hidden from sight but not
   from a screen reader, and the page keeps its outline. */
.artband-h {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Below 48em the baked type is ~140px tall and unreadable, so the message is
   repeated as real, reflowing text with real buttons. */
.artband-copy {
  padding: var(--sp6) var(--sp5) var(--sp7);
  background: var(--cream-2);
  border-block-end: 1px solid var(--line);
}
.artband-title {
  font-family: var(--display); font-size: var(--s2); font-weight: 800;
  line-height: 1.15; letter-spacing: -.02em; color: var(--brick-dk);
}
.artband-sub { margin-block-start: var(--sp3); color: var(--ink-2); }
.artband-cta { display: flex; flex-wrap: wrap; gap: var(--sp3); margin-block-start: var(--sp5); }

@media (min-width: 48em) { .artband-copy { display: none; } }

/* ------------------------------------------- vertical thumb slider -- */
.vsl { padding-block: var(--sec); background: var(--cream-2); }

/* Capped, not full-width. The stage is 4:3 so its height follows its width,
   and across the whole 80rem wrap that came out 822px tall — taller than the
   viewport once the header is counted. 50rem puts the stage near 510px. */
.vsl-in {
  display: grid; gap: var(--sp5);
  width: 100%; max-width: 50rem; margin-inline: auto;
}
@media (min-width: 48em) {
  /* Stage first, rail second — the rail is 6rem of thumbnails beside it. */
  .vsl-in { grid-template-columns: minmax(0, 1fr) 6rem; }
}

/* --- stage --- */
.vsl-stage { position: relative; }
.vsl-panel[hidden] { display: none; }
.js .vsl-panel:not([hidden]) { animation: fade .4s var(--ease) both; }

.vsl-panel img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;   /* the renders' own ratio: no crop */
  background: #eceaea;
  border-radius: var(--r-lg); box-shadow: var(--sh);
}

.vsl-caption {
  position: absolute; inset-inline: 0; bottom: 0;
  display: grid; gap: var(--sp1);
  padding: var(--sp8) var(--sp5) var(--sp5);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  color: #fff;
  background: linear-gradient(
    to top,
    rgba(12,7,3,.94) 0%, rgba(12,7,3,.84) 36%,
    rgba(12,7,3,.4) 70%, rgba(12,7,3,0) 100%
  );
}
.vsl-cat {
  font-family: var(--display); font-size: .72rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--saffron);
}
.vsl-caption b { font-family: var(--display); font-size: var(--s2); line-height: 1.2; }
.vsl-meta { font-size: .9rem; color: #dcc9b4; }
.vsl-caption .link-arrow { color: #fff; justify-self: start; }

/* --- rail --- */
.vsl-thumbs {
  display: flex; gap: var(--sp3);
  overflow-x: auto; overflow-y: hidden;
  padding-block-end: var(--sp2);
  scrollbar-width: none; -ms-overflow-style: none;
}
.vsl-thumbs::-webkit-scrollbar { display: none; }

@media (min-width: 48em) {
  /* Vertical past the breakpoint, scrolling in its own column and capped to
     the stage's height so the two sides stay level. */
  .vsl-thumbs {
    flex-direction: column;
    overflow-x: hidden; overflow-y: auto;
    /* The same expression the stage's height resolves from — container width
       less the rail and the gap, times 3/4 — so the two columns stay level
       without the rail's own content deciding the row height. */
    max-height: calc((min(50rem, 100vw - 4rem) - 7.5rem) * 0.75);
    padding-inline-end: var(--sp1);
  }
}

.vsl-thumbs button {
  position: relative; flex: 0 0 auto;
  width: 4.5rem; padding: 0;
  background: none; border: 0; cursor: pointer;
  border-radius: var(--r); overflow: hidden;
  opacity: .62;
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
@media (min-width: 48em) { .vsl-thumbs button { width: 100%; } }

.vsl-thumbs img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  background: #eceaea; border-radius: var(--r);
  /* The selected ring is drawn here, so it follows the rounded corners. */
  box-shadow: inset 0 0 0 2px transparent;
  transition: box-shadow .2s var(--ease);
}
.vsl-thumbs button:hover { opacity: 1; }
.vsl-thumbs button[aria-selected="true"] { opacity: 1; }
.vsl-thumbs button[aria-selected="true"] img { box-shadow: inset 0 0 0 3px var(--brick); }

/* The name is for screen readers and for the no-JS case, where every panel
   is visible and the rail is decoration. */
.vsl-thumb-name {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .js .vsl-panel:not([hidden]) { animation: none; }
  .vsl-thumbs { scroll-behavior: auto; }
}

/* ------------------------------------------------ mobile compaction -- */
/*
 * Three blocks collapse to a single column on a phone and each becomes a
 * very tall run of near-identical cards — together they were about a third
 * of the page's 16,500px height. None of them needs full width at 375px.
 */
@media (max-width: 47.99em) {
  /* Four short claim cards: two up reads fine and halves the run. */
  .stat-row { grid-template-columns: 1fr 1fr; gap: var(--sp3); padding: var(--sp4); }
  .stat-row li { padding: var(--sp4) var(--sp3); }
  /* The dividers were drawn for a single column; in a 2x2 they cut across. */
  .stat-row li + li { border-inline-start: 0; border-block-start: 0; }
  .stat-ic { width: 2.2rem; height: 2.2rem; }
  .stat-row b { font-size: 1rem; }

  /* Same again for the four "what is on every pack" cards. */
  .cert-row { grid-template-columns: 1fr 1fr; gap: var(--sp4); }
  .cert-row li { padding: var(--sp5) var(--sp3); }

  /* Three 4:5 frames stacked are ~1370px of page, so they stay a swipe strip
     rather than a column.

     The cards were 74% wide, which showed a slice of the next one. That was
     free when they were placeholders; now they are live embeds and 74% of a
     375px phone is 254px -- under the ~326px Instagram needs, so every card
     would render with its own header squeezed. Full width it is. The peek is
     gone and there is no width left to buy it back, so the scroll has to be
     signalled by the strip's own momentum instead. */
  .ig-row {
    grid-auto-flow: column; grid-auto-columns: 100%;
    grid-template-columns: none;
    overflow-x: auto; overscroll-behavior-inline: contain;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .ig-row::-webkit-scrollbar { display: none; }
}

/* ------------------------------------------------- card stack backdrop -- */
/*
 * The card is 4:3 and the band is the viewport, so there is always a gutter
 * either side — at 1400x950 the card is 1139 wide inside 1385, and the flat
 * brown read as an empty hole.
 *
 * Full-bleed was the alternative and it cannot be done without cropping: a
 * 4:3 card at 1385 wide is 1039 tall against 854 of usable height, so it
 * would lose ~18% off the top and bottom of every render. The gutter gets a
 * backdrop instead.
 *
 * Both layers hang off .stack-sticky, not .stack: the sticky IS the pinned,
 * viewport-sized element, so the glow stays centred behind the card as it
 * pins. On .stack they would be positioned against a 5,000px pin-spacer and
 * the centre would sit far off screen.
 */
.stack[data-pinned="true"] .stack-sticky::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
}

/* The pack's own floral line art.

   It is WHITE line work with a mean alpha of 4.7/255 — only about 0.1% of
   its pixels are meaningfully opaque. At the 10% opacity this first carried
   it was invisible on the dark ground, which is the whole reason the backdrop
   appeared to do nothing. Sparse white art on #45150a needs to run near full
   strength before it reads at all.

   On the SECTION, not the pinned band. Carried on .stack-sticky it stopped
   dead where the band did, so the heading above it sat on flat colour and
   the join showed as a hard horizontal edge across the section. */
.stack { position: relative; }
.stack-head { position: relative; z-index: 1; }

/* Warm centre plus a corner vignette, so the band has a light source and
   some depth instead of being one flat colour. */
.stack[data-pinned="true"] .stack-sticky::after {
  background:
    radial-gradient(42rem 30rem at 50% 48%,
                    rgba(239, 168, 24, .30), rgba(239, 168, 24, 0) 70%),
    radial-gradient(125% 95% at 50% 50%,
                    rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, .40) 100%);
}

/* Above both backdrop layers.

   z-index only — NOT `position`. Setting the counter to `position: relative`
   here took it out of absolute positioning, so it became a flex sibling of
   the frame and ate 75px of the row: the "full bleed" card came out 1310
   wide inside a 1385 band. The counter has its own `position: absolute`
   further up and must keep it. */
.stack[data-pinned="true"] .stack-frame { position: relative; z-index: 1; }
.stack[data-pinned="true"] .stack-count { z-index: 2; }
/* The pinned band must sit over the section's pattern layer. */
.stack .pin-spacer, .stack-sticky { position: relative; z-index: 1; }


/* ====================================================================== */
/* DEMO 4 — the three-page client build                                   */
/*                                                                        */
/* Everything above is demo 3's stylesheet, unchanged. What follows is    */
/* the four things demo 4 adds: a sticky contact dock, one enquiry form,  */
/* an inner-page header, and the contact route cards.                     */
/* ====================================================================== */

.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ------------------------------------------------ sticky contact dock -- */
/*
 * Two plain links, fixed to the viewport. Nothing intercepts the click, so
 * the phone dials and WhatsApp opens the way the browser intends — which is
 * the one behaviour a contact dock cannot afford to get clever about.
 *
 * Bottom-right on desktop as a stacked pair; a full-width split bar on a
 * phone, where a thumb is at the bottom of the screen already.
 */
.dock {
  position: fixed;
  z-index: 60;
  right: var(--sp5);
  bottom: var(--sp5);
  display: grid;
  gap: var(--sp3);
}

.dock-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp2);
  padding: 0.7rem 1.05rem;
  border-radius: var(--r-pill);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--s-1);
  letter-spacing: 0.01em;
  text-decoration: none;
  color: var(--white);
  box-shadow: var(--sh-lg);
  transition: transform 0.2s var(--ease), filter 0.2s var(--ease);
  /* 44px minimum, so the target clears the guideline on its own rather than
     depending on the label to make it up. */
  min-height: 44px;
}

.dock-btn svg { width: 20px; height: 20px; flex: none; }

/*
 * Desktop: icon-only circles.
 *
 * A labelled pill pair is 130x102 in the bottom-right corner, and it sat on
 * top of the "This is Raecha's" headline on the about page, where the band is
 * right-aligned. A fixed dock always overlays something -- the fix is to make
 * it small enough that what it overlays is corner, not copy.
 *
 * The label stays in the DOM and only stops being painted, so the link keeps
 * its accessible name and screen readers still hear "Call" and "WhatsApp".
 * Below 48em the bar is full width at the bottom of the screen, nothing is
 * behind it, and the labels earn their place.
 */
@media (min-width: 48em) {
  .dock-btn {
    width: 54px;
    height: 54px;
    min-height: 54px;
    padding: 0;
    justify-content: center;
  }
  .dock-btn svg { width: 24px; height: 24px; }
  .dock-btn span {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
}

/* --brick, and WhatsApp's own green. Both carry white text: #c13b1b is
   4.9:1 against white, #128c4a is 4.6:1. */
.dock-btn--call { background: var(--brick); }
.dock-btn--wa   { background: #128c4a; }

.dock-btn:hover { transform: translateY(-2px); filter: brightness(1.06); }
.dock-btn:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}

@media (max-width: 47.99em) {
  .dock {
    right: 0; left: 0; bottom: 0;
    grid-template-columns: 1fr 1fr;
    gap: 0;
  }
  .dock-btn {
    justify-content: center;
    border-radius: 0;
    padding: 0.95rem 0.5rem;
    min-height: 52px;
    box-shadow: none;
  }
  .dock-btn:hover { transform: none; }
  /* Clear the bar so it never covers the last line of the footer. */
  body { padding-bottom: 52px; }
}

@media (prefers-reduced-motion: reduce) {
  .dock-btn { transition: none; }
  .dock-btn:hover { transform: none; }
}

/* --------------------------------------------------------- page head -- */

.phead {
  padding: calc(var(--sec) * 0.8) 0 calc(var(--sec) * 0.55);
  background:
    radial-gradient(60% 120% at 12% 0%, var(--cream-3) 0%, transparent 62%),
    var(--cream-2);
  border-bottom: 1px solid var(--line);
}

.phead-in { max-width: 54rem; }

.phead-title {
  margin: var(--sp2) 0 0;
  font-family: var(--display);
  font-size: var(--s4);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.phead-sub {
  margin: var(--sp4) 0 0;
  font-size: var(--s1);
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 44rem;
}

/* ------------------------------------------------------ enquiry form -- */

.enq {
  padding: var(--sec) 0;
  background: var(--cream-2);
  border-block: 1px solid var(--line);
}

.enq-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: start;
}

@media (min-width: 58em) {
  .enq-in { grid-template-columns: minmax(18rem, 24rem) 1fr; }
}

.enq-copy p { color: var(--ink-2); line-height: 1.6; }

.enq-routes {
  list-style: none;
  margin: var(--sp6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp3);
}

.enq-routes li {
  display: flex;
  align-items: center;
  gap: var(--sp3);
  font-size: var(--s0);
  color: var(--ink-2);
}

.enq-routes svg { width: 20px; height: 20px; flex: none; color: var(--brick); }
.enq-routes a { color: var(--ink); text-decoration-color: var(--line); }
.enq-routes a:hover { color: var(--brick-dk); }

.enq-form {
  display: grid;
  gap: var(--sp4);
  padding: clamp(var(--sp5), 3vw, var(--sp7));
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}

@media (min-width: 40em) {
  .enq-form { grid-template-columns: 1fr 1fr; }
  .enq-field--wide, .enq-submit { grid-column: 1 / -1; }
}

.enq-field { margin: 0; display: grid; gap: var(--sp2); }

.enq-field label {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.enq-field label span { color: var(--brick); }

.enq-field input,
.enq-field select,
.enq-field textarea {
  width: 100%;
  padding: 0.7rem 0.85rem;
  font: inherit;
  font-size: var(--s0);
  color: var(--ink);
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  /* 46px keeps the target above 44px, and a 16px-equivalent font size stops
     iOS zooming the page the moment the field takes focus. */
  min-height: 46px;
}

.enq-field textarea { min-height: 7rem; resize: vertical; line-height: 1.5; }

.enq-field input::placeholder,
.enq-field textarea::placeholder { color: var(--ink-3); opacity: 1; }

.enq-field :is(input, select, textarea):focus-visible {
  outline: 3px solid var(--saffron);
  outline-offset: 1px;
  border-color: var(--saffron-dp);
}

/* :user-invalid, not :invalid — an untouched required field is empty, and
   marking it red before the visitor has typed anything is just wrong. */
.enq-field :is(input, textarea):user-invalid { border-color: var(--brick); }

.enq-submit {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp4);
}

.enq-submit .btn { display: inline-flex; align-items: center; gap: var(--sp2); }
.enq-submit .btn svg { width: 20px; height: 20px; }
.enq-note { font-size: var(--s-1); color: var(--ink-3); }

/* ---------------------------------------------------- contact routes -- */

.croutes { padding: var(--sec) 0; }

.ccards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.ccard {
  display: grid;
  gap: var(--sp2);
  align-content: start;
  padding: var(--sp6);
  background: var(--cream-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.ccard-ico {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  margin-bottom: var(--sp2);
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--brick);
  box-shadow: var(--sh);
}

.ccard-ico svg { width: 22px; height: 22px; }

.ccard-label {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ccard-line { font-size: var(--s1); color: var(--ink); }
.ccard-line a { color: inherit; text-decoration-color: var(--line); }
.ccard-line a:hover { color: var(--brick-dk); }

.caddr {
  margin-top: var(--sp7);
  padding: var(--sp6);
  border: 1px dashed var(--line);
  border-radius: var(--r-lg);
}

.caddr h3 {
  margin: 0 0 var(--sp3);
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}

.caddr address {
  font-style: normal;
  line-height: 1.6;
  color: var(--ink-2);
}

.caddr-fssai {
  margin: var(--sp4) 0 0;
  font-size: var(--s-1);
  color: var(--ink-3);
}


/* ------------------------------------------- banner, finished artwork -- */
/*
 * Demo 4's hero, on the three banners the client designed.
 *
 * These are NOT photographs in a slot. The headline, the claim icons and the
 * button are drawn into the file, which changes three things:
 *
 *   1. they are never cropped. object-fit: cover would cut the baked type, so
 *      the image is shown whole at its own 2.667:1 and the band is as tall as
 *      the artwork makes it;
 *   2. the page must not print the headline twice, so .bnr-h is visually
 *      hidden. It still exists for the document outline and for screen
 *      readers, and the alt text carries the same words;
 *   3. at phone width the band is about 140px tall and the baked type is
 *      roughly 8px, which nobody can read. Below 48em .bnr-copy repeats the
 *      message and the button as real HTML underneath.
 */
.banner--art { background: var(--cream); }

.bnrs { display: grid; }

.bnr {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity .7s var(--ease), visibility .7s var(--ease);
}
.bnr[data-active="true"] { opacity: 1; visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .bnr { transition: none; }
}

/* Visually hidden, not display:none -- the heading is the slide's accessible
   name and the h1 of the page. */
.bnr-h {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.bnr-art { display: block; }
.bnr-art picture { display: block; }
.bnr-art img {
  display: block;
  width: 100%;
  height: auto;
}

.bnr-copy { display: none; }

@media (max-width: 47.99em) {
  .bnr-copy {
    display: block;
    padding: var(--sp6) var(--sp5) var(--sp7);
    text-align: center;
    background: var(--cream);
  }
  .bnr-title {
    margin: 0;
    font-family: var(--display);
    font-size: var(--s2);
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--ink);
  }
  .bnr-sub {
    margin: var(--sp3) 0 var(--sp5);
    color: var(--ink-2);
    line-height: 1.5;
  }
  .bnr-copy p:last-child { margin: 0; }

  /* The nav sits under the copy on a phone rather than over the artwork,
     where it would land on the baked-in button. */
  .banner--art .banner-nav {
    position: static;
    transform: none;
    justify-content: center;
    padding-bottom: var(--sp5);
    background: var(--cream);
  }
}


/* ----------------------------------------------------------- the map -- */
/*
 * Map beside the address it belongs to.
 *
 * The iframe has no intrinsic ratio of its own, so the frame sets one and the
 * iframe fills it absolutely. Without that the embed collapses to the iframe
 * default of 150px tall, which is the usual way an embedded map ends up as a
 * letterbox strip.
 *
 * 4:3 on a phone, 1:1 from 58em where it sits beside the copy and has to hold
 * its own height against it.
 */
.cmap {
  padding: var(--sec) 0;
  background:
    radial-gradient(70% 100% at 100% 0%, var(--cream-3) 0%, transparent 60%),
    var(--cream-2);
  border-block: 1px solid var(--line);
}

.cmap-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: center;
}

@media (min-width: 58em) {
  /* The map leads; the copy beside it is a column, not a caption. */
  .cmap-in { grid-template-columns: 1.15fr 1fr; }
}

.cmap-frame {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  background: var(--cream-3);
}

@media (min-width: 58em) {
  .cmap-frame { aspect-ratio: 1 / 1; }
}

.cmap-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.cmap-copy address {
  margin: var(--sp5) 0 0;
  font-style: normal;
  font-size: var(--s1);
  line-height: 1.65;
  color: var(--ink-2);
}
.cmap-copy address b {
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}

.cmap-meta {
  list-style: none;
  margin: var(--sp6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp3);
}
.cmap-meta li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp3);
  padding-block-end: var(--sp3);
  border-bottom: 1px dashed var(--line);
}
.cmap-meta span {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.cmap-meta b {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.cmap-cta {
  margin: var(--sp6) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp4);
}

.cmap-note {
  margin: var(--sp5) 0 0;
  font-size: var(--s-1);
  line-height: 1.5;
  color: var(--ink-3);
}

/* -------------------------------------------------- contact cards, lift -- */
/*
 * The four route cards were flat panels. A hover lift and a white face make
 * them read as the things you click, which on this page they are.
 */
.ccard {
  background: var(--white);
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.ccard:hover {
  transform: translateY(-3px);
  box-shadow: var(--sh);
}
.ccard-ico { background: var(--cream-2); }

@media (prefers-reduced-motion: reduce) {
  .ccard { transition: none; }
  .ccard:hover { transform: none; }
}


/* ------------------------------------------- card stack, photo ground -- */
/* --------------------------------------------------------- page head -- */

.phead {
  padding: calc(var(--sec) * 0.8) 0 calc(var(--sec) * 0.55);
  background:
    radial-gradient(60% 120% at 12% 0%, var(--cream-3) 0%, transparent 62%),
    var(--cream-2);
  border-bottom: 1px solid var(--line);
}

.phead-in { max-width: 54rem; }

.phead-title {
  margin: var(--sp2) 0 0;
  font-family: var(--display);
  font-size: var(--s4);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.phead-sub {
  margin: var(--sp4) 0 0;
  font-size: var(--s1);
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 44rem;
}

/* ------------------------------------------------------ enquiry form -- */

.enq {
  padding: var(--sec) 0;
  background: var(--cream-2);
  border-block: 1px solid var(--line);
}

.enq-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: start;
}

@media (min-width: 58em) {
  .enq-in { grid-template-columns: minmax(18rem, 24rem) 1fr; }
}

.enq-copy p { color: var(--ink-2); line-height: 1.6; }

.enq-routes {
  list-style: none;
  margin: var(--sp6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp3);
}

.enq-routes li {
  display: flex;
  align-items: center;
  gap: var(--sp3);
  font-size: var(--s0);
  color: var(--ink-2);
}

.enq-routes svg { width: 20px; height: 20px; flex: none; color: var(--brick); }
.enq-routes a { color: var(--ink); text-decoration-color: var(--line); }
.enq-routes a:hover { color: var(--brick-dk); }

.enq-form {
  display: grid;
  gap: var(--sp4);
  padding: clamp(var(--sp5), 3vw, var(--sp7));
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}

@media (min-width: 40em) {
  .enq-form { grid-template-columns: 1fr 1fr; }
  .enq-field--wide, .enq-submit { grid-column: 1 / -1; }
}

.enq-field { margin: 0; display: grid; gap: var(--sp2); }

.enq-field label {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.enq-field label span { color: var(--brick); }

.enq-field input,
.enq-field select,
.enq-field textarea {
  width: 100%;
  padding: 0.7rem 0.85rem;
  font: inherit;
  font-size: var(--s0);
  color: var(--ink);
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  /* 46px keeps the target above 44px, and a 16px-equivalent font size stops
     iOS zooming the page the moment the field takes focus. */
  min-height: 46px;
}

.enq-field textarea { min-height: 7rem; resize: vertical; line-height: 1.5; }

.enq-field input::placeholder,
.enq-field textarea::placeholder { color: var(--ink-3); opacity: 1; }

.enq-field :is(input, select, textarea):focus-visible {
  outline: 3px solid var(--saffron);
  outline-offset: 1px;
  border-color: var(--saffron-dp);
}

/* :user-invalid, not :invalid — an untouched required field is empty, and
   marking it red before the visitor has typed anything is just wrong. */
.enq-field :is(input, textarea):user-invalid { border-color: var(--brick); }

.enq-submit {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp4);
}

.enq-submit .btn { display: inline-flex; align-items: center; gap: var(--sp2); }
.enq-submit .btn svg { width: 20px; height: 20px; }
.enq-note { font-size: var(--s-1); color: var(--ink-3); }

/* ---------------------------------------------------- contact routes -- */

.croutes { padding: var(--sec) 0; }

.ccards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.ccard {
  display: grid;
  gap: var(--sp2);
  align-content: start;
  padding: var(--sp6);
  background: var(--cream-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.ccard-ico {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  margin-bottom: var(--sp2);
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--brick);
  box-shadow: var(--sh);
}

.ccard-ico svg { width: 22px; height: 22px; }

.ccard-label {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ccard-line { font-size: var(--s1); color: var(--ink); }
.ccard-line a { color: inherit; text-decoration-color: var(--line); }
.ccard-line a:hover { color: var(--brick-dk); }

.caddr {
  margin-top: var(--sp7);
  padding: var(--sp6);
  border: 1px dashed var(--line);
  border-radius: var(--r-lg);
}

.caddr h3 {
  margin: 0 0 var(--sp3);
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}

.caddr address {
  font-style: normal;
  line-height: 1.6;
  color: var(--ink-2);
}

.caddr-fssai {
  margin: var(--sp4) 0 0;
  font-size: var(--s-1);
  color: var(--ink-3);
}


/* ------------------------------------------- banner, finished artwork -- */
/*
 * Demo 4's hero, on the three banners the client designed.
 *
 * These are NOT photographs in a slot. The headline, the claim icons and the
 * button are drawn into the file, which changes three things:
 *
 *   1. they are never cropped. object-fit: cover would cut the baked type, so
 *      the image is shown whole at its own 2.667:1 and the band is as tall as
 *      the artwork makes it;
 *   2. the page must not print the headline twice, so .bnr-h is visually
 *      hidden. It still exists for the document outline and for screen
 *      readers, and the alt text carries the same words;
 *   3. at phone width the band is about 140px tall and the baked type is
 *      roughly 8px, which nobody can read. Below 48em .bnr-copy repeats the
 *      message and the button as real HTML underneath.
 */
.banner--art { background: var(--cream); }

.bnrs { display: grid; }

.bnr {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity .7s var(--ease), visibility .7s var(--ease);
}
.bnr[data-active="true"] { opacity: 1; visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .bnr { transition: none; }
}

/* Visually hidden, not display:none -- the heading is the slide's accessible
   name and the h1 of the page. */
.bnr-h {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.bnr-art { display: block; }
.bnr-art picture { display: block; }
.bnr-art img {
  display: block;
  width: 100%;
  height: auto;
}

.bnr-copy { display: none; }

@media (max-width: 47.99em) {
  .bnr-copy {
    display: block;
    padding: var(--sp6) var(--sp5) var(--sp7);
    text-align: center;
    background: var(--cream);
  }
  .bnr-title {
    margin: 0;
    font-family: var(--display);
    font-size: var(--s2);
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--ink);
  }
  .bnr-sub {
    margin: var(--sp3) 0 var(--sp5);
    color: var(--ink-2);
    line-height: 1.5;
  }
  .bnr-copy p:last-child { margin: 0; }

  /* The nav sits under the copy on a phone rather than over the artwork,
     where it would land on the baked-in button. */
  .banner--art .banner-nav {
    position: static;
    transform: none;
    justify-content: center;
    padding-bottom: var(--sp5);
    background: var(--cream);
  }
}


/* ----------------------------------------------------------- the map -- */
/*
 * Map beside the address it belongs to.
 *
 * The iframe has no intrinsic ratio of its own, so the frame sets one and the
 * iframe fills it absolutely. Without that the embed collapses to the iframe
 * default of 150px tall, which is the usual way an embedded map ends up as a
 * letterbox strip.
 *
 * 4:3 on a phone, 1:1 from 58em where it sits beside the copy and has to hold
 * its own height against it.
 */
.cmap {
  padding: var(--sec) 0;
  background:
    radial-gradient(70% 100% at 100% 0%, var(--cream-3) 0%, transparent 60%),
    var(--cream-2);
  border-block: 1px solid var(--line);
}

.cmap-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: center;
}

@media (min-width: 58em) {
  /* The map leads; the copy beside it is a column, not a caption. */
  .cmap-in { grid-template-columns: 1.15fr 1fr; }
}

.cmap-frame {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  background: var(--cream-3);
}

@media (min-width: 58em) {
  .cmap-frame { aspect-ratio: 1 / 1; }
}

.cmap-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.cmap-copy address {
  margin: var(--sp5) 0 0;
  font-style: normal;
  font-size: var(--s1);
  line-height: 1.65;
  color: var(--ink-2);
}
.cmap-copy address b {
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}

.cmap-meta {
  list-style: none;
  margin: var(--sp6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp3);
}
.cmap-meta li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp3);
  padding-block-end: var(--sp3);
  border-bottom: 1px dashed var(--line);
}
.cmap-meta span {
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.cmap-meta b {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.cmap-cta {
  margin: var(--sp6) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp4);
}

.cmap-note {
  margin: var(--sp5) 0 0;
  font-size: var(--s-1);
  line-height: 1.5;
  color: var(--ink-3);
}

/* -------------------------------------------------- contact cards, lift -- */
/*
 * The four route cards were flat panels. A hover lift and a white face make
 * them read as the things you click, which on this page they are.
 */
.ccard {
  background: var(--white);
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.ccard:hover {
  transform: translateY(-3px);
  box-shadow: var(--sh);
}
.ccard-ico { background: var(--cream-2); }

@media (prefers-reduced-motion: reduce) {
  .ccard { transition: none; }
  .ccard:hover { transform: none; }
}


/* ------------------------------------------- card stack, photo ground -- */


/*
 * Full-section background, once the stack is pinned.
 *
 * The pinned section is about 4,500px tall. `cover` on a 1900x700 source over
 * that has to scale the picture to roughly 12,000px wide -- which is exactly
 * why this started life as a band at the top.
 *
 * `background-attachment: fixed` sizes the background to the VIEWPORT instead
 * of the element: at 1400x950 the image goes from 1900 to 2578 wide, a 1.36x
 * upscale, and stays sharp. It also holds still while the cards move over it,
 * which is the right behaviour for a pinned sequence.
 *
 * iOS Safari ignores `fixed` and treats it as `scroll`, which would bring the
 * upscale back. It never gets the chance: below 62em the stack does not pin,
 * and this rule does not apply there.
 */


/*
 * The full-section background only became possible once the stack stopped
 * pinning. A pinned section is around 4,500px tall, and `cover` on a
 * 1900x700 source has to scale it to roughly 12,000px wide to fill that --
 * a blurry mess. The slider section is about 700px, so cover behaves.
 *
 * There is no mask any more: the picture runs edge to edge and the whole
 * section sits on it.
 */



/* No z-index work needed for the copy: .stack-head and .stack-sticky are
   already at z-index 1 further up this stylesheet, and both pseudo-elements
   sit at 0. Deliberately NOT restating position on .stack-sticky -- that is
   the pinned element, and the last time something set position on a child of
   this section it baked the pin at 48x112. */


/* ------------------------------------------ contact card, long values -- */
/*
 * @raechas_gourmetgujarati is 24 characters with no space, hyphen or other
 * break opportunity in it, so at the shared card size it simply ran out of
 * the card. Shrinking it is better than breaking it: a handle split across
 * two lines mid-word is no longer recognisable as a handle.
 *
 * overflow-wrap is still there as the last resort for a value longer than
 * this one -- it keeps text inside the box rather than outside it.
 */
.ccard-line--long {
  font-size: var(--s0);
  letter-spacing: -0.005em;
  overflow-wrap: anywhere;
}

/* ------------------------------------- card stack, lighter treatment -- */
/*
 * The dark wash over the kitchen photograph was 85%. It is now 70%, so the
 * photograph reads roughly twice as strongly.
 *
 * That was only possible by lightening two of the three text colours first.
 * The binding constraint was never the photo -- it was the saffron kicker,
 * which fell under 4.5:1 the moment the background lifted. Measured against
 * pure white, which is the worst the photograph can ever be:
 *
 *                    was (15% photo)      now (30% photo)
 *     kicker         #EFA818  4.86        #FFE0A3  4.65
 *     title          #FFFFFF  9.93        #FFFFFF  5.94
 *     sub            #E0C8B6  6.20        #F7EDE2  5.14
 *
 * All three still clear AA. 31% is the hard ceiling for this palette; past
 * that the kicker fails again.
 */


.stack-head .kicker { color: #ffe0a3; }
.stack-head .sec-sub { color: #f7ede2; }

/* The card captions sit on the photographs themselves, which already carry
   their own scrim, so they are untouched. */

/* ----------------------------------------------- inner page headers -- */
/*
 * The About and Contact headers were a flat cream band with type on it. The
 * pack pattern gives them the same ground the rest of the site uses, and a
 * short saffron rule under the kicker ties them to the section headings.
 */
.phead { position: relative; overflow: hidden; }

.phead {
  padding-block: calc(var(--sec) * 0.7) calc(var(--sec) * 0.55);
  min-height: 22rem;
  display: flex;
  align-items: center;
}

/* A short saffron rule under the kicker, the same accent the section
   headings use. */
.phead .kicker { display: inline-block; }
.phead .kicker::after {
  content: "";
  display: block;
  width: 2.5rem;
  height: 3px;
  margin-top: var(--sp2);
  border-radius: var(--r-pill);
  background: var(--saffron);
}

/* ---- the photograph, and the veil that decides how much of it shows ---- */

.phead-art {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.phead-art picture,
.phead-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% 55%;
}

.phead-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Solid until just past where the copy ends, then open. The stops come from
     the measured column: the text runs out at about 60% of the band. */
  background: linear-gradient(
    92deg,
    var(--cream-2) 0%,
    var(--cream-2) 46%,
    rgba(251, 240, 220, .95) 58%,
    rgba(251, 240, 220, .55) 74%,
    rgba(251, 240, 220, .12) 100%
  );
}

/*
 * The left side of the band is flat cream, and the pack pattern cannot help
 * there: the tile is pure white line art, mean alpha 4.7 out of 255, with
 * 0.07% of its pixels more than half opaque. It only ever reads on a dark
 * ground, which is why the card stack can use it and this band cannot.
 *
 * The logomark does read on cream. Set large and low, bleeding off the left
 * edge behind the copy, it gives that half of the band something to be
 * without competing with the heading in front of it.
 */
.phead::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: -6rem;
  bottom: -30%;
  width: 34rem;
  height: 34rem;
  pointer-events: none;
  opacity: .07;
  background: url("../../assets/brand/raechas-logomark.svg") no-repeat center / contain;
}

@media (max-width: 57.99em) {
  /* Behind full-width copy on a phone, so it sits lower and smaller. */
  .phead::before {
    left: -8rem;
    bottom: -45%;
    width: 22rem;
    height: 22rem;
    opacity: .05;
  }
}

.phead-in { position: relative; z-index: 2; width: 100%; }

/*
 * The copy column was centred in the wrap -- 864px sitting at 19% to 81% of
 * the band -- which left the photograph nowhere to be.
 *
 * The fix is NOT to move .phead-in. That element IS the .wrap, so setting
 * margin-inline: 0 on it stops the whole column being centred and the text
 * ends up against the edge of the window instead of lined up with the logo
 * and the nav. It has to stay an 80rem column, centred like every other
 * section, and the CONTENT inside it gets capped instead.
 */
.phead-in { max-width: 80rem; }

@media (min-width: 58em) {
  .phead .crumbs,
  .phead .kicker,
  .phead-title,
  .phead-sub {
    max-width: min(54rem, 64%);
  }
}

@media (max-width: 57.99em) {
  /*
   * The copy runs the full width here, so the picture cannot sit beside it.
   * It drops to a warm tint across the whole band instead -- present, but not
   * competing with the heading.
   */
  .phead { min-height: 0; }
  .phead-veil {
    background: linear-gradient(
      180deg,
      rgba(251, 240, 220, .96) 0%,
      rgba(251, 240, 220, .93) 55%,
      rgba(251, 240, 220, .86) 100%
    );
  }
  .phead-art img { object-position: 50% 50%; }
}

/* --------------------------------------------------------- breadcrumb -- */

.crumbs { margin-block-end: var(--sp5); }

.crumbs ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp2) var(--sp3);
  font-size: var(--s-1);
}

.crumbs li { display: flex; align-items: center; gap: var(--sp3); }

/* The separator is generated, so it never lands in the accessible name and
   is never read out as a word. */
.crumbs li + li::before {
  content: "\203A";
  color: var(--ink-3);
  font-size: 1.1em;
  line-height: 1;
}

.crumbs a {
  color: var(--amber-tx);
  text-decoration-color: rgba(122, 84, 5, .35);
  text-underline-offset: 3px;
}
.crumbs a:hover { color: var(--brick-dk); }

.crumbs [aria-current="page"] {
  color: var(--ink-2);
  font-weight: 600;
}


/* ------------------------------------------------------- brand split -- */
/*
 * Copy left on flat brick, photograph right at full strength.
 *
 * This replaced a full-bleed photograph with the copy laid over it. That
 * arrangement always costs something: the scrim has to be heavy enough to
 * carry the text, which buries the picture, and any change to one side has
 * to be re-checked against the other. Split apart, neither constrains the
 * other -- white on #45150A is 15.4:1 and the saffron kicker 7.5:1, with
 * nothing behind them that can lift the background.
 */
.split {
  background: #45150a;
  display: grid;
}

@media (min-width: 58em) {
  .split {
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
    min-height: clamp(26rem, 38vw, 36rem);
  }
}

.split-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(var(--sp7), 6vw, var(--sp8));
  /* --sp4 is .wrap's own inline padding, so below the split the copy lines up
     with everything else on the page rather than sitting 16px further in. */
  padding-inline: var(--sp4);
}

@media (min-width: 58em) {
  /*
   * Line the left edge up with the logo and the nav rather than with the
   * halfway point of the band.
   *
   * The percentage resolves against this grid cell, which is exactly half the
   * band, so 100% - 40rem + --sp6 gives the same x as .wrap's content edge:
   * (band - 80rem) / 2 + 2rem. Deliberately NOT 100vw -- vw includes the
   * scrollbar, and that alone put this 7px to the right of the logo.
   */
  .split-copy {
    padding-inline: max(var(--sp6), calc(100% - 40rem + var(--sp6)))
                    clamp(var(--sp6), 5vw, var(--sp8));
  }
}

.split-kicker {
  margin: 0 0 var(--sp4);
  font-family: var(--display);
  font-size: var(--s-1);
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--saffron);
}

.split-title {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(2.1rem, 1.2rem + 3.6vw, 4rem);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -.025em;
  color: #fff;
}

.split-body {
  margin-block-start: var(--sp5);
  max-width: 34rem;
  font-size: var(--s1);
  line-height: 1.6;
  color: #e5d7c6;
}

.split-cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp4);
  margin-block-start: var(--sp6);
}

.split-art { position: relative; min-height: 18rem; }

.split-art picture,
.split-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 45% 55%;
}

@media (min-width: 58em) {
  .split-art img { position: absolute; inset: 0; }
}

@media (max-width: 57.99em) {
  /* Photograph first, then the copy: the band reads as a picture with a
     caption rather than as a wall of colour with a photo tacked on. */
  .split { grid-template-areas: "art" "copy"; }
  .split-art { grid-area: art; aspect-ratio: 16 / 10; min-height: 0; }
  .split-copy { grid-area: copy; }
}


/* ============================================================== rhythm == */
/*
 * Section spacing, audited across all three pages.
 *
 * The system underneath is sound: a contained section pads itself by --sec
 * top and bottom, and a full-bleed band has no padding because it IS its own
 * block. Three places broke that, and all three came from a rule written for
 * one page and then reused on another.
 */

/*
 * 1. .stats shipped with padding-bottom: 0.
 *
 * That works on the home page, where the card stack follows and supplies its
 * own top space. On About the next section is a full-bleed band with no
 * padding at all, so the stat cards ran straight into it with nothing
 * between them.
 *
 * The bottom padding comes back, and the one case that relied on its absence
 * is named explicitly rather than left to luck.
 */
.stats { padding-block: var(--sec); }

/*
 * The one page that relied on stats having no bottom padding is the home
 * page, where the card stack follows.
 *
 * Note which side gives way: the stack KEEPS its own top padding and the
 * stats drop theirs. Doing it the other way round -- which is what the first
 * attempt did -- puts the 88px of space outside the brick band instead of
 * inside it, and the "Card by card" kicker ends up flush against the top
 * edge of the colour. Measured at 0px of breathing room before this was
 * corrected.
 */
.stats:has(+ .stack) { padding-block-end: 0; }

/*
 * 2. .phead was 0.7 and 0.55 of --sec, which matched nothing else on the
 * site. Every other contained section is --sec on both sides.
 */
.phead { padding-block: var(--sec); }

/*
 * 3. Two full-bleed bands stacked directly on each other read as one long
 * band -- on About the header's photograph ran straight into the split's
 * brick with a hard seam between them. A band that follows another band
 * separates itself; a band next to a contained section does not need to,
 * because that section's own padding is already the gap.
 */
:is(.split, .artband, .travel) + :is(.split, .artband, .travel),
.phead + :is(.split, .artband, .travel) {
  margin-block-start: var(--sec);
}

/*
 * The marquee ribbon is the deliberate exception. It is a rule across the
 * page rather than a section, so it butts against its neighbours on purpose.
 */
.marq { margin-block: 0; }


/* ================================================== about page content == */
/* Sections written from the client's filled-in About Us questionnaire.      */

/* ------------------------------------------------- founders' story -- */
/*
 * Five equal columns under a hairline was the first attempt and it did not
 * work. A five-track grid forces every beat of a story to be the same length,
 * and these are not: 2009 carries the store, the first sale and the first
 * day's takings, while "Corporate tiffin" is one sentence. Side by side they
 * became five ragged columns of small type that read as a wall.
 *
 * Stacked, each beat is as long as it needs to be, the eye has one path
 * instead of five, and the year is large enough to anchor its entry.
 */
.story2 { padding-block: var(--sec); background: var(--cream-2); }

.story2-in {
  display: grid;
  gap: clamp(var(--sp6), 5vw, var(--sp8));
  align-items: start;
}

@media (min-width: 62em) {
  .story2-in { grid-template-columns: minmax(17rem, 23rem) 1fr; }

  /* The heading stays put while the story moves past it. 7rem clears the
     sticky header, which measures 77px at this width. */
  .story2-head { position: sticky; top: 7rem; }
}

.story2-head .sec-sub { margin-block-start: var(--sp4); }

.story2-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--sp7);
  position: relative;
}

/* One rule down the whole story, behind the markers. It stops short of the
   last entry's foot so the line does not dangle past the final beat. */
.story2-list::before {
  content: "";
  position: absolute;
  left: 7px;
  top: 10px;
  bottom: 5rem;
  width: 2px;
  background: linear-gradient(var(--saffron), var(--line));
}

.story-item { position: relative; padding-block-end: clamp(2.5rem, 4vw, 3.5rem); }
.story-item:last-child { padding-block-end: 0; }

.story-item::before {
  content: "";
  position: absolute;
  top: 10px;
  left: calc(var(--sp7) * -1);
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--brick);
  box-shadow: 0 0 0 4px var(--cream-2);
}

.story-when {
  margin: 0;
  font-family: var(--display);
  font-size: var(--s2);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -.01em;
  color: var(--amber-tx);
}

.story-what {
  margin: var(--sp2) 0 var(--sp3);
  font-family: var(--display);
  font-size: var(--s1);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}

.story-body {
  margin: 0;
  max-width: 44rem;
  font-size: var(--s0);
  line-height: 1.65;
  color: var(--ink-2);
}

/*
 * The first day's takings is the best detail in the whole story, and buried
 * in a paragraph it read as one more clause. Pulled out so it lands.
 */
.story-note {
  display: inline-block;
  margin: var(--sp4) 0 0;
  padding: var(--sp3) var(--sp5);
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 3px solid var(--saffron);
  border-radius: var(--r);
  font-size: var(--s-1);
  line-height: 1.5;
  color: var(--ink);
}

@media (max-width: 61.99em) {
  .story2-list { padding-inline-start: var(--sp6); }
  .story-item::before { left: calc(var(--sp6) * -1); }
  .story2-list::before { bottom: 4rem; }
}

/* ------------------------------------------------------------- craft -- */

.craft { padding-block: var(--sec); }

.craft-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: center;
}

@media (min-width: 58em) {
  .craft-in { grid-template-columns: 1fr 1fr; }
  /* Picture first in reading order on the left, copy on the right, without
     moving either in the markup. */
  .craft-copy { order: 2; }
  .craft-art { order: 1; }
}

.craft-lead {
  font-size: var(--s1);
  line-height: 1.55;
  color: var(--ink);
}

.craft-copy p { color: var(--ink-2); line-height: 1.6; }

.craft-quote {
  margin: var(--sp6) 0 0;
  padding: var(--sp5) 0 var(--sp5) var(--sp5);
  border-inline-start: 3px solid var(--saffron);
}
.craft-quote p {
  margin: 0;
  font-family: var(--display);
  font-size: var(--s2);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--ink);
}
.craft-quote cite {
  display: block;
  margin-block-start: var(--sp3);
  font-style: normal;
  font-size: var(--s-1);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.craft-roles {
  list-style: none;
  margin: var(--sp6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp4);
}
.craft-roles li {
  display: grid;
  gap: var(--sp1);
  padding-block-end: var(--sp4);
  border-bottom: 1px dashed var(--line);
}
.craft-roles b { font-family: var(--display); color: var(--ink); }
.craft-roles span { color: var(--ink-2); }

.craft-art picture, .craft-art img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}

/* ---------------------------------------------------------- promises -- */

.promises { padding-block: var(--sec); background: var(--cream-2); }

.promise-row {
  list-style: none;
  margin: var(--sp7) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp5);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.promise {
  padding: var(--sp6);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.promise-ico {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  margin-block-end: var(--sp4);
  border-radius: var(--r-pill);
  background: var(--cream-2);
  color: var(--brick);
}
.promise-ico svg { width: 23px; height: 23px; }
.promise h3 {
  margin: 0 0 var(--sp3);
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}
.promise p { margin: 0; line-height: 1.6; color: var(--ink-2); }

/* ------------------------------------------------------------ vision -- */

.vision { padding-block: var(--sec); }

.vision-in {
  display: grid;
  gap: clamp(var(--sp6), 4vw, var(--sp8));
  align-items: start;
}

@media (min-width: 58em) {
  .vision-in { grid-template-columns: minmax(18rem, 26rem) 1fr; }
}

.vision-copy p { color: var(--ink-2); line-height: 1.6; }

.vision-note {
  margin-block-start: var(--sp5);
  font-size: var(--s-1);
  color: var(--ink-3);
}

.vision-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp4);
}
.vision-list li {
  display: grid;
  gap: var(--sp2);
  padding: var(--sp5) var(--sp6);
  background: var(--cream-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.vision-list b {
  font-family: var(--display);
  font-size: var(--s1);
  color: var(--ink);
}
.vision-list span { color: var(--ink-2); line-height: 1.55; }


/* --------------------------------------------- travel band, Gujarati -- */
/*
 * The brand's own tagline, set in Gujarati under the English headline --
 * the same line that runs on the yellow brush stroke in the client's banner
 * artwork.
 *
 * lang="gu" is on the element, and fonts.css already carries Noto Sans
 * Gujarati with a U+0A80-0AFF range, so the glyphs come from the real face
 * rather than whatever the operating system would substitute.
 *
 * Saffron on the travel scrim: --saffron on rgba(12,7,3,.92) is the same
 * pairing the kicker above it already uses.
 */
.travel-guj {
  margin-block-start: var(--sp4);
  font-family: "Noto Sans Gujarati", var(--body);
  font-size: clamp(1.15rem, 0.95rem + 0.8vw, 1.6rem);
  font-weight: 600;
  line-height: 1.7;
  color: var(--saffron);
}
