/* ==========================================================================
   Everyday Macros — Miranda's Kitchen
   Brand: Lora (display) + Work Sans (text/labels)
   ========================================================================== */

/* Self-hosted so the first paint waits on nothing but this file. Loading these
   from fonts.googleapis.com cost a stylesheet round trip to one origin plus a
   font fetch from a second before any text could render. Both are variable
   fonts, so one file per subset covers every weight the site uses. Regenerate
   by pulling the latin and latin-ext woff2 out of the Google CSS for
   Lora:wght@400..600 and Work Sans:wght@400..700 -- the site sets no italics,
   so the italic faces are deliberately not shipped. */
@font-face {
  font-family: "Lora";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/lora-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Lora";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/lora-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Work Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/worksans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Work Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/worksans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --ink:        #262220;  /* near-black text */
  --ink-muted:  #5c5551;  /* secondary headings */
  --ink-label:  #8a8177;  /* small-caps labels */
  --rule:       #d8d2cb;  /* hairlines */
  --green:      #5C7D65;  /* brand green */
  --green-deep: #4a6852;  /* the same green, darkened for hover */
  --paper:      #ffffff;  /* page background */

  --serif: "Lora", Georgia, "Times New Roman", serif;
  --sans:  "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --measure: 62ch;
  --gutter: clamp(1.5rem, 5vw, 4rem);
  --max: 1120px;

  /* the gallery's gutter between tiles. Declared here rather than on .plates
     because the carousel arrows are siblings of the track, not descendants,
     and a custom property does not travel sideways. */
  --plate-gap: clamp(1.25rem, 3.5vw, 2rem);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.7;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

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

/* ---------- typography ---------------------------------------------------- */

h1, h2, h3 { font-family: var(--serif); font-weight: 500; margin: 0; }

h1 {
  font-size: clamp(3rem, 11vw, 6.5rem);
  line-height: 0.95;
  letter-spacing: -0.015em;
  text-transform: uppercase;
}

h2 {
  font-size: clamp(1.75rem, 4vw, 2.75rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

h3 { font-size: 1.3125rem; line-height: 1.3; }

p { margin: 0 0 1.15em; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

/* letterspaced small-caps label — the cookbook's signature device */
.label {
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-label);
  margin: 0;
  /* labels are short — never inherit the paragraph measure, or the box ends up
     narrower than its container and centred text sits left of centre */
  max-width: none;
}

.label--strong { font-weight: 700; }

.lede {
  font-family: var(--serif);
  font-size: clamp(1.125rem, 2.2vw, 1.5rem);
  line-height: 1.55;
  color: var(--ink-muted);
  text-wrap: pretty;
}

.hairline {
  width: 68px; height: 1px;
  background: var(--rule);
  border: 0;
  margin: 2rem auto;
}

/* ---------- layout ------------------------------------------------------- */

.wrap {
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

section { padding-block: clamp(4rem, 9vw, 8rem); }
.rule-top { border-top: 1px solid var(--rule); }

.center { text-align: center; }
.center p { margin-inline: auto; }

/* ---------- header ------------------------------------------------------- */

.site-head {
  padding-block: 1.75rem;
  border-bottom: 1px solid var(--rule);
}
.site-head .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
/* the logo carries the name now, so the header holds an image rather than type */
.wordmark {
  display: block;
  line-height: 0;   /* no descender gap under the image */
}
.wordmark img {
  height: 64px;
  width: auto;
  /* The logo's two lines of type sit 14.5% of its height below the middle of
     the file, because the cutting board reaches higher than the text does.
     Centring the box therefore leaves the name looking low next to the button;
     lifting it by that same fraction lines the two up optically. */
  transform: translateY(-14.5%);
}
.nav-buy {
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-muted);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  transition: color .25s ease, border-color .25s ease;
}
.nav-buy:hover { color: var(--ink); border-color: var(--ink); }

/* ---------- hero --------------------------------------------------------- */

.hero {
  text-align: center;
  padding-block: clamp(1.5rem, 3.5vw, 2.75rem) clamp(3.5rem, 8vw, 6rem);
}

.hero-mark {
  /* Bowl.png carries deliberate asymmetric margin so the bowl — not the sprig —
     lands on the page centre. Never trim it. The canvas is ~83% artwork, so this
     width is sized up so the drawing itself renders at ~275px. */
  width: min(378px, 70vw);
  margin: 0 auto clamp(1.5rem, 3.5vw, 2.25rem);
  opacity: .92;
}

/* the COOKBOOK kicker only — scoped to its own class, because as `.hero .label`
   this was also landing on the tagline row, inflating it to 14px and adding a
   20px bottom margin to every tag */
.hero .kicker {
  margin-bottom: 1.25rem;
  font-size: 0.875rem;
  letter-spacing: 0.3em;
  color: var(--ink-muted);
}

.hero h1 + .hairline { margin-block: clamp(1.5rem, 4vw, 2.25rem); }

.hero-sub {
  font-family: var(--serif);
  font-size: clamp(1.125rem, 2.6vw, 1.625rem);
  line-height: 1.45;
  color: var(--ink);
  margin: 0 auto;
  max-width: 34ch;
  text-wrap: balance;
}

.hero-tags {
  margin-top: clamp(1rem, 2.2vw, 1.5rem);
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
}
.hero-tags .label {
  display: inline-flex;
  align-items: center;
  gap: 1.25rem;
  font-size: 0.8125rem;
}
.hero-tags .label::after { content: "•"; letter-spacing: 0; color: var(--rule); }
.hero-tags .label:last-child::after { content: none; }

.hero-cta { margin-top: clamp(2.5rem, 6vw, 3.5rem); }

/* Staggered page-load reveal. Gated on .anim like the scroll reveals, so the
   hero is never hidden behind an animation that might not run — with JS off,
   these elements are simply visible. */
@media (prefers-reduced-motion: no-preference) {
  .anim .reveal { animation: rise 1s cubic-bezier(.22,.68,.15,1) both; }
  .anim .reveal:nth-child(1) { animation-delay: .08s; }
  .anim .reveal:nth-child(2) { animation-delay: .15s; }
  .anim .reveal:nth-child(3) { animation-delay: .25s; }
  .anim .reveal:nth-child(4) { animation-delay: .35s; }
  .anim .reveal:nth-child(5) { animation-delay: .45s; }
  .anim .reveal:nth-child(6) { animation-delay: .55s; }
  .anim .reveal:nth-child(7) { animation-delay: .65s; }
}

/* ---------- scroll reveals ----------------------------------------------- */
/* Gated on .anim, which a tiny inline script adds to <html>. With JS off nothing
   is ever hidden, so the page is fully readable either way.

   Keyframe animations, not transitions: animation-fill-mode pins the end state,
   so a reveal can't stall partway and leave half-faded text. Removing .in resets
   the element, which is what lets a section replay when you scroll back to it.

   Per-child delay comes from --d, set in JS from each child's real position, so
   the wave follows the layout at any column count. */

@media (prefers-reduced-motion: no-preference) {
  .anim .fx,
  .anim .fx-stagger > * { opacity: 0; }

  .anim .fx.in { animation: rise .8s cubic-bezier(.22,.68,.15,1) both; }

  .anim .fx-stagger.in > * {
    animation: rise .8s cubic-bezier(.22,.68,.15,1) both;
    animation-delay: var(--d, 0s);
  }

  /* the sample pages and the meal photos get a touch of scale, like sheets and
     plates settling onto a table */
  .anim .pages.fx-stagger.in > *,
  .anim .plates.fx-stagger.in > * {
    animation-name: settle;
    animation-duration: .9s;
  }

  /* thirty recipe names — keep their travel short so the wave stays quiet */
  .anim .index-grid.fx-stagger.in > * {
    animation-name: riseShort;
    animation-duration: .6s;
  }

  /* the hairline draws itself outward */
  .anim .fx .hairline,
  .anim .fx-stagger .hairline { width: 0; }
  .anim .fx.in .hairline,
  .anim .fx-stagger.in .hairline {
    animation: draw .9s cubic-bezier(.22,.68,.15,1) .2s both;
  }

  @keyframes draw {
    from { width: 0; }
    to   { width: 68px; }
  }

  @keyframes riseShort {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }

  @keyframes settle {
    from { opacity: 0; transform: translateY(24px) scale(.985); }
    to   { opacity: 1; transform: none; }
  }
}

/* ---------- buttons ------------------------------------------------------ */

.btn {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 1.0625rem 2.25rem;
  border: 1px solid var(--green);
  background: var(--green);
  color: var(--paper);
  cursor: pointer;
  white-space: nowrap;   /* a button label never breaks onto a second line */
  /* <a> would inherit the body's 1.7 while <button> defaults to ~1.2, making
     the two render at different heights. Pin it so they always match. */
  line-height: 1.2;
  font-family: var(--sans);
  transition: background .28s ease, color .28s ease, border-color .28s ease;
}
/* darkens rather than inverting: an outlined hover state would drop the green
   out of the page at exactly the moment someone is about to click */
.btn:hover { background: var(--green-deep); border-color: var(--green-deep); color: var(--paper); }

.btn-quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule);
}
.btn-quiet:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.btn-block { display: block; width: 100%; text-align: center; }

.btn:disabled,
.btn[aria-disabled="true"] {
  cursor: default;
  color: var(--ink-label);
  border-color: var(--rule);
  background: transparent;
}
.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
  background: transparent;
  color: var(--ink-label);
  border-color: var(--rule);
}

/* header-sized button */
.btn-sm {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  padding: 0.85rem 1.55rem;
}

/* ---------- three-up value props ---------------------------------------- */

.trio {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: clamp(2rem, 5vw, 3.5rem);
  margin-top: clamp(2.5rem, 6vw, 4rem);
}
.trio > div { border-top: 1px solid var(--rule); padding-top: 1.5rem; }
.trio .num {
  font-family: var(--serif);
  font-size: 0.9375rem;
  color: var(--ink-label);
  display: block;
  margin-bottom: 0.75rem;
}
.trio h3 { margin-bottom: .5rem; }
.trio p { font-size: 0.9375rem; color: var(--ink-muted); max-width: 34ch; }

/* ---------- look inside ------------------------------------------------- */

.pages {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(1.5rem, 4vw, 2.5rem);
  margin-top: clamp(2.5rem, 6vw, 4rem);
  align-items: start;
}
.sheet {
  margin: 0;
  border: 1px solid var(--rule);
  background: var(--paper);
  box-shadow: 0 1px 2px rgba(38,34,32,.04), 0 12px 32px -18px rgba(38,34,32,.22);
  transition: transform .5s cubic-bezier(.2,.7,.2,1), box-shadow .5s ease;
}
.sheet:hover {
  transform: translateY(-6px);
  box-shadow: 0 1px 2px rgba(38,34,32,.05), 0 22px 46px -20px rgba(38,34,32,.28);
}
/* The sample pages are painted as backgrounds rather than <img> elements: there is
   no image node to drag onto the desktop, and no "Save image as" in the context
   menu. A determined visitor can still pull the file out of the network tab. */
.sheet-page {
  aspect-ratio: 816 / 1056;
  background-size: cover;
  background-position: top center;
  background-repeat: no-repeat;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* ---------- on the table (cooked meal photos) ---------------------------- */

/* Deliberately not folded in with .pages: that row turns into a horizontal
   drag-scroll track on a narrow viewport, which works for exactly three sample
   pages and stops working as soon as the gallery grows. This one wraps. */
.plates {
  display: grid;
  /* min() so a single column can still shrink below 240px on a small phone */
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: var(--plate-gap);
  margin-top: clamp(2.5rem, 6vw, 4rem);
  padding: 0;
  list-style: none;
}

/* ---------- the gallery as a carousel ----------------------------------- */

/* Everything below only applies once carousel.js has upgraded the grid, which
   it does only when there are more photos than fit in one row. Until then the
   grid above is the whole story — with three photos and three visible there is
   nothing to rotate, and a carousel that cannot move looks broken. */

.carousel { position: relative; }

/* the window the track slides behind */
.carousel-viewport {
  overflow: hidden;
  /* Room for the tiles' drop shadow and their hover lift, taken back out of the
     layout with a matching negative margin so the row still sits where it
     should. The hover shadow reaches about 19px below the tile and the lift
     takes 6px off the top, hence the lopsided pair.

     Deliberately vertical only. Horizontal padding here would shrink the tiles,
     and horizontal margin would widen the clipping window — which is the one
     thing holding the copies parked either side of the row out of sight. */
  margin-block: -0.75rem -2.25rem;
  padding-block: 0.75rem 2.25rem;

  /* The same trick sideways, and for the same reason: without it the clip edge
     lands exactly on the outer edge of the first and last tile, so those two
     lose the shadow the ones between them keep and read as sliced off — most
     visibly on hover, where the shadow reaches 3px past the tile.

     Paired margin and padding cancel, so the track keeps its width and the
     tiles keep their size — the objection above is to padding on its own. The
     window does get wider, so this has to stay comfortably under --plate-gap
     (20px at its smallest): the nearest parked copy sits a full gap out, and
     anything past that would bring it into view. */
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
}

.plates.is-carousel {
  --visible: 3;
  display: flex;
  gap: var(--plate-gap);
  transition: transform .6s cubic-bezier(.22, .68, .15, 1);
}
/* while a finger is down the track follows the finger, so no easing */
.plates.is-carousel.is-dragging { transition: none; }
/* set by the script for the instant, un-animated jump that closes the loop */
.plates.is-carousel.is-jumping  { transition: none; }

.plates.is-carousel > .plate {
  flex: 0 0 calc(
    (100% - (var(--visible) - 1) * var(--plate-gap)) / var(--visible)
  );
}

/* The breakpoints live here rather than in the script: CSS already knows how
   to watch a viewport, and the script reads --visible back out. */
@media (max-width: 1023px) { .plates.is-carousel { --visible: 2; } }
@media (max-width: 767px)  { .plates.is-carousel { --visible: 1; } }

/* --- one photo at a time on a phone, the same way the sample pages and the
   price cards do it (see the 1000px block further down) ---
   The tile is sized so the next one peeks in, which is what tells you the row
   goes somewhere, and the viewport is full-bleed so a photo can travel to the
   screen edge. The matching inline padding keeps the first one lined up with
   the text above it. */
@media (max-width: 767px) {
  .plates.is-carousel > .plate { flex-basis: min(76%, 340px); }

  /* The photo on its way out parks one gap to the left of the one on screen, so
     it clears the screen edge only if the gap is at least as wide as the bleed
     below. --plate-gap is the narrower of the two at every width this block
     covers (20px against 24px on a phone, 27 against 38 by 767px), which left
     the tail of the previous photo showing as a sliver down the left edge —
     4px on a phone, 11px by the top of this range. Widening the gap to match
     parks it flush instead, plus a hair for its own 1px shadow. */
  .plates.is-carousel { gap: calc(var(--gutter) + 2px); }

  /* full-bleed: cancel the wrap's gutter so a photo can travel to the screen
     edge, then put it back inside the scroller so the first one still lines up
     with the text above. Vertical spacing is handled once, above.

     This replaces the small bleed above rather than adding to it, so the
     shadow-room the wider viewports get is not on offer here — at full-bleed
     the clip edge is the screen edge, and there is nothing to bleed into. */
  .carousel-viewport {
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
  }
}

/* Hand horizontal gestures to the script and keep vertical ones for the page.
   Without this the browser owns both axes, and on a touch screen it claims the
   gesture as a page scroll the moment a finger moves — which cancels the swipe
   before it has travelled far enough to count as one. */
.carousel-viewport { touch-action: pan-y; }

/* nothing to rotate at this width: the row is standing still, so it should not
   look or behave like a track */
.carousel.is-static .carousel-viewport { touch-action: auto; }

.carousel-arrow {
  position: absolute;
  top: calc(50% - 1.375rem);
  z-index: 2;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.25rem/1 var(--sans);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(38,34,32,.08), 0 12px 28px -16px rgba(38,34,32,.3);
  transition: background .2s ease, color .2s ease, transform .2s ease;
}
.carousel-arrow:hover {
  background: var(--green);
  border-color: var(--green);
  color: var(--paper);
}
.carousel-arrow:active { transform: scale(.94); }
/* `hidden` alone is not enough: the display above is an author rule and beats
   the browser's own [hidden] { display: none }, so the arrows would stay on
   screen on a row that cannot move. */
.carousel-arrow[hidden] { display: none; }

/* Squarely on top of the photos. An arrow that sits half on and half off the
   image edge reads as a mistake rather than a choice, and at most widths there
   is not enough gutter beside the row to clear the image at all. */
.carousel-arrow--prev { left: .75rem; }
.carousel-arrow--next { right: .75rem; }

/* Wide enough to put them beside the row instead, which suits the whitespace
   this page is built on. The button is 2.75rem and wants half a tile-gap of air
   between it and the photo, so it needs about 60px of margin next to the row —
   the gutter plus whatever is left over once the wrap hits its 1120px cap. That
   only adds up from about 1128px, so 1200 is the round number with room to
   spare. */
@media (min-width: 1200px) {
  .carousel-arrow--prev { left: calc(-2.75rem - var(--plate-gap) / 2); }
  .carousel-arrow--next { right: calc(-2.75rem - var(--plate-gap) / 2); }
}

/* a swipeable track should say so */
@media (hover: none) {
  .carousel-arrow { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .plates.is-carousel { transition: none; }
  .carousel-arrow { transition: none; }
}


.plate {
  display: flex;
  flex-direction: column;
  margin: 0;
  border: 1px solid var(--rule);
  background: var(--paper);
  box-shadow: 0 1px 2px rgba(38,34,32,.04), 0 12px 32px -18px rgba(38,34,32,.22);
  transition: transform .5s cubic-bezier(.2,.7,.2,1), box-shadow .5s ease;
  overflow: hidden;
}
.plate:hover {
  transform: translateY(-6px);
  box-shadow: 0 1px 2px rgba(38,34,32,.05), 0 22px 46px -20px rgba(38,34,32,.28);
}

/* Unlike the sample pages, these are real <img> elements. The sheets are
   painted as backgrounds to make the paid recipe pages awkward to save; a photo
   of dinner is marketing, so it gets lazy loading and a srcset instead. */
.plate img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  flex-shrink: 0;
}

.plate figcaption {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 0.625rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-label);
  text-align: center;
  padding: 0.9rem 1rem 1.05rem;
  border-top: 1px solid var(--rule);
}

.plate--zoomable { cursor: zoom-in; }

/* ---------- recipe index ------------------------------------------------ */

.index-grid {
  columns: 3 200px;
  column-gap: clamp(1.5rem, 4vw, 3rem);
  margin-top: clamp(2rem, 5vw, 3rem);
  padding: 0;
  list-style: none;
}
.index-grid li {
  break-inside: avoid;
  font-family: var(--serif);
  font-size: 1.0625rem;
  padding: .55rem 0;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-muted);
}

/* Tuned so the line breaks after "on the right," on a wide screen. */
.lede--inside { max-width: 44rem; }

/* Only used on mobile, where the recipe list is collapsed. */
.index-more {
  display: none;
  margin: 1.75rem auto 0;
  padding: 0.7rem 1.5rem;
  background: none;
  border: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease;
}
.index-more:hover { color: var(--ink); border-color: var(--ink); }

.lede--why {
  margin-top: 1.25rem;
  max-width: 46ch;
  font-size: clamp(1rem, 1.7vw, 1.1875rem);
}

.footnote { font-size: 0.875rem; color: var(--ink-label); margin-top: 2rem; }

/* ---------- meet Miranda -------------------------------------------------- */

.about {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 6vw, 4.5rem);
  align-items: center;
}

/* Two photos: Miranda large, the family shot tucked in below and to the right
   so the pair reads as one arrangement rather than a row of two. */
.portraits { position: relative; }

.portrait {
  margin: 0;
  border: 1px solid var(--rule);
  background: var(--paper);
  box-shadow: 0 1px 2px rgba(38,34,32,.04), 0 12px 32px -18px rgba(38,34,32,.22);
}
/* the frame owns the crop, so any photo dropped in behaves the same */
.portrait img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center;
}

/* the section rule is left-aligned here, unlike the centred ones elsewhere */
.hairline--left { margin-inline: 0; }

.portrait--solo { width: 84%; }

/* pulled up into the solo shot's lower edge; the paper-coloured outline keeps
   the two from touching where they overlap */
.portrait--family {
  width: 54%;
  margin-left: auto;
  margin-top: -15%;
  position: relative;
  outline: 8px solid var(--paper);
}
.portrait--family img { aspect-ratio: 1 / 1; }

.about-copy p { color: var(--ink-muted); max-width: 46ch; }

/* ---------- product shot ------------------------------------------------- */

.product {
  position: relative;
  width: min(240px, 56vw);
  margin: 0 auto clamp(2rem, 4vw, 2.75rem);
  filter: drop-shadow(0 18px 30px rgba(38, 34, 32, .16));
}

.product img { width: 100%; height: auto; display: block; }

.product-badge {
  position: absolute;
  top: 14%;
  left: -14%;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.7rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 999px;
  font-family: var(--sans);
  font-size: 0.5rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 6px 16px -6px rgba(38, 34, 32, .45);
}

.product-badge svg {
  width: 11px;
  height: 11px;
  flex: none;
}

/* ---------- pricing ----------------------------------------------------- */

.tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
  margin-top: clamp(2.5rem, 6vw, 4rem);
  align-items: stretch;
}

.tier {
  border: 1px solid var(--rule);
  padding: clamp(1.75rem, 4vw, 2.5rem);
  display: flex;
  flex-direction: column;
  text-align: center;
  position: relative;
  background: var(--paper);
}
.tier--feature { border-color: var(--ink); }

/* not on sale yet: recede, and make the state unmistakable */
.tier--soon { border-style: dashed; }
.tier--soon h3,
.tier--soon .price { color: var(--ink-muted); }
.tier--soon .flag { color: var(--ink-label); }
.tier--soon li { color: var(--ink-label); }
/* The coming-soon cards carry an empty price block so every row — feature list
   and button alike — lines up with the Digital card and reads across. */

.tier--feature .flag,
.tier--soon .flag {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -50%);
  background: var(--paper);
  padding-inline: .85rem;
  font-family: var(--sans);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.tier h3 { font-size: 1.5rem; margin-bottom: .35rem; }
.tier .tier-kind { margin-bottom: 1.5rem; }

.price {
  font-family: var(--serif);
  font-size: clamp(2.5rem, 6vw, 3.25rem);
  line-height: 1;
  letter-spacing: -0.02em;
  display: block;
}
.price-note {
  font-size: 0.8125rem;
  color: var(--ink-label);
  margin-top: .6rem;
}

.tier ul {
  list-style: none;
  padding: 0;
  margin: 1.75rem 0 2rem;
  border-top: 1px solid var(--rule);
  text-align: left;
}
.tier li {
  font-size: 0.9375rem;
  color: var(--ink-muted);
  padding: .7rem 0;
  border-bottom: 1px solid var(--rule);
}
.tier .btn { margin-top: auto; }

.pay-note { font-size: 0.8125rem; color: var(--ink-label); margin-top: 2.5rem; text-align: center; }

/* ---------- faq --------------------------------------------------------- */

.faq { max-width: 720px; margin-inline: auto; margin-top: clamp(2rem, 5vw, 3rem); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq details:first-of-type { border-top: 1px solid var(--rule); }
.faq summary {
  cursor: pointer;
  list-style: none;
  padding: 1.35rem 2.5rem 1.35rem 0;
  font-family: var(--serif);
  font-size: 1.125rem;
  position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
/* Transition colour only. Putting font-weight in a transition leaves it pinned
   at the start value in some engines, and animating weight would reflow the line
   anyway — the weight change should snap. */
.faq summary { transition: color .18s ease; }
.faq summary:hover,
.faq summary:focus-visible,
.faq details[open] > summary { font-weight: 600; color: var(--ink); }
.faq summary::after {
  content: "+";
  position: absolute;
  right: .35rem; top: 50%;
  transform: translateY(-50%);
  font-family: var(--sans);
  font-size: 1.1rem;
  color: var(--ink-label);
  transition: transform .3s ease;
}
.faq details[open] summary::after { content: "–"; }
.faq .answer { padding: 0 2.5rem 1.6rem 0; }
.faq .answer p { font-size: 0.9375rem; color: var(--ink-muted); }

/* ---------- footer ------------------------------------------------------ */

.site-foot {
  border-top: 1px solid var(--rule);
  padding-block: 3rem;
  text-align: center;
}
.site-foot .label + .label { margin-top: .9rem; }

.foot-logo {
  width: 100px;
  height: auto;
  margin: 0 auto 1.5rem;
}
/* one colour for everything in the footer, and no rule under the address */
.site-foot a {
  color: inherit;
  text-decoration: none;
  border-bottom: 0;
  transition: opacity .2s ease;
}
.site-foot a:hover { opacity: .62; }

/* ---------- thank-you page --------------------------------------------- */

.receipt {
  max-width: 620px;
  margin-inline: auto;
  text-align: center;
}
.receipt .steps {
  list-style: none;
  padding: 0;
  margin: clamp(2rem, 5vw, 3rem) 0 0;
  text-align: left;
  border-top: 1px solid var(--rule);
}
.receipt .steps li {
  padding: 2.25rem 0;
  border-bottom: 1px solid var(--rule);
}
.receipt .steps .label { display: block; margin-bottom: .8rem; }
.receipt .steps p { font-size: 0.9375rem; color: var(--ink-muted); margin: 0; }

:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }


/* ==========================================================================
   Narrow viewports (tablets, and a desktop window dragged small)
   --------------------------------------------------------------------------
   Everything a phone gets that is really about width rather than about being
   a phone lives here, so a 900px browser window behaves like an iPad instead
   of like a broken desktop.
   ========================================================================== */

@media (max-width: 1000px) {

  /* --- the value props stack rather than breaking 2 + 1 ---
     Three across needs ~770px of content box; below that auto-fit drops to two
     columns and leaves the third stranded on a row of its own. One column all
     the way down is the honest answer. */
  .trio {
    grid-template-columns: 1fr;
    gap: 2rem;
  }

  /* no rule between the section heading and the first value prop */
  .trio > div:first-child {
    border-top: 0;
    padding-top: 0;
  }

  /* --- and the whole Why this book section centres ---
     Stacked, the copy is a narrow column (the headline is capped at 20ch, the
     lede at 46ch, each prop at 34ch) hugging the left edge with a wide empty
     margin beside it, which reads as unfinished. Centred, the rules between
     the props become dividers the text sits on. The blocks keep their caps and
     get auto side margins so only the column moves, not the measure. */
  .why { text-align: center; }
  .why h2,
  .why .lede--why,
  .why .trio p { margin-inline: auto; }

  /* --- sample pages and price cards scroll sideways instead of wrapping ---
     Three cards, always one row: below this width they would break 2 + 1, so
     the row becomes a swipeable (and, with drag-scroll.js, draggable) track.
     Full-bleed: the negative margin cancels the wrap's gutter so cards can
     travel to the screen edge, and the matching padding keeps the first one
     aligned with the text above it. Cards are sized so the next one peeks in,
     which is what tells you the row scrolls. */
  .pages,
  .tiers {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(76%, 340px);
    gap: 1rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-left: var(--gutter);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scrollbar-width: none;
  }

  /* overflow-x: auto makes overflow-y compute to auto, so anything drawn
     outside a card gets clipped. These paddings are the room that the sheet
     shadows (below) and the Available now / Coming soon flags (above) need. */
  .pages  { padding-block: 0.25rem 2.25rem; }
  .tiers  { padding-block: 1rem 1.25rem; }

  .pages::-webkit-scrollbar,
  .tiers::-webkit-scrollbar { display: none; }

  .pages > *,
  .tiers > * { scroll-snap-align: start; }

  /* a card that lifts on hover inside a clipped scroller just gets its shadow
     cut off, and on a touch screen the hover state sticks after a tap */
  .sheet:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   Drag to scroll (mouse only; touch already scrolls on its own)
   drag-scroll.js adds .can-drag once a row actually overflows, so the grab
   cursor never appears on a row that has nowhere to go.
   -------------------------------------------------------------------------- */

.can-drag { cursor: grab; }
.can-drag.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
  scroll-behavior: auto;
  user-select: none;
  -webkit-user-select: none;
}
/* while dragging, the cursor belongs to the track, not to the cards */
.can-drag.is-dragging * { cursor: grabbing !important; }
.can-drag.is-dragging .sheet:hover { transform: none; }


/* ==========================================================================
   Mobile
   ========================================================================== */

@media (max-width: 720px) {

  /* --- type scale: bigger headlines on a small screen, not smaller ---
     h1 is capped by viewport width so EVERYDAY still fits on one line on a
     320px phone, where 3.5rem would overflow. */
  h1 { font-size: min(3.75rem, 16vw); }
  h2 { font-size: min(2.125rem, 9vw); }

  /* --- the header logo gives way before the button does --- */
  .wordmark img { height: 46px; }
  .btn-sm { font-size: 0.625rem; padding: 0.72rem 1.1rem; }


  /* --- the tagline row: keep the first two on one line --- */
  /* the labels inherit the body's 1.7 line-height, which is what opened up the
     gap between the two rows — pin it so the lines sit close together */
  .hero-tags {
    gap: 0.15rem 0.7rem;
    margin-top: clamp(1.75rem, 5vw, 2.5rem);
  }
  .hero-tags .label {
    letter-spacing: 0.14em;
    gap: 0.7rem;
    line-height: 1.35;
  }
  /* the row breaks 2 + 1 here, so the second item's bullet would dangle at the
     end of the first line with nothing after it */
  .hero-tags .label:nth-child(2)::after { content: none; }

  /* --- on a phone the cards go wider than the tablet cap above, so the next
     one still peeks in at the edge of a 320px screen --- */
  .pages,
  .tiers { grid-auto-columns: 76%; }

  /* Keep the closing sentence whole on its own line, at any phone width.
     Chasing a max-width that breaks correctly from 320 to 430 is not
     achievable; making the sentence a block is. */
  .lede-tail { display: block; }

  /* --- recipe list collapses to 12 with the last three fading out --- */
  .index-more { display: block; }

  .index-grid.is-collapsed > li:nth-child(n+13) { display: none; }

  /* The fade has to be a mask on the list, not opacity on the items. The
     reveal keyframes end at opacity: 1 with fill-mode both, and an animation
     outranks a normal declaration in the cascade — so per-item opacity was
     being overwritten the moment the section animated in. A mask sits outside
     that fight, and gives a smooth fade instead of three steps. */
  .index-grid.is-collapsed {
    -webkit-mask-image: linear-gradient(to bottom, #000 64%, rgba(0, 0, 0, 0) 100%);
            mask-image: linear-gradient(to bottom, #000 64%, rgba(0, 0, 0, 0) 100%);
  }
  .index-grid.is-collapsed > li:nth-child(12) { border-bottom-color: transparent; }

  /* --- Miranda stacks: photo first, then the story --- */
  .about { grid-template-columns: 1fr; gap: 2rem; }
  .portraits { width: min(360px, 100%); }
  .about-copy p { max-width: none; }

  /* narrower cards need the button padding to give way before the label does */
  .tier { padding-inline: 1.25rem; }
  .tier .btn { padding-inline: 1.25rem; }
}

/* Very narrow phones (320px and similar). At the sizes above, HIGH PROTEIN and
   FAMILY FRIENDLY need ~318px and only have 272, so the tagline is tightened
   just here rather than shrinking it on every phone. */
@media (max-width: 360px) {
  /* The button won't shrink, so on a 320px screen the logo gives way further
     rather than letting the button hang off the edge. */
  .wordmark img { height: 38px; }
  .btn-sm {
    font-size: 0.5625rem;
    letter-spacing: 0.14em;
    padding: 0.65rem 0.9rem;
  }

  .hero-tags { gap: 0.3rem 0.4rem; }
  .hero-tags .label {
    font-size: 0.625rem;
    letter-spacing: 0.08em;
    gap: 0.4rem;
  }
}


/* ==========================================================================
   Waitlist dialog
   ========================================================================== */

.modal[hidden] { display: none; }

.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
}

/* The scrolling lives here rather than on .modal, so the backdrop is not a
   child of a scroller. iOS gives a touch-scrolling element its own containing
   block, which pins a fixed backdrop to the scrolled area instead of the
   screen and leaves the page showing below it. */
.modal-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  display: flex;
  padding: var(--gutter);
}

.modal-backdrop {
  /* covers .modal, which spans the whole viewport and is never resized, so the
     keyboard cannot shrink it */
  position: absolute;
  inset: 0;
  background: rgba(38, 34, 32, .72);
  animation: fadeIn .25s ease both;
}

/* focused on open so the dialog is announced; it is not a control, so it
   should not draw a focus ring */
.modal-card:focus { outline: none; }

.modal-card {
  position: relative;
  width: min(30rem, 100%);
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: clamp(2rem, 5vw, 2.75rem);
  text-align: center;
  box-shadow: 0 24px 60px -24px rgba(38, 34, 32, .4);
  animation: modalIn .38s cubic-bezier(.22,.68,.15,1) both;
  /* auto margins rather than the flex centring properties: a centred flex item
     that outgrows its scroll container has its top clipped and unreachable */
  margin: auto;
}

@media (prefers-reduced-motion: reduce) {
  .modal-backdrop, .modal-card { animation: none; }
}

@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes modalIn {
  from { opacity: 0; transform: translateY(14px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

.modal-x {
  position: absolute;
  top: .5rem; right: .75rem;
  background: none;
  border: 0;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--ink-label);
  cursor: pointer;
  padding: .25rem .4rem;
  transition: color .2s ease;
}
.modal-x:hover { color: var(--ink); }

.modal-card h3 {
  font-size: 1.75rem;
  margin: .6rem 0 .85rem;
}

.modal-copy {
  font-size: .9375rem;
  color: var(--ink-muted);
  margin: 0 auto 1.75rem;
  max-width: 34ch;
}

.modal-label {
  display: block;
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-label);
  text-align: left;
  margin-bottom: .5rem;
}

.modal-input {
  width: 100%;
  font-family: var(--serif);
  font-size: 1.0625rem;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: .6rem 0;
  margin-bottom: 1.5rem;
  transition: border-color .2s ease;
}
.modal-input::placeholder { color: var(--rule); }
.modal-input:focus {
  outline: none;
  border-bottom-color: var(--green);
}

/* honeypot — off-screen rather than display:none, which some bots skip */
.modal-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.modal-error {
  font-size: .8125rem;
  color: var(--ink);
  text-align: left;
  margin: -1rem 0 1rem;
}


/* ---------- sample recipe lightbox --------------------------------------- */

.lightbox[hidden] { display: none; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  /* Hand horizontal gestures to the script so a swipe walks the gallery, and
     keep pinch-zoom, which is how anyone reads the small type on a sample
     recipe page. There is nothing to pan vertically — the body is locked while
     this is open — but leaving pan-y in costs nothing and keeps a tall picture
     reachable on a short screen. */
  touch-action: pan-y pinch-zoom;
  display: grid;
  /* a definite row, so the page's max-height has something to resolve against
     and a tall recipe is scaled down rather than run off the bottom */
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  --lb-top: clamp(3.25rem, 7vw, 4rem);
  --lb-pad: clamp(1rem, 4vw, 2.5rem);
  --lb-cap: 3.25rem;
  padding: var(--lb-top) var(--lb-pad) var(--lb-pad);
}

.lightbox-backdrop {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100lvh;
  background: rgba(38, 34, 32, .82);
  animation: fadeIn .25s ease both;
}

.lightbox-figure {
  position: relative;
  margin: 0;
  min-height: 0;
  max-height: 100%;
  animation: modalIn .32s cubic-bezier(.22,.68,.15,1) both;
  /* eases the picture back when a swipe does not go far enough to count; the
     script turns this off while a finger is actually down */
  transition: transform .3s cubic-bezier(.22, .68, .15, 1);
}

.lightbox-figure img {
  display: block;
  /* measured against the viewport, not the figure: the figure has no height of
     its own, and a percentage there would leave the picture free to run off the
     bottom of a short window. --lb-cap reserves the caption's own line, so the
     name is never the thing that gets pushed off. */
  max-height: calc(100vh - var(--lb-top) - var(--lb-pad) - var(--lb-cap));
  max-height: calc(100dvh - var(--lb-top) - var(--lb-pad) - var(--lb-cap));
  max-width: 100%;
  width: auto;
  height: auto;
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, .7);
}

/* the close control sits above the page rather than on it, so it never covers
   a corner of the recipe */
.lightbox-x {
  position: absolute;
  top: clamp(.75rem, 2.5vw, 1.25rem);
  right: clamp(.75rem, 2.5vw, 1.25rem);
  z-index: 1;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--paper);
  font-size: 2rem;
  line-height: 1;
  cursor: pointer;
  opacity: .85;
  transition: opacity .2s ease;
}
.lightbox-x:hover { opacity: 1; }

.lightbox-figure figcaption {
  font-family: var(--sans);
  font-size: 0.625rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--paper);
  text-align: center;
  padding-top: 1.15rem;
  opacity: .8;
}
.lightbox-figure figcaption:empty { display: none; }

.lightbox-nav {
  position: absolute;
  top: calc(50% - 1.5rem);
  z-index: 1;
  width: 3rem;
  height: 3rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--paper);
  font: 400 2.5rem/1 var(--sans);
  cursor: pointer;
  opacity: .7;
  transition: opacity .2s ease, transform .2s ease;
}
.lightbox-nav:hover { opacity: 1; }
.lightbox-nav:active { transform: scale(.92); }
.lightbox-nav[hidden] { display: none; }
.lightbox-nav--prev { left: clamp(.25rem, 2vw, 1.5rem); }
.lightbox-nav--next { right: clamp(.25rem, 2vw, 1.5rem); }

@media (prefers-reduced-motion: reduce) {
  .lightbox-backdrop, .lightbox-figure { animation: none; }
  .lightbox-figure, .lightbox-nav { transition: none; }
}

/* only set once the script has wired the sheet up */
.sheet--zoomable { cursor: zoom-in; }

body.modal-open { overflow: hidden; }

/* a button that sits inside a sentence and reads as a link */
.linklike {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  border-bottom: 1px solid var(--rule);
  transition: border-color .2s ease;
}
.linklike:hover { border-bottom-color: var(--ink); }
