/* ===========================================================
   DS SATVA — Dil Se Pure
   Light palette, taken from the brand sticker (Label_31.08.26):
   the label is cream-led with gold and green as accents, and so is this.
   cream #fcedde (oddbox) · gold #bf8a17 · sage #55714e · band green #3c5128
   =========================================================== */

:root{
  /* warm neutrals, lightest to deepest.
     --cream is oddbox.co.uk's cream, read off their live page 5 Sep 2026
     (their --color-bg-secondary-minimal, the ground under .boxes and
     .curated-posts-section). They alternate it with plain white; we alternate
     it with --ivory, which is the same hue taken lighter so the pale band does
     not read cold beside it. */
  --ivory:#f4f3e7;   /* sweetgreen's off-white, 10 Sep 2026 */
  --cream:#e8dcc6;   /* sweetgreen's sand, 10 Sep 2026 (was oddbox #fcedde) */
  --sand:#efe7d5;    /* a lighter step of the new sand */
  --oat:#ede2cc;
  --clay:#e0d2b3;
  --line:#e8ddc6;
  --line-soft:#f0e8d7;

  /* greens — used as ink and accent, not as ground */
  --ink:#1e2c16;
  --green:#2c4220;
  --band:#3c5128;
  --sage:#4e6848;    /* darkened for the deeper grounds - see the note below */
  --sage-soft:#64775d;   /* 4.84:1 on white. On a --sage-mist ground use --sage. */

  /* gold */
  --gold:#bf8a17;
  --gold-lt:#dfb459;
  --gold-pale:#f1ddab;
  --gold-dk:#7f5a0f;  /* darkened with --sage, same reason */
  --gold-grad:linear-gradient(160deg,#f7dc96 0%,#e0a51a 42%,#a8760f 100%);

  /* spice accents — one per masala, taken off the powders in the label photo */
  --turmeric:#e8a713;
  --himalayan:#d98a8a;   /* unrefined pink salt */
  --chilli:#c3341a;
  --coriander:#93a04b;
  --kala:#6f5433;
  --garlic:#cf5a2e;
  --groundnut:#d98f2f;
  --javas:#a99444;
  --karala:#7d8b56;
  --leafgreen:#6f9440;

  /* ---- alternating section grounds, after sweetgreen.com --------------------
     Sampled from the live page 4 Sep 2026: their cream is #f4f3e7 and their sage
     ground is #d8e5d6, with #00473c as ink. What makes it cohere is that the cream
     is not warm — it carries the same green-yellow cast as the sage, so the two
     read as one family rather than as two decisions.
     RESAMPLED 10 Sep 2026 and now used as-is, at the owner's asking ("this colours
     are very nice, incorporate these colours in my site"): sand #e8dcc6, mint
     #d8e5d6, off-white #f4f3e7, read off their live page. Note this REVERSES the
     5 Sep decision to use oddbox.co.uk's warmer cream (#fcedde) - the two are not
     compatible, one is a warm peach and the other a green-cast khaki, and the
     newer instruction wins. Say the word and #fcedde comes back.

     What made this more than a find-and-replace: sweetgreen's grounds are DARKER
     than the ones they replaced, and sweetgreen can afford that because their body
     ink is #0e150e, near-black. Ours is a mid-green. Measured before changing
     anything, --sage (#55714e) fell to 4.01:1 on the new sand and 4.17:1 on the
     new mint, and --gold-dk to 4.04 and 4.20 - all four below AA. Both were
     darkened by the smallest amount that clears 4.5:1 on the DARKER of the two
     new grounds, holding their hue and saturation: --sage #55714e -> #4e6848
     (4.56:1 on sand) and --gold-dk #8a6210 -> #7f5a0f (4.59:1). Nothing else in
     the palette had to move. */
  --cream-sg:#e8dcc6;   /* sweetgreen's sand band */
  --sage-mist:#d8e5d6;   /* sweetgreen's mint band */
  --sage-mist-2:#c9dbc5; /* one step deeper than the mint */

  /* pale spice grounds — bright, but still light */
  --tint-cream:#fdf3e6;
  --tint-turmeric:#fdf4dc;
  --tint-turmeric-2:#f9e7bf;
  --tint-chilli:#fdefe7;
  --tint-cardamom:#eff5e4;

  /* warm shadows — never grey on a cream ground */
  --sh-sm:0 2px 8px -4px rgba(104,80,34,.20);
  --sh-md:0 16px 34px -18px rgba(104,80,34,.34);
  --sh-lg:0 34px 70px -34px rgba(104,80,34,.42);

  --ease:cubic-bezier(.66,0,.24,1);
  --maxw:1280px;
  --pad:clamp(18px,4vw,52px);
}

*,*::before,*::after{box-sizing:border-box;}
/* The [hidden] attribute is a UA display:none, so ANY author display: rule beats
   it and the "hidden" element keeps rendering. This has now caught three
   elements here (.etrack, .recgrid, .rangepick), so it is settled once, globally,
   rather than patched per selector each time something new is toggled. */
[hidden]{display:none!important;}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;}
body{
  margin:0;background:var(--cream);color:var(--green);
  font-family:"Jost",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  font-size:clamp(15.5px,.55vw + 14px,17.5px);line-height:1.7;font-weight:400;
  /* clip, NOT hidden. overflow-x:hidden forces the other axis to compute as
     `auto`, which turns body into its own scroll container - and that broke
     scroll snapping outright, because the sections then snapped to body while
     the page actually scrolls html. `clip` stops sideways scroll without
     creating a scroll container. */
  overflow-x:clip;
}
img{max-width:100%;display:block;}
a{color:inherit;text-decoration:none;}
button{font:inherit;}
::selection{background:var(--gold-pale);color:var(--ink);}

/* ---------- type ---------- */
.dsp{
  font-family:"Fraunces",Georgia,"Times New Roman",serif;
  font-variation-settings:"SOFT" 45,"WONK" 1;
  font-weight:600;line-height:1.03;letter-spacing:-.021em;
  font-optical-sizing:auto;color:var(--ink);
}
.dv{font-family:"Tiro Devanagari Hindi",serif;font-weight:400;letter-spacing:normal;}
.kick{
  font-size:.7rem;letter-spacing:.3em;text-transform:uppercase;font-weight:600;
  color:var(--gold-dk);margin:0 0 14px;
}
.kick.dv{letter-spacing:.06em;font-weight:400;font-size:.85rem;text-transform:none;color:var(--sage);}
.goldtext{
  background:var(--gold-grad);-webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:var(--gold-dk);
}
h2.big{font-size:clamp(2.1rem,4.6vw,3.7rem);margin:0 0 16px;}
.lede{font-size:clamp(1rem,.5vw + .9rem,1.16rem);color:var(--sage);max-width:60ch;}

/* the sticker's gold double-hairline frame */
.frame{position:relative;}
.frame::before,.frame::after{
  content:"";position:absolute;pointer-events:none;border:1px solid rgba(191,138,23,.42);
}
.frame::before{inset:0;border-radius:inherit;}
.frame::after{inset:7px;border-radius:calc(max(0px,var(--r,18px) - 7px));opacity:.5;}

/* the sticker's gold rule + fleuron, as a section ornament */
.orn{
  height:46px;background:url("../assets/rule-gold.png") center/auto 100% no-repeat;
  margin:0 auto 18px;opacity:.9;
}
.orn.sm{height:34px;}

.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--pad);}
section{position:relative;}

/* barely-there paper grain, multiplied into the cream */
.grain::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.32;mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23n)' opacity='.28'/%3E%3C/svg%3E");
}
.leaf{
  position:absolute;pointer-events:none;
  -webkit-mask-image:url("../assets/leaf-mask.png");mask-image:url("../assets/leaf-mask.png");
  -webkit-mask-size:cover;mask-size:cover;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}

/* ===================== top line ===================== */
.topline{
  background:var(--sand);color:var(--sage);font-size:.76rem;letter-spacing:.05em;
  border-bottom:1px solid var(--line);
}
.topline .wrap{display:flex;gap:18px;align-items:center;justify-content:space-between;
  min-height:38px;padding-block:6px;}
/* the rotating announcement: messages stack in one grid cell so the bar never
   changes height as they swap, and only the active one is readable */
.topline .left{display:grid;align-items:center;min-width:0;}
.topline .tmsg{
  grid-area:1 / 1;opacity:0;visibility:hidden;transform:translateY(4px);
  transition:opacity .5s var(--ease),transform .5s var(--ease),visibility .5s;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.topline .tmsg.is-on{opacity:1;visibility:visible;transform:none;}
.topline .sep{color:var(--gold);opacity:.75;margin-inline:9px;}
.topline a{white-space:nowrap;color:var(--gold-dk);font-weight:500;}
.topline a:hover{color:var(--ink);}
@media (max-width:620px){ .topline .left .sep{display:none;} .topline{font-size:.72rem;} }

/* ===================== nav ===================== */
.nav{
  position:sticky;top:0;z-index:60;background:rgba(254,247,240,.88);
  backdrop-filter:blur(16px) saturate(150%);-webkit-backdrop-filter:blur(16px) saturate(150%);
  border-bottom:1px solid var(--line);
}
.nav .wrap{display:flex;align-items:center;gap:26px;min-height:80px;}
/* The stacked lockup here too, 18 Sep 2026, at the owner's word ("I want the
   logo to be changed in the whole site"). 50px was the horizontal file's height;
   the stacked one is square, so at 50px it would be a 53px stamp.
   76px, and the number is measured rather than chosen: the bar's height is set
   by its own padding and the WhatsApp button, not by the logo, so there is free
   headroom above 50. The bar stays exactly 81px deep at 62, 68, 72 AND 76, and
   only starts growing at 82 (83px) and 88 (89px). 76 is therefore the largest
   this mark can be for nothing - it buys a 23% bigger logo than 62px without
   moving the header, the full-screen blocks' nav clearance or anything below. */
.nav .logo{height:76px;width:auto;flex:none;}
.nav ul{display:flex;gap:clamp(14px,1.9vw,30px);list-style:none;margin:0;padding:0;margin-left:auto;}
.nav ul a{
  color:var(--green);font-size:.82rem;letter-spacing:.14em;text-transform:uppercase;
  font-weight:500;padding:8px 0;position:relative;
  white-space:nowrap;   /* "OUR RANGE" must never break across two lines */
}
.nav ul a::after{
  content:"";position:absolute;left:0;right:100%;bottom:2px;height:1.5px;background:var(--gold);
  transition:right .4s var(--ease);
}
.nav ul a:hover{color:var(--gold-dk);}
.nav ul a:hover::after{right:0;}
/* ---- Our Range dropdown (after curiospice.com) ----
   Opens on hover and, for keyboard users, on :focus-within - so it needs no JS
   to open, and the parent anchor still works on its own. */
.nav .hasmenu{position:relative;}
/* This link is formatted exactly like its neighbours - plain inline - so its
   text sits on the SAME BASELINE as they do. It used to be inline-flex, which
   centres the text inside a box as tall as the <li> rather than putting it on
   the shared baseline: measured on the glyphs themselves, "Our Range" sat at
   37.4-56.4 while every other item sat at 33.9-52.9, i.e. 3.5px low.
   The caret is inline-block and vertical-align:middle, which positions it
   against the text without changing the line box. */
.nav .hasmenu > a{display:inline;}
.nav .caret{
  display:inline-block;vertical-align:middle;margin-left:5px;
  position:relative;top:-1px;
  transition:transform .3s var(--ease);
}
.nav .caret{width:13px;height:13px;}
.nav .hasmenu:hover .caret,.nav .hasmenu:focus-within .caret{transform:rotate(180deg);}

.nav .submenu{
  display:block;margin-left:0;position:absolute;top:calc(100% + 14px);left:50%;transform:translate(-50%,-6px);
  min-width:212px;list-style:none;margin:0;padding:8px;
  background:rgba(254,247,240,.98);backdrop-filter:blur(14px);
  border:1px solid var(--line);border-radius:14px;box-shadow:var(--sh-md);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .28s var(--ease),transform .28s var(--ease),visibility .28s;
}
/* a small bridge so the pointer can cross the gap without the menu closing.
   It spans the item, not 150% of it - at 150% and centred it hung 85px past each
   edge and pushed the whole page 67px wide once the nav stacked on mobile. */
.nav .hasmenu::after{
  content:"";position:absolute;top:100%;left:0;right:0;height:16px;
}
.nav .hasmenu:hover .submenu,.nav .hasmenu:focus-within .submenu{
  opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0);
}
.nav .submenu li{margin:0;}
.nav .submenu a{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:9px 12px;border-radius:9px;font-size:.78rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--green);transition:background .2s,color .2s;
}
.nav .submenu a:hover{background:var(--sand);color:var(--gold-dk);}
.nav .submenu a::after{display:none;}          /* the nav's underline effect, not wanted here */
.nav .submenu .mn{
  font-size:.7rem;letter-spacing:.04em;color:var(--gold-dk);opacity:.75;
  font-variant-numeric:tabular-nums;
}
.nav .submenu .all a{border-bottom:1px solid var(--line);border-radius:9px 9px 0 0;margin-bottom:4px;}

.btn-gold{
  display:inline-flex;align-items:center;gap:9px;background:var(--gold-grad);color:#2a1f08;
  font-weight:600;font-size:.86rem;letter-spacing:.05em;padding:12px 22px;border-radius:999px;
  border:0;cursor:pointer;box-shadow:0 10px 24px -12px rgba(160,116,20,.85);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),filter .3s;
}
.btn-gold:hover{transform:translateY(-2px);box-shadow:0 16px 32px -14px rgba(160,116,20,.95);filter:brightness(1.06);}
.btn-ghost{
  display:inline-flex;align-items:center;gap:9px;border:1.4px solid var(--clay);color:var(--green);
  background:rgba(255,255,255,.5);
  padding:12px 22px;border-radius:999px;font-size:.86rem;letter-spacing:.05em;font-weight:500;
  transition:background .3s,border-color .3s,color .3s,transform .3s var(--ease);
}
.btn-ghost:hover{background:#fff;border-color:var(--gold);color:var(--gold-dk);transform:translateY(-2px);}
.navcta{margin-left:6px;flex:none;}
.burger{display:none;margin-left:auto;background:none;border:1px solid var(--clay);
  border-radius:10px;color:var(--green);padding:9px 11px;cursor:pointer;}
.burger span{display:block;width:19px;height:1.7px;background:currentColor;margin:4px 0;transition:.3s;}

/* ===================== hero slider ===================== */
.hero{position:relative;overflow:hidden;background:var(--cream);
  height:clamp(580px,88vh,880px);color:var(--green);}
.slides{position:absolute;inset:0;}
.slide{position:absolute;inset:0;overflow:hidden;visibility:hidden;
  transform:translate3d(100%,0,0);will-change:transform;}
.slide.is-active{visibility:visible;transform:translate3d(0,0,0);z-index:2;}
.slide.is-leaving{visibility:visible;z-index:1;}
.hero.anim .slide{transition:transform 1s var(--ease),visibility 1s step-end;}
.hero.anim .slide.is-active{transition:transform 1s var(--ease),visibility 0s;}

/* the gold frame from the label, laid over the whole hero */
.hero::before,.hero::after{
  content:"";position:absolute;pointer-events:none;z-index:5;border-radius:6px;
}
.hero::before{inset:16px;border:1px solid rgba(191,138,23,.45);}
.hero::after{inset:23px;border:1px solid rgba(191,138,23,.24);}

.field{position:absolute;inset:0;background:var(--sf,linear-gradient(155deg,#fef7f0,#f0e7d3));}
.field .leaf{inset:-8% -12% auto auto;width:min(58vw,700px);height:min(58vw,700px);
  background:rgba(191,138,23,.14);transform:rotate(8deg);}
/* soft cream scrim so copy stays readable where the spices rise behind it */
.vig{position:absolute;inset:0;background:
  radial-gradient(118% 128% at 0% 50%,rgba(254,247,240,.97) 0%,rgba(254,247,240,0) 100%),
  /* every slide dissolves into the same ivory at its foot (rgb of --ivory,
     kept literal because these need alpha ramps; update both if --ivory moves), so the hero meets
     the next section with no tonal step whichever colour field is showing */
  linear-gradient(to bottom,rgba(254,247,240,0) 82%,rgba(254,247,240,1) 100%);}

/* the spice band, floating in the cream rather than cut off against it */
.band{position:absolute;left:0;right:0;bottom:0;height:clamp(210px,42%,350px);overflow:hidden;
  mask-image:linear-gradient(to bottom,transparent 0%,#000 34%,#000 84%,transparent 100%);
  -webkit-mask-image:linear-gradient(to bottom,transparent 0%,#000 34%,#000 84%,transparent 100%);}
.band img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center bottom;
  transform:scale(1.06);will-change:transform;}
.slide.is-active .band img{animation:kb 11s ease-out forwards;}
@keyframes kb{from{transform:scale(1.02) translate3d(0,0,0);}to{transform:scale(1.13) translate3d(-1.6%,0,0);}}

/* hero copy */
.slide .wrap{position:relative;z-index:3;height:100%;display:flex;align-items:center;}
.copy{max-width:min(600px,55%);padding-block:clamp(24px,7vh,80px);}
.copy h1{font-size:clamp(2.15rem,4.3vw,4rem);margin:0 0 18px;color:var(--ink);}
.copy h1 em{font-style:normal;display:block;}
.copy p{color:var(--sage);font-size:clamp(1rem,.55vw + .9rem,1.18rem);max-width:46ch;margin:0 0 30px;}
.cta-row{display:flex;gap:12px;flex-wrap:wrap;}
.copy>*{opacity:0;transform:translateY(22px);}
.slide.is-active .copy>*{animation:rise .95s var(--ease) forwards;}
.slide.is-active .copy>*:nth-child(1){animation-delay:.16s;}
.slide.is-active .copy>*:nth-child(2){animation-delay:.26s;}
.slide.is-active .copy>*:nth-child(3){animation-delay:.36s;}
.slide.is-active .copy>*:nth-child(4){animation-delay:.46s;}
@keyframes rise{to{opacity:1;transform:none;}}

/* floating product card */
.floatcard{
  position:absolute;right:clamp(28px,5.5vw,92px);top:50%;transform:translateY(-42%);z-index:4;
  width:clamp(180px,20vw,246px);background:#fff;border-radius:20px;--r:20px;
  padding:14px 14px 16px;box-shadow:var(--sh-lg);opacity:0;
}
.slide.is-active .floatcard{animation:pop 1s var(--ease) .5s forwards;}
@keyframes pop{from{opacity:0;transform:translateY(-32%) scale(.94);}to{opacity:1;transform:translateY(-42%) scale(1);}}
.floatcard img{border-radius:12px;aspect-ratio:1;object-fit:cover;}
.floatcard .fc-en{font-size:1.02rem;color:var(--band);margin:11px 0 1px;line-height:1.3;font-weight:600;}
.floatcard .fc-dv{font-size:.86rem;color:var(--sage-soft);line-height:1.3;}

/* controls */
/* controls sit together at the foot of the hero. Edge-centred arrows collided
   with the headline at any width where the wrap is not comfortably inset. */
.arrow{
  position:absolute;bottom:30px;top:auto;z-index:6;width:46px;height:46px;
  border-radius:50%;border:1px solid var(--clay);background:rgba(255,255,255,.86);
  backdrop-filter:blur(6px);color:var(--green);display:grid;place-items:center;cursor:pointer;
  box-shadow:var(--sh-sm);
  transition:background .3s,border-color .3s,color .3s,transform .3s var(--ease);
}
.arrow:hover{background:var(--gold);color:#fff;border-color:var(--gold);transform:scale(1.08);}
.arrow.prev{left:calc(var(--pad) + 8px);}
.arrow.next{left:calc(var(--pad) + 62px);right:auto;}
.arrow svg{width:16px;height:16px;}

.dots{position:absolute;left:calc(var(--pad) + 126px);bottom:48px;z-index:6;display:flex;gap:9px;align-items:center;}
.dots button{width:10px;height:10px;padding:0;border-radius:50%;border:1px solid rgba(62,48,20,.28);
  background:rgba(255,255,255,.8);cursor:pointer;transition:.35s var(--ease);
  box-shadow:0 1px 3px rgba(90,70,35,.18);}
.dots button[aria-current="true"]{background:var(--gold);border-color:var(--gold);width:32px;border-radius:999px;}

/* progress sits at the very top of the hero, clear of the gold frame */
.progress{position:absolute;left:0;bottom:0;height:2px;width:100%;z-index:7;background:transparent;}
.progress i{display:block;height:100%;width:0;border-radius:0 2px 2px 0;
  background:linear-gradient(90deg,rgba(191,138,23,.22) 0%,#dfb459 55%,#bf8a17 100%);}

/* "next up" preview card */
.nextup{
  position:absolute;right:32px;bottom:32px;z-index:6;display:flex;align-items:stretch;
  background:rgba(255,255,255,.9);backdrop-filter:blur(10px);
  border:1px solid var(--clay);border-radius:14px;overflow:hidden;cursor:pointer;
  max-width:min(74vw,320px);box-shadow:var(--sh-md);
  transition:background .3s,border-color .3s,transform .3s var(--ease);
}
.nextup:hover{background:#fff;border-color:var(--gold);transform:translateY(-2px);}
.nextup img{width:68px;height:68px;object-fit:cover;flex:none;}
.nextup .nx{padding:11px 17px 11px 14px;display:flex;flex-direction:column;justify-content:center;min-width:0;}
.nextup .nx small{font-size:.63rem;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-dk);}
.nextup .nx b{font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;font-weight:500;
  font-size:.96rem;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* the eight masalas as a colour spectrum — a rule made of the range itself */
.spectrum{display:flex;height:6px;width:100%;position:relative;z-index:4;}
.spectrum i{flex:1;transition:flex .5s var(--ease);}
.spectrum:hover i{flex:.85;}
.spectrum i:hover{flex:2.2;}

/* ===================== pillars ===================== */
/* ===================== why choose ===================== */
/* Three columns, big monoline illustrations, one CTA underneath. The old
   four-icon .pillars strip lived here and did the same job at a tenth the
   size; this is that idea given room. */
/* One block, one screen - the same treatment the shelves got, for the same
   reason: the owner wants this to arrive as a whole thing, not to be met
   half-scrolled. min-height so that at two columns or stacked on a phone it can
   still grow past a screen. The padding-top floor holds the kick line clear of
   the sticky nav once the content is centred (see the matching note on .cats). */
.reasons{
  background:var(--ivory);padding:clamp(38px,5vw,66px) 0;
  padding-top:max(96px,clamp(38px,5vw,66px));
  border-bottom:1px solid var(--line);
  min-height:100vh;
  min-height:100svh;
  display:flex;flex-direction:column;justify-content:center;
}
/* width:100% because .wrap carries margin:0 auto, and an auto cross-axis margin
   switches off a flex item's stretch - the trap that narrowed the shelf cards. */
.reasons .wrap{width:100%;}
.reasons .rhead{text-align:center;margin:0 auto clamp(34px,4.6vw,58px);}
.reasons .rhead h2{margin:0 0 16px;color:var(--ink);}
.reasons .rhead .rule{margin-inline:auto;}

/* four across now the standards circle has joined; 2 then 1 as it narrows */
.rgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(20px,3vw,48px);
  max-width:1280px;margin-inline:auto;}
/* This was 1080, which overflowed the page by 54px between 1081px and ~1224px:
   a CIRCLE's minimum width was its longest word ("Nutrient-preserved") plus 15%
   padding each side, about 254px, and `1fr` cannot shrink a track below its
   min-content. It was raised to 1240 to stop the sideways scroll; with the arch
   shape that minimum fell to about 150px, so four across fits comfortably again
   and the switch is back down at 1000px. Verified by measurement at 1081px and
   1024px rather than by eye - that is how the original bug got in. */
@media (max-width:1000px){ .rgrid{grid-template-columns:repeat(2,1fr);} }
/* Two-across band only (821-1000px). The width cap is tuned for four-across, and
   at two-across that same 23vw left a 210px arch adrift in a 400px track - sparse
   rather than airy. Written as `.reasons .reason`, and banded off the one-column
   rule below, because the base .reason rule comes LATER in this file: a media
   query adds no specificity, so an equal-specificity override placed up here
   would simply lose. Do not widen this band to cover <=820px - the one-per-row
   rule at the foot of the file owns that. */
@media (max-width:1000px) and (min-width:821px){
  .reasons .reason{max-width:clamp(240px,34vw,320px);}
}
@media (max-width:560px){ .rgrid{grid-template-columns:1fr;} }

/* Three colours the owner picked off uk.huel.com - the 1st, 3rd and 4th tiles
   in their "Food to fuel your goals" row. Sampled from the tile images
   themselves rather than guessed: the sweep behind each product is a gradient,
   so these are the median of its LIT half, which is the colour the eye reads.
     blush     #ce9f8b   (their "More protein" tile)
     pistachio #d2d9b8   (their "Manage weight" tile)
     caramel   #ddaf7b   (their "Save time" tile)
   Each column is now a panel in one of them, as Huel does it. The line art
   takes a darkened version of its own panel colour so it reads ON the panel
   rather than floating over it. */
.reason{
  text-align:center;--rc:var(--sage);
  background:var(--panel,var(--cream));
  /* AN ARCH, not a circle (16 Sep 2026, the owner: "try this in some other shape
     so that i see a uniqueness when compare to the other wesite flow").
     Four arches side by side read as a colonnade - a shopfront, or the niches in
     a temple wall - which is a shape a pastel-circle row or a rounded-card row
     is not, and it belongs to this brand rather than to a template.
     It is also the reason the block can fit a screen at all. A circle can only
     hold type inside its INSCRIBED square, about 70% of its width, so it needed
     15% padding on every side and its longest word ("Nutrient-preserved") pushed
     its minimum width out to ~254px - which is what made four of them overflow
     the page between 1081px and 1224px (see the .rgrid note). An arch is
     straight-sided where the words are, so the padding is a couple of dozen
     pixels rather than a seventh of the width, and the same copy sits in a
     ~150px minimum. Narrower minimum, four across at far more widths, and no
     wasted corners. */
  --r:22px;                          /* the flat foot of the arch */
  border-radius:50% 50% var(--r) var(--r) / 44% 44% var(--r) var(--r);
  /* Height in svh, like the shelf pictures, because this block is also asked to
     fill the screen - and the arch's proportion should answer to the window
     rather than to how many columns happen to fit.
     40svh, capped at 380: the group of icon + title + copy is about 194px, so a
     430px arch left a third of its foot as dead colour. The section still fills
     the screen - it is min-height:100svh with its content centred, so the slack
     goes into the ivory above and below the colonnade, where it reads as air,
     instead of into the bottom of four coloured panels, where it read as a
     mistake. */
  height:clamp(300px,40vh,380px);
  height:clamp(300px,40svh,380px);
  width:100%;max-width:clamp(210px,23vw,300px);margin-inline:auto;
  /* px, not %: see above. Slightly deeper at the top so the centred group of
     icon + title + copy sits in the body of the arch and not up in its dome. */
  padding:clamp(30px,3.2vw,46px) clamp(16px,1.5vw,26px) clamp(20px,2vw,30px);
  display:flex;flex-direction:column;align-items:center;
  /* centred, and the group stays together. space-evenly was tried first, to use
     up the depth of the taller arch, and it pushed the copy a clear 50px away
     from the title it belongs to - three separate things floating in a panel
     rather than one statement. The arch came down in height instead. */
  justify-content:center;
  transition:transform .55s var(--ease),box-shadow .55s var(--ease);
}
.reason:hover{transform:translateY(-4px);box-shadow:0 24px 44px -28px rgba(60,45,20,.5);}
/* Brightened 10 Sep 2026. Mixing the Huel colours with white lightened them but
   also DRAINED them - white mix cuts saturation as well as depth, which is why
   they read dull. These keep each hue and its lightness and put the chroma back
   instead: blush S41->62, pistachio S30->50, caramel S59->76.
     #ce9f8b (Huel) -> #e4cabf (dull) -> #edc2b1
     #d2d9b8 (Huel) -> #e6ead8 (dull) -> #e3ecc6
     #ddaf7b (Huel) -> #ecd3b6 (dull) -> #f3cfa5
   The line art keeps the DARK version of each colour - that contrast is what
   stops a light panel going flat. */
.reason:nth-child(1){--panel:#edc2b1;--rc:#5c3323;}
.reason:nth-child(2){--panel:#e3ecc6;--rc:#41501f;}
.reason:nth-child(3){--panel:#f3cfa5;--rc:#69411a;}
/* The fourth is sleepyowl.co's blue, sampled off their page (#d7e5fa, the
   colour under most of that site) and brought into this family: same hue, but
   the saturation and lightness of the other three, which are S50-76 / L80-85
   against their S78 / L91.
   Their blush (#e9d0cf) was the other candidate and is the weaker one HERE -
   it sits at hue 2 against our blush at hue 17, fifteen degrees apart, so the
   two would read as one colour twice. The blue is 145 degrees from its nearest
   neighbour and is the only cool note in the row, which is what makes four
   circles read as four rather than three-and-a-spare. */
.reason:nth-child(4){--panel:#bed1ef;--rc:#26405f;}

.reason .art{
  /* The drawing occupies only the middle of its 200x200 viewBox, so the box is
     always bigger than the ink in it. On the old flat ground that was invisible;
     inside a panel it read as a hole between the icon and the heading. The box
     is smaller now and the slack is cropped rather than padded. */
  /* One custom property, used by BOTH the wrapper and the svg, so the two can
     never be sized by different rules. The svg took width:100% before, which
     was quietly overridden by a leftover short-window rule further down the
     file (see the max-height:1000px block): the wrapper stayed 55px while the
     drawing laid out at 81px and, with overflow:visible, painted 26px of itself
     straight down over the heading. That is the overlap the owner saw, and it
     only ever appeared on a short window, which is why it survived this long. */
  --icon:clamp(40px,4.4vw,62px);
  width:var(--icon);height:var(--icon);
  /* room between the drawing and the title, so they read as two things */
  margin:0 auto clamp(9px,1.1vw,15px);color:var(--rc);
  transition:transform .55s var(--ease);
}
/* overflow:hidden, not visible: every one of the four drawings sits inside its
   0-200 viewBox (widest is 16.3-184.4), so this clips nothing today - it is
   here so a future icon drawn past the edge cannot land on the type again. */
.reason .art svg{width:var(--icon);height:var(--icon);display:block;overflow:hidden;}
.reason:hover .art{transform:translateY(-6px);}

.reason h3{
  margin:0 0 10px;font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(.82rem,.92vw,.98rem);font-weight:600;line-height:1.16;color:var(--ink);
}
/* --ink at 82%, not --sage: sage was chosen for an ivory ground and is far
   too light on these panels. Measured after the change. */
/* min-height is five lines of this type (line-height 1.4, so 5 x 1.4em = 7em).
   The four circles are the same size and their content is centred in them, so a
   body of three lines instead of five centres its whole block lower - which is
   why "Uncompromising standards" sat 15px below its neighbours. Reserving the
   tallest body's height on all four puts every icon and every title on one line
   across the row. It is a floor, not a fixed height: longer copy still grows. */
.reason p{margin:4px auto 0;font-size:clamp(.66rem,.7vw,.76rem);line-height:1.4;
  color:rgba(30,44,22,.82);max-width:21ch;min-height:7em;}

.rcta{text-align:center;margin-top:clamp(34px,4.4vw,56px);}

@media (max-width:820px){
  .rgrid{grid-template-columns:1fr;gap:clamp(30px,5vw,44px);}
  .reason p{max-width:34ch;}
  /* One per row here, so the arch can have the whole column rather than sitting
     at the narrow cap meant for four-across. (This note used to explain a bug
     the shape no longer has: as a circle with aspect-ratio:1/1 it silently
     stretched into a 230x287 oval whenever the copy was taller than the square,
     and unevenly, since each panel carries a different amount. The arch is
     given an explicit height, so there is nothing left to give way.) */
  .reason{max-width:330px;}
}
@media (prefers-reduced-motion:reduce){ .reason:hover .art{transform:none;} }

/* ===================== the shelf slider ===================== */
/* After jainfarmfresh.com/products/spices, which the owner sent on 6 Sep 2026.
   Four things are taken from it and nothing else: a flat pale ground rather than
   a photograph behind the words, a two-line serif headline in the deep brand
   green, one paragraph at reading width, and an outlined pill whose arrow sits
   in a filled circle at its right end.
   The ground is per-slide (--sg) and pulled toward that shelf's own colour, the
   way the hero slides already carry data-tint - so the turmeric slide warms and
   the seed slide cools, and the five do not read as one repeated template. */

/* As the hero (see index.html), this fills the screen: the section is the
   viewport less the sticky header, the bar of tabs and arrows sits at its foot,
   and the stage takes whatever is left. svh, not vh, so a phone's collapsing
   address bar does not leave a gap. */
.shelves{position:relative;overflow:hidden;border-block:1px solid var(--line);}
.shelves.is-hero{
  min-height:calc(100svh - 81px);
  display:flex;flex-direction:column;
}
.shelves.is-hero .shstage{flex:1 1 auto;min-height:0;}
.shelves.is-hero .shbar{flex:none;}

/* every slide occupies the same grid cell, so the stage is exactly as tall as
   the tallest slide and nothing has to be measured in JS */
.shstage{display:grid;}
.shelf{
  grid-area:1/1;position:relative;isolation:isolate;
  /* the hero stage now sets the height (.shelves.is-hero above); this floor is
     what the block falls back to anywhere it is NOT the hero */
  min-height:clamp(320px,min(52vw,62vh),610px);display:flex;align-items:center;
  opacity:0;visibility:hidden;
  /* .7s, up from .62s (20 Sep 2026): kovaikisan's slide animations all run at
     0.7s ease-in-out, and with the interval down to 4s the crossfade wants to be
     a touch slower rather than faster - a quick fade inside a short hold reads
     as a flicker. Keep this in step with the shBusy guard in js/satva.js. */
  transition:opacity .7s var(--ease),visibility .7s step-end;
}
.shelf.is-on{opacity:1;visibility:visible;transition:opacity .7s var(--ease),visibility 0s;}
.shelf::before{content:"";position:absolute;inset:0;z-index:0;background:var(--sg);}

/* the photograph bleeds off the right edge and is feathered back into the
   ground on its left, so it sits on the cream rather than being pasted onto it */
/* Made smaller 6 Sep 2026 (was inset:0 and min(50%,840px), full height).
   Narrowing ALONE made the subject look bigger, not smaller: object-fit:cover
   crops harder the taller and narrower its box gets, so a 42%-wide full-height
   frame zoomed into the cinnamon instead of showing the flat-lay. The height
   had to come down with the width - a shorter box crops less, so more of the
   photograph fits and everything in it reads smaller. */
/* LENGTHENED 6 Sep 2026 at the owner's request - "a little more longer, and then
   taper it down near the writings". Was min(44%,620px) inset 58px, which put the
   frame's left edge at x=820 on a 1440 viewport and, with the taper below only
   reaching full strength at 78%, left barely 140px of the photograph at its real
   colour. The picture read short and washed. Now it reaches x=605 and the taper
   does its dying in the gap between the copy and the subject, which is what was
   actually being asked for: more photograph, and the fade nearer the words. */
.sh-shot{
  position:absolute;
  inset:clamp(20px,3.2vw,42px) 0 clamp(20px,3.2vw,42px) auto;
  width:min(58%,900px);z-index:1;
  /* the frame is inset now, so its top and bottom edges are on-canvas and would
     read as two hard cuts; the left taper is on the img inside */
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 11%,#000 89%,transparent 100%);
          mask-image:linear-gradient(to bottom,transparent 0,#000 11%,#000 89%,transparent 100%);
}

/* Every photo in the hero is shot on a pale ground (the stone under the
   panoramas, the beige under the seed bowls, the marble under the salt), which
   is what lets the picture dissolve into the cream at all rather than sitting
   on it as a rectangle - and what makes the block look like the page the owner
   sent. A photo shot on a dark ground would need its own handling.

   --shot-pos picks WHICH part of the photograph survives the crop, per slide,
   since the two panoramas are 1700px wide and only ~800px of that is ever seen.
   --vib lifts a flat photo. Kept between 1.00 and 1.20 on purpose: this is a
   brand whose whole argument is that colour comes from the spice and not from a
   dye drum, so the already-vivid shots are barely touched (ground spices sit at
   1.03) and only the soft ones - the oil bottle, the seed bowls - are pushed. */
.sh-shot img{
  width:100%;height:100%;object-fit:cover;display:block;
  object-position:var(--shot-pos,center);
  filter:saturate(var(--vib,1)) contrast(var(--vcon,1.02));
  /* ONE mask layer here: the left taper. The vertical feather for the inset top
     and bottom edges lives on the parent .sh-shot instead. Two single-layer
     masks on two elements, rather than two layers composited on one - mixing
     -webkit-mask-composite:source-in with mask-composite:intersect left a hard
     vertical edge down the whole photograph, because the two syntaxes disagree
     about which layer wins. Nothing here needs compositing.

     Five stops, not two: a straight two-stop ramp bands visibly over this
     distance, so the slope has to ease rather than step.

     WHERE THE STOPS SIT is the whole trick, and it is measured against the copy
     rather than chosen for its own sake. The frame starts at x=605 on a 1440
     viewport and the longest line of type ends near x=680, so the mask is still
     under 5% there - the photograph is invisible behind the words. It then
     climbs through the empty gap and is at full strength by 58% (x=1089),
     giving ~350px of the picture at its real colour instead of 140px. Raise
     that 58% and the fade crowds the type; lower it and the taper turns back
     into an edge. */
  -webkit-mask-image:linear-gradient(to right,transparent 0,rgba(0,0,0,.05) 10%,rgba(0,0,0,.24) 24%,rgba(0,0,0,.58) 40%,rgba(0,0,0,.88) 50%,#000 58%);
          mask-image:linear-gradient(to right,transparent 0,rgba(0,0,0,.05) 10%,rgba(0,0,0,.24) 24%,rgba(0,0,0,.58) 40%,rgba(0,0,0,.88) 50%,#000 58%);
  transform:scale(1.06);transition:transform 1.4s var(--ease);
}
/* `.sh-shot.deep`, a longer dissolve for a photo carrying a dark ground of its
   own, is gone as of 6 Sep 2026. It dated from when the whole and ground slides
   used the slate and dark-board crops. The default taper above now reaches full
   strength at 58% where the old default reached it at 24%, so `.deep` at 56%
   had become the same rule under a name that claimed otherwise - worse than no
   escape hatch. A dark shelf-<id>.jpg needs no special case now. */
.shelf.is-on .sh-shot img{transform:scale(1);}

.shelf .wrap{position:relative;z-index:2;width:100%;}
/* The column is wide enough, and the display size small enough, that every one
   of the five headlines holds its intended two lines. At the first sizing the
   name and the phrase under it broke into three, which lost the reference's
   whole rhythm - two lines, the second one carrying the gold.

   The ceiling is set by one line: "Ground When You Cook", which measures 11px
   of width for every 1px of type. In a 620px column that means 56.4px fills it
   exactly and takes a third line one pixel later, so 3.35rem (53.6px) is the
   most that can be asked for - it fills 95% of the column and leaves the
   two-line rhythm intact on all five slides. */
/* 62%, up from 53%: the photograph occupies the right 44% but its left half is
   a whisper under the taper, so the copy can run well past the old 53% before
   it meets anything solid - and the middle of the hero was reading as a hole. */
.sh-copy{max-width:min(780px,62%);padding-block:clamp(46px,7vw,86px);}
.sh-copy .kick{color:var(--sage);}
.sh-copy h2{
  font-size:clamp(2.15rem,4.9vw,3.9rem);line-height:1.03;margin:10px 0 20px;color:var(--ink);
}
.sh-copy h2 em{font-style:normal;display:block;}
/* --green, not --sage. Widening the column to 62% runs the paragraph over the
   faint left end of the photograph, where --sage measured 4.28:1 at its
   darkest pixel - under AA. --green is 8.1:1 on the same ground and still
   reads as secondary next to the --ink headline. */
.sh-copy > p{color:var(--green);font-size:clamp(1.05rem,.62vw + .92rem,1.26rem);line-height:1.62;
  max-width:56ch;margin:0 0 30px;}

/* THE COPY ARRIVES AFTER THE PICTURE, 20 Sep 2026.

   The owner: "the transitioning between all the slides are not happening in our
   site as in this site." Filming both pages at 70ms intervals found the
   difference, and it was NOT the crossfade - theirs is a pure opacity swap over
   ~700ms with no movement at all (transform and left stay at 0 the whole way),
   which is exactly what this site already does.

   The difference is the ORDER. Ours started the copy at .10s, so the words rose
   while the photograph was still dissolving and both finished together -
   everything moving at once. Kovaikisan sequences it: the crossfade finishes,
   THEN the content arrives - their picture animates at +0.6s and their text at
   +0.9s, each over 0.7s. The eye gets to read the new photograph before being
   asked to read anything.

   So the stagger now starts at .62s, as the 0.7s crossfade lands, and the last
   line is in place by 1.47s - comfortably inside the 4s hold. */
.sh-copy > *{opacity:0;transform:translateY(20px);}
.shelf.is-on .sh-copy > *{animation:rise .85s var(--ease) forwards;}
.shelf.is-on .sh-copy > *:nth-child(1){animation-delay:.62s;}
.shelf.is-on .sh-copy > *:nth-child(2){animation-delay:.71s;}
.shelf.is-on .sh-copy > *:nth-child(3){animation-delay:.80s;}
.shelf.is-on .sh-copy > *:nth-child(4){animation-delay:.89s;}
.shelf.is-on .sh-copy > *:nth-child(5){animation-delay:.98s;}

/* the reference's pill: outline, wide letter-spaced label, filled circle arrow.
   Theirs is black on cream; ours is --ink, which is the same move in the brand's
   own green, and the circle turns gold on hover rather than simply inverting. */
.pill{
  display:inline-flex;align-items:center;gap:clamp(14px,2vw,26px);
  padding:7px 7px 7px clamp(20px,2.4vw,30px);
  border:1.5px solid var(--ink);border-radius:999px;color:var(--ink);
  font-family:"Jost",system-ui,sans-serif;font-weight:500;
  font-size:clamp(.82rem,.9vw,.95rem);letter-spacing:.09em;text-transform:uppercase;
  transition:background .32s var(--ease),color .32s var(--ease),transform .32s var(--ease),box-shadow .32s var(--ease);
}
.pill .parrow{
  width:clamp(38px,3.6vw,46px);aspect-ratio:1;border-radius:50%;flex:none;
  background:var(--ink);color:var(--ivory);display:grid;place-items:center;
  transition:background .32s var(--ease),color .32s var(--ease),transform .32s var(--ease);
}
.pill .parrow svg{width:44%;height:44%;}
.pill:hover{background:var(--ink);color:var(--ivory);transform:translateY(-2px);box-shadow:var(--sh-md);}
.pill:hover .parrow{background:var(--gold);color:var(--ink);transform:translateX(3px);}
.pill:focus-visible{outline:2px solid var(--gold-dk);outline-offset:3px;}

/* counted off PRODUCTS in js/satva.js, so it cannot go stale the way a typed
   number would - the same rule the category cards and the tabs already follow */
/* set well clear of the pill and pitched quieter than it: sitting tight
   underneath at gold, it read as a label belonging to the button rather than as
   a note about the shelf */
.sh-count{margin:26px 0 0;font-size:.74rem;letter-spacing:.17em;text-transform:uppercase;
  color:var(--sage-soft);}
.sh-count:empty{display:none;}

/* ---- the strip underneath: named tabs first, arrows second ---- */
.shbar{position:relative;z-index:3;background:var(--ivory);border-top:1px solid var(--line);}
.shbar .wrap{display:flex;align-items:center;gap:clamp(16px,3vw,40px);
  justify-content:space-between;min-height:70px;padding-block:12px;}
.shtabs{flex:1 1 auto;min-width:0;}
.shnav{display:flex;gap:9px;flex:none;}
/* .arrow is absolutely positioned for the hero; here it sits in the flow */
.shnav .arrow{position:static;bottom:auto;left:auto;right:auto;width:42px;height:42px;
  background:#fff;}

/* 1024-1080 is the last width that still runs two columns, and it is tight:
   the panel has to give ground so the taper can finish before the type does. */
@media (max-width:1080px){
  .sh-copy{max-width:min(620px,64%);}
  .sh-shot{width:min(46%,540px);}
}

/* Below 1024px there is no room for a column of words beside a picture, so the
   two stack: photograph on top at full strength, copy under it on the ground.

   This replaced a full-bleed photo behind a near-opaque cream scrim. That
   version was legible, but it spent the picture to do it - a wash at .965 over
   the turmeric and chilli left almost none of the colour that is the whole
   reason these shots are here. Stacking costs some height and gives the colour
   back at 100%, with the type on clean cream where it belongs. The photo
   already precedes .wrap in the markup, so the order needs no reshuffling.

   THE BREAKPOINT WAS 900px AND HAD TO MOVE UP. Once the panel was lengthened
   the two-column layout stopped fitting between 900 and 1080: the viewport is
   too narrow there for a 58%-wide copy column and a long photograph to share,
   so the taper was still at 18-43% where the body text ended, i.e. a visible
   photograph directly behind the words. Measured across all five slides at
   1060 / 1000 / 940. Stacking from 1024 down, plus the narrower panel in the
   1080 block above, puts the worst case back under 5% at every width. If the
   copy column or the panel width is ever widened again, re-measure this. */
@media (max-width:1024px){
  .shelf{min-height:0;display:flex;flex-direction:column;align-items:stretch;}
  /* Taller here too, for the same reason as the desktop panel: the owner asked
     for a longer picture. 210-320px was a strip; this is a band. */
  .sh-shot{
    position:relative;inset:auto;width:100%;flex:none;
    height:clamp(250px,54vw,380px);
  }
  /* The container's inset feather is off - there is no inset to hide here, and
     it would fade the top of the band against the nav. */
  .sh-shot{-webkit-mask-image:none;mask-image:none;}
  /* But the bottom of the band DOES taper, into the cream the copy sits on.
     This is the stacked layout's version of "taper it down near the writings":
     where the desktop panel dies out horizontally as it nears the column of
     type, this one dies out vertically as it nears the type below it. Without
     it the photograph stops at a ruled horizontal line directly above the
     kicker, which is the one edge the desktop layout no longer has. */
  .sh-shot img{
    -webkit-mask-image:linear-gradient(to bottom,#000 0,#000 58%,rgba(0,0,0,.72) 78%,rgba(0,0,0,.24) 92%,transparent 100%);
            mask-image:linear-gradient(to bottom,#000 0,#000 58%,rgba(0,0,0,.72) 78%,rgba(0,0,0,.24) 92%,transparent 100%);
  }
  /* the band is wide and short where the desktop panel is narrow and tall, so a
     slide can name a second crop for it. Only the oil bottle needs one so far:
     the framing that centres it in a tall panel cuts its neck off in a band. */
  .sh-shot img{object-position:var(--shot-pos-m,var(--shot-pos,center));}
  .sh-copy{max-width:none;padding-block:clamp(34px,7vw,52px);}
  .sh-copy > p{max-width:44ch;}
  /* the tabs need both rows to themselves once they wrap, so the arrows drop
     below them rather than being pulled up alongside - tucked up on the right
     they landed on top of the second row of tab labels */
  .shbar .wrap{flex-direction:column;align-items:stretch;gap:14px;}
  .shnav{justify-content:flex-end;}
}

/* Below 620px the hero's arrows are already dropped in favour of swipe (see the
   620px block further down) and that rule catches these too, since they share
   the .arrow styling. Left alone, .shnav stayed in the column as an empty row
   and the bar carried its gap for nothing - so the whole holder goes with them.
   Nothing is lost: the tab strip names every shelf, and the stage still swipes. */
@media (max-width:620px){
  .shnav{display:none;}
}

@media (max-width:560px){
  .pill{gap:12px;}
}

@media (prefers-reduced-motion:reduce){
  .shelf,.shelf.is-on{transition:none;}
  .sh-shot img,.shelf.is-on .sh-shot img{transform:none;transition:none;}
  .sh-copy > *,.shelf.is-on .sh-copy > *{opacity:1;transform:none;animation:none;}
  .pill:hover{transform:none;}
}

/* ===================== shop by category ===================== */
/* Arch-topped cards - the one shape borrowed wholesale from vahdam.com, because
   it is the detail that makes their block read as a shelf rather than a table. */
/* ---- SHOP BY CATEGORY: the way into the range ----------------------------
   Rebuilt 5 Sep 2026 from two references the owner sent.
   From uk.huel.com: the label sits ON the photograph rather than under it, and
   the row is given far more air than a product grid gets - their four cards
   carry a whole viewport between them. From jainfarmfresh.com/products/spices:
   one honest photograph per category, no icon standing in for a product.
   Three across, not six. Six categories at six-across made each photograph a
   stamp; at three-across on two rows every picture is large enough to be worth
   photographing, which is the point of the block. */
/* cream, not sage: Everyday Essentials used to sit between this block and the
   range and carried the cream, so with it gone the two sage sections met and
   the alternation broke. Cream is also what jainfarmfresh puts behind its own
   category cards, so the reference and the ground rule agree here. */
/* 16 Sep 2026, the owner: "the screen has only that block and what we dont do
   goes for the next scrolling". So the shelves hold a whole screen on their own
   and "What we will not do to your food" begins below the fold, on the next
   scroll, instead of crowding in under the cards.
   min-height, not height: at two columns, or stacked on a phone, four cards are
   taller than a screen and the section simply grows. The content is centred in
   whatever is left over, and the slack at the top is what keeps the heading out
   from under the sticky nav. */
.cats{
  padding:clamp(40px,5vw,68px) 0 clamp(34px,4.4vw,60px);background:var(--cream-sg);
  /* The nav is sticky and translucent, and this section is now exactly a screen
     tall with its content centred - so on a short window the centred content
     rose until "SOMETHING FOR EVERY SHELF" sat behind the nav (measured: 83px
     against a nav 81px deep, a 2px escape). The floor is the nav's own height
     plus air. It costs nothing on a tall window, where the centring slack is
     already bigger than this. */
  padding-top:max(96px,clamp(40px,5vw,68px));
  min-height:100vh;      /* fallback for anything without svh */
  min-height:100svh;     /* the small viewport, so a phone's toolbars cannot clip it */
  display:flex;flex-direction:column;justify-content:center;
}
.chead{text-align:center;margin:0 auto clamp(34px,4.4vw,58px);max-width:60ch;}
.chead h2{margin:0;color:var(--ink);}
/* the one remaining line is an <h2> for the outline but is still the KICK to
   look at - .chead h2 would otherwise repaint it ink and lose its gold */
.chead h2.kick{color:var(--gold-dk);margin:0;}
.chead .lede{margin:14px auto 0;color:var(--sage);line-height:1.65;}

/* This section's wrap is wider than the page's standard 1280px. Without it the
   grid cannot fill the breadth however wide its own max-width is - a child
   cannot outgrow its parent, and .wrap was leaving 365px empty on each side of
   a 1920px screen. */
/* width:100% is load-bearing now that .cats is a flex column. .wrap carries
   margin:0 auto, and an auto margin on the CROSS axis switches off a flex item's
   stretch - so the wrap fell back to fit-content and, on a phone, the single
   card came out 282px wide inside a 366px wrap. This puts the stretch back; the
   max-width still caps it, and border-box keeps the padding inside. */
.cats .wrap{max-width:min(1720px,94vw);width:100%;}

/* Six across on a wide screen, filling the breadth; then 3, 2, 1. The counts are
   explicit rather than auto-fit because there are SIX cards and auto-fit does
   not care about that: at 1280px it produced five tracks and left the sixth card
   stranded alone on a second row. Every count here divides six, so a row is
   never left with an orphan. The old fixed three columns capped at 1280px left
   a third of the page empty on either side. */
/* Four columns because the range is four shelves (16 Sep 2026). It was six, and
   when Spices absorbed ground/whole/blends and Healthy Essentials absorbed
   pantry/oils, a six-column rule left one card stranded alone on a second row.
   Four across on a wide screen, then 2x2, then a single column on a phone - so
   no breakpoint ever ends in an orphan. */
.catgrid{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(14px,1.4vw,24px);
  width:100%;max-width:1720px;margin-inline:auto;
}
/* ONE CARD, ONE FRAME - 16 Sep 2026, the owner: "design it so that these things
   are in one frame and one block".
   The photograph used to be the only framed thing: it had the radius and the
   shadow, and the blurb and the ITEMS line sat underneath it on the section's
   bare sand, outside any border. So a card read as a picture with two loose
   scraps of text below it, and with only four cards left they were big enough
   that the scraps drifted a long way from their own photograph.
   Now the picture and the words share ONE frame: one border, one radius, one
   shadow, overflow hidden, and the whole thing lifts together on hover. */
.catcard{
  appearance:none;padding:0;cursor:pointer;font:inherit;
  text-align:left;display:flex;flex-direction:column;height:100%;
  background:#fff;border:1px solid var(--line);
  border-radius:20px;overflow:hidden;
  box-shadow:0 18px 38px -24px rgba(60,81,40,.45);
  transition:transform .55s var(--ease),box-shadow .55s var(--ease),
             border-color .55s var(--ease);
}
.catcard .cshot{
  /* A LONG RECTANGLE (16 Sep 2026, the owner). It was 3:2 landscape, which is
     what let the next section crowd in under the cards; portrait was rejected
     back when there were six cards in two rows, because a tall first row pushed
     the second row off the screen. With four cards on ONE row that reason is
     gone, so the picture is now tall.
     Height in svh rather than an aspect-ratio: the point is to fill the screen,
     so the picture has to answer to the window's height, not to the card's own
     width. At 1290x860 this is a 288x482 card picture - a 3:5 rectangle. The
     240px floor keeps it sane on a short laptop window, the 520px ceiling stops
     it becoming a full-height sliver on a tall monitor.
     No radius and no shadow of its own - the frame around it carries both, and
     doubling them made a visible seam between picture and words. */
  position:relative;display:block;width:100%;overflow:hidden;
  height:clamp(280px,56vh,520px);
  height:clamp(280px,56svh,520px);
  background:var(--tint);color:#fff;
  display:grid;place-items:center;
}
.catcard .cshot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  /* the owner asked for the spice colour of jainfarmfresh, which runs its
     photography far more saturated than this site had. */
  filter:saturate(1.18) contrast(1.03);
  transition:transform .8s var(--ease);}
.catcard .cshot > svg{width:32%;height:32%;opacity:.92;}
/* the scrim only exists under the words, so the photograph keeps its own light
   in the top two-thirds of the frame */
.catcard .cscrim{
  position:absolute;left:0;right:0;bottom:0;height:46%;pointer-events:none;
  background:linear-gradient(to top,rgba(20,28,15,.92) 0%,rgba(20,28,15,.58) 45%,rgba(20,28,15,0) 100%);
}
.catcard .cname{
  position:absolute;left:clamp(16px,1.6vw,22px);right:clamp(58px,5vw,70px);
  bottom:clamp(15px,1.5vw,20px);
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.12rem,1.5vw,1.42rem);font-weight:600;color:#fff;line-height:1.15;
  text-shadow:0 1px 12px rgba(12,18,8,.55);
}
.catcard .carrow{
  position:absolute;right:clamp(14px,1.4vw,18px);bottom:clamp(14px,1.4vw,18px);
  width:clamp(34px,3.2vw,40px);height:clamp(34px,3.2vw,40px);border-radius:50%;
  display:grid;place-items:center;background:rgba(255,255,255,.94);color:var(--band);
  transition:transform .45s var(--ease),background .45s var(--ease);
}
.catcard .carrow svg{width:52%;height:52%;}
/* the whole frame lifts now, not the photograph inside it */
.catcard:hover,.catcard:focus-visible{
  transform:translateY(-7px);box-shadow:0 30px 52px -26px rgba(60,81,40,.55);
  border-color:var(--gold-lt);
}
.catcard:hover .cshot img{transform:scale(1.055);}
.catcard:hover .carrow{transform:translateX(3px);background:#fff;}
/* flex:1 + margin-top:auto keeps every ITEMS line on one baseline across a row,
   whether its blurb wrapped to one line or two. The side padding is what puts
   the words INSIDE the frame rather than flush against its edge. */
.catcard .cmeta{
  display:flex;flex-direction:column;gap:6px;flex:1;
  padding:clamp(12px,1vw,15px) clamp(14px,1.1vw,18px) clamp(14px,1.1vw,17px);
}
.catcard .cblurb{font-size:.9rem;line-height:1.55;color:var(--sage);max-width:32ch;}
.catcard .ccount{
  margin-top:auto;padding-top:7px;font-size:.68rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--gold-dk);font-weight:600;
}

/* Four across holds much further down the page than it used to. At the old
   1400px switch a 1280-1400px laptop - which is most of them - dropped to two
   columns, and two cards that wide made each one ~550px tall, so the four
   shelves needed two screens instead of reading as one block. Four across only
   gives way at 1080px, where a card would be under ~230px wide. */
/* Two columns and one column both mean two or four ROWS of cards, and four rows
   of a 56svh picture would be a very long scroll for the same four shelves. The
   picture comes down accordingly - still a tall rectangle, just not one sized to
   fill a screen it can no longer fill on its own. */
@media (max-width:1080px){
  .catgrid{grid-template-columns:repeat(2,1fr);}
  .catcard .cshot{height:clamp(240px,38vh,380px);height:clamp(240px,38svh,380px);}
}
@media (max-width:560px){
  .catgrid{grid-template-columns:1fr;}
  .catcard .cshot{height:clamp(230px,34vh,330px);height:clamp(230px,34svh,330px);}
}
@media (prefers-reduced-motion:reduce){
  .catcard:hover .cshot{transform:none;} .catcard:hover .cshot img{transform:none;}
}

/* ===================== section tabs ===================== */
/* These were the Everyday Essentials tabs; that section was removed on
   5 Sep 2026 as a duplicate of the range, and the tab styling stayed because
   the recipes block now uses it. Nothing else in that block survives. */
.etabs{display:flex;gap:clamp(14px,2vw,26px);flex-wrap:wrap;}
.etab{
  appearance:none;background:none;border:0;padding:3px 0;cursor:pointer;
  font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  /* --sage-soft is 4.22:1 on the cream ground (it was already only 4.46:1 on the
     old one) - inactive tabs use --sage, 4.74:1, and stay clearly secondary
     because the selected tab is --ink with a gold rule under it */
  font-size:clamp(.98rem,1.1vw,1.1rem);font-weight:500;color:var(--sage);
  border-bottom:1.6px solid transparent;transition:color .3s,border-color .3s;
}
.etab:hover{color:var(--gold-dk);}
.etab[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--gold);}

/* Autoplay's progress line. It rides the active shelf tab so the movement is
   announced before it happens - a carousel that jumps with no warning reads as
   a fault. Added and removed by js/satva.js, and it never appears when autoplay
   is stopped, paused or was never started (reduced motion). */
.shtabs .etab{position:relative;}
.shtabs .etab.is-timing::after{
  content:"";position:absolute;left:0;bottom:-1.6px;height:1.6px;width:100%;
  background:var(--gold);transform-origin:left center;
  animation:shtick 6s linear forwards;
}
@keyframes shtick{ from{transform:scaleX(0);} to{transform:scaleX(1);} }
@media (prefers-reduced-motion:reduce){
  .shtabs .etab.is-timing::after{display:none;}
}
.etab .ecount{font-size:.72em;color:var(--gold-dk);margin-left:5px;vertical-align:.25em;}

/* ===================== range ===================== */
/* cream, not the sage ramp: the recipes row above it is sage now, so sage met
   sage. The white product cards read at least as well on cream. */
.range{padding:clamp(38px,5vw,70px) 0;background:linear-gradient(180deg,var(--cream-sg) 0%,var(--sand) 100%);}
.sechead{display:flex;gap:clamp(24px,4vw,56px);align-items:flex-end;justify-content:space-between;
  flex-wrap:wrap;margin-bottom:clamp(28px,4vw,52px);}
.sechead>div{flex:1 1 380px;} .sechead>.lede{flex:0 1 400px;padding-bottom:4px;}
.sechead h2{color:var(--ink);}
.rule{height:1px;background:linear-gradient(to right,var(--gold),transparent);margin:14px 0 0;}
.grid-p{display:grid;grid-template-columns:repeat(auto-fill,minmax(248px,1fr));gap:clamp(16px,2vw,26px);}
.pcard{--acc:var(--gold);--acc-ink:var(--gold-dk);
  background:#fff;border-radius:20px;--r:20px;overflow:hidden;position:relative;
  border:1px solid var(--line);box-shadow:var(--sh-sm);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease),border-color .4s;
  display:flex;flex-direction:column;
}
.pcard:hover{transform:translateY(-7px);box-shadow:var(--sh-md);
  border-color:color-mix(in srgb,var(--acc) 55%,transparent);}
.pthumb{aspect-ratio:1;position:relative;overflow:hidden;background:var(--oat);}
.pthumb::after{content:"";position:absolute;left:0;right:0;bottom:0;height:5px;background:var(--acc);z-index:2;}
.pthumb img{width:100%;height:100%;object-fit:cover;transition:transform .8s var(--ease);}
.pcard:hover .pthumb img{transform:scale(1.07);}
.pthumb.tile{display:grid;place-items:center;background:var(--tint,linear-gradient(150deg,#f0e7d3,#e0d2b3));}
.pthumb.tile .glyph{font-family:"Tiro Devanagari Hindi",serif;font-size:clamp(3.6rem,13vw,5.2rem);
  color:rgba(255,255,255,.42);line-height:1;user-select:none;}
.pthumb.tile .mortar{position:absolute;bottom:16px;right:16px;width:34px;height:34px;color:rgba(255,255,255,.6);}
.pthumb .veg{position:absolute;top:12px;left:12px;width:18px;height:18px;border:2px solid #1f7a34;
  background:#fff;display:grid;place-items:center;border-radius:2px;}
.pthumb .veg i{width:8px;height:8px;border-radius:50%;background:#1f7a34;display:block;}
.pbody{padding:16px 18px 20px;display:flex;flex-direction:column;flex:1;}
.pbody h3{font-size:1.12rem;line-height:1.3;color:var(--acc-ink);margin:0 0 2px;font-weight:600;
  letter-spacing:normal;text-transform:none;}
.pbody .dv{font-size:.95rem;color:var(--sage-soft);line-height:1.35;margin:0 0 8px;}
.pform{display:inline-block;font-size:.6rem;letter-spacing:.15em;text-transform:uppercase;
  font-weight:600;color:var(--sage-soft);border:1px solid var(--line);border-radius:999px;
  padding:2px 9px;margin:0 0 10px;}
.pbody p{margin:0 0 15px;font-size:.9rem;color:var(--sage-soft);flex:1;}
.pbody .ask{font-size:.8rem;letter-spacing:.04em;color:var(--band);font-weight:600;display:inline-flex;
  align-items:center;gap:7px;}
.pbody .ask svg{width:15px;height:15px;color:var(--acc-ink);}
.pbody .ask:hover{color:var(--acc-ink);}
.soon{position:absolute;bottom:12px;left:12px;font-size:.6rem;letter-spacing:.14em;text-transform:uppercase;
  background:rgba(255,255,255,.94);color:var(--band);padding:4px 9px;border-radius:999px;font-weight:600;}

/* ===================== why ===================== */
/* ===================== promise ===================== */

/* ===================== story ===================== */
.story{padding:clamp(58px,8vw,112px) 0;background:var(--sage-mist);}
.storygrid{display:grid;grid-template-columns:1.32fr .68fr;gap:clamp(30px,5vw,72px);align-items:start;}
.story h2{color:var(--ink);}
.story p{color:var(--sage);}
.storyart{position:sticky;top:112px;display:grid;place-items:center;padding:clamp(24px,4vw,44px);
  border-radius:26px;--r:26px;background:linear-gradient(150deg,var(--sand),var(--oat));
  box-shadow:var(--sh-sm);}
.storyart img{width:min(300px,78%);}

/* ===================== feedback ===================== */
.fb{background:linear-gradient(155deg,var(--tint-cardamom) 0%,#dfe9d1 100%);padding:clamp(34px,4.4vw,62px) 0;overflow:hidden;}
.fbgrid{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(28px,4vw,60px);align-items:center;}
.fb h2{color:var(--ink);}
.fb p{color:var(--sage);}
.fbcard{border-radius:22px;--r:22px;padding:clamp(24px,3vw,34px);text-align:center;
  background:#fff;border:1px solid var(--clay);box-shadow:var(--sh-sm);}
.fbcard .num{font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(2.4rem,5vw,3.4rem);line-height:1;}
.fbcard small{display:block;font-size:.76rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--sage-soft);margin-top:8px;}

/* ===================== contact ===================== */
.contact{padding:clamp(38px,5vw,70px) 0;background:var(--sage-mist);}
.contact h2{color:var(--ink);}
.cgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:clamp(18px,2.4vw,30px);
  margin-top:clamp(26px,3vw,44px);}
.ccard{background:#fff;border:1px solid var(--line);border-radius:18px;--r:18px;
  padding:26px 24px;box-shadow:var(--sh-sm);
  transition:border-color .4s,transform .5s var(--ease),box-shadow .4s;}
.ccard:hover{border-color:color-mix(in srgb,var(--cc) 55%,transparent);transform:translateY(-4px);box-shadow:var(--sh-md);}
.ccard{--cc:var(--leafgreen);}
.ccard:nth-child(2){--cc:var(--turmeric);}
.ccard:nth-child(3){--cc:var(--garlic);}
.ccard:nth-child(4){--cc:var(--chilli);}
.ccard .ic{width:42px;height:42px;border-radius:50%;display:grid;place-items:center;margin-bottom:14px;
  background:var(--cc);color:#fff;}
.ccard .ic svg{width:19px;height:19px;}
.ccard h3{margin:0 0 5px;font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;color:var(--gold-dk);}
.ccard a,.ccard span{font-size:1.02rem;color:var(--band);font-weight:500;}
.ccard a:hover{color:var(--gold-dk);}

/* ===================== footer ===================== */
/* the one deep-green block on the page — the label has the same green band at its foot */
/* the bottom padding is not decoration: .wafab is fixed at the bottom-right
   and would otherwise sit on top of the last line of the footer */
/* The footer stands on a pinned photograph, after the band at the foot of
   dip-a-chip.com. Measured off their page: one section carrying
   background-attachment:fixed with background-size:cover, so the image holds
   still in the viewport and the section scrolls over it like a window.

   The picture is ours: assets/footer-collage.jpg is a contact sheet built from
   twelve photographs already on this site - the six category shots and six of
   the recipe shots - laid out 4x3 with band-green gutters and desaturated to
   0.86 so the spice colour does not fight the gold in the type.

   TWO LAYERS, TWO ATTACHMENTS. The scrim scrolls with the footer so it always
   covers exactly the footer; only the photograph is pinned. A scrim this heavy
   (.90 to .94 of --ink) is not decoration - it is what keeps the existing
   footer type legible over twelve different photographs. */
/* LIGHT since 10 Sep 2026. The Get in touch section was deleted - every one of
   its four details was already in this footer - and the owner asked for its
   ground, the sweetgreen mint, to come down here. So this footer was dark green
   with cream type and is now mint with green type: the overlay, the logo, every
   link, the rule between the columns and the veg mark all had to turn round.
   The collage stays. It is our own contact sheet and it was carrying the whole
   footer visually; the overlay is now a 94-97% MINT wash instead of a 90-94%
   green one, so a ghost of it still shows through without touching contrast. */
/* GROUND CHANGED 18 Sep 2026 to #372e35, "plum grey" - a warm charcoal with a
   trace of violet in it, chosen off a page of eighteen charcoals previewed
   against the sand band that arrives above this one.

   WHY: the 10 Sep pass left the page with no dark band anywhere, and the note
   at the foot of that entry asked whether it still had enough weight low down.
   It did not. This also keeps the violet the Satwa Saheli band wore until the
   17th somewhere on the page, now that Saheli has gone to the salt wash.

   THIS IS THE 10 SEP INVERSION, RUN BACKWARDS. Everything in here was flipped
   for a light ground and had to flip again - the logo, the links, the headings,
   the muted text and the veg mark. Measured on #372e35:
     blurb + links   --cream #e8dcc6   9.65:1
     headings + btm  --gold-pale       9.77
     muted           #c9bda4           7.04
     veg mark        #7fbf6a           5.96
     link hover      --gold-lt         6.74
   And what had to GO, because none of it survives here: --green 1.19:1,
   --sage 2.12, --gold-dk 2.10, the dark veg mark #2f7d32 2.56.

   THE MUTED TEXT IS A SOLID, NOT AN ALPHA. The original dark footer set .fnote
   to rgba(cream,.68), which measured 3.89:1 and was only legible because of the
   photograph behind it; the mint fixed that by accident in September. #c9bda4
   is the same idea done properly. Do not put the alpha back.

   The collage wash is 92-95%, between the 90-94% this footer used when it was
   dark green and the 94-97% the mint used. That pair of numbers is the dial: a
   lower one shows more photograph, a higher one more flat colour. */
footer{
  background-color:#372e35;
  background-image:
    linear-gradient(rgba(55,46,53,.92),rgba(55,46,53,.95)),
    url("../assets/footer-collage.jpg");
  background-repeat:no-repeat,no-repeat;
  background-position:center,center;
  background-size:cover,cover;
  background-attachment:scroll,fixed;
  color:var(--cream);
  /* one token for every dimmed thing down here - the fnote, the address line and
     the bottom bar - so they cannot drift apart the way they did before */
  --f-muted:#c9bda4;
  padding:clamp(44px,5.5vw,72px) 0 clamp(76px,7vw,88px);font-size:.86rem;}

/* background-attachment:fixed is a known jank source on touch devices, and iOS
   Safari ignores it outright - it renders as scroll, which still looks right,
   just without the parallax. Made explicit rather than left to the browser. */
@media (max-width:900px){
  footer{background-attachment:scroll,scroll;}
}
@media (prefers-reduced-motion:reduce){
  footer{background-attachment:scroll,scroll;}
}

/* the rule between the columns and the bottom bar: a dark translucent green was
   right on the mint, and disappears on the plum. Back to a light one. */
.fgrid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1.15fr;gap:clamp(24px,3.4vw,52px);
  padding-bottom:clamp(26px,3.4vw,40px);border-bottom:1px solid rgba(255,255,255,.18);}

/* THE STACKED LOCKUP, 18 Sep 2026 - the DS sits above the wordmark here, not
   beside it. 52px was right for the horizontal file (ratio 3.27); the stacked
   one is all but square (760x722, ratio 1.05), so at that height it came out a
   third of the width and read as a stamp rather than as the brand. 112px gives
   it back its presence and makes "Dil Se Pure" and the wheat sprigs legible
   instead of decorative; it also makes this column taller than the three link
   columns, which is deliberate.
   The floor is 84px because below roughly that the strapline inside the artwork
   stops being readable at all. The nav still carries the HORIZONTAL lockup on
   purpose - a square mark there drives the sticky header deeper on every
   screen, which is a separate decision. */
.fbrand .logo{height:clamp(84px,9vw,112px);width:auto;margin-bottom:16px;}
.fbrand p{margin:0 0 18px;max-width:38ch;line-height:1.6;}
/* THE LINE WITH A HISTORY. On the first dark footer this was rgba(cream,.68)
   and measured 3.89:1 - below AA, and only the photograph behind it made it
   readable at all. The mint fixed that by accident in September (--sage, 4.74).
   The ground is dark again, so --sage is gone (2.12 here), but the alpha is NOT
   coming back with it: --f-muted is a solid #c9bda4 and measures 7.04:1. */
.fbrand .fnote{margin:12px 0 0;font-size:.78rem;color:var(--f-muted);}
.fwa{
  display:inline-flex;align-items:center;gap:9px;background:var(--gold-grad);color:#2a1f08;
  font-weight:600;font-size:.83rem;letter-spacing:.03em;padding:11px 20px;border-radius:999px;
  transition:transform .3s var(--ease),filter .3s;
}
.fwa:hover{transform:translateY(-2px);filter:brightness(1.06);}

.fcol h3{
  margin:0 0 14px;font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--gold-pale);font-weight:600;
}
.fcol ul{list-style:none;margin:0;padding:0;display:grid;gap:10px;}
.fcol a{color:var(--cream);transition:color .3s;}
.fcol a:hover{color:var(--gold-lt);}
.fcol span{color:var(--f-muted);line-height:1.5;}

footer .btm{display:flex;gap:12px 26px;justify-content:space-between;flex-wrap:wrap;
  padding-top:20px;font-size:.78rem;color:var(--f-muted);}
footer .btm .dv{color:var(--gold-pale);}
.fveg{display:inline-flex;align-items:center;gap:7px;}
/* The mark is a green square and dot, and which green depends on the ground:
   the dark #2f7d32 is correct on a light one and measures 2.56:1 here, so the
   dark footer takes the pale #7fbf6a (5.96:1) it carried before September.
   Anything lighter than about #554a46 as a ground needs it brighter again. */
.fveg i{width:12px;height:12px;border:1.3px solid #7fbf6a;border-radius:2px;position:relative;flex:none;}
.fveg i::after{content:"";position:absolute;inset:2.4px;background:#7fbf6a;border-radius:50%;}

@media (max-width:900px){
  .fgrid{grid-template-columns:1fr 1fr;gap:32px 24px;}
  .fbrand{grid-column:1 / -1;}
}
@media (max-width:520px){
  .fgrid{grid-template-columns:1fr;}
}

/* ---------- scroll reveal ---------- */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .9s var(--ease),transform .9s var(--ease);}
.reveal.in{opacity:1;transform:none;}
.reveal.d1{transition-delay:.08s;} .reveal.d2{transition-delay:.16s;}
.reveal.d3{transition-delay:.24s;} .reveal.d4{transition-delay:.32s;}

/* floating whatsapp */
.wafab{position:fixed;right:18px;bottom:18px;z-index:70;width:54px;height:54px;border-radius:50%;
  background:#25d366;color:#fff;display:grid;place-items:center;box-shadow:0 12px 28px -10px rgba(30,44,22,.45);
  transition:transform .35s var(--ease),opacity .35s;opacity:0;pointer-events:none;transform:scale(.85);}
.wafab.show{opacity:1;pointer-events:auto;transform:scale(1);}
.wafab.show:hover{transform:scale(1.09);}
.wafab svg{width:28px;height:28px;}

/* ===================== responsive ===================== */
@media (max-width:1080px){
  .storygrid,.fbgrid{grid-template-columns:1fr;}
  .storyart{order:-1;}
}
/* 901-1200px: full nav, but no room for the CTA beside it */
@media (max-width:1200px) and (min-width:901px){
  .navcta{display:none;}
  .nav ul{gap:clamp(12px,1.6vw,22px);}
  .nav ul a{font-size:.78rem;letter-spacing:.10em;}
}
@media (max-width:900px){
  .nav ul,.navcta{display:none;}
  .burger{display:block;}
  .nav.open ul{
    display:flex;position:absolute;left:0;right:0;top:100%;flex-direction:column;gap:0;
    background:var(--ivory);border-bottom:1px solid var(--line);
    /* the top strip is reserved for .navcta, which is anchored to the same edge */
    padding:66px var(--pad) 18px;
    box-shadow:var(--sh-md);
  }
  .nav.open ul a{display:block;padding:13px 0;border-bottom:1px solid var(--line-soft);}
  /* no hover on a touch screen, so the submenu is simply always open, indented */
  .nav.open .submenu{
    display:block;position:static;transform:none;opacity:1;visibility:visible;pointer-events:auto;
    background:none;border:0;box-shadow:none;backdrop-filter:none;
    min-width:0;padding:0 0 6px 14px;
  }
  .nav.open .submenu a{display:flex;padding:10px 0;border-bottom:1px dashed var(--line-soft);justify-content:flex-start;gap:10px;}
  .nav.open .hasmenu .caret{display:none;}
  .nav.open .hasmenu::after{display:none;}
  .nav.open .navcta{display:inline-flex;position:absolute;right:var(--pad);top:calc(100% + 14px);z-index:1;}
  .copy{max-width:100%;}
  .floatcard{display:none;}
  .hero{height:clamp(540px,80vh,700px);}
}
@media (max-width:620px){
  .topline .left .hide-s{display:none;}
  .arrow{display:none;}
  .hero::before{inset:10px;} .hero::after{inset:16px;}
  .nextup{max-width:66vw;right:20px;bottom:20px;}
  .nextup img{width:58px;height:58px;}
  .band{height:clamp(170px,36%,250px);}
  .dots{bottom:104px;left:calc(var(--pad) + 4px);}
  .orn{height:34px;}
}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;}
  *,*::before,*::after{animation-duration:.001s!important;transition-duration:.001s!important;}
  .slide.is-active .band img{animation:none;}
  .copy>*{opacity:1;transform:none;}
  .floatcard{opacity:1;}
  .reveal{opacity:1;transform:none;}
}

/* ===========================================================
   Added after studying how the strongest brands in this
   category build trust: Burlap & Barrel and Diaspora Co lead
   with sourcing proof and named pillars; Suhana leads with
   heritage and dish-first navigation. Ours leads with the one
   proof we can actually give — a grind date on every pouch.
   =========================================================== */

/* ---- marquee of the dishes these masalas are actually for ---- */
.marquee{
  background:var(--band);color:var(--gold-pale);overflow:hidden;padding:14px 0;
  border-block:1px solid rgba(241,221,171,.22);
}
.mtrack{display:flex;width:max-content;animation:slide-l 42s linear infinite;}
.marquee:hover .mtrack{animation-play-state:paused;}
.mtrack span{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:1.06rem;padding:0 clamp(16px,2.4vw,34px);
  white-space:nowrap;opacity:.92;
}
.mtrack span::after{content:"·";margin-left:clamp(16px,2.4vw,34px);color:var(--gold);opacity:.7;}
@keyframes slide-l{from{transform:translate3d(0,0,0);}to{transform:translate3d(-50%,0,0);}}

/* ---- category tabs on the range ---- */
.tabs{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 clamp(20px,3vw,30px);}
.tab{
  border:1px solid var(--clay);background:#fff;color:var(--sage);border-radius:999px;
  padding:9px 20px;font-size:.83rem;letter-spacing:.06em;font-weight:500;cursor:pointer;
  transition:background .3s,color .3s,border-color .3s,transform .3s var(--ease);
}
.tab:hover{transform:translateY(-2px);border-color:var(--gold-lt);}
.tab[aria-selected="true"]{background:var(--band);border-color:var(--band);color:var(--gold-pale);}
.tab .n{opacity:.6;margin-left:6px;font-size:.78em;}
.pcard.hide{display:none;}

/* the range starts as a menu: this holds the grid's space until a shelf is
   chosen, and is removed for good on the first choice */
.rangepick{
  display:flex;align-items:center;gap:12px;
  margin:clamp(18px,2.4vw,28px) 0 0;padding:clamp(20px,2.6vw,30px) clamp(20px,2.6vw,32px);
  border:1px dashed var(--line);border-radius:18px;background:rgba(255,255,255,.5);
  color:var(--sage);font-size:.98rem;line-height:1.6;
}
.rangepick .rp-hand{
  flex:none;width:34px;height:34px;border-radius:50%;display:grid;place-items:center;
  background:var(--gold-pale);color:var(--gold-dk);
}
.rangepick .rp-hand svg{width:18px;height:18px;}

/* ---- the grind-date proof section ---- */
.trace{background:var(--ivory);padding:clamp(38px,5vw,70px) 0;overflow:hidden;}
.tracegrid{display:grid;grid-template-columns:1fr .9fr;gap:clamp(32px,5vw,76px);align-items:center;}
.trace h2{color:var(--ink);}
.trace p{color:var(--sage);}
.tracelist{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:14px;}
.tracelist li{display:flex;gap:14px;align-items:flex-start;font-size:.95rem;color:var(--sage);}
.tracelist .tick{
  flex:none;width:24px;height:24px;border-radius:50%;background:var(--band);color:var(--gold-pale);
  display:grid;place-items:center;margin-top:2px;
}
.tracelist .tick svg{width:13px;height:13px;}
.tracelist b{color:var(--ink);font-weight:600;}

/* a facsimile of the sticker on the back of the pouch */
.label{
  background:linear-gradient(150deg,#fefcf7,var(--sand));border-radius:20px;--r:20px;
  padding:clamp(26px,3.4vw,38px);box-shadow:var(--sh-md);position:relative;
  transform:rotate(-1.4deg);
}
.label .lhead{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding-bottom:14px;margin-bottom:18px;border-bottom:1px dashed var(--clay);
}
.label .lhead img{height:34px;width:auto;}
.label .vegmark{width:20px;height:20px;border:2px solid #1f7a34;display:grid;place-items:center;border-radius:2px;}
.label .vegmark i{width:9px;height:9px;border-radius:50%;background:#1f7a34;}
.label dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:11px 18px;align-items:baseline;}
.label dt{font-size:.63rem;letter-spacing:.2em;text-transform:uppercase;color:var(--gold-dk);font-weight:600;}
.label dd{margin:0;font-family:"Jost",monospace;font-size:1.02rem;color:var(--ink);
  letter-spacing:.05em;font-weight:500;}
.label .lfoot{margin-top:20px;padding-top:14px;border-top:1px dashed var(--clay);
  font-size:.78rem;color:var(--sage-soft);}

/* ---- range: the four product families ---- */
.families{
  list-style:none;margin:clamp(26px,3.4vw,40px) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:clamp(14px,1.6vw,20px);
}
.families li{
  background:#fff;border-radius:16px;padding:22px 22px 24px;
  box-shadow:var(--sh-sm,0 8px 20px -14px rgba(60,81,40,.5));position:relative;
}
.families .fdot{
  display:block;width:26px;height:6px;border-radius:999px;
  background:var(--fc);margin-bottom:14px;
}
.families h3{
  margin:0 0 7px;font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:1.02rem;color:var(--ink);font-weight:600;line-height:1.25;
}
.families p{margin:0;font-size:.88rem;line-height:1.5;color:var(--sage);}

.rangesub{
  margin:clamp(38px,4.6vw,58px) 0 0;font-family:"Fraunces",serif;
  font-variation-settings:"SOFT" 50,"WONK" 1;font-weight:600;
  font-size:clamp(1.02rem,1.2vw,1.22rem);color:var(--band);
}
.rangenote{
  margin:clamp(26px,3vw,36px) 0 0;font-size:.92rem;color:var(--sage);
  display:flex;flex-wrap:wrap;align-items:center;gap:14px;
}

/* ---- story: drop cap + pull quote ---- */
.story .first::first-letter{
  float:left;font-family:"Fraunces",serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:3.55em;line-height:.82;padding:.06em .12em 0 0;color:var(--gold-dk);font-weight:600;
}
.pull{
  margin:30px 0;padding:4px 0 4px 26px;border-left:3px solid var(--gold);
  font-family:"Fraunces",serif;font-variation-settings:"SOFT" 55,"WONK" 1;font-weight:500;
  font-size:clamp(1.2rem,1.9vw,1.62rem);line-height:1.34;color:var(--ink);
}
.story .sign{margin-top:26px;font-size:.9rem;color:var(--sage);}
.story .sign b{display:block;font-size:1.02rem;color:var(--band);font-weight:600;}
.story .sign .tag{display:block;font-size:.86rem;color:var(--gold-dk);font-weight:500;
  letter-spacing:.04em;margin-bottom:3px;}
.story .sign .heart{font-size:.8em;vertical-align:1px;}

/* sub-headings inside the story: "Our promise", "Our vision" */
.storysub{
  margin:40px 0 0;font-family:"Fraunces",serif;font-variation-settings:"SOFT" 50,"WONK" 1;
  font-size:clamp(1.06rem,1.2vw,1.26rem);line-height:1.2;color:var(--band);font-weight:600;
}
.storysub+.tracelist{margin-top:16px;}

/* the source line under a pull quote — a claim on the page should say where it
   came from, per "keep them checkable" above */
.pull cite{
  display:block;margin-top:13px;font-family:"Jost",system-ui,sans-serif;font-style:normal;
  font-variation-settings:normal;font-weight:500;font-size:.74rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--gold-dk);
}
.pull.vision{margin-top:16px;border-left-color:var(--sage);}

@media (max-width:1080px){
  .tracegrid{grid-template-columns:1fr;}
  .label{transform:none;}
  .storyart{position:static;}
}
@media (prefers-reduced-motion:reduce){ .mtrack{animation:none;} }

/* ===========================================================
   Added 4 Sep 2026 from the reference walkthrough with the
   owner (Plaud recording "09-04 Meeting"). Four blocks:
   origin (onyxcoffeelab.com), recipes (dandelionchocolate.com),
   Satwa Saheli (rumispice.com) and the scroll colour-shift the
   owner liked on a reference "Our Mission".
   Every one of them is light, warm and low-contrast on purpose:
   the owner rejected both bright and dark palettes repeatedly.
   =========================================================== */

/* ---------------- origin / where it comes from ----------------
   Onyx runs three place cards, each a full-bleed looping video
   behind a name, one paragraph of terroir and a link. The owner
   said plainly "we'll not have a video as of now", so the field
   is a tinted ground with line art - the same honest placeholder
   the category cards already use - and each card keeps a <video>
   slot that switches on the moment a file exists. */
/* ivory: moved to third on 9 Sep, between cats (cream) and recipes (sage), so
   cream would have met cream. Third time a section move has broken this rule -
   always re-run the adjacency check after reordering. */
.origin{padding:clamp(38px,4.6vw,66px) 0;background:var(--ivory);}
.ohead{text-align:center;max-width:64ch;margin:0 auto clamp(30px,4vw,48px);}
.ohead .lede{margin-top:12px;color:var(--sage);font-size:1.02rem;line-height:1.6;}
/* ---- How it is made -------------------------------------------------------
   A coverflow carousel of flip cards, after "Meet Our Canneries" on
   eatfishwife.com. Their measurements, read off the live page: the centre slide
   larger and in front, its neighbours scaled down on negative z-index, a 2px
   border, a 20px radius, and the back face rotated 180deg.
   Position comes from one custom property per card - --d, how many steps that
   card is from centre - so the whole layout is CSS and JS only sets a number. */
/* padding by HEIGHT, not width: the selected card is 444px tall now, and with
   width-based padding the section ran 856px against 819px of usable screen. */
/* cream: "why choose" moved to third on 9 Sep and is ivory, so ivory met ivory. */
.made{padding:clamp(22px,3vh,48px) 0;background:var(--cream-sg);overflow:hidden;}
.made .mhead{text-align:center;max-width:62ch;margin:0 auto clamp(12px,1.8vh,24px);}
.made .mhead .lede{margin-top:12px;color:var(--sage);line-height:1.6;}

/* 50vh, not 58: at 58 the bigger cards pushed this section to 932px against
   819px of usable screen, and the block stopped fitting. */
.made .mstage{
  position:relative;height:clamp(340px,48vh,540px);
  perspective:1600px;                      /* the flip needs real depth */
  margin-bottom:clamp(10px,1.6vh,20px);
}
/* .mkrow, NOT .mtrack: .mtrack already belongs to the Marathi dish marquee
   above (display:flex; width:max-content; a scrolling animation). Reusing the
   name gave this track width:max-content, which with inset:0 computes to ZERO
   width - so every card piled up at x=0 and threw 2849px of page overflow. */
.made .mkrow{position:absolute;inset:0;}
.made .mcard{
  position:absolute;top:50%;left:50%;
  /* The side cards. The SELECTED one is bigger and a taller rectangle - see
     .mcard.is-on below - so these sit a step down from it in both dimensions. */
  width:clamp(240px,31vh,360px);aspect-ratio:4/5;
  margin:0;cursor:pointer;
  /* --d is the signed distance from centre; everything else follows from it */
  transform:
    translate(-50%,-50%)
    translateX(calc(var(--d,0) * clamp(240px,33vh,390px)))
    scale(calc(1 - (var(--abs,0) * .16)));
  opacity:calc(1 - (var(--abs,0) * .3));
  transition:transform .62s var(--ease),opacity .62s var(--ease),
             width .62s var(--ease);
}
.made .mcard[style*="--abs: 3"],.mcard[style*="--abs:3"]{opacity:0;pointer-events:none;}
/* FOUR cards (17 Sep 2026, one per shelf). js/satva.js writes the count onto the
   track. With an even number of cards the one directly opposite the centre has
   no mirror - at six it landed at abs 3 and was already hidden by the rule
   above, but at four it lands at abs 2 and would hang on the right on its own,
   with nothing at -2 to answer it. Hiding abs 2 at this count leaves a
   symmetrical left / centre / right, and the fourth card simply rotates in. */
.made .mkrow[data-count="4"] .mcard[style*="--abs: 2"],
.made .mkrow[data-count="4"] .mcard[style*="--abs:2"]{opacity:0;pointer-events:none;}

.made .mflip{
  position:relative;width:100%;height:100%;
  transform-style:preserve-3d;
  transition:transform .7s var(--ease);
}
.made .mcard.is-flipped .mflip{transform:rotateY(180deg);}
.made .mface{
  position:absolute;inset:0;overflow:hidden;
  border:2px solid rgba(191,138,23,.55);border-radius:26px;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  background:var(--band);
  box-shadow:0 18px 40px -22px rgba(30,44,22,.55);
}
.made .mback{transform:rotateY(180deg);}
.made .mface video,.mface img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
}
/* the scrim only under the words, so the film keeps its own light */
.made .mface::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:80%;
  /* Heavier than the category cards' scrim, and deliberately. Those sit over a
     STILL photograph that can be checked once; these sit over VIDEO, where a
     bright frame can arrive at any moment. Measured with the old .92/.55 ramp:
     the lightest pixel under the copy hit rgb(230,224,218) - white text at
     1.31:1. The scrim now carries the copy on its own, whatever the frame. */
  background:linear-gradient(to top,rgba(20,28,15,.62) 0%,rgba(20,28,15,.4) 45%,rgba(20,28,15,0) 100%);
}
/* The copy carries its OWN backdrop, rather than trusting the card-wide scrim.
   Over a still photograph a gradient can be checked once and trusted; over video
   the frame changes constantly, and the turmeric footage in particular goes very
   bright. Measured against the brightest frame with only the card scrim: body
   copy 3.20:1 and the Devanagari 1.76:1 - both under AA. A local backdrop is the
   only thing that holds whatever the frame does. */
.made .mcopy{
  position:absolute;left:0;right:0;bottom:0;z-index:2;padding:clamp(18px,2.3vh,28px);
  /* Lightened at the owner's request - the old .96/.9/.74 panel was legible but
     it hid the bottom third of every film. Now it fades out towards its top so
     the picture reads through, and the type carries a shadow so it does not
     depend on the panel alone. Measured after the change, not before: see the
     numbers in the README. */
  /* The fade happens ABOVE the type, not through it. A straight ramp lightened
     the panel everywhere, including where the Devanagari sits, and that line -
     --gold-lt on a bright frame - fell to 2.76:1. Holding ~.85 under the text
     and dropping fast in the last third gives the picture back its top edge
     without taking the copy down with it. */
  background:linear-gradient(to top,
    rgba(16,23,12,.93) 0%,
    rgba(16,23,12,.86) 58%,
    rgba(16,23,12,.5) 84%,
    rgba(16,23,12,.16) 100%);
}
.made .mcopy h3,.made .mcopy p{text-shadow:0 1px 10px rgba(10,16,6,.85),0 0 24px rgba(10,16,6,.5);}
.made .mcopy h3{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.22rem,2.7vh,1.62rem);font-weight:600;color:#fff;margin:0;line-height:1.12;
  text-shadow:0 1px 10px rgba(12,18,8,.5);
}
.made .mcopy h3 .dv{display:block;font-size:.78em;color:var(--gold-lt);margin-top:3px;}
.made .mcopy p{
  margin:8px 0 0;color:rgba(255,255,255,.94);font-size:clamp(.86rem,1.7vh,1rem);
  line-height:1.5;max-width:34ch;
}
.made .mtag,.mturn{
  position:absolute;z-index:2;font-size:.6rem;letter-spacing:.14em;text-transform:uppercase;
  font-weight:700;padding:5px 10px;border-radius:999px;
}
.made .mtag{top:12px;left:12px;background:var(--gold-pale);color:var(--ink);}
.made .mturn{
  top:12px;right:12px;background:rgba(255,255,255,.92);color:var(--ink);
  display:inline-flex;align-items:center;gap:5px;
}
.made .mturn svg{width:12px;height:12px;}
/* only the centre card invites a turn */
.made .mcard:not(.is-on) .mturn{opacity:0;}
/* The selected masala: bigger in BOTH dimensions than its neighbours, and a
   taller rectangle - 3:4 against their 4:5. At 1600x900 that is 333x444 beside
   279x349, so it reads as the chosen one by shape as well as by position.
   Not taller than 3:4: at 2:3 the card needs ~500px and stops fitting the
   stage, and the stage cannot grow without the section leaving the screen. */
.made .mcard.is-on{
  /* Widened again, and past square this time: 5:4 LANDSCAPE, where the side
     cards stay 4:5 portrait. The selected masala is now a wide rectangle among
     tall ones, which is the clearest the difference can be made without
     touching the neighbours at all.
     Going wide also buys height back - a 5:4 card is shorter than the 6:7 one it
     replaces - so the stage came down with it and the section got shorter, not
     taller, despite the card being much bigger in area. */
  width:clamp(390px,57vh,680px);aspect-ratio:5/4;
}

/* the centre card is lifted and gold-edged; the others sit back, quiet */
.made .mcard.is-on .mface{
  border-color:var(--gold);
  box-shadow:0 34px 70px -26px rgba(30,44,22,.62), 0 0 0 1px rgba(191,138,23,.35);
}
.made .mcard:not(.is-on) .mface{border-color:rgba(60,81,40,.35);}

.made .mbar{display:flex;align-items:center;gap:clamp(14px,3vw,36px);flex-wrap:wrap;}
.made .mtabs{flex:1 1 auto;min-width:0;}
.made .mnav{display:flex;gap:9px;flex:none;}
/* .arrow is absolutely positioned by default (it belongs to the hero), so both
   buttons stacked on the same spot and only one was visible. Same override the
   shelf bar already makes. */
.made .mnav .arrow{position:static;bottom:auto;left:auto;right:auto;
  width:42px;height:42px;background:#fff;}
.made .mnote{margin:clamp(10px,1.6vh,18px) 0 0;font-size:.78rem;color:var(--sage);}

@media (max-width:700px){
  .mcard{width:clamp(200px,64vw,260px);}
  .mstage{height:clamp(290px,52vh,360px);}
}
@media (prefers-reduced-motion:reduce){
  .mcard,.mflip{transition:none;}
}
.onote{
  margin-top:clamp(22px,2.6vw,30px);font-size:.88rem;color:var(--sage);
  text-align:center;max-width:66ch;margin-inline:auto;line-height:1.6;
}
/* three cards need about 300px each to hold a place name and a paragraph; below
   that they stack rather than squeeze. 3 cards make an orphan at 2 columns. */
@media (max-width:1000px){ .ogrid{grid-template-columns:1fr;} .ocard{min-height:300px;} }

/* ---------------- recipes ----------------
   After dandelionchocolate.com, whose recipe card carries the
   cook time next to the name ("Just Chocolate Cake · 2½ Hours").
   That badge is the whole reason the block works - it turns a
   pretty photo into something you can decide about - so it is
   the one thing taken. Their block is a two-card teaser with a
   MORE RECIPES link; there is nowhere to link to yet, so ours
   shows the full set and links to WhatsApp instead. */
/* NO BACKGROUND HERE. There were two .recipes rules in this file, both setting a
   ground, at equal specificity - so the later one silently won and this one had
   been dead for some time while still looking authoritative. The ground is
   declared ONCE now, in the rule further down; only layout lives here. */
.recipes{padding:clamp(38px,4.6vw,66px) 0;overflow-x:clip;}
.rechead{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:24px;flex-wrap:wrap;margin-bottom:clamp(26px,3.4vw,40px);
}
/* --band, not --sage: see the note on the second .rechead .lede below. Both
   rules are kept in step so they cannot disagree the way the two .recipes
   grounds did. */
.rechead .lede{margin-top:10px;color:var(--band);max-width:52ch;line-height:1.6;}
/* ---- Cook something for someone ------------------------------------------
   Rebuilt after the "Odd for every occasion" row on oddbox.co.uk: four plain
   photographs, the dish name underneath, and nothing else. No card, no border,
   no badge, no scrim over the picture - the whole point of that row is the air
   around it. The moving strip and its two tabs came out with the redesign. */
/* THE MINT, 18 Sep 2026: "Keep this same background for cook for someone also -
   green shade". This is the mission band's own ground, the same two mint stops
   on the same 160deg - so the two green blocks on the page are green in the
   same way rather than approximately.

   It is the STATIC pair, not the mission's live one. That block mixes these
   stops against cream through a --mx custom property as you scroll, which is
   the colour-shifting ground the owner asked for back in September; copying the
   shift as well would put two grounds on the page changing under the reader at
   once. This is where that gradient settles.

   THE ONLY DECLARATION OF THIS GROUND - see the note on the earlier .recipes
   rule above, which used to set one too and lost silently.

   The comment this replaces said cream was "the only one of the four that
   does" - written when Saheli above was dark green and Contact below was sage.
   Neither is true now: Saheli is the salt wash and the footer is plum, so the
   run reads mint, salt, mint, plum and no two neighbours are alike. */
.recipes{
  padding:clamp(30px,4vh,64px) 0;
  background:linear-gradient(160deg,var(--sage-mist),var(--sage-mist-2));
  overflow-x:clip;
}
/* left, to the grid edge - a centred heading floating over a four-column grid
   is what looked unaligned. The cards' own titles are left-aligned too, so
   the whole block now shares one left margin. display is reset to block on
   purpose: the earlier .rechead above is a wrapping flex row (it once put a
   heading and a link on one line), and left as flex the h2 and the lede were
   two wrapped flex items carrying the row's 24px gap between them. */
.rechead{
  display:block;text-align:left;max-width:56ch;
  margin:0 0 clamp(20px,2.8vh,38px);
}
.rechead h2{margin:0;color:var(--ink);}
/* --band, not --sage, from 18 Sep 2026, when this block took the mission's mint.
   The ground is a GRADIENT, so a colour has to clear AA at both stops, and
   --sage only managed the lighter one: 4.74:1 on #d8e5d6 but 4.24 on #c9dbc5.
   (--sage-soft is worse still here, 3.71 / 3.32 - the palette note at the top of
   this file already says to prefer --sage on a sage-mist ground; on the deeper
   stop even that is not enough.) --band measures 6.71 / 6.01. */
.rechead .lede{margin:10px 0 0;color:var(--band);line-height:1.55;}

.recgrid{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(18px,2.2vw,34px);
  max-width:1280px;margin-inline:auto;
}
.recgrid .reccard{
  background:none;border:0;box-shadow:none;padding:0;margin:0;
  display:flex;flex-direction:column;
}
/* the photograph carries the card, so it gets the whole width and a shape that
   suits a plate: 4:3, not the old portrait crop */
/* The bowl shape from sweetgreen.com. Measured on their menu row: the tile is
   just a 366px square with a 20px radius - the shape the owner liked is the
   BOWL inside the photograph, a soft square with corners so full they read as
   round. That is a superellipse (|x|^4+|y|^4=1, the "squircle"), which CSS has
   no keyword for, so it is traced as a 32-point polygon in percentages: the
   points are proportional, so the shape holds at every card width. A plain
   border-radius could not do it - a radius rounds only the corner arc and
   leaves flat sides, while a squircle curves continuously all the way round,
   and that continuous curve is what makes it read as a bowl rather than a
   rounded box. Square, so the eight facets are even. */
.recgrid .rshot{
  position:relative;width:100%;aspect-ratio:1/1;overflow:hidden;
  background:var(--sage-mist-2);
  clip-path:polygon(
    100.0% 50.0%, 99.52% 72.08%, 98.06% 80.93%, 95.59% 87.27%,
    92.04% 92.04%, 87.27% 95.59%, 80.93% 98.06%, 72.08% 99.52%,
    50.0% 100.0%, 27.92% 99.52%, 19.07% 98.06%, 12.73% 95.59%,
    7.96% 92.04%, 4.41% 87.27%, 1.94% 80.93%, 0.48% 72.08%,
    0.0% 50.0%, 0.48% 27.92%, 1.94% 19.07%, 4.41% 12.73%,
    7.96% 7.96%, 12.73% 4.41%, 19.07% 1.94%, 27.92% 0.48%,
    50.0% 0.0%, 72.08% 0.48%, 80.93% 1.94%, 87.27% 4.41%,
    92.04% 7.96%, 95.59% 12.73%, 98.06% 19.07%, 99.52% 27.92%
  );
}
.recgrid .rshot img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(1.12) contrast(1.02);
  transition:transform .7s var(--ease);
}
.recgrid .reccard:hover .rshot img{transform:scale(1.04);}
.recgrid .reccard h3{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.02rem,1.5vw,1.24rem);font-weight:600;color:var(--ink);
  line-height:1.22;margin:14px 0 0;
}
.recgrid .ruses{
  margin:6px 0 0;font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  font-weight:600;
  /* The product accents were picked to sit on WHITE cards; on this sage ground
     two of the four fell under AA (mustard 4.44:1, quinoa 4.38:1). Mixing each
     accent toward the ink keeps its hue and its identity while clearing the
     threshold - the same fix already used for the 01-04 numerals in Why SATVA. */
  color:color-mix(in srgb, var(--acc, var(--gold-dk)) 62%, var(--ink));
}
.recfoot{display:flex;justify-content:flex-start;margin-top:clamp(20px,2.6vh,36px);}

@media (max-width:900px){ .recgrid{grid-template-columns:repeat(2,1fr);} }
@media (max-width:520px){ .recgrid{grid-template-columns:1fr;} }

/* ---------------- Satwa Saheli ----------------
   Rumi Spice runs "Empowers Afghan Women" as one of three
   why-choose columns. The owner wants it as a section of its
   own under the name Satwa Saheli, so it gets the room. Two
   portrait slots and a stat strip.

   GROUND CHANGED 17 Sep 2026 to #f3dfdc, the "pink salt wash" - a pale step of
   --himalayan #d98a8a, the unrefined salt already in the range.

   WHY, because the lilac it replaces was not ugly, it was invisible: this band
   sits between the mission band's mint above and the footer's mint below, and
   #e2d9e3 fell BETWEEN the two in lightness (+0.042 against one, -0.042 against
   the other) while being cool, like both. So the boundary had neither a change
   of warmth nor a step to announce it, and the section read as a continuation
   of the one before it. #f3dfdc is warm against both neighbours AND clearly
   lighter (+0.099 against the mint above), so the edge reads twice over. It is
   also the only candidate that names a product we actually sell.

   Every ink got BETTER, so nothing else had to move:
     --ink 10.70 -> 11.49   --green 8.01 -> 8.60
     --band 6.36 -> 6.83    --gold-dk 4.53 -> 4.86
   White (1.38) and --gold-lt (1.41) are still unusable here, as they were on
   the lilac.

   HISTORY: this was the dark brand green until 10 Sep 2026, when it became the
   brightland.co press-bar lilac and every piece of type in here was inverted
   from light-on-dark. That inversion is what this ground inherits.
   NOTE: the footer is the only dark band left in the page. Worth a look at
   whether there is enough weight low down - the green is one line away. */
.saheli{padding:clamp(38px,4.6vw,66px) 0;background:#f3dfdc;color:var(--green);position:relative;overflow:hidden;}
.saheli::before{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.06;
  background:url("../assets/leaf-mask.png") center/contain no-repeat;
}
.sahgrid{
  display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(28px,4vw,58px);align-items:stretch;
  position:relative;
}
.saheli .kick{color:var(--gold-dk);}
.saheli h2{color:var(--ink);}
.saheli h2 .goldtext{color:var(--gold-dk);-webkit-text-fill-color:currentColor;background:none;}
.sahname{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.9rem,3.4vw,2.7rem);font-weight:600;line-height:1.1;margin:0 0 6px;
}
.sahname .dv{display:block;font-size:.62em;color:var(--gold-dk);margin-bottom:6px;}
/* --green, not --sage. On the old lilac --sage measured 4.49:1, a hundredth
   short of AA, which is what forced this. On the salt wash it comes to 4.83 and
   would now be legal - but --green measures 8.60 here and body copy is the last
   place to spend contrast, so it stays. */
.saheli .lede{color:var(--green);font-size:1.03rem;line-height:1.68;margin-top:14px;}
.saheli .lede b{color:var(--ink);}
.sahstats{display:flex;gap:clamp(20px,3vw,40px);flex-wrap:wrap;margin-top:clamp(22px,2.6vw,30px);}
.sahstat{display:flex;flex-direction:column;gap:3px;}
.sahstat b{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.6rem,2.6vw,2.1rem);color:var(--gold-dk);line-height:1;font-weight:600;
}
.sahstat span{font-size:.74rem;letter-spacing:.13em;text-transform:uppercase;color:var(--band);}
/* the unfilled slots still have to be READABLE - they are a promise to the
   reader that a number is coming, not decoration */
.sahstat.todo b{font-size:1.05rem;color:var(--band);font-style:italic;letter-spacing:0;opacity:.75;}
.sahcta{margin-top:clamp(24px,3vw,34px);display:flex;gap:12px;flex-wrap:wrap;}
/* the ghost button was inverted for the dark ground; the ground is light again,
   so the shared light-ground version is correct and the override goes. */
.sahart{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,18px);}
.sahshot{
  position:relative;overflow:hidden;border-radius:16px;background:var(--tint);
  border:1px solid rgba(30,44,22,.18);min-height:clamp(240px,30vw,340px);
}
.sahshot:nth-child(1){margin-top:clamp(18px,3vw,34px);}
.sahshot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* The two placeholder tiles follow the ground, and always a step DEEPER than it
   (17 Sep 2026: #e9d6d3 -> #d8c6c4 against #f3dfdc, the same 0.96x / 0.89x steps
   the lilac pair used) because a panel painted its own background's colour is
   not a panel; the dashed border carries the rest of the boundary. The tint is
   set per tile in index.html, so it has to be changed THERE whenever this
   ground changes - the two cannot drift apart silently.
   Everything that sat on them was built for the old dark green and brown fills
   and had to come with them: a white icon and a dark-to-transparent caption
   scrim both disappear on a pale tile. */
.sahshot svg{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:40%;height:auto;color:var(--band);opacity:.4;
}
.sahshot.todo{border-style:dashed;border-color:rgba(30,44,22,.34);}
.sahshot .stag{
  position:absolute;bottom:0;left:0;right:0;padding:9px 11px;
  /* no scrim: it existed to lift pale gold off a dark photo tile, and on a pale
     tile it would be a dark smear across the bottom of the panel. --band on the
     tile's darker stop measures 5.33:1 on the salt wash (4.88 on the old lilac);
     --gold-dk was the other candidate and reaches only 3.79 there, so it is not
     used. */
  font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--band);
}
.sahnote{
  margin-top:clamp(24px,3vw,34px);font-size:.86rem;line-height:1.6;
  color:var(--green);max-width:70ch;position:relative;
  border-left:2px solid var(--gold-dk);padding-left:14px;
}
@media (max-width:900px){ .sahgrid{grid-template-columns:1fr;} .sahshot:nth-child(1){margin-top:0;} }

/* ---------------- the mission colour-shift ----------------
   The owner noticed a reference site whose ground changes colour
   only for its "Our Mission" section: "when we come here to our
   mission, the site color is changing. It's like that only for
   our mission." That is the effect, on our own palette - cream
   warming to gold as the section crosses the viewport. The domain
   was never named in the recording, so the mechanism is ours.
   --mx is driven from js/satva.js (0 at entry, 1 at centre). */
.mission{
  --mx:0;
  padding:clamp(64px,8vw,116px) 0;position:relative;overflow:hidden;
  /* At rest this is the cream of the section before it (how it is made). It
     shifts toward sage as it crosses the middle of the screen - which used to
     be the ground of the section after it, when the recipe row followed. Satva
     Saheli was moved up above the recipes on 10 Sep 2026, so what follows is
     now the dark green band: the shift no longer lands on its neighbour's
     colour, it just carries the page from cream into the dark. */
  background:
    linear-gradient(160deg,
      color-mix(in oklab,var(--cream-sg),var(--sage-mist) calc(var(--mx) * 100%)) 0%,
      color-mix(in oklab,var(--sand),var(--sage-mist-2) calc(var(--mx) * 100%)) 100%);
  transition:background .5s linear;
}
/* no color-mix (older Safari/Firefox): cross-fade a second layer instead */
@supports not (background:color-mix(in oklab,red,blue 50%)){
  .mission{background:linear-gradient(160deg,var(--cream-sg),var(--sand));}
  .mission::before{
    content:"";position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(160deg,var(--sage-mist),var(--sage-mist-2));
    opacity:var(--mx);transition:opacity .5s linear;
  }
}
.mission .wrap{position:relative;}
.mission .kick{color:var(--gold-dk);}

/* ---- the mission/story slider ---------------------------------------------
   Layout taken from rumispice.com/pages/about, measured at 1240 wide: the
   photograph is 368x556 - a 2:3 portrait - flush with the left edge of the
   wrap, 71px of air to the copy, and the copy top-aligned with the top of the
   picture rather than centred on it. That top alignment is most of the look:
   it is what leaves the deep well of space under the paragraph.

   The two slides are stacked in ONE grid cell rather than laid side by side in
   a track. A track would need its width measured and shifted (the trap the
   recipe marquee hit, where half the track was not the pitch once a gap
   existed); one cell needs no arithmetic, cross-fades for free, and takes the
   height of the TALLER slide, so the ground never jumps when they swap. */
/* 17 Sep 2026, the owner: "THis block should have a sliding between the our
   story card and the mission card". The two slides used to CROSS-FADE, which
   from a reader's seat is the section quietly becoming a different section -
   nothing tells you a second panel exists or which way it went. They now travel:
   the outgoing card leaves to one side and the incoming card arrives from the
   other, so the movement itself says "there are two of these, side by side".
   The stack stays exactly as it was - both slides still share one grid cell, so
   no track has to be measured (the trap the recipe marquee hit) and the view is
   still as tall as the TALLER slide. Only the transform is new.
   overflow-x:clip, not hidden: `hidden` on one axis forces the other to `auto`
   and silently makes this a scroll container, which would trap the page's own
   scrolling here. `clip` does not, and the 28px margin lets the pictures' own
   shadows sit outside the clip edge instead of being sliced off. */
.msview{display:grid;overflow-x:clip;overflow-clip-margin:28px;}
.msslide{
  grid-area:1 / 1;                     /* every slide in the same cell */
  display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:clamp(24px,5.7vw,71px);align-items:start;
  /* the view is as tall as the TALLER slide, so the shorter one has slack. This
     splits that slack above and below its row instead of dropping it all
     underneath, where it read as a hole rather than as air. align-items:start
     above still tops the copy against the picture inside the row. */
  align-content:center;
  opacity:0;visibility:hidden;pointer-events:none;
  /* --x is the side this slide waits on, written by js/satva.js the instant
     before it moves: the leaving card is sent to one side, the arriving card is
     parked on the other. The opacity ramp is shorter than the travel so the
     card is fully solid before it lands, rather than fading the whole way. */
  transform:translateX(var(--x,100%));
  transition:transform .62s var(--ease),opacity .34s var(--ease),
             visibility 0s linear .62s;
}
.msslide.is-on{
  opacity:1;visibility:visible;pointer-events:auto;
  transform:translateX(0);transition-delay:0s;
}

/* The picture is sized by viewport HEIGHT, not width. This section has to fit a
   screen, and a 2:3 portrait is the tallest thing on the page - at Rumi's 33%
   of the wrap it is 556px, which alone is most of a laptop window. Driving the
   height and letting the aspect give the width is the only way a fixed-ratio
   picture can answer to the space it actually has. At 1100px tall it lands on
   540x360, within a couple of pixels of Rumi's own. */
.msshot{
  /* Enlarged 10 Sep 2026 - the owner asked for a bigger picture. 56vh matched
     Rumi's 556px exactly, but on the laptop window this is actually read on
     (625px of page) that came to 350px tall and left 137px of the screen unused
     under the block. 74vh takes most of that back: 462x308 here, 620x413 on a
     tall window, against Rumi's 556x368. The section still fits a screen, which
     is the constraint that set this number in the first place. */
  height:clamp(300px,74vh,620px);aspect-ratio:2/3;
  border-radius:20px;overflow:hidden;background:var(--sage-mist-2);flex:none;
}
.msshot img{width:100%;height:100%;object-fit:cover;display:block;}

/* 64ch, not 56: the picture is sized by viewport height, so on a short window it
   narrows and drags the copy left with it - at 625px tall that left a 233px
   gutter down the right of the section. A wider measure takes most of it back
   and still breaks near Rumi's own 615px line. */
.mscopy{max-width:64ch;padding-top:clamp(2px,1.4vh,18px);}
.misline{
  font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.5rem,3.3vw,2.5rem);font-weight:600;color:var(--ink);
  line-height:1.22;margin:14px 0 0;
}
.misline em{font-style:normal;display:block;color:var(--sage);}
/* 1.8, which is Rumi's 16px on 28.8px. Long-form copy beside a tall picture
   needs the extra leading; the site's usual 1.6 reads cramped in this column. */
.mission .lede{margin-top:clamp(16px,2vw,22px);color:var(--green);
  font-size:1.04rem;line-height:1.8;}
.mission .rule{margin-top:clamp(20px,2.4vw,28px);}
.msmore{margin-top:clamp(14px,1.8vw,20px);}
.msmore a{font-size:.82rem;letter-spacing:.09em;text-transform:uppercase;font-weight:600;
  color:var(--gold-dk);border-bottom:1px solid currentColor;padding-bottom:2px;}
.msmore a:hover{color:var(--green);}

/* ---- the mission slide, after brightland.co ---------------------------------
   Their block reverses the Rumi one: the copy is on the LEFT and the picture on
   the RIGHT, the mission is set as a QUOTATION rather than a headline, and it
   is signed underneath. Measured on their page at 1240 wide:
     copy column   621px, text starting 28px inside the row
     quote         40px, weight 450, no extra leading
     picture       449 x 520, object-fit cover, 18px radius
     column gap    18px  (much tighter than Rumi's 71)
     caption       12px, letter-spacing .6px, capitalised
   The tight 18px gap is the deliberate part - it is what makes the picture read
   as a panel butting against the words rather than a separate column. */
/* 59/39, which is Brightland's own split (677 and 449 of a 1144 row). The
   picture takes a SHARE of the width here rather than being sized by its own
   ratio like the Rumi slide: on their page the picture fills its column and is
   cropped to fit, and doing the same is what keeps their tight 18px gap tight.
   Sizing it by height instead left the quote ending 201px short of the picture
   on a laptop window - the gap was the whole point of the design and it had
   quietly become the largest space in the block. */
/* vahdam.com's version of this row, which is what the owner asked the picture
   to look like: copy on the left, and on the right a ROUND portrait with the
   signature and name beside it. Measured on their page at 1440 wide - the row
   is 1208, the copy 625, and the right-hand group 584 x 369 with the circle
   about 252px across. The right column is `auto` here rather than a share of
   the width, because a circle has a size of its own and should not be stretched
   by the column it sits in. */
.msslide.brt{grid-template-columns:minmax(0,1fr) auto;gap:clamp(20px,3vw,48px);}
.msaside{display:flex;align-items:center;gap:clamp(14px,1.8vw,26px);}
/* the copy column IS the 1fr here, so it must not carry the Rumi slide's 64ch
   cap - that cap left the box ending at 722 while the picture started at 875,
   turning Brightland's tight 18px gap into 153px of nothing. The measure is
   kept on the quote (26ch) and the paragraph (46ch) instead, where it belongs. */
.msslide.brt .mscopy{max-width:none;}
/* ROUND, and sized by height like everything else in this slider so it holds
   its shape on a short window. Vahdam's circle is 252px; this runs 170-260. */
.msslide.brt .msshot{
  width:auto;height:clamp(170px,34vh,260px);aspect-ratio:1/1;
  border-radius:50%;flex:none;
}
.msq{
  margin:14px 0 0;font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 45,"WONK" 1;
  /* their quote is a flat 40px; ours has to survive a 390px phone, so it is a
     clamp that tops out just above theirs */
  font-size:clamp(1.32rem,2.9vw,2.35rem);font-weight:600;line-height:1.2;
  color:var(--ink);
  /* 26ch is where their line broke: "Brightland exists to bring" is 26
     characters in 621px at 40px */
  max-width:26ch;
}
.msqsub{
  margin:clamp(14px,1.8vh,22px) 0 0;max-width:46ch;
  color:var(--green);font-size:.96rem;line-height:1.75;
}
/* the signature, now beside the circle rather than under the copy - Vahdam sets
   the mark and the caption to the right of the portrait, and that grouping is
   what makes the circle read as a person signing rather than a stray photo. */
.mssign{max-width:19ch;}
/* the signature mark is the stacked lockup as well now; a square mark at the old
   42px cap was 44px wide and read as a speck next to the caption beside it */
.mssign img{display:block;width:auto;height:clamp(46px,6vh,66px);opacity:.92;}
.mssigcap{
  margin:10px 0 0;font-size:.72rem;line-height:1.55;letter-spacing:.08em;
  text-transform:uppercase;font-weight:600;color:var(--sage);
}
/* the brand name leads, the place follows, exactly as Vahdam stacks the name
   above the role above the descriptor */
.mssigcap b{display:block;color:var(--band);letter-spacing:.1em;}
.mssigcap span{display:block;}

/* ---- the whole story, inside the story slide -------------------------------
   The standalone Our Story section was folded in here, so the button opens the
   real thing in place rather than sending the reader somewhere else.
   grid-template-rows 0fr -> 1fr is the animatable way to do height:auto; the
   inner wrapper is what actually gets clipped. */
/* height, driven from satva.js, NOT the grid-template-rows:0fr->1fr trick.
   That trick is the tidier CSS and it is what was here first, but measured on
   the page it did not interpolate: the row sat at 15.4px for the whole half
   second and then snapped to 923 at the end - a dead pause followed by a jump.
   1fr resolves against the container's height, and while the container is
   collapsing to nothing there is nothing for it to resolve against. A pixel
   height measured off the content animates properly because both ends of the
   transition are real lengths; satva.js sets it, then hands back to `auto` so
   the panel still reflows if the window changes. */
.msfull{height:0;overflow:hidden;transition:height .45s var(--ease);}
.msfullin{opacity:0;transition:opacity .3s var(--ease);}
.msslide.is-open .msfullin{opacity:1;transition-delay:.12s;}
/* a little air once it is open, and the story's own top margin is not needed
   because the summary above it has gone */
.msslide.is-open .msfullin{padding-top:clamp(6px,1.4vh,16px);}
/* the summary is the closed state's copy, so it goes when the full text opens */
.msslide.is-open .msgist{display:none;}
/* open, this slide is an article, not a panel beside a picture: the type gets
   a reading measure and the picture stops dictating the height */
.msslide.is-open{align-items:start;}
.msslide.is-open .msshot{height:clamp(240px,34vh,340px);}

/* the story's own elements, restyled for this narrower column. These rules used
   to hang off .story, which no longer exists. */
.msfull p{margin:0 0 1.05em;color:var(--green);font-size:.96rem;line-height:1.8;}
.msfull p:last-child{margin-bottom:0;}
.msfull .first::first-letter{
  float:left;font-family:"Fraunces",Georgia,serif;font-weight:600;
  font-size:3.1em;line-height:.82;margin:.06em .09em 0 0;color:var(--gold-dk);
}
.msfull .pull{
  margin:1.4em 0;padding:2px 0 2px 20px;border-left:3px solid var(--gold);
  font-family:"Fraunces",Georgia,serif;font-weight:600;font-size:1.12rem;
  line-height:1.32;color:var(--ink);
}
.msfull .pull cite{
  display:block;margin-top:10px;font-family:inherit;font-style:normal;
  font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;
  font-weight:600;color:var(--gold-dk);
}
.msfull .storysub{
  margin:1.6em 0 0;font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 50,"WONK" 1;
  font-size:1.14rem;line-height:1.2;color:var(--band);font-weight:600;
}
.msfull .tracelist{margin:14px 0 0;padding:0;list-style:none;display:grid;gap:11px;}
.msfull .tracelist li{display:flex;gap:11px;align-items:flex-start;
  font-size:.92rem;line-height:1.6;color:var(--green);}
.msfull .tracelist .tick{
  flex:none;width:19px;height:19px;margin-top:3px;border-radius:50%;
  background:var(--sage-mist-2);color:var(--band);display:grid;place-items:center;
}
.msfull .tracelist .tick svg{width:12px;height:12px;}
.msfull .sign{margin-top:1.6em;font-size:.9rem;color:var(--sage);}
.msfull .sign b{display:block;font-size:1.02rem;color:var(--band);font-weight:600;}
.msfull .sign .tag{display:block;font-size:.86rem;color:var(--gold-dk);
  font-weight:500;letter-spacing:.04em;margin-bottom:3px;}
.msfull .sign .heart{font-size:.8em;vertical-align:1px;}

/* the disclosure is a button, not a link - it changes this page rather than
   going to another one, and a screen reader needs to hear it is expandable */
.msopen{
  border:0;background:none;padding:0 0 2px;cursor:pointer;
  font:inherit;font-size:.82rem;letter-spacing:.09em;text-transform:uppercase;
  font-weight:600;color:var(--gold-dk);border-bottom:1px solid currentColor;
}
.msopen:hover{color:var(--green);}

/* controls under the copy column, not over the picture: the picture is the
   subject here and an arrow sitting on it would be the second thing you see */
.msnav{display:flex;align-items:center;gap:18px;margin-top:clamp(20px,2.6vh,34px);}
.msnav .arrow{position:static;width:40px;height:40px;}
.msnav .arrow svg{width:15px;height:15px;}
.msarrows{display:flex;gap:9px;}
.msdots{display:flex;gap:9px;align-items:center;}
.msdots button{width:10px;height:10px;padding:0;border-radius:50%;
  border:1px solid rgba(62,48,20,.3);background:rgba(255,255,255,.75);cursor:pointer;
  transition:.35s var(--ease);}
.msdots button[aria-selected="true"]{background:var(--gold-dk);border-color:var(--gold-dk);
  width:30px;border-radius:999px;}

/* one column below the point where a 2:3 picture and a paragraph stop fitting
   side by side; the picture goes wide and short so it does not push the copy
   off the screen */
@media (max-width:820px){
  /* .msslide.brt has to be named here too. A media query adds no specificity,
     so the .msslide.brt rule above (0,2,0) beat a bare .msslide (0,1,0) inside
     this block and the brightland slide stayed two columns on a phone - copy
     crushed into 128px and 637px tall. */
  .msslide,.msslide.brt{grid-template-columns:1fr;gap:clamp(18px,3vw,26px);}
  .msshot,.msslide.is-open .msshot{
    height:auto;width:100%;aspect-ratio:4/3;
  }
  /* the mission circle stays a circle on a phone - it just gets smaller and
     the signature drops under it */
  .msslide.brt .msshot{height:clamp(140px,42vw,190px);width:auto;aspect-ratio:1/1;}
  .msaside{flex-direction:column;align-items:flex-start;gap:14px;}
  .mscopy{padding-top:0;max-width:none;}
  /* the picture goes first on a phone even on the brightland slide, where it is
     second in the source: a screenful of type before any image is a wall */
  .msslide.brt .mscopy{order:2;}
  .msslide.brt .msshot{order:1;}
  .msq{max-width:none;}
}
/* the shift is decorative; anyone who asked for less motion gets the plain ground */
@media (prefers-reduced-motion:reduce){
  .mission{transition:none;background:linear-gradient(160deg,var(--sage-mist),var(--sage-mist-2));}
  .mission::before{display:none;}
  /* the slides still swap - by dot, arrow or swipe - they just do not travel.
     translateX is dropped rather than set to 0, so a waiting slide is not left
     sitting on top of the live one; visibility still takes it out of the way. */
  .msslide{transition:none;transform:none;}
}

/* ---- one block per screen -------------------------------------------------
   The owner asked for each segment to fit the screen when scrolled. Two parts:
   the section padding above was cut roughly a third, which is where the height
   actually goes, and scrolling now settles on section boundaries.

   PROXIMITY, not mandatory. Eight of the fifteen sections are taller than the
   viewport and three of those are far taller (story 1401px, cats 1213px,
   recipes 1117px) - content, not padding. Mandatory snapping on a section
   nobody can see the bottom of traps the reader: the browser drags them back to
   the top of a block they were trying to read down. Proximity snaps when they
   land near a boundary and leaves them alone otherwise.

   scroll-padding-top keeps a snapped section clear of the sticky header, which
   would otherwise cover its first 81px. */
/* Set on html, which is the page's only scroll container now that body uses
   overflow-x:clip rather than hidden (see the body rule). While body was a
   scroll container these rules did nothing at all - the sections snapped to
   body, which had no snap type, and nothing ever snapped. */
html{
  scroll-snap-type:y proximity;
  scroll-padding-top:88px;
}
main > section,
body > section,
footer{scroll-snap-align:start;}
@media (prefers-reduced-motion:reduce){
  html{scroll-snap-type:none;}
}

/* ---- making the recipes block fit a screen --------------------------------
   At a 660px-tall window the usable height is 579px and this section was
   1055px: rechead 217 + tabs 34 + strip 624 + 118 padding. The strip is the
   bulk of it, and a strip's height is set by its CARD WIDTH through the image
   aspect - so the card width is now driven by viewport HEIGHT (vh), not width.
   A short window gets narrower cards and therefore shorter images, which is the
   only way a fixed-aspect card can respond to the height available. */
.recipes{padding:clamp(24px,3.2vh,52px) 0;}
.recipes .rechead{margin-bottom:clamp(10px,1.6vh,22px);}
.recipes .rechead h2{font-size:clamp(1.6rem,3.6vh,2.9rem);}
.recipes .rechead .lede{
  font-size:clamp(.84rem,1.55vh,1rem);line-height:1.5;max-width:58ch;margin-top:6px;
}
.recmq .reccard{flex:0 0 clamp(168px,25vh,268px);}
.recmq .reccard .rbody{padding:clamp(9px,1.35vh,18px);}
.recmq .reccard h3{font-size:clamp(.94rem,1.85vh,1.06rem);}
.recmq .reccard .rhi{font-size:.8em;}
.recmq .reccard .rdesc{
  margin-top:6px;font-size:clamp(.76rem,1.45vh,.87rem);line-height:1.45;
  /* three lines, then stop: the copy is written to be read on the card, but a
     five-line block is what pushed the strip past the screen */
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
.recmq .reccard .ruses{padding-top:9px;font-size:.66rem;letter-spacing:.08em;}

/* ---- short windows --------------------------------------------------------
   Every size on this page was tied to viewport WIDTH, so a wide-but-short
   window (a laptop with browser chrome and a bookmarks bar - about 660px of
   page) got full-height headings and cards and nothing fitted. This pass makes
   the shared parts answer to HEIGHT. */
/* 1000px, not 780: every value inside is a vh-based clamp, so it scales up
   with the window rather than snapping. Cutting it off at 780 meant a 900px
   laptop window got none of it and nothing fitted there either. */
@media (max-height:1000px){
  .shelves,.recipes,.reasons,.cats,.range,.origin,.trace,
  .mission,.story,.saheli,.contact{
    padding-top:clamp(16px,2.4vh,34px);
    padding-bottom:clamp(16px,2.4vh,34px);
  }
  /* except the two blocks that are a full screen with their content centred:
     they keep a top padding deep enough to hold the heading clear of the sticky
     nav. See the notes on .cats and .reasons. */
  .cats,.reasons{ padding-top:max(96px,clamp(16px,2.4vh,34px)); }
  .dsp.big{font-size:clamp(1.5rem,3.4vh,2.3rem);}
  .lede{font-size:clamp(.8rem,1.55vh,.96rem);line-height:1.46;}
  .chead{margin-bottom:clamp(14px,2vh,26px);}

  /* the recipe strip: narrower cards mean shorter images, and two lines of copy
     rather than three */
  .recmq .reccard{flex:0 0 clamp(148px,22vh,220px);}
  .recmq .reccard .rdesc{-webkit-line-clamp:2;}
  .recipes .rechead .lede{
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  }

  /* the category cards, same idea: the photo aspect is fixed, so the only way
     down is a narrower card */
  .catgrid{gap:clamp(12px,1.5vh,22px);}
  .catcard .cmeta{padding-top:9px;gap:4px;}
  .catcard .cblurb{font-size:.82rem;line-height:1.4;}

  /* the hero slide: its min-height now answers to vh, but the copy's own
     padding was still 86px top and bottom and that is what kept it 719px */
  .sh-copy{padding-block:clamp(16px,3vh,46px);}
  /* was min(4.2vw,5.6vh) with a 1.45rem floor - on a 660px-tall window that
     resolved to a 37px headline against a half-metre of photograph, which is
     what made the type look small. The height cap is now 7.4vh and the floor
     1.95rem, so the headline holds its scale on a short screen. */
  .sh-copy h2{font-size:clamp(1.95rem,min(4.9vw,7.4vh),3.9rem);margin:8px 0 14px;}
  .sh-copy > p{font-size:clamp(.98rem,min(1.35vw,2.6vh),1.26rem);line-height:1.55;max-width:56ch;}
  .sh-cta{margin-top:clamp(12px,2vh,22px);}

  /* the range opens as a menu, so its height is the families list plus the
     pills - the list goes to two columns rather than five stacked rows */
  /* NOT two columns - the default is auto-fit/minmax(210px), which already
     puts all five on ONE row at desktop widths. Forcing two columns made three
     rows and pushed this section from 686px to 829px. Only the spacing and the
     type come down. */
  .families{margin-top:clamp(12px,2vh,24px);gap:10px 14px;}
  .families h3{font-size:.92rem;}
  .families p{font-size:.78rem;line-height:1.38;}
  .rangepick{padding:clamp(12px,2vh,22px) clamp(16px,2vw,26px);font-size:.9rem;}
  .rangenote{font-size:.8rem;}

  /* the why-choose illustrations are the one thing in that section that can
     lose height without losing meaning. This USED to size the svg directly -
     width:clamp(74px,13vh,120px) - and that was written when the icons were
     212px line drawings on a flat ground. Once they moved inside the circles
     the wrapper became 55px while this rule still gave the svg 81px (13vh of a
     625px window), and since it is the later rule of two with equal
     specificity it won: the drawing painted 26px past its box, straight down
     onto the heading. It now moves the SAME custom property the circle uses,
     so the wrapper and the drawing can never disagree again - and on a short
     window the icon gets smaller, which was the intent all along. */
  .reasons .rgrid,.reasons .wgrid{gap:clamp(12px,1.8vh,22px);}
  .reason .art{--icon:clamp(40px,8.6vh,58px);}
  .reasons .rhead{margin-bottom:clamp(10px,1.6vh,20px);}
  .reasons .rcta{margin-top:clamp(10px,1.6vh,20px);}

  /* the last few pixels on the ones that were only just over */
  .recmq .reccard{flex-basis:clamp(140px,21vh,210px);}
  .rtabs{margin-bottom:clamp(10px,1.6vh,20px);}
  .rangesub{margin-bottom:clamp(8px,1.4vh,16px);}
  .rangenote{margin-top:clamp(8px,1.3vh,16px);font-size:.78rem;}
  .range .sechead{margin-bottom:clamp(10px,1.6vh,20px);}

  /* Shop by category: 351px-wide cards give a 234px photo, and two rows of that
     plus the heading is 813px. Capping the grid narrows the card, and the photo
     follows it down because the aspect is fixed. */
  /* no max-width here any more: capping this by viewport HEIGHT is what put
     the six cards in a narrow middle column on a wide screen. One row of six
     is shorter than two rows of three anyway, so the height problem this was
     solving solves itself. */
  .chead .lede{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}

  /* Origin: the photo field is 428px of the card's 430 - it is the section */
  /* the card is otag 26 + photo + place 87 + blurb 54 + link 24; the photo is
     the only part that can lose real height without losing information */
  .origin .ofield{height:clamp(108px,20vh,240px);}
  .origin .oplace{font-size:clamp(.95rem,2vh,1.2rem);line-height:1.2;}
  .origin .ohi{font-size:.78em;}
  .origin .olink{font-size:.76rem;}
  .origin .ohead{margin-bottom:clamp(10px,1.6vh,20px);}
  .origin .ohead .lede{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
  .origin .oblurb{font-size:.8rem;line-height:1.4;}
  .origin .onote{margin-top:clamp(8px,1.4vh,16px);font-size:.76rem;line-height:1.4;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
}

/* ---- "How it's made": the Recipe button and the recipe sheet (8 Oct 2026) ----
   The button sits under the line on the BACK of a card, so it only shows
   once the card has turned to the ingredient photograph. The sheet is a
   native <dialog>: Esc closes it, focus is kept inside, the page behind
   cannot be scrolled. Same photograph as the card, then the recipe. */
.made .mrecipe{
  margin-top:12px;display:inline-flex;align-items:center;gap:7px;
  font:700 .7rem/1 "Jost",system-ui,sans-serif;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink);background:var(--gold-grad);border:0;border-radius:999px;
  padding:10px 16px;cursor:pointer;box-shadow:0 6px 18px -8px rgba(0,0,0,.6);
  transition:transform .2s ease,box-shadow .2s ease;
}
.made .mrecipe svg{width:14px;height:14px;}
.made .mrecipe:hover{transform:translateY(-1px);box-shadow:0 10px 22px -8px rgba(0,0,0,.7);}
.made .mrecipe:focus-visible{outline:2px solid #fff;outline-offset:3px;}
.made .mcard:not(.is-on) .mrecipe{visibility:hidden;}

html.rsheet-open{overflow:hidden;}
.rsheet{
  width:min(1080px,calc(100vw - 32px));max-height:calc(100vh - 32px);
  padding:0;border:0;border-radius:24px;overflow:hidden;
  background:var(--cream);color:var(--ink);
  box-shadow:0 30px 80px -20px rgba(10,16,6,.6);
}
.rsheet::backdrop{background:rgba(16,23,12,.62);backdrop-filter:blur(4px);}
.rsheet[open]{animation:rs-in .28s ease-out;}
@keyframes rs-in{from{opacity:0;transform:translateY(14px) scale(.985);}to{opacity:1;transform:none;}}
.rs-box{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,6fr);height:min(760px,calc(100vh - 32px));}
.rs-shot{margin:0;position:relative;background:var(--ink);}
.rs-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
.rs-body{position:relative;overflow-y:auto;padding:clamp(28px,4vw,52px) clamp(22px,3.6vw,48px);overscroll-behavior:contain;}
.rs-close{
  position:sticky;top:0;float:right;margin:-12px -12px 0 12px;width:40px;height:40px;
  display:grid;place-items:center;border-radius:50%;border:1px solid rgba(30,44,22,.18);
  background:rgba(255,255,255,.75);color:var(--ink);cursor:pointer;z-index:2;
}
.rs-close svg{width:18px;height:18px;}
.rs-close:hover{background:#fff;}
.rs-kick{margin:0 0 10px;font:700 .68rem/1.3 "Jost",system-ui,sans-serif;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-dk);}
.rsheet h2{
  margin:0;font-family:"Fraunces",Georgia,serif;font-variation-settings:"SOFT" 45,"WONK" 1;
  font-size:clamp(1.8rem,3.4vw,2.6rem);font-weight:600;line-height:1.05;color:var(--green);
}
.rsheet h2 .dv{display:block;margin-top:6px;font-size:.5em;font-weight:400;color:var(--gold-dk);}
.rs-meta{display:flex;gap:10px;flex-wrap:wrap;margin:16px 0 0;}
.rs-meta span{font-size:.78rem;font-weight:600;padding:6px 12px;border-radius:999px;background:var(--gold-pale);color:var(--ink);}
.rs-desc{margin:16px 0 0;font-size:1rem;line-height:1.6;color:rgba(30,44,22,.85);}
.rsheet h3{
  margin:28px 0 10px;font-family:"Fraunces",Georgia,serif;font-size:1.15rem;font-weight:600;color:var(--green);
  padding-bottom:8px;border-bottom:1px solid rgba(191,138,23,.45);
}
.rs-ing{list-style:none;margin:0;padding:0;}
.rs-ing li{display:grid;grid-template-columns:minmax(84px,30%) 1fr;gap:14px;padding:8px 0;border-bottom:1px dashed rgba(30,44,22,.14);font-size:.95rem;line-height:1.45;}
.rs-ing li span:first-child{font-weight:700;color:var(--gold-dk);}
.rs-ours{font-weight:700;color:var(--green);}
.rs-steps{margin:0;padding:0;list-style:none;counter-reset:rs;}
.rs-steps li{position:relative;padding:0 0 14px 44px;font-size:.97rem;line-height:1.6;counter-increment:rs;}
.rs-steps li::before{
  content:counter(rs);position:absolute;left:0;top:-2px;width:30px;height:30px;border-radius:50%;
  display:grid;place-items:center;font:600 .85rem/1 "Fraunces",Georgia,serif;
  background:var(--green);color:var(--gold-pale);
}
@media (max-width:760px){
  .rsheet{width:calc(100vw - 24px);max-height:calc(100vh - 24px);border-radius:20px;}
  .rs-box{grid-template-columns:1fr;grid-template-rows:auto;height:auto;max-height:calc(100vh - 24px);overflow-y:auto;}
  .rs-shot{height:38vh;}
  .rs-body{overflow:visible;}
  .rs-ing li{grid-template-columns:minmax(72px,34%) 1fr;}
}
@media (prefers-reduced-motion:reduce){.rsheet[open]{animation:none;}}
/* A turned card's hidden face still takes clicks in Chrome (backface-visibility
   hides it but does not take it out of hit-testing), so the front's copy sat
   over the back's Recipe button and turned the card instead. Only the face
   you can see takes the pointer. */
.made .mcard.is-flipped .mfront,
.made .mcard:not(.is-flipped) .mback{pointer-events:none;}
