/* ==========================================================================
   THE DOUGHJO JA — MAIN
   Colour and type lead. Each section is a flat field of one brand colour at
   full strength, with the type big enough to carry it. Photographs upgrade
   this page; they are not what holds it up.
   ========================================================================== */

/* ------------------------------------------------------------------ RESET */

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

/* An anchor has to clear the nav AND the announcement above it, or a jump
   to #shop lands with the heading tucked behind them. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  scroll-padding-top: calc(5.5rem + var(--ann-h, 0px)); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink-2);
  font-family: var(--text);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--display);
  font-weight: 800;
  line-height: var(--lh-head);
  letter-spacing: var(--tr-head);
  color: var(--ink);
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
dl, dd { margin: 0; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }
figure { margin: 0; }

a { color: inherit; text-decoration-thickness: 2px; text-underline-offset: .2em; text-decoration-color: rgba(191,126,70,.5); transition: color var(--d-1) var(--ease), text-decoration-color var(--d-1) var(--ease); }
a:where(:not(.btn, .tlink, .add, .chip)):hover { text-decoration-color: currentColor; }

/* --------------------------------------------------- THE BROWSER SURFACES */

::selection { background: var(--cocoa); color: var(--cream); }
:root { caret-color: var(--brown); accent-color: var(--caramel); }
html { scrollbar-color: var(--tan) var(--blush); scrollbar-width: thin; }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--blush); }
::-webkit-scrollbar-thumb { background: var(--tan); border: 3px solid var(--blush); border-radius: var(--r-pill); }
::-webkit-scrollbar-thumb:hover { background: var(--caramel); }
:focus-visible { outline: 3px solid var(--cocoa); outline-offset: 3px; border-radius: 4px; }
.on-dark :focus-visible, .f--brown :focus-visible, .f--forest :focus-visible, .f--olive :focus-visible, .foot :focus-visible { outline-color: var(--cream); }

.fig, table, input { font-variant-numeric: tabular-nums; }

.skip { position: absolute; top: var(--s-3); left: var(--s-3); z-index: var(--z-skip); padding: var(--s-3) var(--s-5); background: var(--cocoa); color: var(--cream); border-radius: var(--r-pill); font-family: var(--display); font-weight: 700; font-size: var(--t-sm); text-decoration: none; transform: translateY(-200%); }
.skip:focus { transform: translateY(0); }
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ------------------------------------------------------------- PRIMITIVES */

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }
.shell--narrow { max-width: var(--shell-narrow); }

/* FIELDS. A section is one colour, at full strength, edge to edge. */
.sec { padding-block: var(--section); }
.f--cream  { background: var(--cream);  color: var(--ink-2); }
.f--blush  { background: var(--blush);  color: var(--ink-2); }
.f--tan    { background: var(--tan);    color: var(--on-warm-2); }
.f--caramel{ background: var(--caramel);color: var(--on-warm-2); }
.f--brown  { background: var(--brown);  color: var(--on-dark-2); }
.f--forest { background: var(--forest); color: var(--on-dark-2); }
.f--tan h1, .f--tan h2, .f--tan h3, .f--caramel h1, .f--caramel h2, .f--caramel h3 { color: var(--cocoa); }
.f--brown h1, .f--brown h2, .f--brown h3, .f--brown h4,
.f--forest h2, .f--forest h3 { color: var(--cream); }

/* The chip: a rounded pill label, straight off a bakery bag. */
.chip {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .45rem 1rem; border-radius: var(--r-pill);
  background: var(--cocoa); color: var(--cream);
  font-family: var(--display); font-weight: 700;
  font-size: var(--t-mark); letter-spacing: var(--tr-mark); text-transform: uppercase;
}
.chip--light { background: var(--cream); color: var(--cocoa); }
.chip--outline { background: transparent; color: inherit; box-shadow: inset 0 0 0 2px currentColor; }

.display { font-family: var(--display); font-weight: 900; letter-spacing: var(--tr-mega); line-height: var(--lh-mega); }
/* Every section heading is centred, at the client's request. The heading
   centres over its whole section, so the .lift wrapper it sits in has to
   centre too - otherwise the text would centre inside a block that is still
   sitting hard left. Body copy below stays ranged left, which is where long
   measures read best. */
.h-sect { font-family: var(--script); font-style: italic; font-weight: 500; font-size: var(--t-sect); line-height: 1.12; letter-spacing: 0; text-align: center; margin-inline: auto; }

.filters { justify-content: center; }
/* The button that closes a section sits under its middle, like the heading. */
.sec__cta { margin-top: clamp(1.75rem, 3.5vw, 2.5rem); text-align: center; }
/* A heading plus its standfirst. The block has to centre itself as well as
   its text, or a capped measure just sits hard left with centred lines in it. */
.sec__intro { max-width: 46rem; margin-inline: auto; text-align: center; }
.sec__intro .lead { margin-top: var(--s-5); text-wrap: pretty; }
/* The glyph rides with the label rather than sitting apart from it. */
.ig { display: inline-flex; align-items: center; gap: .6rem; }
.ig svg { width: 18px; height: 18px; stroke: currentColor; fill: none; flex: none; }

/* In the story split the heading centres over its own column, not the page,
   because the photograph owns the other half. */
.split .h-sect { text-align: center; }

/* Page headers on the nav-linked pages. */
.phead__in { text-align: center; }


/* The press card's masthead line and headline follow the same rule. */
.press__meta { justify-content: center; }
.press__h { text-align: center; }

/* Standalone blocks that carry their own heading. */
.faqset__h { text-align: center; }
.lead { font-size: var(--t-lead); line-height: 1.6; max-width: var(--measure); }
/* Centring the TEXT in a box that is itself flush left only looks centred
   when the text fills the line. Under a centred heading it reads as nudged
   to the left, because the box is narrower than its container and never
   moves. Centre the box too, and the two agree.

   Both selectors are needed: .sec__intro is the centred block the shop,
   method, gallery and reviews headings all use, and the attribute match
   catches the handful of places that centre a lead with an inline style.
   Fixing only the second left the method intro sitting off to the left. */
.sec__intro .lead,
.lead[style*="center"] { margin-inline: auto; }
.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--s-4); }

/* --------------------------------------------------------------- BUTTONS */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  padding: 1.05rem 2rem; min-height: 56px; border-radius: var(--r-pill);
  background: var(--cocoa); color: var(--cream); border: 2px solid var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-body);
  letter-spacing: -.01em; text-decoration: none; white-space: nowrap;
  transition: transform var(--d-1) var(--ease), background var(--d-2) var(--ease), color var(--d-2) var(--ease), border-color var(--d-2) var(--ease);
}
.btn:hover { transform: translateY(-2px); background: var(--brown); border-color: var(--brown); }
.btn:active { transform: translateY(0); }
.btn[disabled], .btn[aria-disabled="true"] { background: transparent; color: var(--ink-3); border-color: var(--rule-ink); cursor: not-allowed; transform: none; }

.btn--outline { background: transparent; color: var(--ink); border-color: var(--cocoa); }
.btn--outline:hover { background: var(--cocoa); color: var(--cream); }
.btn--light { background: var(--cream); color: var(--cocoa); border-color: var(--cream); }
.btn--light:hover { background: var(--tan); border-color: var(--tan); color: var(--cocoa); }
.btn--light-outline { background: transparent; color: var(--cream); border-color: var(--cream); }
.btn--light-outline:hover { background: var(--cream); color: var(--cocoa); }
.btn--wide { width: 100%; }

.tlink {
  display: inline-flex; align-items: center; gap: .5rem; padding: .35rem 0;
  font-family: var(--display); font-weight: 700; font-size: var(--t-body);
  color: inherit; text-decoration: none;
  box-shadow: inset 0 -2px 0 currentColor;
  transition: box-shadow var(--d-2) var(--ease), gap var(--d-1) var(--ease);
}
.tlink:hover { gap: .85rem; box-shadow: inset 0 -3px 0 currentColor; }
.tlink svg { width: 16px; height: 16px; stroke: currentColor; fill: none; }

/* ------------------------------------------------------------------- NAV */

.nav {
  /* Sits below the announcement when there is one, at the very top when
     there is not. --ann-h is 0 until banner.js measures a real strip. */
  position: sticky; top: var(--ann-h, 0px); z-index: var(--z-nav);
  background: var(--cream);
  border-bottom: 2px solid var(--cocoa);
}
/* Equal outer columns, so the badge sits dead centre whatever the two link
   groups measure. The longest label lives on the left to offset the basket. */
.nav__in { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: clamp(1rem, 2.2vw, 1.9rem); min-height: 84px; }
/* The side columns stay equal so the badge keeps its exact centre, but the
   link groups are pushed to the inner edge of each column so they read as a
   set either side of the crest rather than as two far-flung clusters. The
   burger and the basket stay pinned to the outer edges, where utilities go. */
.nav__side { display: flex; align-items: center; gap: clamp(.4rem, 1.2vw, 1.1rem); min-width: 0; justify-self: stretch; }
.nav__side--left  .nav__links { margin-left: auto; }
.nav__side--right .nav__links { margin-right: auto; }
.nav__brand { justify-self: center; display: grid; place-items: center; text-decoration: none; }
.nav__brand .seal { width: 64px; flex: none; }


.nav__links { display: flex; align-items: center; gap: clamp(.4rem, 1.2vw, 1.1rem); }
.nav__links a {
  padding: .5rem .85rem; border-radius: var(--r-pill);
  font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  color: var(--ink); text-decoration: none;
  transition: background var(--d-1) var(--ease), color var(--d-1) var(--ease);
}
.nav__links a:hover { background: var(--blush); }
.nav__links a[aria-current="page"] { background: var(--cocoa); color: var(--cream); }

/* --------------------------------------------------------- THEIR ORDERS */
/* What a customer sees of their own history: the reference they paid
   against, what was in it, and whether it is settled. Enough to answer
   "did that go through", which is the only question anyone opens this to
   ask. */

.ordlist { display: grid; gap: var(--s-4); margin-top: var(--s-5); }
.ordcard { padding: clamp(1rem, 2.6vw, 1.35rem); border: 1.5px solid var(--rule-ink);
  border-radius: var(--r-lg); background: var(--cream); }
.ordcard__top { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap; }
.ordcard__ref { font-family: var(--display); font-weight: 800; font-size: var(--t-sm);
  color: var(--ink); font-variant-numeric: tabular-nums; }
.ordcard__when { margin-top: .1rem; font-size: var(--t-xs); color: var(--ink-3); }
.ordcard__tags { display: flex; gap: .4rem; flex-wrap: wrap; }

.ordtag { display: inline-block; padding: .2rem .6rem; border-radius: var(--r-pill);
  border: 1.5px solid var(--rule-ink); font-size: var(--t-xs); color: var(--ink-2); }
.ordtag--ok { border-color: var(--cocoa); color: var(--ink); }
.ordtag--due { background: var(--cocoa); color: var(--cream); border-color: var(--cocoa); }

.ordcard__items { list-style: none; margin: var(--s-4) 0 0; padding: 0;
  display: grid; gap: .2rem; font-size: var(--t-sm); color: var(--ink-2); }
.ordcard__items span { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.ordcard__foot { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-4); padding-top: var(--s-4);
  border-top: 1.5px solid var(--rule-ink-sf); font-size: var(--t-sm); color: var(--ink-2); }
.ordcard__foot strong { margin-left: auto; font-family: var(--display); font-weight: 800;
  font-size: var(--t-base); color: var(--ink); font-variant-numeric: tabular-nums; }
.ordcard__off { color: var(--ink-3); }

/* ------------------------------------------------------- ACCOUNT BUTTON */
/* Beside the basket, and deliberately quieter than it. The basket is the
   thing the shop wants pressed; this is a door the regulars already know
   they want. Outline against the basket's fill, same height and the same
   pill so the pair read as one control. */

.acct {
  flex: none; display: inline-flex; align-items: center; gap: .45rem;
  padding: .6rem .95rem; min-height: 46px; border-radius: var(--r-pill);
  background: transparent; color: var(--ink); border: 2px solid var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  white-space: nowrap; transition: background var(--d-1) var(--ease);
}
.acct:hover { background: var(--blush); }
.acct svg { width: 19px; height: 19px; flex: none; }

/* Signed in it carries their name and their balance, so the points are in
   front of them on every page rather than only on the points page. */
.acct__l { display: inline-flex; align-items: baseline; gap: .4rem; }
.acct__who { max-width: 7ch; overflow: hidden; text-overflow: ellipsis; }
.acct__pts {
  display: inline-grid; place-items: center; min-width: 1.6rem; padding: 0 .35rem;
  height: 1.45rem; border-radius: var(--r-pill);
  background: var(--cocoa); color: var(--cream);
  font-size: var(--t-xs); font-variant-numeric: tabular-nums;
}

/* On a phone the row is burger, logo, account, basket. Words would not fit,
   so signed out it is the icon alone; signed in the balance stays, because a
   number is the one part worth the space. */
@media (max-width: 900px) {
  .acct { padding: .6rem .7rem; }
  .acct:not(.is-in) .acct__l { display: none; }
  .acct.is-in .acct__who { display: none; }
}

.nav__cart {
  flex: none; display: inline-flex; align-items: center; gap: .5rem;
  padding: .6rem 1.05rem; min-height: 46px; border-radius: var(--r-pill);
  background: var(--caramel); color: var(--cocoa); border: 2px solid var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  transition: background var(--d-1) var(--ease);
}
.nav__cart:hover { background: var(--tan); }
.nav__cart svg { width: 17px; height: 17px; stroke: currentColor; fill: none; }
.nav__count { min-width: 22px; height: 22px; padding: 0 6px; display: grid; place-items: center; border-radius: var(--r-pill); background: var(--cocoa); color: var(--cream); font-size: .75rem; }
.nav__count[data-empty="true"] { background: transparent; color: var(--cocoa); box-shadow: inset 0 0 0 2px currentColor; }

.nav__burger { display: none; flex: none; width: 46px; height: 46px; place-items: center; border-radius: var(--r-pill); border: 2px solid var(--cocoa); color: var(--ink); }
.nav__burger svg { width: 22px; height: 22px; stroke: currentColor; }
@media (max-width: 1080px) {
  .nav__links { display: none; }
  .nav__burger { display: grid; }
  /* With the links gone each side column holds a single control, and a flex
     row packs it to the start — which left the basket sitting against the
     crest instead of out at the edge where a utility belongs. The left
     column already starts at the left edge, so only the right one needs
     turning round. */
  .nav__side--right { justify-content: flex-end; }
}
@media (max-width: 560px) { .nav__brand .seal { width: 50px; } .nav__in { gap: var(--s-3); min-height: 74px; } }

/* The badge, always in its own colours on a cream disc. */
/* A small default width, not 100%: every real use sets its own, and an
   unsized badge then stays an avatar instead of filling its container. */
.seal { position: relative; display: grid; place-items: center; aspect-ratio: 1; width: 44px; border-radius: 50%; background: var(--cream); }
.seal img { width: 92%; height: auto; mix-blend-mode: multiply; }

/* Mobile sheet */
.sheet { position: fixed; inset: 0; z-index: var(--z-drawer); background: var(--caramel); color: var(--cocoa); padding: var(--s-5) var(--gutter) var(--s-8); overflow-y: auto; opacity: 0; visibility: hidden; transition: opacity var(--d-2) var(--ease), visibility var(--d-2); }
.sheet[data-open="true"] { opacity: 1; visibility: visible; }
.sheet__top { display: flex; justify-content: space-between; align-items: center; min-height: 76px; margin-bottom: var(--s-6); }
.sheet__close { width: 46px; height: 46px; display: grid; place-items: center; border-radius: var(--r-pill); border: 2px solid var(--cocoa); }
.sheet__close svg { width: 22px; height: 22px; stroke: var(--cocoa); }
.sheet__links { display: grid; gap: var(--s-2); }
.sheet__links a { font-family: var(--display); font-weight: 800; font-size: clamp(1.9rem, 8vw, 2.6rem); letter-spacing: var(--tr-head); color: var(--cocoa); text-decoration: none; }

/* ------------------------------------------------------------------ HERO */
/* The client's photograph, full bleed, with the type centred in the cream
   well the picture leaves open. The well is light, so the ink is cocoa. */

.hero { position: relative; min-height: 100svh; display: grid; align-items: center; background: var(--cream); overflow: hidden;
  padding-bottom: var(--facts-room); }
/* Room for the bake-day card at the foot of the hero: roughly its height plus
   the gap under it. Three stacked rows on a phone, one row from 860px up. */
.hero { --facts-room: clamp(10.5rem, 22vw, 12rem); }
@media (max-width: 820px) { .hero { padding-top: clamp(5rem, 17vw, 9rem); } }
@media (min-width: 860px) { .hero { --facts-room: clamp(8rem, 12vw, 10rem); } }
.hero__img { position: absolute; inset: 0; z-index: 0; }
.hero__img img { width: 100%; height: 100%; object-fit: cover; object-position: center; }

.hero__in { position: relative; z-index: 2; width: 100%; }
/* The photograph's clear well is narrow, so the type is sized to sit inside
   it rather than spilling over the loaves on either side. With the crest
   gone from the hero, the claim takes the room the badge was using - capped
   so it still clears the fold with its button on a 1280x800 laptop. */
.hero__well { max-width: 32rem; margin-inline: auto; text-align: center; }

.hero__h {
  font-family: var(--script); font-style: italic; font-weight: 500;
  font-size: clamp(2.6rem, 5.4vw, 4.6rem); line-height: 1.06; letter-spacing: 0;
  color: var(--cocoa); text-wrap: balance;
}
.hero__act { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-4); margin-top: clamp(1.5rem, 3vw, 2.25rem); }

/* The photograph's well is clean cream, but a crop at an unexpected aspect
   could slide a loaf under the type. This barely-there cream wash keeps the
   ink above 4.5:1 whatever the viewport does, without reading as a scrim. */
.hero__veil {
  position: absolute; z-index: 1; inset: 0; pointer-events: none;
  background: radial-gradient(58% 46% at 50% 50%, rgba(250, 245, 236, .92) 0%, rgba(250, 245, 236, .72) 45%, rgba(250, 245, 236, 0) 78%);
}
@media (max-width: 820px) {
  .hero { min-height: 100svh; }
  .hero__well { max-width: 19rem; }
  .hero__veil { background: radial-gradient(72% 36% at 50% 48%, rgba(250, 245, 236, .94) 0%, rgba(250, 245, 236, .78) 48%, rgba(250, 245, 236, 0) 80%); }
}

/* ------------------------------------------------------- BAKE-DAY FACTS */
/* The practical detail, immediately under the landing image. */

/* The three facts a first-time visitor needs before anything else: when the
   bake is, where to collect, when delivery runs. An icon each so the eye can
   pick out the one it wants without reading all three, and a hairline between
   them so they read as three answers rather than one run of text. */
/* The card sits on the landing photograph itself, pinned near the foot of the
   hero rather than straddling the seam below it. Absolutely positioned, so it
   adds no height; the hero reserves room for it with padding-bottom and the
   claim stays centred in what is left. */
.facts { position: absolute; left: 0; right: 0; bottom: clamp(1.1rem, 2.6vw, 2.25rem);
  z-index: 4; color: var(--on-dark-2); }

/* Glass rather than a solid block, so the bread reads through it and it
   belongs to the photograph instead of covering it. The alpha and the blur
   were set by measuring the composited pixels under the type, not by eye:
   .58 looked right and measured wrong, because the loaves behind the card
   blow out near white and the small caramel label landed at 2.6:1. At .80
   the photo still reads through and the worst label clears 5.4:1. */
.facts__in {
  display: grid; gap: clamp(.9rem, 2vw, 1.4rem); grid-template-columns: 1fr;
  background: rgba(36, 23, 9, .80);
  -webkit-backdrop-filter: blur(18px) saturate(1.2);
  backdrop-filter: blur(18px) saturate(1.2);
  border: 1px solid rgba(224, 187, 147, .34); border-radius: var(--r-lg);
  box-shadow: 0 18px 44px rgba(36, 23, 9, .34);
  padding: clamp(1.1rem, 2.4vw, 1.5rem) clamp(1.25rem, 3vw, 2rem); }
/* Without backdrop-filter there is no blur to darken the bread behind the
   type, so the ground has to carry it alone. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .facts__in { background: rgba(36, 23, 9, .92); }
}

/* The date is the one fact here worth size. Three equal columns with a
   circled icon each gave the delivery window the same weight as the day
   she bakes, which is not how anybody reads this. */
.facts__k { font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase;
  color: var(--caramel-lt); }
.facts__d { margin-top: .12rem; font-family: var(--display); font-weight: 800;
  font-size: clamp(1.5rem, 3.4vw, 2.1rem); line-height: 1.04; letter-spacing: -.02em;
  color: var(--cream); text-wrap: balance; }
.facts__soon { margin-top: .3rem; font-size: var(--t-xs); color: var(--caramel-lt); }
.facts__soon:empty { display: none; }

.facts__list { display: grid; gap: .55rem; }
.facts__row { display: grid; grid-template-columns: 5.5rem 1fr; gap: .2rem .9rem; align-items: baseline; }
.facts__row dt { font-size: var(--t-xs); letter-spacing: .12em; text-transform: uppercase;
  color: var(--caramel-lt); }
.facts__row dd { font-family: var(--display); font-weight: 700; font-size: 1rem;
  line-height: 1.3; color: var(--cream); }
/* The pickup point is the quieter half of that line: the time is what
   people scan for, the address is what they need once they have decided. */
.facts__at { display: block; font-family: var(--text); font-weight: 400;
  font-size: var(--t-xs); line-height: 1.45; color: var(--on-dark-2); }
/* Free is worth saying and not worth a badge: a pill here was one more
   piece of furniture in a card that already had too much. */
.facts__free { font-family: var(--text); font-weight: 500; font-size: var(--t-xs);
  color: var(--caramel-lt); }
.facts__free::before { content: "·"; margin: 0 .3rem 0 .15rem; }

/* A dotted rule, the same device the review cards use, instead of the
   hairline dividers every three-column strip on the web has. */
@media (max-width: 719px) {
  .facts__list { padding-top: clamp(.8rem, 2.4vw, 1.1rem); border-top: 2px dotted rgba(224, 187, 147, .32); }
}
@media (min-width: 720px) {
  .facts__in { grid-template-columns: auto 1fr; align-items: center;
    gap: clamp(1.5rem, 4vw, 3rem); }
  .facts__list { padding-left: clamp(1.5rem, 4vw, 3rem); border-left: 2px dotted rgba(224, 187, 147, .32); }
}
/* Wide enough for the two windows to sit side by side, which fills the bar
   rather than leaving half of it empty next to the date. */
@media (min-width: 1040px) {
  .facts__list { grid-template-columns: 1fr 1fr; gap: 0 clamp(1.5rem, 4vw, 3rem); }
}

.shop__head { display: grid; gap: var(--s-5); grid-template-columns: 1fr; margin-bottom: clamp(2rem, 4vw, 3rem); }
/* Centred headings mean this row cannot stay a heading-plus-button split:
   the heading would centre inside its own column and sit left of the page.
   One stacked column, both parts centred. */
.shop__head { justify-items: center; text-align: center; }

/* The heading sits closer to its filter row than to the grid below it. */
.shop__head:has(+ .filters) { margin-bottom: clamp(1.1rem, 2.2vw, 1.6rem); }

/* Filter pills. Same outline-to-solid language as the basket button beside
   the heading and the current link in the nav, so a chosen filter reads as
   "you are here" rather than as a fourth kind of button. */
/* On a wide screen the pills wrap and centre. On a phone five of them stack
   into four ragged rows and push the bakes below the fold, so there they
   become one row that scrolls sideways instead: the first two are visible,
   the rest are a swipe away, and the row keeps its height whatever she
   names her categories.

   -webkit-overflow-scrolling for momentum on iOS, scroll-snap so a swipe
   settles on a pill rather than halfway through one, and the gutter is
   padding rather than margin so the last pill can reach the screen edge
   and still have room to breathe. */
.filters { display: flex; flex-wrap: wrap; gap: .55rem; margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }

@media (max-width: 700px) {
  .filters {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--gutter);
    /* Break out of the shell so the row runs edge to edge, then put the
       gutter back as padding. A row that stops short of the edge does not
       look swipeable. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-block: .15rem;
    /* No scrollbar: the pills themselves show there is more. */
    scrollbar-width: none;
  }
  .filters::-webkit-scrollbar { display: none; }
  .filter { flex: none; scroll-snap-align: start; }
}
.filter {
  display: inline-flex; align-items: baseline; gap: .45rem;
  padding: .55rem 1.05rem; border-radius: var(--r-pill);
  border: 2px solid var(--cocoa); background: var(--cream); color: var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  transition: background var(--d-1) var(--ease), color var(--d-1) var(--ease);
}
.filter:hover { background: var(--blush); }
.filter[aria-pressed="true"] { background: var(--cocoa); color: var(--cream); }
/* The count carries no ground of its own, so it can never fall below the
   label it sits beside, whichever state the pill is in. */
.filter__n { font-weight: 500; font-size: .78em; }

/* A filtered-out card must stay out: .bake sets display, which outranks the
   user agent's [hidden] rule on its own. */
.bake[hidden] { display: none; }

/* Two up on a phone rather than one: a single column makes the shop a very
   long scroll and hides how much there is. The column count is set rather
   than left to auto-fill, so it never lands on an awkward in-between. */
.shop__grid { display: grid; gap: clamp(.7rem, 2vw, 1.75rem); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 700px)  { .shop__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1040px) { .shop__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }


/* Cards stretch to the tallest in their row and pin their foot to the
   bottom, so a longer name never leaves one card's Add button floating
   above its neighbour's. */
.bake { height: 100%; display: flex; flex-direction: column; background: var(--cream); border: 1.5px solid var(--cocoa); border-radius: var(--r-lg); overflow: hidden; transition: transform var(--d-2) var(--ease), box-shadow var(--d-2) var(--ease); }
.bake:hover { transform: translateY(-4px); box-shadow: var(--lift); }

/* A price that has been reduced. The old figure is struck through and
   stepped back so the eye lands on what it costs today; the new one is the
   one that reads as the price. Both sit on one line so a card on sale is
   the same height as a card that is not. */
.was { color: var(--ink-3); text-decoration-thickness: 1.5px; text-underline-offset: 2px;
  font-weight: 500; margin-right: .35rem; white-space: nowrap; }
.now { color: var(--ink); white-space: nowrap; }
/* On a card the price shares a narrow row with the Add button. Two figures
   need about 150px and the box was only giving them 129, so they wrapped.
   Letting the price claim the spare room in that row, and setting the old
   figure a size down since it is a reference rather than the price, puts
   them side by side the way a sale is normally read. */
.bake__price { white-space: nowrap; }
.bake__price .was { font-size: .78em; }
.bake__price .now { font-weight: 800; }
.bake__foot .add { flex: none; }

.bake__face { position: relative; aspect-ratio: 1; display: grid; place-items: center; padding: var(--s-6) var(--s-5); background: var(--field, var(--tan)); border-bottom: 1.5px solid var(--cocoa); overflow: hidden; }
.bake__face img.bake__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.bake__title { font-family: var(--display); font-weight: 800; font-size: clamp(1.4rem, 2.4vw, 1.9rem); line-height: 1.02; letter-spacing: var(--tr-head); text-align: center; text-wrap: balance; }
.bake__face--light .bake__title { color: var(--cocoa); }
.bake__face--dark .bake__title { color: var(--cream); }
.bake__mark { position: absolute; right: 12px; bottom: 12px; width: 34px; opacity: .5; }
.bake__face--dark .bake__mark { filter: brightness(0) saturate(100%) invert(97%) sepia(6%) saturate(290%) hue-rotate(337deg) brightness(104%) contrast(95%); }

.bake__body { display: flex; flex-direction: column; flex: 1; padding: var(--s-5); }
.bake__name { font-family: var(--display); font-weight: 700; font-size: 1.1rem; letter-spacing: -.01em; line-height: 1.2; color: var(--ink); }
.bake__name--kind { font-size: var(--t-sm); letter-spacing: var(--tr-mark); text-transform: uppercase; color: var(--ink-3); }
/* LONG COPY IS FOLDED, NOT CUT.
   Her descriptions run to four hundred characters, which left the cards at
   wildly different heights and pushed the price and the Add button so far
   down the row that the shop read as a wall of text. Three lines, then a
   way to open the rest. Whether a given description actually needs folding
   is MEASURED in js/app.js once it is laid out, because whether 400
   characters is three lines or six depends on the column width. */
.bake__desc { margin-top: var(--s-3); font-size: var(--t-sm); line-height: 1.55; color: var(--ink-3);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.bake[data-open="true"] .bake__desc { -webkit-line-clamp: unset; overflow: visible; }
/* Padding carries the tap target so the control does not cost a whole line
   of the height the fold just saved. */
.bake__more { display: inline-flex; align-items: center; min-height: 44px; padding: 0 .1rem;
  margin-top: -.35rem; font-family: var(--display); font-weight: 700; font-size: var(--t-xs);
  color: var(--ink); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--rule-ink); }
.bake__more:hover { text-decoration-color: currentColor; }
/* What is left of it, when she has said how many there are. Quiet: it is
   information, not a countdown clock. See stockNote() in js/app.js for when
   it is shown at all. */
.bake__left { margin-top: var(--s-3); font-size: var(--t-xs); font-weight: 700; letter-spacing: .01em; color: var(--brown); }
.bake__left--out { color: var(--ink-3); font-weight: 600; }

/* Why an order was not taken, said on the checkout rather than in an alert
   box. Only ever shown with a reason in it. */
.refused { margin-top: var(--s-5); padding: var(--s-4) var(--s-5); border: 2px solid var(--cocoa); border-radius: var(--r-md); background: var(--blush); color: var(--ink); font-size: var(--t-sm); font-weight: 600; line-height: 1.5; }
.bake__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); margin-top: auto; padding-top: var(--s-5); }
/* A category she is still filling. Quiet, and it offers the way back. */
.shopempty { display: grid; justify-items: center; gap: .5rem;
  padding: clamp(2.5rem, 8vw, 4.5rem) 1rem; text-align: center; }
.shopempty__t { font-family: var(--display); font-weight: 800;
  font-size: var(--t-sub); color: var(--ink); }
.shopempty__s { max-width: 34ch; font-size: var(--t-sm); color: var(--ink-2); }
.shopempty .btn { margin-top: var(--s-4); }

/* ------------------------------------------------------------ LOAF STYLE */
/* Whole or sliced, on the cards that offer it. A segmented pair rather than
   a dropdown: two options, both worth seeing without a tap, and the choice
   has to be obvious before the Add button is pressed rather than after. */

.styles { display: flex; gap: .4rem; margin: var(--s-4) 0; padding: 0; border: 0; }
.styles__opt { flex: 1 1 0; position: relative; }
.styles__opt input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.styles__opt span {
  display: grid; place-items: center; min-height: 38px; padding: 0 .5rem;
  border: 1.5px solid var(--rule-ink); border-radius: var(--r-pill);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-2);
  transition: background var(--d-1) var(--ease), border-color var(--d-1) var(--ease);
}
.styles__opt small { font-weight: 500; opacity: .8; }
.styles__opt input:checked + span {
  background: var(--cocoa); border-color: var(--cocoa); color: var(--cream);
}
.styles__opt input:focus-visible + span { outline: 2px solid var(--cocoa); outline-offset: 2px; }

/* Where a line names its style: the drawer, the basket, the checkout. */
.line__style {
  display: inline-block; margin-left: .35rem; padding: .05rem .4rem;
  border: 1.5px solid var(--rule-ink); border-radius: var(--r-pill);
  font-size: .72em; font-weight: 600; color: var(--ink-3); vertical-align: middle;
}

.bake__price { font-family: var(--display); font-weight: 800; font-size: 1.35rem; color: var(--ink); }
/* Declared after the base rule above so it survives it.
   Side by side where the card is wide enough, and the old figure above the
   new one where it is not. Forcing one line clipped the price behind the
   Add button at four cards across, which is worse than a wrap: a price that
   is cut in half is a price nobody can read. Each figure is unbreakable, so
   it is only ever the pair that wraps, never a number. */
.bake__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 .3rem;
  flex: 1 1 auto; min-width: 0; }

.add {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .65rem 1.15rem; min-height: 46px; border-radius: var(--r-pill);
  background: var(--caramel); color: var(--cocoa); border: 2px solid var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  transition: background var(--d-1) var(--ease);
}
.add:hover { background: var(--cocoa); color: var(--cream); }
.add[disabled] { background: transparent; color: var(--ink-3); border-color: var(--rule-ink); cursor: not-allowed; }
.add[disabled]:hover { background: transparent; color: var(--ink-3); }
.add svg { width: 14px; height: 14px; stroke: currentColor; fill: none; }
.add[data-state="added"] { background: var(--olive-lt); color: var(--cream); }

/* --------------------------------------- the card at two across on a phone */
/* This block has to sit after the card's own rules, not before them: it was
   written above them and every property it shares with the base rule lost,
   which is why the photo stayed square and the name stayed at 1.1rem. */
@media (max-width: 560px) {
  /* A square photo, a three-line name and a stacked foot made the card about
     300px tall, so two rows filled the screen. A 4:3 crop keeps the loaf
     whole and takes about 40px off every card. */
  .bake__face { aspect-ratio: 4 / 3; padding: 0; }

  /* A wider text column: the old padding took a fifth of a 170px card, which
     is what pushed names onto a third line. */
  .bake__body { padding: .8rem .75rem .85rem; }

  /* The name is the one thing on a product card that must never be cut: you
     cannot buy "Onion Chili Crunch &...". It wraps to as many lines as it
     needs and is never clamped. Nothing is reserved for it either; the card
     stretches to its row and the foot is pinned to the bottom, so the prices
     and the buttons land on the same line whatever the names do. */
  .bake__name { font-size: .88rem; line-height: 1.28; letter-spacing: -.015em; }
  .bake__name--kind { font-size: var(--t-xs); }

  /* The description comes out rather than being truncated. Clamped into a
     150px column it read as three words and an ellipsis, which looks broken
     rather than brief. Photo, name, price and Add is the whole shopping unit
     on a phone; the full description is still in the basket and at checkout. */
  .bake__desc, .bake__more { display: none; }

  .bake__foot { flex-direction: column; align-items: stretch; gap: .45rem; padding-top: var(--s-3); }
  .bake__price { font-size: 1.08rem; }
  .add { justify-content: center; width: 100%; padding-block: .6rem; min-height: 44px; }
}


/* ------------------------------------------------------------- SPLITS */

.split { display: grid; gap: clamp(2rem, 5vw, 4.5rem); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) { .split { grid-template-columns: 1fr 1fr; } .split--wide-left { grid-template-columns: 1.25fr 1fr; } .split--wide-right { grid-template-columns: 1fr 1.25fr; } }
.split > * { min-width: 0; }

/* A picture frame: a flat field with the badge, or the photograph. */
.frame { position: relative; display: grid; place-items: center; border: 2px solid var(--cocoa); border-radius: var(--r-lg); background: var(--field, var(--blush)); overflow: hidden; aspect-ratio: 4 / 3; }
.frame--tall { aspect-ratio: 3 / 4; }
.frame--square { aspect-ratio: 1; }
.frame--wide { aspect-ratio: 16 / 10; }
.frame img.frame__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--pos, center); }
/* A real photograph retires the badge stand-in automatically. */
.frame:has(.frame__photo) .seal { display: none; }
.frame .seal { width: min(44%, 190px); background: none; }
.frame__cap { position: absolute; left: 14px; bottom: 14px; }

/* --------------------------------------------------------------- NUMBERS */

/* The oven photograph sits behind the method, under a forest scrim. The step
   cards carry only a border and no ground of their own, so the scrim is what
   holds their text up; its alpha is measured against the real pixels, not
   picked by eye. */
.how { position: relative; isolation: isolate; overflow: hidden; }
.how__img { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; object-fit: cover; object-position: center 58%; }
/* Heavier where the heading and standfirst sit, lighter over the cards,
   because the cards carry a ground of their own below. That keeps the oven
   visible through the middle of the section instead of flattening it out. */
/* Cocoa, not forest. A green scrim over warm bread reads as a colour cast on
   the photograph; the brand's own ink keeps the crust warm and still carries
   the type. The section's forest ground stays underneath as the fallback if
   the image never loads, which is a pairing already known to be legible. */
.how__scrim { position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(48,32,15,.88) 0%, rgba(48,32,15,.85) 26%, rgba(48,32,15,.58) 52%, rgba(48,32,15,.72) 100%); }
.how__in { position: relative; }

.steps { display: grid; gap: clamp(.7rem, 2vw, var(--s-5)); grid-template-columns: repeat(2, minmax(0, 1fr)); counter-reset: s; }
@media (min-width: 760px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .steps { grid-template-columns: repeat(3, 1fr); } }
/* Over a photograph a border is not a ground: the cards take the same glass
   the bake-day card uses, so their text sits on a known colour. */
.step-card { height: 100%; padding: var(--s-6); border: 1px solid rgba(227, 213, 192, .26); border-radius: var(--r-md);
  background: rgba(36, 23, 9, .72);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  backdrop-filter: blur(14px) saturate(1.1); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .step-card { background: rgba(36, 23, 9, .9); }
}
.step-card__n { display: grid; place-items: center; width: 46px; height: 46px; border-radius: 50%; background: var(--caramel); color: var(--cocoa); font-family: var(--display); font-weight: 800; font-size: 1.05rem; }
.step-card h3 { margin-top: var(--s-4); font-size: 1.2rem; }
.step-card p { margin-top: var(--s-3); font-size: var(--t-sm); line-height: 1.6; }
@media (max-width: 560px) {
  /* Two up on a phone leaves about 150px of text column, so the number moves
     onto the same line as the title instead of taking a row of its own, and
     the padding comes in. Nothing is cut: the method is the point of the
     section and all six steps still read in full. */
  .step-card { padding: .85rem .8rem 1rem; display: grid; align-content: start; }
  .step-card__n { width: 28px; height: 28px; font-size: .82rem; grid-area: n; }
  .step-card h3 { margin-top: 0; font-size: .97rem; line-height: 1.2; grid-area: h; align-self: center; }
  .step-card p { margin-top: .6rem; font-size: var(--t-xs); line-height: 1.45; grid-area: p; }
  .step-card { grid-template-areas: "n h" "p p"; grid-template-columns: auto 1fr; column-gap: .5rem; }
}




/* -------------------------------------------------------------- ORDERING */

.zones { width: 100%; border-collapse: collapse; }
.zones caption { text-align: left; padding-bottom: var(--s-4); font-size: var(--t-sm); color: var(--ink-3); }
.zones th, .zones td { text-align: left; padding: var(--s-4) var(--s-3); border-bottom: 2px dotted var(--rule-ink); vertical-align: top; }
.zones th { font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink-3); }
.zones th:first-child, .zones td:first-child { padding-left: 0; }
.zones th:last-child, .zones td:last-child { padding-right: 0; text-align: right; }
.zones td:first-child { font-family: var(--display); font-weight: 700; font-size: 1.08rem; color: var(--ink); }
.zones .fig { font-family: var(--display); font-weight: 800; font-size: 1.15rem; color: var(--ink); white-space: nowrap; }
.zones .free { color: var(--olive); }
.zones small { display: block; margin-top: .4rem; max-width: 46ch; font-family: var(--text); font-weight: 400; font-size: var(--t-xs); line-height: 1.6; color: var(--ink-3); }

.ways { display: grid; gap: clamp(1rem, 2vw, 1.75rem); grid-template-columns: 1fr; margin-top: clamp(2rem, 4vw, 3rem); }
@media (min-width: 820px) { .ways { grid-template-columns: 1fr 1fr; } }
.way { padding: clamp(1.5rem, 3vw, 2rem); background: var(--cream); border: 2px solid var(--cocoa); border-radius: var(--r-lg); }
.way h3 { font-size: var(--t-sub); }
.way p { margin-top: var(--s-4); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); }
.way__when { display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-6); margin-top: var(--s-5); padding-top: var(--s-4); border-top: 2px dotted var(--rule-ink); }
.way__when div { display: grid; gap: .15rem; }
.way__when dt { font-size: var(--t-xs); color: var(--ink-3); }
.way__when dd { font-family: var(--display); font-weight: 700; font-size: 1.05rem; color: var(--ink); }

/* --------------------------------------------------------------- NOTICE */

.notice { display: grid; grid-template-columns: 2.75rem 1fr; gap: var(--s-4); padding: clamp(1.25rem, 2.5vw, 1.75rem); border-radius: var(--r-lg); background: var(--blush); border: 2px solid var(--cocoa); align-items: start; }
.notice__icon { display: grid; place-items: center; width: 42px; height: 42px; border-radius: 50%; background: var(--caramel); }
.notice__icon svg { width: 20px; height: 20px; stroke: var(--cocoa); fill: none; }
.notice h3 { font-size: 1.15rem; }

/* The same block, folded. The grid above gives it two columns, so the
   summary spans both and lays its own row out; the body then sits in the
   second column, lined up under the heading rather than under the icon. */
.notice--fold { display: block; }
.notice--fold summary {
  display: grid; grid-template-columns: 2.75rem 1fr auto; align-items: center;
  gap: var(--s-4); cursor: pointer; list-style: none;
}
.notice--fold summary::-webkit-details-marker { display: none; }
.notice__h { font-family: var(--display); font-weight: 800; font-size: 1.15rem; color: var(--ink); }
.notice__chev {
  flex: none; display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: 50%; background: var(--caramel); color: var(--cocoa);
  transition: transform var(--d-2) var(--ease);
}
.notice__chev svg { width: 14px; height: 14px; }
.notice--fold[open] .notice__chev { transform: rotate(45deg); }
.notice--fold summary:focus-visible { outline: 2px solid var(--cocoa); outline-offset: 3px; border-radius: var(--r-sm); }

/* Indented to the heading, not the icon. */
.notice__body { padding-left: calc(2.75rem + var(--s-4)); }
@media (max-width: 560px) { .notice__body { padding-left: 0; } }
/* The notice sits in a full-width box, so capping the line at 72ch left a
   third of the box empty and made a short warning look long. It now runs to
   the width it is given. */
.notice p { margin-top: var(--s-3); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); text-wrap: pretty; }
.notice strong { font-weight: 700; color: var(--ink); }
/* On a phone the icon column and the generous padding pushed a four-line
   warning out to eight. The mark drops to a smaller inline badge, the
   heading sits tight above the text, and the padding comes in. It stays
   fully visible: this is the one thing on the page a reader with an allergy
   has to be able to find without opening anything. */
@media (max-width: 560px) {
  .notice { grid-template-columns: 2rem 1fr; gap: var(--s-3); padding: var(--s-4) var(--s-4) var(--s-5); border-radius: var(--r-md); }
  .notice__icon { width: 30px; height: 30px; }
  .notice__icon svg { width: 16px; height: 16px; }
  .notice h3 { font-size: 1rem; line-height: 1.3; }
  .notice p { margin-top: var(--s-2); font-size: var(--t-xs); line-height: 1.5; }
}

/* ----------------------------------------------------------------- QUOTE */

.quote { max-width: 30ch; }
.quote blockquote { margin: 0; font-family: var(--display); font-weight: 800; font-size: var(--t-sect); line-height: 1.04; letter-spacing: var(--tr-head); color: var(--cream); }
.quote figcaption { margin-top: var(--s-5); font-family: var(--display); font-weight: 700; color: var(--caramel-lt); }

/* ----------------------------------------------------------------- STORY */
/* A spread rather than a section: the photograph takes one half of the
   screen edge to edge, full height, and the account is set opposite it on
   bark, the deepest ground the brand owns. The dark break also gives the
   page a rhythm, sitting between the cream shop above and the forest
   method below. */

/* The site's paper white, which is the cream everything else sits on rather
   than a pure #fff that would read cold beside it. The shop above is the
   same ground, so a rule across the top keeps the two sections from running
   together into one long page of cream. */
.story { background: var(--cream); color: var(--ink-2); border-top: 2px solid var(--rule-ink-sf); }
@media (min-width: 980px) {
  /* The picture is a column, not half the screen: roughly a third of the
     width, so the account is what the section is mostly made of. The frame
     keeps the shell's gutter on the outside, like everything else. */
  .story { display: grid; grid-template-columns: minmax(0, .62fr) minmax(0, 1fr); align-items: center;
    padding-block: clamp(3rem, 6vw, 5rem);
    padding-inline: max(var(--gutter), calc((100vw - var(--shell)) / 2 + var(--gutter))); }
}

/* Framed, not bled: the picture sits inside the dark ground with room around
   it, a hairline in the brand's caramel and the corner radius the rest of the
   site uses. */
.story__shot { position: relative; margin: 0; align-self: center;
  border: 1.5px solid var(--cocoa); border-radius: var(--r-lg);
  overflow: hidden; background: var(--cocoa); box-shadow: var(--lift-deep); }
.story__shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 54% 24%; aspect-ratio: 4 / 5; }


/* Her name sits on the picture on a solid bar. Cream over a fade on a
   photograph cannot be contrast-checked from CSS, and the apron is pale. */
.story__cap { position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: baseline; gap: .6rem;
  padding: clamp(.85rem, 1.8vw, 1.15rem) clamp(1.1rem, 2.6vw, 1.9rem);
  background: var(--bark); color: var(--on-dark-2);
  font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase; }
.story__cap span { font-family: var(--display); font-weight: 800; font-size: var(--t-sm); letter-spacing: .06em; color: var(--cream); }
/* A fade above the bar so it lands on the photograph instead of cutting a
   hard line across it. */
.story__cap::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%;
  height: clamp(44px, 8vw, 72px); pointer-events: none;
  background: linear-gradient(180deg, rgba(36,23,9,0) 0%, var(--bark) 100%); }
/* Full bleed on a phone, so the bar reaches both edges and its text still
   lines up with the page gutter. */


.story__text { display: grid; align-items: center;
  padding: clamp(2.5rem, 6vw, 4rem) var(--gutter); }
@media (min-width: 980px) { .story__text { padding: 0 0 0 clamp(2.25rem, 4.5vw, 4rem); } }
/* Centred in its column rather than pinned to the left of it. The well is
   capped at 36rem while the column keeps growing with the window, so all
   the slack used to pile up on the right: 39px lopsided at 1136px and more
   than twice that on a wide screen, which reads as a block that slipped.
   The text itself stays left aligned - three paragraphs set ragged-centre
   would be harder to read, not more centred. */
.story__well { max-width: 36rem; margin-inline: auto; }

/* A quiet label, not a chip: it names the section without competing with it. */
.story__eyebrow { font-size: var(--t-xs); letter-spacing: .2em; text-transform: uppercase; color: var(--ink-2); }

.story__h { margin-top: var(--s-4);
  font-family: var(--script); font-style: italic; font-weight: 500;
  font-size: clamp(2.2rem, 3.7vw, 3.6rem); line-height: 1.12; letter-spacing: 0;
  color: var(--cocoa); text-wrap: balance; }

.story__prose { margin-top: var(--s-6); }
.story__prose p { color: var(--ink-2); }
.story__prose p + p { margin-top: var(--s-4); }
/* The opening paragraph carries her voice, so it is set up a size. */
.story__prose p:first-child { font-size: var(--t-lead); line-height: 1.6; color: var(--ink); }

/* Three facts, the kind a reader skims for. Each is already stated elsewhere
   on the site or in the Observer piece. */
.story__facts { margin-top: var(--s-7); padding-top: var(--s-5);
  display: grid; gap: var(--s-4); grid-template-columns: 1fr;
  border-top: 2px dotted var(--rule-ink); }
@media (min-width: 520px) { .story__facts { grid-template-columns: repeat(3, 1fr); gap: var(--s-5); } }
.story__facts dt { font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.story__facts dd { margin-top: .3rem; font-family: var(--display); font-weight: 700; font-size: var(--t-sm); line-height: 1.35; color: var(--ink); }
/* ------------------------------------------------- THE STORY, ON A PHONE */
/* It used to run the photograph edge to edge with no border, radius or
   shadow, on the theory that a bled picture opens a section better than a
   boxed one. On a phone it just read as unfinished: a photograph that had
   escaped the page rather than one that had been placed on it, with a hard
   black slab across the bottom of it and the headline jammed underneath.

   So it is framed, in the same 2px cocoa border and var(--r-lg) radius that
   every product card, gallery cell and review on this site already uses. The
   frame is the house style; the bleed was the exception.

   Measured at 390px wide: the section was 1422px, which is 1.68 screens.
   Where that went was the point — 468px of photograph, 506px of her account,
   99px of headline, and 202px for three short facts stacked one above the
   other. The account is the content and stays. The other three are all
   addressed below. */

@media (max-width: 979px) {
  /* Inset by the page gutter, so the frame has a margin to sit in and the
     photograph lines up with every other edge on the page. */
  .story__shot {
    margin-inline: var(--gutter);
    border: 1.5px solid var(--cocoa);
    border-radius: var(--r-lg);
    /* The deep lift is for a photograph floating in a dark column on a wide
       screen. On cream at arm's length it reads as a smudge, so the standard
       card shadow instead. */
    box-shadow: var(--lift);
  }
  /* 5:6 rather than square: a square crop of a 3:4 portrait took a quarter
     of the height out and the caption bar then covered the bottom of the
     loaf she is holding. At 5:6 only about a tenth is cropped, so she and
     the loaf both sit clear of the bar. Inset by two gutters the frame is
     40px narrower than the screen, which takes ~48px off the photograph's
     height for free. */
  .story__shot img { aspect-ratio: 5 / 6; object-position: 54% 30%; }

  /* The caption is a plate inside the frame now, not a bar across the
     screen, so it takes the frame's own padding and its bottom corners are
     clipped to the radius by the figure's overflow. */
  .story__cap { padding-inline: clamp(.95rem, 4vw, 1.3rem); }

  /* The eyebrow stays gone. On a phone it lands directly under the caption,
     so the section opened with two lines of small caps saying nearly the
     same thing. Her name and her role are on the picture and the account
     under it is in her own voice; nothing needs to announce that it is the
     story. On a wide screen the caption is nowhere near it, so it stays. */
  .story__eyebrow { display: none; }

  .story { padding-block: clamp(2.25rem, 6vw, 3rem); }
  /* Enough air under the frame that the headline belongs to the photograph
     rather than colliding with it. */
  .story__text { padding: clamp(1.6rem, 6vw, 2.2rem) var(--gutter) 0; }
  .story__h { font-size: clamp(1.85rem, 7vw, 2.4rem); margin-top: 0; }

  /* The three facts were 202px of mostly empty cream: a single column of
     label-over-value with a full line of air between each. As rows with the
     label left and the value right they are about 120px and read as the
     credits block they are, with the site's own dotted rule between them
     instead of only above. */
  .story__facts {
    margin-top: var(--s-6); padding-top: var(--s-5);
    grid-template-columns: 1fr; gap: 0;
  }
  .story__facts > div {
    display: grid; grid-template-columns: auto 1fr;
    align-items: baseline; gap: .5rem 1rem;
    padding-block: .62rem;
    border-bottom: 2px dotted var(--rule-ink-sf);
  }
  .story__facts > div:last-child { border-bottom: 0; padding-bottom: 0; }
  .story__facts dd { margin-top: 0; text-align: right; }
}


/* ------------------------------------------------------------- THE POINTS */
/* Dough Points and the bank-transfer flow: the balance card, the statement,
   the three-step explainer, the checkout panel, the account block shown once
   an order is placed, and the receipt picker. All of these sit on cream, so
   they use the page's own ink rather than the on-dark colours the hero and
   the method section need. */

.ptscard { padding: clamp(1.5rem, 4vw, 2.25rem); text-align: center;
  background: var(--caramel); border: 2px solid var(--cocoa); border-radius: var(--r-lg);
  box-shadow: var(--lift); }
.ptscard__k { font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase; color: var(--cocoa); }
.ptscard__v { margin-top: .3rem; font-family: var(--display); font-weight: 900;
  font-size: clamp(3rem, 14vw, 4.5rem); line-height: .95; letter-spacing: -.03em; color: var(--cocoa); }
.ptscard__u { font-size: clamp(1rem, 3.5vw, 1.4rem); font-weight: 700; letter-spacing: 0; }
.ptscard__w { margin-top: .4rem; font-size: var(--t-sm); font-weight: 600; color: var(--cocoa); }

.ptsnext { margin-top: var(--s-5); padding: var(--s-5); background: var(--blush);
  border-radius: var(--r-md); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); }

.ptslist { margin-top: var(--s-5); display: grid; gap: 0; }
.ptslist__row { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: .5rem 1rem; padding: .8rem 0; border-bottom: 2px dotted var(--rule-ink-sf); }
.ptslist__row:last-child { border-bottom: 0; }
.ptslist dt { font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink); }
.ptslist dt span { display: block; margin-top: .15rem; font-family: var(--text); font-weight: 400;
  font-size: var(--t-xs); color: var(--ink-3); }
.ptslist dd { font-family: var(--display); font-weight: 800; font-size: 1.05rem; }
.ptslist dd.is-up { color: #3f6b3a; }
.ptslist dd.is-down { color: var(--ink-3); }

.how3 { margin-top: clamp(1.75rem, 4vw, 2.5rem); display: grid; gap: var(--s-4); }
@media (min-width: 720px) { .how3 { grid-template-columns: repeat(3, 1fr); } }
.how3__step { padding: var(--s-5); background: var(--cream);
  border: 2px solid var(--cocoa); border-radius: var(--r-lg); }
.how3__n { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%;
  background: var(--caramel); color: var(--cocoa); font-family: var(--display); font-weight: 800; }
.how3__step h3 { margin-top: var(--s-4); font-size: 1.05rem; color: var(--ink); }
.how3__step p { margin-top: var(--s-3); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); }

/* At checkout, inside the summary panel. */
.pts { margin-top: var(--s-5); padding-top: var(--s-5); border-top: 2px dotted var(--rule-ink-sf); }
.pts__h { font-family: var(--display); font-weight: 800; font-size: var(--t-sm); color: var(--ink); }
.pts__p { margin-top: .3rem; font-size: var(--t-xs); line-height: 1.6; color: var(--ink-2); }

/* The account details, shown once an order is placed. A definition list
   rather than a paragraph: these are fields somebody copies one at a time
   into a banking app, and a sentence is the wrong shape for that. */
.done { padding: clamp(1.25rem, 3.5vw, 2rem); background: var(--cream);
  border: 2px solid var(--cocoa); border-radius: var(--r-lg); box-shadow: var(--lift); }
.done__top { display: flex; align-items: flex-start; gap: var(--s-4); }
.done__tick { flex: none; display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 50%; background: var(--sage); color: var(--cocoa); }
.done__tick svg { width: 22px; height: 22px; stroke: currentColor; fill: none; }
.done__h { font-family: var(--display); font-weight: 800; font-size: var(--t-sub); line-height: 1.2; color: var(--ink); }
.done__sub { margin-top: .2rem; font-size: var(--t-sm); color: var(--ink-2); }
.done__note { margin-top: var(--s-5); font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); }
.done__act { margin-top: var(--s-4); display: grid; gap: var(--s-3); }

.bank { margin-top: var(--s-5); display: grid; gap: 0;
  border: 1.5px solid var(--rule-ink); border-radius: var(--r-md); overflow: hidden; }
.bank__row { display: grid; grid-template-columns: 9.5rem 1fr; gap: .4rem .9rem;
  align-items: baseline; padding: .7rem .9rem; background: var(--blush); margin: 0; }
.bank__row:nth-child(even) { background: var(--cream); }
.bank__row dt { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.bank__row dd { margin: 0; font-family: var(--display); font-weight: 700; font-size: var(--t-sm);
  color: var(--ink); display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; word-break: break-word; }
.bank__copy { min-height: 28px; padding: .1rem .55rem; border-radius: var(--r-pill);
  border: 2px solid var(--cocoa); background: transparent; color: var(--ink);
  font-family: var(--text); font-weight: 600; font-size: var(--t-xs); }
.bank__copy:hover { background: var(--cocoa); color: var(--cream); }
@media (max-width: 480px) { .bank__row { grid-template-columns: 1fr; } }

/* The receipt picker. */
.rcpt { margin-top: var(--s-6); display: grid; gap: var(--s-4); }
.rcpt__drop { display: grid; place-items: center; gap: .35rem;
  padding: clamp(1.75rem, 6vw, 2.75rem) 1rem; border: 2px dashed var(--rule-ink);
  border-radius: var(--r-lg); background: var(--blush); cursor: pointer;
  text-align: center; color: var(--ink-2); }
.rcpt__drop:hover { border-color: var(--cocoa); }
.rcpt__drop svg { width: 30px; height: 30px; }
.rcpt__t { font-family: var(--display); font-weight: 800; font-size: var(--t-sm); color: var(--ink); }
.rcpt__s { font-size: var(--t-xs); color: var(--ink-3); }
.rcpt__shot { margin: 0; border: 2px solid var(--cocoa); border-radius: var(--r-md); overflow: hidden; }
.rcpt__shot img { display: block; width: 100%; max-height: 46svh; object-fit: contain; background: var(--bark); }
.rcpt__shot figcaption { padding: .5rem .8rem; font-size: var(--t-xs); color: var(--ink-3); background: var(--blush); }

/* ---------------------------------------------------------- ANNOUNCEMENT */
/* A narrow line above the nav. Deliberately plain: it is the one thing on
   the page allowed to interrupt, so it earns that by being small, by sitting
   above everything rather than over it, and by scrolling away like any other
   part of the page. It does not follow the reader down the screen. */

/* Stuck to the top, above the nav, and the nav stacks underneath it.
   Its height is measured into --ann-h by js/banner.js rather than guessed,
   because the line wraps to two on a narrow screen and a hardcoded offset
   would either leave a gap or hide part of the nav. */
.ann { position: sticky; top: 0; z-index: calc(var(--z-nav) + 1);
  background: var(--cocoa); color: var(--cream);
  -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.ann--light { background: var(--blush); color: var(--ink); }

.ann__in { display: flex; align-items: center; gap: var(--s-4);
  min-height: 42px; padding-block: .45rem; padding-inline: var(--gutter); }

/* The window the message travels through. overflow hidden here rather than
   on the strip, so the close button beside it is never clipped. */
.ann__vp { flex: 1 1 auto; min-width: 0; overflow: hidden; }

/* Two copies of the message side by side. Sliding exactly half the track's
   width puts the second copy where the first began, so the loop repeats
   with no jump. The lap is timed in script from the real width. */
.ann__track {
  display: flex; width: max-content;
  animation: annslide var(--ann-lap, 26s) linear infinite;
}
.ann__track:hover { animation-play-state: paused; }
.ann__vp:focus-within .ann__track { animation-play-state: paused; }

@keyframes annslide {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.ann__t { margin: 0; white-space: nowrap; padding-inline-end: 4rem;
  font-size: var(--t-xs); line-height: 1.45; letter-spacing: .01em; }

/* Anyone who has asked their machine for less movement gets none: the track
   stops and the message simply sits there, trimmed with an ellipsis if it
   is too long for the strip. */
@media (prefers-reduced-motion: reduce) {
  .ann__track { animation: none; width: 100%; }
  .ann__track .ann__t + .ann__t { display: none; }
  .ann__t { overflow: hidden; text-overflow: ellipsis; padding-inline-end: 0; }
}

.ann__link { color: inherit; text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px; font-weight: 600; white-space: nowrap; }
.ann__link:hover { text-decoration-thickness: 2px; }

/* Sits at the end of the line and takes the space of the gap on the other
   side, so the words stay centred rather than nudged left by the button. */
.ann__x { flex: none; width: 30px; height: 30px; margin-inline-start: calc(var(--s-4) * -1);
  display: grid; place-items: center; border: 0; border-radius: var(--r-pill);
  background: transparent; color: inherit; opacity: .72; cursor: pointer; }
.ann__x:hover { opacity: 1; }
.ann__x svg { width: 15px; height: 15px; }

@media (max-width: 560px) {
  .ann__in { gap: .5rem; }
  .ann__x { margin-inline-start: 0; }
  /* Less of a run-up on a narrow screen, or the gap between the two copies
     is most of what a phone can show. */
  .ann__t { padding-inline-end: 2.5rem; }
}

/* --------------------------------------------------------------- GALLERY */
/* An even grid of squares.

   It was a masonry of CSS columns, which kept each photograph's own
   proportions but left the set ragged: the pictures run from square to tall
   portrait, so one column ran far longer than its neighbours and the
   reveal animation, which changes an item's height while the columns are
   being balanced, opened gaps the browser never closed.

   A fixed square cell crops a little off the top and bottom of the tall
   ones, and that is the trade: a gallery that reads as one set, at the cost
   of a few centimetres of tray. The crop is centred, so the loaf stays in
   the middle of the frame. */

.gal { margin-top: clamp(1.75rem, 3.5vw, 2.75rem);
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(.6rem, 1.6vw, 1.25rem); }
@media (max-width: 900px) { .gal { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .gal { grid-template-columns: repeat(2, 1fr); gap: .6rem; } }

.gal__cell { position: relative; margin: 0; aspect-ratio: 1;
  border: 1.5px solid var(--cocoa); border-radius: var(--r-lg);
  overflow: hidden; background: var(--blush); box-shadow: var(--lift); }
.gal__cell img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The class photograph leads the gallery across the full width.
   It is the only picture here with people in it, and a square crop of a row
   of seven bakers holding loaves throws most of them away. A letterbox strip
   keeps the whole line in frame and gives the grid underneath something to
   sit against. Shallower on a phone, where a 3:1 strip would be a sliver. */
.gal__cell--wide { grid-column: 1 / -1; aspect-ratio: 16 / 7; }
@media (max-width: 900px) { .gal__cell--wide { aspect-ratio: 16 / 9; } }
@media (max-width: 560px) { .gal__cell--wide { aspect-ratio: 4 / 3; } }

/* The crest, signed into the corner of each photograph.

   Rendered in cream rather than its own brown: these run from a near-white
   tray of muffins to the inside of a dark oven, and the brown mark would
   disappear into the dark ones. Cream with a soft shadow under it holds on
   both, which a flat mark of either colour does not.

   A pseudo-element rather than seven more <img> tags: it is decoration, it
   must never be read out or clicked, and the file is the same badge the nav
   already loaded. */
.gal__cell::after {
  content: ""; position: absolute; z-index: 1; pointer-events: none;
  right: clamp(.6rem, 2.5%, 1rem); bottom: clamp(.6rem, 2.5%, 1rem);
  width: clamp(34px, 14%, 58px); aspect-ratio: 1;
  background: url("../assets/brand/logo-360.webp") center / contain no-repeat;
  filter: brightness(0) invert(1) drop-shadow(0 1px 2px rgba(20, 12, 4, .55));
  opacity: .72;
}

/* ----------------------------------------------------------------- PRESS */
/* Built from the site's own parts rather than dressed up as a newspaper: a
   masthead line, the headline, a pull quote, the facts, and a way out to the
   source. The article's own wording is not reproduced. */

.press { background: var(--cream); border: 2px solid var(--cocoa); border-radius: var(--r-lg); padding: clamp(1.5rem, 3.5vw, 2.75rem); }
.press__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  font-size: var(--t-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-2); }
.press__paper { font-family: var(--display); font-weight: 800; letter-spacing: .06em; color: var(--ink); }
.press__dot { color: var(--ink-3); }

.press__h { margin-top: var(--s-4); font-family: var(--script); font-style: italic; font-weight: 500;
  font-size: clamp(1.75rem, 3.6vw, 2.7rem); line-height: 1.14; letter-spacing: 0;
  color: var(--ink); text-wrap: balance; }
.press__lead { margin-top: var(--s-5); font-size: var(--t-lead); line-height: 1.6; color: var(--ink-2); text-wrap: pretty; }

/* The one direct quotation on the page, marked as one. */
.press__quote { margin-top: var(--s-6); padding-left: clamp(1rem, 2.5vw, 1.5rem); border-left: 3px solid var(--caramel); }
.press__quote p { font-family: var(--display); font-weight: 700;
  font-size: clamp(1.15rem, 2.2vw, 1.5rem); line-height: 1.25; letter-spacing: var(--tr-head); color: var(--ink); }
.press__quote cite { display: block; margin-top: var(--s-3); font-style: normal; font-size: var(--t-sm); color: var(--ink-3); }

.press__sub { margin-top: var(--s-7); font-family: var(--display); font-weight: 800;
  font-size: var(--t-sub); letter-spacing: var(--tr-head); color: var(--ink); }
.press__list { margin-top: var(--s-4); display: grid; gap: var(--s-4); list-style: none; }
.press__list li { position: relative; padding-left: 1.6rem; line-height: 1.6; color: var(--ink-2); }
.press__list li::before { content: ""; position: absolute; left: 0; top: .62em;
  width: 8px; height: 8px; border-radius: 50%; background: var(--caramel); }
.press__list strong { color: var(--ink); }

/* The article predates the current menu, so the page says so where a reader
   would otherwise take its prices as live. */
.press__note { margin-top: var(--s-6); padding: var(--s-5); border-radius: var(--r-md);
  background: var(--blush); border: 2px solid var(--cocoa); line-height: 1.6; color: var(--ink-2); }
.press__note strong { color: var(--ink); }
.press__note a { color: var(--ink); text-decoration-thickness: 2px; text-underline-offset: 3px; }

.press__out { margin-top: var(--s-6); }
.press__out .btn svg { width: 15px; height: 15px; margin-left: .55rem; stroke: currentColor; fill: none; vertical-align: -2px; }
.press__fine { margin-top: var(--s-5); font-size: var(--t-xs); line-height: 1.55; color: var(--ink-3); }

/* Contact links in the policy pages sit in address blocks rather than in a
   sentence, so the inline exemption does not cover them and 20px is under
   the 24px floor. An inline-block with a minimum height clears it without
   opening up the line. */
.doc p a, .doc li a { display: inline-block; min-height: 24px; }

/* ------------------------------------------------------------- THE REELS */
/* Instagram's embed brings its own white card, so the frame around it is the
   site's: cocoa border, the usual radius, and the embed clipped to it. Each
   one is given a fixed height because the embed cannot report its own, and
   anything the embed would scroll to is on Instagram anyway. */

.reels { margin-top: clamp(1.75rem, 3.5vw, 2.75rem);
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); }
.reel { position: relative; height: clamp(470px, 56vw, 560px);
  border: 2px solid var(--cocoa); border-radius: var(--r-lg); overflow: hidden;
  background: var(--cream); box-shadow: var(--lift); }
.reel iframe { display: block; width: 100%; height: 100%; border: 0; }
/* The embeds load from Instagram, so they can be slow or blocked; the frame
   holds its shape either way and the link out below never depends on them. */

/* ------------------------------------------------------------ NEWSLETTER */
/* A band rather than a section: the client asked for it to stay out of the
   way, so its padding is roughly a third of what .sec uses and the heading
   tops out well below a section heading. */

.news { position: relative; isolation: isolate; overflow: hidden; border-block: 2px solid var(--cocoa); }
.news__img { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; object-fit: cover; object-position: center 62%; }
/* The photograph is brightest exactly where the dough sits, so cream type
   needs a real ground beneath it rather than a wash. The alphas below were
   set by measuring the composited pixels under the type, not by eye. */
.news__scrim { position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(36,23,9,.84) 0%, rgba(36,23,9,.73) 45%, rgba(36,23,9,.88) 100%); }
.news__in { padding-block: clamp(3rem, 6vw, 4.5rem); }
.news__well { max-width: 36rem; margin-inline: auto; text-align: center; }

.news__h { font-family: var(--script); font-style: italic; font-weight: 500;
  font-size: clamp(2rem, 3.8vw, 3.1rem); line-height: 1.12; letter-spacing: 0;
  color: var(--cream); text-wrap: balance; }
.news__sub { margin-top: var(--s-4); font-size: var(--t-lead); color: var(--on-dark-2); text-wrap: pretty; }

/* Field and button read as one control, in the same pill the basket uses. */
.news__form { display: flex; gap: .5rem; margin-top: var(--s-5); }
.news__input {
  flex: 1 1 auto; min-width: 0;
  padding: 1rem 1.35rem; border-radius: var(--r-pill);
  border: 2px solid var(--cream); background: var(--cream); color: var(--cocoa);
  font: inherit; font-size: var(--t-body);
}
.news__input::placeholder { color: #6b5947; opacity: 1; }
.news__input[aria-invalid="true"] { border-color: var(--caramel-lt); }
.news__btn {
  flex: none; padding: 1rem 1.9rem; border-radius: var(--r-pill);
  border: 2px solid var(--cocoa); background: var(--caramel); color: var(--cocoa);
  font-family: var(--display); font-weight: 700; font-size: var(--t-body);
  transition: background var(--d-1) var(--ease);
}
.news__btn:hover { background: var(--caramel-lt); }
.news__note { margin-top: var(--s-5); font-size: var(--t-sm); color: var(--on-dark-2); }

@media (max-width: 360px) { .news__form { flex-wrap: wrap; } .news__btn { width: 100%; } }
/* On the smallest phones the band is scaled back so it does not swallow the
   whole screen on its own. */
@media (max-width: 400px) { .news__in { padding-block: clamp(2rem, 9vw, 2.6rem); } .news__h { font-size: 1.7rem; } }

/* -------------------------------------------------------------- WHATSAPP */
/* Fixed, bottom right, above everything except the basket drawer. WhatsApp's
   own green rather than a brand colour, because the point of it is to be
   recognised at a glance; the cocoa ring ties it back to the page. */

.wa {
  position: fixed; right: clamp(.9rem, 2.2vw, 1.5rem); bottom: clamp(.9rem, 2.2vw, 1.5rem);
  z-index: var(--z-wa);
  display: grid; place-items: center;
  width: clamp(52px, 5.5vw, 60px); aspect-ratio: 1;
  border-radius: 50%; border: 2px solid var(--cocoa);
  background: #25d366; color: #0b2e18;
  box-shadow: var(--lift-deep);
  transition: transform var(--d-2) var(--ease), background var(--d-1) var(--ease), opacity var(--d-2) var(--ease);
}
.wa svg { width: 58%; height: 58%; fill: currentColor; }
.wa:hover { transform: translateY(-2px); background: #1fbe5a; }
/* Out of the way while the basket is open, so it cannot sit over Checkout. */
body:has(.drawer[data-open="true"]) .wa { opacity: 0; pointer-events: none; }
/* And out of the way of any control it would otherwise cover; mountWaDodge
   in js/app.js decides when, by reading what is under the button. */
.wa.is-hidden { opacity: 0; pointer-events: none; transform: scale(.85); }
@media (prefers-reduced-motion: reduce) { .wa { transition: none; } }

/* ---------------------------------------------------------------- FOOTER */

.foot { background: var(--cocoa); color: var(--on-dark-2); padding-top: var(--s-5); }
@media (min-width: 760px) { .foot { padding-top: var(--s-6); } }

/* The seal sits beside both lines rather than above them, so the brand
   block is one seal tall on a phone instead of three stacked rows. */
.foot__brand { display: flex; align-items: center; gap: var(--s-4); padding-bottom: var(--s-4); }
.foot__brand .seal { width: 58px; flex: none; }
.foot__words { display: grid; gap: .15rem; min-width: 0; }
.foot__line { font-family: var(--display); font-weight: 800; font-size: clamp(1.2rem, 4.2vw, 1.6rem); line-height: 1.15; letter-spacing: var(--tr-head); color: var(--cream); margin: 0; }
.foot__day { margin: 0; font-size: var(--t-xs); color: var(--caramel-lt); }

/* The dividing rules run the whole width: across the gap between two
   columns, and across a column that has run out of links. A border on each
   cell cannot do that, because it stops where its column stops. So the
   rules are painted once on the container as a repeating gradient, and
   every row is pinned to one height so the two line up.

   The fixed row height is a promise that no link ever wraps onto a second
   line. footmeasure.mjs is what keeps the promise honest: it compares each
   row's scrollHeight against the height it was given, on eight pages at
   eight widths. */
.foot__cols {
  --fr: 2.9rem;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--s-5);
  background-image: repeating-linear-gradient(to bottom,
    transparent 0 calc(var(--fr) - 1px),
    var(--rule-light-sf) calc(var(--fr) - 1px) var(--fr));
}
.foot__col { display: grid; grid-auto-rows: var(--fr); align-content: start; min-width: 0; }
.foot__col > * { display: flex; align-items: center; min-width: 0; margin: 0; font-size: var(--t-sm); line-height: 1.3; color: var(--on-dark-2); text-decoration: none; }
.foot__h { font-family: var(--display); font-weight: 700; color: var(--cream); letter-spacing: var(--tr-head); }
.foot__col a { transition: color var(--d-1) var(--ease); }
.foot__col a:hover { color: var(--cream); }
.foot__mail { text-decoration: underline; text-decoration-color: var(--rule-light); text-underline-offset: 4px; }

/* Two columns on a phone, with Contact across the foot of them. Three in a
   row only once there is width for the phone number to sit on one line. */
.foot__col--contact { grid-column: 1 / -1; }
@media (min-width: 760px) {
  .foot__cols { --fr: 3.5rem; grid-template-columns: 1fr 1fr 1.5fr; column-gap: var(--s-6); }
  .foot__col--contact { grid-column: auto; }
}

.foot__bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 var(--s-5); padding-block: var(--s-4) var(--s-2); font-size: var(--t-xs); color: var(--on-dark-3); }
@media (min-width: 760px) { .foot__bar { padding-block: var(--s-5) var(--s-3); } }
.foot__legal { margin: 0; max-width: 58ch; line-height: 1.55; }
.foot__small { margin: 0; display: flex; flex-wrap: wrap; gap: 0 var(--s-4); }
/* Not inline in a sentence, so these get a real target rather than the
   exemption prose links have. */
.foot__small a { display: inline-flex; align-items: center; min-height: 44px; color: var(--on-dark-3); text-decoration: none; }
.foot__small a:hover { color: var(--cream); }

/* The studio credit, set apart as a band of its own under the dark footer. */
.foot__by { margin: 0; background: var(--blush); color: var(--ink-2); text-align: center; padding-block: 1rem; font-size: .7rem; letter-spacing: .2em; text-transform: uppercase; }
/* The one link in the band, and the only thing here anyone taps, so it
   carries a real target without making the band any taller. */
.foot__by a { display: inline-block; padding-block: 1rem; margin-block: -1rem; color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.foot__by a:hover { color: var(--brown); }

/* ----------------------------------------------------------- CART DRAWER */

.scrim { position: fixed; inset: 0; z-index: calc(var(--z-drawer) - 1); background: rgba(36,23,9,.5); opacity: 0; visibility: hidden; transition: opacity var(--d-2) var(--ease), visibility var(--d-2); }
.scrim[data-open="true"] { opacity: 1; visibility: visible; }
.drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: var(--z-drawer); width: min(28rem, 100vw); background: var(--cream); display: flex; flex-direction: column; transform: translateX(101%); visibility: hidden; transition: transform var(--d-2) var(--ease), visibility var(--d-2); box-shadow: var(--lift-deep); }
.drawer[data-open="true"] { transform: translateX(0); visibility: visible; }
.drawer__top { display: flex; align-items: center; justify-content: space-between; padding: var(--s-5); border-bottom: 2px solid var(--cocoa); }
.drawer__title { font-family: var(--display); font-weight: 800; font-size: 1.3rem; color: var(--ink); }
.drawer__close { width: 44px; height: 44px; display: grid; place-items: center; border-radius: var(--r-pill); border: 2px solid var(--cocoa); }
.drawer__close svg { width: 18px; height: 18px; stroke: var(--ink); }
.drawer__body { flex: 1; overflow-y: auto; padding: var(--s-5); }
.drawer__foot { padding: var(--s-5); border-top: 2px solid var(--cocoa); display: grid; gap: var(--s-4); background: var(--blush); }

.line { display: grid; grid-template-columns: 62px 1fr auto; gap: var(--s-4); align-items: start; }
.line + .line { margin-top: var(--s-4); padding-top: var(--s-4); border-top: 2px dotted var(--rule-ink); }
.line__thumb { aspect-ratio: 1; border-radius: var(--r-sm); border: 2px solid var(--cocoa); background: var(--field, var(--tan)); display: grid; place-items: center; overflow: hidden; }
.line__thumb img { width: 76%; mix-blend-mode: multiply; }
.line__thumb img.line__photo { width: 100%; height: 100%; object-fit: cover; mix-blend-mode: normal; }
.line__name { font-family: var(--display); font-weight: 700; font-size: .98rem; line-height: 1.2; color: var(--ink); }
.line__price { margin-top: .2rem; font-size: var(--t-xs); color: var(--ink-3); }
.line__right { display: grid; gap: var(--s-3); justify-items: end; }
.line__total { font-family: var(--display); font-weight: 800; color: var(--ink); }

.step { display: inline-flex; align-items: center; border: 2px solid var(--cocoa); border-radius: var(--r-pill); overflow: hidden; }
.step button { width: 32px; height: 32px; display: grid; place-items: center; color: var(--ink); }
.step button:hover { background: var(--caramel); }
.step svg { width: 12px; height: 12px; stroke: currentColor; fill: none; }
.step output { min-width: 28px; text-align: center; font-family: var(--display); font-weight: 700; font-size: .9rem; }

.sum { display: grid; gap: var(--s-2); }
.sum__row { display: flex; justify-content: space-between; gap: var(--s-4); font-size: var(--t-sm); color: var(--ink-2); }
.sum__row .fig { font-family: var(--display); font-weight: 700; color: var(--ink); }
.sum__row--total { padding-top: var(--s-3); border-top: 2px solid var(--cocoa); color: var(--ink); }
.sum__row--total span:first-child { font-family: var(--display); font-weight: 700; }
.sum__row--total .fig { font-size: 1.35rem; font-weight: 800; }

.empty { display: grid; gap: var(--s-4); justify-items: center; text-align: center; padding-block: var(--s-8); }
.empty .seal { width: 84px; }
.empty p { font-size: var(--t-sm); color: var(--ink-3); max-width: 28ch; }

/* -------------------------------------------------------- INTERIOR HEADER */

/* ------------------------------------------------------------ WATERMARK */
/* An oversized word set a shade into the ground behind each page header, and
   behind the gallery on the landing page. It runs off the right edge on
   purpose: a word that fits looks like a heading, a word that is cut looks
   like a surface the page was printed on.

   Gabarito upright rather than the italic of the reference. Gabarito ships
   no italic, so an italic here would be the browser slanting the upright by
   maths, and a faux italic at 20rem is exactly the size at which that shows.
   The site's whole voice is this face, so the device uses it as drawn.

   It is decorative: aria-hidden, not selectable, and never in the way of a
   pointer. Its tone is a hair off the ground it sits on, so ink over it
   measures the same as ink over the ground. */

.wm {
  position: absolute; z-index: 0; pointer-events: none; user-select: none;
  top: 50%; left: clamp(-1.5rem, -2vw, -0.5rem); translate: 0 -50%;
  font-family: var(--display); font-weight: 900;
  font-size: clamp(5rem, 21vw, 17rem); line-height: .8; letter-spacing: -.04em;
  white-space: nowrap; color: var(--wm-ink, rgba(48, 32, 15, .055));
}
/* In a section rather than a page header it centres on the heading itself,
   not on the section: pinned to the section top it floated in the padding
   with nothing behind it, which reads as a stray word rather than a ground. */
.wm-anchor { position: relative; }
.wm-anchor > .h-sect { position: relative; z-index: 1; }
.wm--sec { left: 50%; translate: -50% -50%; }

/* THE WORD IS CENTRED ON THE HEADING, NOT ON THE BAND.
 *
 * It used to sit at top:50% of the whole band, which meant its position
 * depended on the back link, the padding, and how many lines the sentence
 * underneath happened to take. Those differ per page and per width, so the
 * same seven headings measured +25px, +8px and -9px out from the word behind
 * them with identical one-line titles. No arithmetic could hold that; two
 * attempts chased it and a sweep caught them both.
 *
 * Wrapping the heading and the word together is the pattern this site
 * already uses for the gallery heading (.wm-anchor). Now nothing above or
 * below the heading can move the word.
 *
 * What is left is a constant: the two faces hang their glyphs at different
 * depths inside their own line boxes, .1704em for the heading's Playfair and
 * .0994em for the word's Gabarito. Those come from the FACE, so they are the
 * same for Checkout, Policies and Dough Points. tools/inkcentre.mjs measures
 * it on seven page heads at nine widths. */
.phead__h { position: relative; }
.phead__h > h1 { position: relative; z-index: 1; }
.phead__h > .wm {
  left: 50%; top: 50%;
  translate: -50% calc(-50% + .1704 * var(--t-hero) - .0994em);
}

.phead, .wm-host { position: relative; overflow: hidden; }
/* The shell clips nothing, so the watermark can run past the text column. */
.wm-host > .shell { overflow: visible; }
.phead__in, .wm-host > .shell { position: relative; z-index: 1; }

/* On a phone the word would be most of the screen, so it steps back to a
   size that still reads as a surface rather than a shout. */
@media (max-width: 680px) { .wm { font-size: clamp(4rem, 26vw, 7rem); opacity: .85; } }
@media (prefers-reduced-motion: no-preference) { .wm { will-change: auto; } }

.phead { background: var(--blush); padding-block: clamp(3rem, 6vw, 5rem); }
/* THE HEADING SITS ON THE CENTRE OF THE WORD BEHIND IT.
 *
 * Their boxes already shared a centre line and it still looked wrong,
 * because a box centre is not where the letters are. Both faces hang their
 * glyphs off-centre inside their own line box:
 *
 *     watermark   Gabarito 900, line-height .8   glyphs sit .0994em low
 *     heading     Playfair italic, line-height 1.1  glyphs sit .1704em low
 *
 * Those two numbers come from the FACE, not from the word, so they are the
 * same for Checkout, Questions and Points - which matters, because my first
 * attempt measured the ink of each particular word and sent every page with
 * a descender off in its own direction.
 *
 * The error is therefore .0994*Fw - .1704*Fh. Dropping the heading by x
 * closes only x/2 of it, because the band grows by x and the watermark is
 * centred on the band, so the correction is twice the error. Both terms are
 * lengths, so this tracks every width without a magic pixel anywhere.
 *
 * tools/inkcentre.mjs checks it on seven page heads at nine widths. */
.phead h1 { font-family: var(--script); font-style: italic; font-weight: 500; font-size: var(--t-hero); line-height: 1.1; letter-spacing: 0; max-width: 16ch; margin-inline: auto; text-wrap: balance; }
/* A little further down the band, as asked. The word follows it now, so
   this is purely how low the pair sits and not part of the alignment. */
.phead__h { margin-top: calc(.18 * var(--t-hero)); }
.phead p { margin-top: var(--s-5); max-width: 54ch; margin-inline: auto; font-size: var(--t-lead); line-height: 1.6; color: var(--ink-2); text-wrap: pretty; }
/* The gap under the back link is what sets the heading down the page. It
   was 16px, which read as cramped under the nav. At 32px the heading also
   lands on the centre of the watermark behind it, which it was 10px above
   before: the word is centred on the whole band, so moving the heading down
   by x only moves the word down by x/2. */
.back { display: inline-flex; gap: .5rem; align-items: center; min-height: 44px; padding-right: .5rem;
  margin-bottom: var(--s-6); font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink-2); text-decoration: none; }
.back svg { width: 15px; height: 15px; stroke: currentColor; fill: none; transition: transform var(--d-1) var(--ease); }
.back:hover svg { transform: translateX(-3px); }

/* --------------------------------------------------------------- REVIEWS */

.revgrid { display: grid; gap: clamp(1rem, 2vw, 1.5rem); grid-template-columns: repeat(auto-fill, minmax(min(100%, 290px), 1fr)); }
/* Reviews arrive at different lengths, so the card stretches to its row and
   keeps the name pinned to the bottom. Without this the first real review
   that runs long would leave the others sitting short beside it. */
.rev { height: 100%; padding: clamp(1.5rem, 3vw, 2rem); background: var(--cream); border: 2px solid var(--cocoa); border-radius: var(--r-lg); display: grid; gap: var(--s-4); align-content: start; grid-template-rows: auto auto 1fr auto; }
.rev__stars { display: flex; gap: 4px; }
.rev__stars svg { width: 17px; height: 17px; fill: var(--caramel); stroke: none; }
.rev__stars svg[data-off] { fill: rgba(48,32,15,.15); }
.rev__body { font-size: var(--t-sm); line-height: 1.65; color: var(--ink-2); }
.rev__who { display: grid; gap: .1rem; padding-top: var(--s-4); border-top: 2px dotted var(--rule-ink); }
.rev__name { font-family: var(--display); font-weight: 700; color: var(--ink); }
.rev__meta { font-size: var(--t-xs); color: var(--ink-3); }

/* Real reviews, so the card carries the parts a real one has: the rating and
   the verified badge on one line, then a title and the words. */
.rev__top { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }
.rev__ok { display: inline-flex; align-items: center; gap: .35rem;
  padding: .2rem .55rem; border-radius: var(--r-pill);
  background: var(--sage); color: var(--cocoa);
  font-size: var(--t-mark); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
.rev__title { font-family: var(--display); font-weight: 800; font-size: 1.05rem; line-height: 1.25; color: var(--ink); }

/* ----------------------------------------------------- WRITE A REVIEW */
/* The form lives in a cream panel inside the brown band: the field styles
   paint labels in ink, which is unreadable on brown, and repainting them
   for one section is how a stylesheet starts to rot. */

.revform { text-align: left; }
/* A fieldset brings a border and a notched legend with it, which is a box
   the rest of this form does not have. */
.revform fieldset { border: 0; padding: 0; margin: 0; min-inline-size: 0; }
.revform legend { font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink-2); padding: 0; }
.revform__alt { margin-top: var(--s-5); font-size: var(--t-sm); color: var(--on-dark-2); text-align: center; }
.revform__alt a { color: var(--cream); text-decoration: underline; text-underline-offset: 3px; }

/* Five radios wearing stars. The radio stays the control, so the arrow keys
   and a screen reader get the real thing and only the paint is ours. */
.rate__row { display: flex; gap: .15rem; margin-top: .4rem; }
.rate__one { position: relative; display: grid; place-items: center;
  min-width: 46px; min-height: 46px; cursor: pointer; border-radius: var(--r-sm); }
.rate__one input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.rate__st { width: 27px; height: 27px; fill: rgba(48, 32, 15, .16); stroke: none;
  transition: fill var(--d-1) var(--ease), transform var(--d-1) var(--ease); }
.rate__one:hover .rate__st { transform: scale(1.12); }
.rate__one:has(input:focus-visible) { outline: 3px solid var(--cocoa); outline-offset: 2px; }

/* Fill the first n, where n is what she picked. Set by the script, so the
   paint cannot disagree with the radio that is actually checked. */
.rate__row[data-stars="1"] .rate__one:nth-child(-n+1) .rate__st,
.rate__row[data-stars="2"] .rate__one:nth-child(-n+2) .rate__st,
.rate__row[data-stars="3"] .rate__one:nth-child(-n+3) .rate__st,
.rate__row[data-stars="4"] .rate__one:nth-child(-n+4) .rate__st,
.rate__row[data-stars="5"] .rate__one:nth-child(-n+5) .rate__st { fill: var(--caramel); }

.rate__said { margin-top: .35rem; font-family: var(--display); font-weight: 700;
  font-size: var(--t-sm); color: var(--ink-2); }

/* The thank-you replaces the form rather than sitting under it, so there is
   no second Send button to press twice. */
.revform__done { display: grid; gap: var(--s-4); place-items: center; text-align: center;
  padding: clamp(.5rem, 2vw, 1.25rem) 0; }
.revform__done h3 { font-family: var(--display); font-weight: 800; font-size: var(--t-sub); color: var(--ink); }
.revform__done p { font-size: var(--t-sm); color: var(--ink-2); max-width: 32ch; }
.revform__done svg { width: 44px; height: 44px; fill: none; stroke: var(--cocoa); stroke-width: 2; }

@media (prefers-reduced-motion: reduce) {
  .rate__one:hover .rate__st { transform: none; }
}

/* ------------------------------------------------------- REVIEW CAROUSEL */
/* A scroll container with snap points, so a swipe and the arrow keys work
   with no script at all; the script only nudges scrollLeft along. */

.revcar { position: relative; }
.revtrack {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * clamp(1rem, 2vw, 1.5rem)) / 3);
  gap: clamp(1rem, 2vw, 1.5rem);
  overflow-x: auto; overflow-y: hidden;
  /* No snap while it drifts: mandatory snapping fights a continuous scroll
     and drags it back onto the nearest card every frame. The arrows and the
     dots scroll to exact card positions themselves. */
  scroll-padding-inline: 0;
  padding-block: .35rem;          /* room for the cards' own shadow */
  scrollbar-width: none;
}
.revtrack::-webkit-scrollbar { display: none; }

@media (max-width: 900px) { .revtrack { grid-auto-columns: calc((100% - clamp(1rem, 2vw, 1.5rem)) / 2); } }
@media (max-width: 620px) { .revtrack { grid-auto-columns: 86%; } }
.revtrack:focus-visible { outline: 3px solid var(--cocoa); outline-offset: 4px; border-radius: var(--r-lg); }

/* Every card in the track is roughly a third of the shell wide, so a long
   product name wraps onto a second line and a short one does not, and the
   reviewer name lands at two different heights in neighbouring cards. The
   second line is reserved rather than the month truncated away: the card
   already stretches to the row, so this only moves space that was sitting
   above the name to below the month. */
.revtrack .rev__meta { line-height: 1.5; min-height: 3em; }

.revcar__bar { display: flex; align-items: center; justify-content: center; gap: .6rem;
  margin-top: clamp(1.1rem, 2.4vw, 1.6rem); flex-wrap: wrap; }
.revcar__btn { display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: 50%; border: 2px solid var(--cocoa); background: transparent; color: var(--ink);
  transition: background var(--d-1) var(--ease), color var(--d-1) var(--ease); }
.revcar__btn:hover { background: var(--cocoa); color: var(--cream); }
.revcar__btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; }
.revcar__btn--play svg { fill: currentColor; stroke: none; }
/* One button, two faces: whichever one is not the current state is hidden. */
.revcar__go { display: none; }
.revcar[data-paused="true"] .revcar__pause { display: none; }
.revcar[data-paused="true"] .revcar__go { display: block; }

.revcar__dots { display: flex; align-items: center; gap: 0; }
/* The dot you see is 9px; the dot you can hit is 24px, which is the floor
   WCAG 2.5.8 sets for a control. A 9px button is a 9px target, and ten of
   them in a row on a phone is a game rather than a control. */
.revcar__dot { width: 24px; height: 24px; display: grid; place-items: center;
  padding: 0; border: 0; background: none; }
.revcar__dot::before { content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--rule-ink); transition: background var(--d-1) var(--ease), transform var(--d-1) var(--ease); }
.revcar__dot[aria-selected="true"]::before { background: var(--cocoa); transform: scale(1.35); }
.revcar__dot:hover::before { background: var(--ink-3); }

/* Nothing slides for anyone who has asked their system to calm down. */
@media (prefers-reduced-motion: reduce) {
  .revtrack { scroll-behavior: auto; }
  .revcar__dot::before { transition: none; }
}

/* The average, computed from the reviews themselves. */
.revsum { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: .5rem .7rem;
  /* The display italic above has long descenders, so the rating line was
     sitting right under them. */
  margin-top: clamp(.9rem, 2vw, 1.4rem);
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem); font-size: var(--t-sm); color: var(--ink-2); }
.revsum strong { font-family: var(--display); font-weight: 800; font-size: 1.15rem; color: var(--ink); }

/* Stars, title, words, then the name pinned to the bottom: the words take
   the slack so a short review and a long one end level in the same row. */
.rev { grid-template-rows: auto auto 1fr auto; }
.rev[data-placeholder] { border-style: dashed; background: transparent; }
.rev[data-placeholder] .rev__body { color: var(--ink-3); font-style: italic; }
/* This is what stops a sample review being mistaken for a real one, so it
   has to be unmissable, not a bare span. */
.ph-ribbon {
  justify-self: start;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .4rem .9rem; border-radius: var(--r-pill);
  background: var(--caramel); color: var(--cocoa);
  border: 2px dashed var(--cocoa);
  font-family: var(--display); font-weight: 700;
  font-size: var(--t-mark); letter-spacing: var(--tr-mark); text-transform: uppercase;
}

/* ------------------------------------------------------------------- FAQ */

.faq { display: grid; gap: var(--s-3); }
/* The inline padding is held in a variable because the summary has to
   cancel it with a negative margin to let its hover fill the card. Two
   hard-coded clamps that must stay equal is a drift waiting to happen. */
.faq details { --faq-pad: clamp(1.25rem, 2.5vw, 1.75rem);
  background: var(--cream); border: 2px solid var(--cocoa); border-radius: var(--r-md); padding: 0 var(--faq-pad); }
.faq summary { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); padding-block: clamp(1.1rem, 2vw, 1.4rem); cursor: pointer; list-style: none; font-family: var(--display); font-weight: 700; font-size: clamp(1.02rem, 1.5vw, 1.18rem); line-height: 1.3; color: var(--ink); }
.faq summary::-webkit-details-marker { display: none; }
/* Hovering a question fills the whole row with the same brown a button
   takes, so it reads as something you press rather than something you
   read. The row bleeds out to the card's inner edge, inside its 2px
   border, which is where the radius comes from. */
.faq summary { margin-inline: calc(-1 * var(--faq-pad)); padding-inline: var(--faq-pad);
  border-radius: calc(var(--r-md) - 2px);
  transition: background var(--d-2) var(--ease), color var(--d-2) var(--ease); }
.faq summary:hover { background: var(--brown); color: var(--cream); }
.faq details[open] summary { border-radius: calc(var(--r-md) - 2px) calc(var(--r-md) - 2px) 0 0; }
@media (prefers-reduced-motion: reduce) { .faq summary { transition: none; } }
.faq summary > span:last-child { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--caramel); transition: transform var(--d-2) var(--ease); }
.faq summary svg { width: 14px; height: 14px; stroke: var(--cocoa); fill: none; }
.faq details[open] summary > span:last-child { transform: rotate(45deg); }
.faq__a { padding-bottom: clamp(1.1rem, 2vw, 1.5rem); max-width: 68ch; font-size: var(--t-sm); line-height: 1.7; color: var(--ink-2); }
.faq__a p + p { margin-top: var(--s-3); }
.faq__a strong { font-weight: 700; color: var(--ink); }
.faqset + .faqset { margin-top: clamp(2.5rem, 4vw, 3.5rem); }
.faqset__h { font-size: var(--t-sub); margin-bottom: var(--s-4); }

/* -------------------------------------------------------------- POLICIES */

.pol { display: grid; gap: clamp(2rem, 4vw, 3.5rem); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 940px) { .pol { grid-template-columns: 16rem minmax(0, 1fr); } }
.pol > * { min-width: 0; }
.pol__index { position: sticky; top: 6rem; display: grid; gap: var(--s-2); align-content: start; }
@media (max-width: 939px) { .pol__index { position: static; } }
.pol__index a { display: inline-flex; align-items: center; min-height: 44px; padding: .65rem 1.1rem; border-radius: var(--r-pill); font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink-2); text-decoration: none; }
.pol__index a:hover { background: var(--blush); }
.pol__index a.is-at { background: var(--cocoa); color: var(--cream); }

.doc { max-width: 68ch; }
.doc + .doc { margin-top: clamp(3rem, 5vw, 4.5rem); padding-top: clamp(3rem, 5vw, 4.5rem); border-top: 2px dotted var(--rule-ink); }
.doc h2 { font-size: var(--t-sect); }
.doc__stamp { margin-top: var(--s-3); font-size: var(--t-xs); color: var(--ink-3); }
.doc h3 { margin-top: var(--s-6); font-size: 1.2rem; }
.doc p { margin-top: var(--s-4); font-size: var(--t-sm); line-height: 1.75; color: var(--ink-2); }
.doc ul { margin-top: var(--s-4); display: grid; gap: var(--s-2); }
.doc li { position: relative; padding-left: var(--s-5); font-size: var(--t-sm); line-height: 1.7; color: var(--ink-2); }
.doc li::before { content: ""; position: absolute; left: 0; top: .66em; width: 8px; height: 8px; border-radius: 50%; background: var(--caramel); }
.doc strong { font-weight: 700; color: var(--ink); }

/* ------------------------------------------------------------ CART / FORM */

.checkout { display: grid; gap: clamp(2rem, 4vw, 3.5rem); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 940px) { .checkout { grid-template-columns: minmax(0, 1fr) 22rem; } }
.checkout > * { min-width: 0; }
.aside { position: sticky; top: 6rem; }
@media (max-width: 939px) { .aside { position: static; } }
.panel { padding: clamp(1.5rem, 3vw, 2rem); background: var(--cream); border: 2px solid var(--cocoa); border-radius: var(--r-lg); }

.fieldset { border: 0; padding: 0; margin: 0; }
.fieldset + .fieldset { margin-top: clamp(2rem, 4vw, 3rem); }
.fieldset__h { display: flex; align-items: baseline; gap: var(--s-4); padding-bottom: var(--s-3); margin-bottom: var(--s-5); border-bottom: 2px solid var(--cocoa); }
.fieldset__h h2 { font-size: var(--t-sub); }
.fieldset__h span { font-size: var(--t-xs); color: var(--ink-3); }

.grid2 { display: grid; gap: var(--s-4); grid-template-columns: 1fr; }
@media (min-width: 620px) { .grid2 { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: .4rem; }
.field--full { grid-column: 1 / -1; }
.field label { font-family: var(--display); font-weight: 700; font-size: var(--t-sm); color: var(--ink-2); }
.field input, .field select, .field textarea { width: 100%; padding: .85rem 1rem; min-height: 52px; background: var(--cream); border: 2px solid var(--rule-ink); border-radius: var(--r-sm); color: var(--ink); font-size: var(--t-body); transition: border-color var(--d-1) var(--ease); }
.field textarea { min-height: 112px; resize: vertical; line-height: 1.6; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-3); opacity: .8; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--cocoa); outline: none; }
.field select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%2330200f' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; padding-right: 2.5rem; }
.field__hint { font-size: var(--t-xs); line-height: 1.6; color: var(--ink-3); }
.field__err { font-size: var(--t-xs); font-weight: 600; color: #8f3a16; display: none; }
.field[data-invalid="true"] input, .field[data-invalid="true"] select { border-color: #8f3a16; }
.field[data-invalid="true"] .field__err { display: block; }

.choice { display: grid; gap: var(--s-4); grid-template-columns: 1fr; }
@media (min-width: 620px) { .choice { grid-template-columns: 1fr 1fr; } }
.choice label { position: relative; display: grid; gap: .3rem; padding: var(--s-5); background: var(--cream); border: 2px solid var(--rule-ink); border-radius: var(--r-md); cursor: pointer; transition: border-color var(--d-1) var(--ease), background var(--d-1) var(--ease); }
.choice label:hover { border-color: var(--cocoa); }
.choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.choice__t { font-family: var(--display); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.choice__d { font-size: var(--t-xs); line-height: 1.6; color: var(--ink-3); }
.choice__f { margin-top: var(--s-2); font-family: var(--display); font-weight: 800; font-size: 1.15rem; color: var(--ink); }
.choice label:has(input:checked) { border-color: var(--cocoa); background: var(--blush); }

.handoff { display: grid; grid-template-columns: 2.75rem 1fr; gap: var(--s-4); padding: clamp(1.25rem, 2.5vw, 1.5rem); margin-top: var(--s-5); border-radius: var(--r-lg); background: var(--blush); border: 2px solid var(--cocoa); align-items: start; }
.handoff p { font-size: var(--t-xs); line-height: 1.65; color: var(--ink-2); }
.handoff strong { font-weight: 700; color: var(--ink); }
.handoff p + p { margin-top: var(--s-3); }

.cartpage { display: grid; gap: var(--s-5); }
.cartline { display: grid; grid-template-columns: 96px 1fr auto; gap: var(--s-5); align-items: center; padding-bottom: var(--s-5); border-bottom: 2px dotted var(--rule-ink); }
.cartline__right { display: grid; gap: var(--s-3); justify-items: end; }
/* After the rule it overrides, not before it: written above, the mobile
   basket line kept its controls hard right under a two-column layout where
   they belong under the item. */
@media (max-width: 560px) { .cartline { grid-template-columns: 76px 1fr; } .cartline__right { grid-column: 2; justify-items: start; } }
.remove { font-family: var(--display); font-weight: 700; font-size: var(--t-xs); color: var(--ink-3); text-decoration: underline; text-underline-offset: .25em; }
.remove:hover { color: var(--ink); }

/* --------------------------------------------------------------- REVEAL */

@media (prefers-reduced-motion: no-preference) {
  .js .lift { opacity: 0; transform: translateY(16px); }
  .js .lift.is-in { opacity: 1; transform: none; transition: opacity var(--d-3) var(--ease), transform var(--d-3) var(--ease); }
}

/* ---------------------------------------------------------------- TOAST */

/* The add-to-basket confirmation.

   A pill is the right shape for three words and the wrong shape for
   anything that wraps. With the product name in the same string, "Pumpkin
   Cream Cheese Sourdough Muffins" took it to three lines and the 999px
   radius made a blob of it, sitting over the shop grid. It is a card now,
   with the confirmation on one line and the item under it held to one more,
   so its height is the same whatever was added. */
.toast {
  position: fixed; left: 50%; z-index: calc(var(--z-drawer) + 1);
  /* Clear of the WhatsApp button, which is fixed in the same corner of the
     screen and is 52-60px plus its own offset. */
  bottom: calc(clamp(.9rem, 2.2vw, 1.5rem) + clamp(52px, 5.5vw, 60px) + var(--s-3));
  transform: translate(-50%, 220%);
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
  gap: .7rem; padding: .85rem 1.15rem;
  width: min(24rem, calc(100vw - 2 * var(--gutter)));
  border-radius: var(--r-md); border: 2px solid var(--bark);
  background: var(--cocoa); color: var(--cream);
  box-shadow: var(--lift-deep);
  transition: transform var(--d-2) var(--ease);
}
.toast[data-open="true"] { transform: translate(-50%, 0); }
.toast svg { width: 18px; height: 18px; stroke: var(--caramel); fill: none; flex: none; }
.toast__text { display: grid; gap: .1rem; min-width: 0; }
.toast__text b { font-family: var(--display); font-weight: 800; font-size: var(--t-sm); line-height: 1.25; }
/* One line, cut with an ellipsis rather than wrapped: the basket drawer has
   the full name, and a confirmation that changes height with the length of
   the product is a confirmation that reads as an accident. */
.toast__text i { font-style: normal; font-size: var(--t-xs); line-height: 1.3; color: var(--on-dark-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* On a phone it sits above the WhatsApp button rather than beside it, and
   takes the page gutter so it lines up with everything else. */
@media (max-width: 560px) {
  .toast { left: var(--gutter); right: var(--gutter); transform: translate(0, 220%); width: auto; }
  .toast[data-open="true"] { transform: translate(0, 0); }
}

[hidden] { display: none !important; }
