/* ============================================================
   site.css - 24 Carrots Bakery
   Built against Shockwave 2.1.0

   Loads after base.css and wins on the cascade. Never replaced by an engine
   update, so anything written here survives every version of Shockwave.

   Colours, fonts and the type scale are set in Settings > Branding and reach
   this file as custom properties. Never hardcode a hex or a font family.

   Brand palette:
     --color-ink              #363436
     --color-paper            #FFFFFF
     --color-action           #ED873F   anything clickable, incl. the primary button fill
     --color-soft-apricote    #F9D9B3   hero and feature bands
     --color-accent-green     #BADA55   nav underline only

   Fonts come from the Adobe kit set under Branding > Typography. No @import
   is needed here; the engine emits the kit stylesheet itself.

   base.css already supplies most of this design: the eyebrow is already
   orange, uppercase and tracked; .cta--primary is already an orange button;
   the footer is already a four-region auto-fit grid. What follows overrides
   base rather than restating it, so anything not mentioned here is base's
   and is deliberately left alone.

   Centring a block is the native Align field (align-centre / align-end),
   not a CSS class hook - base.css's own .align-centre / .align-end handle
   it since 1.11.2. site.css only fills the gap those two leave open: see
   SECTION HEADERS below.

   Opt-in hooks (put these in a block's "CSS class" field in the admin):
     has-panel        puts the block's list into a tinted rounded panel
     has-shaped-cta   pill button with a circular ear past each end
   ============================================================ */


/* ============================================================
   PAGE
   ============================================================ */

/* Several elements bleed to the viewport edges with `100vw` /
   `calc(50% - 50vw)` - the sticky header's band and the mobile nav panel.
   On any browser with a space-consuming scrollbar those resolve wider than
   the content box - a strip of empty page you can scroll to on the right.
   Clipped on both html and body, because root-overflow propagation means
   html alone does not always catch it. `clip` rather than `hidden`: it
   does not become a scroll container, so `position: sticky` on the header
   keeps working. `hidden` first as the pre-Safari-16 fallback. */
html,
body {
  overflow-x: hidden;
  overflow-x: clip;
}


/* ============================================================
   ENGINE WORKAROUNDS
   Each is a message to Scott. Remove the block once the fix lands.
   ============================================================ */

/* 1. Split hero never engages for a `.hero-media` carrier (an image or video
      picked on the block itself, as distinct from a background). base.css
      grids `.hero-inner` and expects `.hero-media` inside it, but hero.php
      emits the media as a SIBLING of `.hero-inner`. base.css also flips the
      pair on `[data-direction="rtl"]`, which the renderer no longer emits -
      it emits `.is-reverse` on the section. Worked around below under HERO
      by gridding the section itself for that carrier. A `.block-bg` carrier
      (a background image or video) does not have this problem - base.css
      already positions it absolutely over the whole section - so a
      background-photo hero needs no such workaround. */

/* 2. Full bleed. Dead until 1.11.2; since then the renderer maps the old
      `bleed: true` (and the newer Width = Full) to `.is-full`, and base.css
      answers it with `padding-inline: 0` and children at 100% - which drops
      the gutter entirely, so copy sits against the viewport edge. That
      contradicts base.css's own comment ("Full width keeps the gutter").
      Not overridden here, because that would kill the admin's Width control:
      the form band and the CTA band should simply be set back to the
      default width in the admin. A section is already full width, so their
      backgrounds bleed without it. -> Scott: is-full with no gutter. */

/* 3. --muted is color-mix(--ink 60%, --paper), which lands at 3.68:1 on paper.
      It is base.css's colour for card body copy, subheadings, the footer
      blurb and archive intros - all small text, all needing 4.5:1. Tightened
      to 70%, which reads the same weight of grey and clears the floor at
      4.8:1. Still derived from the two brand tokens, so it keeps tracking
      whatever Branding is set to. */
:root {
  --muted: color-mix(in srgb, var(--ink) 70%, var(--paper));

  /* Page gutter. base.css is clamp(1.25rem, 5vw, 4rem), which pins to
     1.25rem / 20px on any phone (5vw is below the floor under ~400px) -
     tight against this type scale. Floor raised to 1.75rem / 28px. clamp()
     means this only bites on small screens; from ~560px up, 5vw already
     exceeds it and nothing changes. One token, so every block's inline
     padding plus the header, footer, nav and form all gain the room. */
  --gutter: clamp(1.75rem, 5vw, 4rem);
}

/* 4. Blocks that render nothing still reserve a --space-block of padding, so
      an unmatched collection binding or an embed with no URL leaves an
      unexplained gap. Three of those are on the home page right now. Collapse
      only sections that would draw literally nothing; the moment content
      arrives they come back on their own. */
.block--collection-grid:not(:has(.card)):not(:has(.block-heading)):not(:has(.block-subheading)),
.block--embed:not(:has(*)) {
  display: none;
}


/* ============================================================
   CONTROL TOKENS
   One shape, size and focus treatment for every button and form field, read
   by BUTTONS and FORMS below (and by anything new). Change a value here and
   every control follows.
   ============================================================ */

:root {
  /* Buttons are full pills. Every form field, single-line or textarea,
     takes one rounded-box radius, so all fields share one shape. */
  --control-radius: 999px;
  --field-radius: var(--radius-lg);

  /* One height for a button and a field, so they line up in a row (the
     newsletter band) and both clear the 44px touch target. It is the floor,
     not padding: a single-line field's own content is ~36px, so this sets
     its height, and anything lower would go under 44px. */
  --control-height: 2.75rem;

  --button-padding: var(--space-2xs) var(--space-l);
  --button-size: var(--step--1);
  --button-weight: 700;

  --field-padding: var(--space-3xs) var(--space-m);
  --field-padding-tall: var(--space-xs) var(--space-m);
  /* The border is a field's only boundary on paper, so it answers to the 3:1
     non-text floor. base.css's 45% mix measures 2.5:1 on this palette; 60%
     is 3.6:1. */
  --field-border: 1px solid color-mix(in srgb, var(--color-ink) 60%, var(--color-paper));

  /* Focus ring. --color-action on paper is 2.6:1, under the 3:1 a focus
     indicator needs; ink is 12:1. Dark grounds swap it for paper below. */
  --focus-ring: var(--color-ink);
}

/* Panel colours for the wide sign (the home hero and every page hero), so
   a tweak lands on all of them. An ink ground, and the inset line in paper
   at half strength: mixed with transparent rather than set with opacity,
   so only the line fades, not the panel or its text. It sits on the
   panel's own ink, so it reads as a soft grey. */
:root {
  --panel-ground: var(--color-ink);
  --panel-line-colour: color-mix(in srgb, var(--color-paper) 50%, transparent);
}

.newsletter,
.block--hero:has(> .hero-media),
.home-hero,
.page-hero,
.site-footer { --focus-ring: var(--color-paper); }


/* ============================================================
   HEADER
   ============================================================ */

/* base.css makes .site-header a centred, width-limited box
   (`inline-size: min(100% - gutter*2, --wrap); margin-inline: auto`), so
   anything needing to reach the viewport edge had to break out of it with
   `calc(50% - 50vw)`. That is the bug: `100vw` counts the scrollbar,
   `clientWidth` does not, so on any browser that reserves scrollbar space
   the band lands wider than the page and the surplus is really scrollable.

   Made full width instead, with the wrap re-created as padding - the same
   technique `.block` uses, and percentage-based, so it tracks clientWidth
   and never overshoots. The band is now just the header's own background;
   the bleed pseudo-element is gone, and with it the last `vw` in the
   header. */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 50;
  inline-size: 100%;
  max-inline-size: none;
  margin-inline: 0;
  padding-block: var(--space-s);
  padding-inline: max(var(--gutter), calc((100% - var(--wrap)) / 2));
  gap: var(--space-l);
  background: var(--color-paper);
  border-block-end: var(--border);
}

.site-header::before { content: none; }

.site-logo img { block-size: 2.75rem; }

/* ---- Primary nav -------------------------------------------------------- */

/* The toggle button (injected by site.js) only exists to be shown below the
   800px breakpoint; hidden everywhere else. Declared here so it is hidden
   even before the media query in SMALL SCREENS is read. */
.nav-toggle { display: none; }

.site-nav .nav-list { gap: var(--space-l); }

.site-nav .nav-item > a {
  font-weight: 500;
  font-size: var(--step--1);
  padding-block: var(--space-2xs);
  position: relative;
}

/* Green underline on hover. Grows from the left rather than fading, which
   reads as deliberate at this small a size. */
.site-nav .nav-item > a::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 2px;
  background: var(--color-accent-green);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--transition);
}

.site-nav .nav-item > a:hover::after,
.site-nav .nav-item > a:focus-visible::after {
  transform: scaleX(1);
}

/* The text stays ink on hover and focus; the underline is the only change.
   base.css turns every link orange on hover (`a:hover { color:
   var(--accent) }`, and --accent is --color-action), which reached the nav
   too. `inherit` keeps the nav's own colour, the header's ink, rather than
   restating it. Dropdown items keep their orange shift below, at higher
   specificity: they have no underline to show instead. */
.site-nav .nav-item > a:is(:hover, :focus-visible) { color: inherit; }

/* ---- Dropdown ----------------------------------------------------------- */
/* No menu item has children today, so none of this is on screen. Kept because
   it is a menu away from being needed and costs nothing until then. */

.site-nav .nav-list--sub {
  background: var(--color-paper);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 28px color-mix(in srgb, var(--color-ink) 14%, transparent);
  min-inline-size: 14rem;
}

/* The underline animation is wrong inside a dropdown; colour shift instead. */
.site-nav .nav-list--sub .nav-item > a::after { content: none; }
.site-nav .nav-list--sub .nav-item > a:hover { color: var(--color-action); }

/* ---- Header CTA --------------------------------------------------------- */
/* Not currently rendered: layout.php needs both a label and a URL, and
   `header_cta_label` is empty. Styled in BUTTONS with every other button, so
   filling the label in produces a primary button (and the second, if set, a
   secondary one) rather than a bare link. */


/* ============================================================
   COLOUR BOUNDARIES
   base.css zeroes a block's top padding whenever it follows another block
   (since 1.18.0, `.sw-section + .sw-section > .block:first-child`), on the
   assumption that the block above already supplies the gap via its own
   bottom padding. That holds between two blocks on the same ground and
   reads wrong at every colour change, where the block above's bottom
   padding is part of ITS band, not breathing room for the next one.

   The rule: a block keeps padding-block-start whenever its background
   differs from the block before it, and drops to 0 only when both are the
   same colour. Four backgrounds exist on this site: the background-photo
   hero's apricote, ink (the "Carrot cake" hero), stripes (the `.newsletter`
   band and the page heroes), and paper, which is every other block's
   default. `.block--hero` itself is
   the only check needed for the heroes - base.css gives every hero
   `background: var(--color-soft-apricote)` unconditionally, and only the
   `.hero-media` carrier overrides it to ink, so every `.block--hero` is
   non-paper regardless of which carrier it is. Checking the carrier here
   would be checking something this rule doesn't actually care about. The
   band is marked by the `.newsletter` class on its section, not by its
   archetype - a plain `.block--form` (the contact form) is paper like any
   other block, so it is not treated as coloured. The same goes the other
   way for `.story-split` (a hero, but on white, so it counts as paper) and
   `.community` (an apricote band made of plain rich-text blocks, so it
   does not). A linen `.products-feature` band on /our-products/ counts as
   coloured on the side before the next section, so the white section
   after it keeps its top padding instead of starting flush against the
   linen; its own blocks sit in a two-column section, which pads itself.
   The Wholesale page's linen `.customer-tags` band, and any section with
   the reusable `.band-linen` class, count as coloured on both sides, and
   the Wholesale `.info-cards` keep their own padding whatever comes
   before them.

   Restored by default for every block following a section, at the exact
   selector base.css itself zeroes it with
   (`.sw-section + .sw-section > .block:first-child`, 0,4,0) - matched
   rather than merely beaten, since site.css loads after base.css and wins
   outright on an identical selector. (The first attempt at this left off
   `:first-child` and landed at 0,3,0, which still lost outright: nothing
   below was wrong, the restore itself just never fired.) Zeroed back out
   only for the one case where colour is unchanged: two consecutive blocks
   that are each neither a `.block--hero` nor inside a `.newsletter` section -
   comfortably higher specificity, so it wins over the restore without
   needing to match it. `.block--cta-band` manages its own rhythm via
   min-block-size and opts back out near its own rules below, since this
   would otherwise restore the top padding it never wants.

   One-column sections only: a two- or three-column section carries its own
   `padding-block` and puts its blocks in grid cells, and restoring the top
   padding on just the FIRST block (the only one `:first-child` matches)
   pushed that column down by a full --space-block while its neighbour
   stayed put. The attribute selector takes the restore to (0,5,0), which
   still clears base.css's (0,4,0) zeroing rule wherever it applies.

   The first version of the zero-out rule below checked the carrier anyway
   - `:not(:is(.block--hero:has(> .block-bg), .block--hero:has(> .hero-media),
   .block--form))` - and silently broke in a way worth recording: `:has()`
   cannot contain another `:has()`, and Chrome's :is()/:not() parsing is
   forgiving - it drops only the invalid branch rather than the whole
   selector. The branches reading the CURRENT block (not nested in another
   :has()) stayed intact; the branches reading the PRECEDING block (nested
   inside this rule's own leading `:has()`) silently lost both
   `.block--hero:has(...)` branches and kept only `.block--form` (the rule
   has since been rewritten without the nesting), so any
   hero preceding another block stopped reading as coloured on that side
   only. Confirmed with `CSS.supports('selector(...)')` against the exact
   string in this file, and by reading `document.styleSheets` back out of
   the live page, which showed the truncated `:is()` the browser had
   actually kept. */
.sw-section + .sw-section[data-sw-layout="one"] > .block:first-child {
  padding-block-start: var(--space-block);
}

.sw-section:not(.newsletter, .community, .customer-tags, .band-linen, .products-feature:not(.products-feature--white)):is(.story-split, :has(> .block:not(.block--hero)))
  + .sw-section:not(.newsletter, .community, .customer-tags, .band-linen, .info-cards)
  > .block:not(.block--hero) {
  padding-block-start: 0;
}

/* ---- Band: `band-linen` ------------------------------------------------
   A reusable section class, set in the admin on any section: a full-width
   linen band behind it. The colour runs the width of the page from a
   border-image outset (as on the products-feature bands), so it also
   reaches the edges on a two- or three-column section, which is only as
   wide as the content; the background is the fallback. It counts as
   coloured on both sides above, so its first block keeps its top padding
   and the section after it keeps its own: the band always has a full
   --space-block of linen above and below its copy, whatever it meets.
   Ink text on linen is 11.3:1, and paper cards sit clear of it. Homepage
   "This week's specials" wears it. */
.sw-section.band-linen {
  background: var(--color-linen);
  border-image: conic-gradient(var(--color-linen) 0 0) fill 0 / / 0 100vw;
}


/* ============================================================
   SECTION HEADERS
   base.css already makes the eyebrow orange, uppercase and tracked, and
   already caps the heading at 22ch. Only the weight and rhythm change here.
   ============================================================ */

.block-eyebrow {
  font-weight: 700;
  letter-spacing: .14em;
}

/* base.css builds the wrap by giving every direct child of .block its own
   `inline-size: min(100% - gutter*2, --wrap)` plus `margin-inline: auto`. That
   works until a child is narrower than the wrap - .block-heading is capped at
   22ch and .block-body at --measure - because `margin-inline: auto` then
   centres that child in the VIEWPORT rather than aligning it to the wrap. The
   result is that every section header on the site is accidentally centred.

   The wrap moves onto the section as padding instead, so children start at
   the wrap's leading edge and their own max-inline-size caps the line length
   without moving the box. Centring is then a deliberate choice, not a
   side effect - see "Centred / end-aligned blocks" below. */
.block {
  padding-inline: max(var(--gutter), calc((100% - var(--wrap)) / 2));
}

.block > * {
  --block-column: 100%;
  inline-size: auto;
  max-inline-size: none;
  margin-inline: 0;
}

/* Put back the two caps base.css sets, which the reset above cleared. */
.block > .block-heading { max-inline-size: 22ch; }
.block > :is(.block-subheading, .block-body) { max-inline-size: var(--measure); }

/* Since 2.6.1 base.css places a block's heading, subheading, body and form
   on their own, `margin-inline: calc((100% - var(--block-column)) / 2)
   auto` at (0,2,0), which outranks the reset above. --block-column is
   base.css's wrap, worked out as if the block had no padding of its own;
   with the wrap moved onto the block's padding here, the formula indented
   those four by a further gutter, while the lists, grids and cards beside
   them stayed on the wrap's edge. Here the column is the block's whole
   content box, so it is 100% (above), and the margins go back to 0 at the
   same specificity, after base.css. Align Centre and End (0,3,0) still
   win. Two- and three-column sections already set both themselves. */
.block > :is(.block-heading, .block-subheading, .block-body, .page-title-inner, .cms-form) {
  margin-inline: 0;
}

/* Spacing between the eyebrow, heading, body and grid is base.css's and
   already reads correctly, so none of it is restated here. */

/* ---- Centred / end-aligned blocks: native .align-centre / .align-end ----
   Replaces a custom `is-centered` CSS-class hook: base.css has shipped
   .align-centre / .align-end since 1.11.2, text-align plus margin-inline on
   .block-body/.block-subheading, and no site has ever put `is-centered` on
   a block, so there was nothing to carry forward by keeping it.

   Upstream, that's enough on its own - a block's children still carry the
   wrap's own `margin-inline: auto`, so centring the text also centres the
   box. Here it is not: `.block > *` above resets margin-inline to 0, for
   the accidental-centring reason explained above, and base.css's align
   rules never touch .block-eyebrow or .block-heading at all. Left alone,
   `align-centre` would centre the TEXT inside the eyebrow and heading boxes
   while those boxes themselves stayed pinned to the wrap's leading edge -
   centred words in a left-aligned box. Restored only for the two elements
   base.css's own rule doesn't reach; .block-body and .block-subheading
   already get their margin-inline from base.css and don't need it again
   here. */
.block.align-centre :is(.block-eyebrow, .block-heading) { margin-inline: auto; }
.block.align-end :is(.block-eyebrow, .block-heading) { margin-inline-start: auto; }

/* A centred or end-aligned column, but its lists still read left to right. */
.block.align-centre .block-body :is(ul, ol),
.block.align-end .block-body :is(ul, ol) {
  text-align: start;
}


/* ============================================================
   BUTTONS
   One system for `.block-cta`, the form submit button and the header CTA
   (`.site-header-cta`): the same shape, padding, weight, height, lift and
   focus ring, with two colourways.

   - Primary: --color-action fill with WHITE text. White on this orange is
     2.58:1, below WCAG AA for normal text. That is a deliberate decision, not
     an oversight: do not switch the label back to ink or deepen the fill
     without asking.
   - Secondary: the white pill with ink text, as on the home hero.

   Each colourway only sets four custom properties; the shared rule reads
   them. Hover darkens the primary fill a little toward ink. `.cta--tertiary` is a text link, not a button, and
   keeps its own rule below.
   ============================================================ */

.cta--primary,
.site-header-cta,
.cms-form button[type="submit"],
.where-to-buy .map-block__search button[type="submit"] {
  --button-bg: var(--color-action);
  --button-bg-hover: color-mix(in srgb, var(--color-action) 85%, var(--color-ink));
  --button-fg: var(--color-paper);
  --button-border: var(--color-action);
}

.cta--secondary,
.site-header-cta--secondary,
.where-to-buy .map-block__locate {
  --button-bg: var(--color-paper);
  --button-bg-hover: color-mix(in srgb, var(--color-paper) 88%, var(--color-ink));
  --button-fg: var(--color-ink);
  --button-border: var(--color-paper);
}

.block-cta:not(.cta--tertiary),
.site-header-cta,
.cms-form button[type="submit"],
.where-to-buy .map-block__search button[type="submit"],
.where-to-buy .map-block__locate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--control-height);
  padding: var(--button-padding);
  font-size: var(--button-size);
  font-weight: var(--button-weight);
  text-decoration: none;
  white-space: nowrap;
  color: var(--button-fg);
  background: var(--button-bg);
  border: 1px solid var(--button-border);
  border-radius: var(--control-radius);
  cursor: pointer;
  transition: background var(--transition), color var(--transition),
              border-color var(--transition), transform var(--transition);
}

.block-cta:not(.cta--tertiary):is(:hover, :focus-visible),
.site-header-cta:is(:hover, :focus-visible),
.cms-form button[type="submit"]:is(:hover, :focus-visible),
.where-to-buy .map-block__search button[type="submit"]:is(:hover, :focus-visible),
.where-to-buy .map-block__locate:is(:hover, :focus-visible) {
  background: var(--button-bg-hover);
  color: var(--button-fg);
  transform: translateY(-1px);
}

/* A text link in the CTA slot, not a button. Ink text with an orange
   underline: orange TEXT is 2.6:1 on paper and 3.7:1 even in the AA-safe
   ink mix once it sits on the apricote band, where ink is 10:1 on both. */
.cta--tertiary {
  color: var(--color-ink);
  font-size: var(--button-size);
  font-weight: var(--button-weight);
  padding: var(--space-2xs) 0;
  white-space: nowrap;
  text-decoration: underline;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
  text-underline-offset: .2em;
}

/* The ring is the same on every button and field; the colour comes from
   --focus-ring so it flips to paper on a dark ground. */
:is(.block-cta, .site-header-cta, .cms-form button,
    .cms-form input, .cms-form textarea, .cms-form select,
    .where-to-buy .map-block__find :is(button, input)):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

/* ---- Shaped CTA, opt in with `has-shaped-cta` ---------------------------
   Traced from the client's own reference asset
   (24carrots-nameplate-shape.svg): a rounded rectangle, corner radius
   roughly 11px, with a true semicircular ball - radius 16px - bulging from
   the mid-height of each short end. Not a pill with a bump on top of one,
   which is what the first attempt at this was; the body itself is far less
   rounded than that, and the ball is a full half-circle rather than a thin
   lens riding the edge.

   Applied as a mask rather than a shaped pseudo-element, so it carves the
   button's own box into this silhouette instead of adding overflow outside
   it. That also keeps colour orthogonal: a mask reads only the asset's
   alpha, never its fill, so cta--primary's orange, cta--secondary's paper,
   and the form band's own submit button all come through unchanged, and
   the shape survives any of them changing later. The submit button is a
   bare <button>, not a $cta()-rendered anchor, so it is listed alongside
   .block-cta and .hero-actions > a rather than covered by either.

   The two image layers are the SAME source path, cropped to a different
   27x54 viewBox window each - the left end and the right end of the
   original 223x54 asset. A plain rectangle fills the flat middle, which has
   no curve in it to trace. All three are sized and positioned in the
   asset's own absolute pixels rather than percentages, because a percentage
   would rescale the corner radius and the ball into an ellipse the moment
   the button's width changes with its label - which every button here
   does, since the label comes from the CMS. Block-size is fixed to the
   asset's own 54px for the same reason: the caps are only undistorted at
   the height they were drawn for. */

.has-shaped-cta .block-cta,
.has-shaped-cta .hero-actions > a,
.has-shaped-cta .cms-form button[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  block-size: 3.375rem;
  padding-inline: var(--space-l);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='27' height='54' viewBox='0 0 27 54'%3E%3Cpath d='M196 0C201.885 0 206.691 4.6217 206.985 10.4336L207 11C215.837 11 223 18.1634 223 27C223 35.8366 215.837 43 207 43C207 49.0751 202.075 54 196 54H27C20.9249 54 16 49.0751 16 43C7.16344 43 0 35.8366 0 27C0 18.1634 7.16344 11 16 11L16.0146 10.4336C16.3094 4.6217 21.1148 0 27 0H196Z' fill='black'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='27' height='54' viewBox='196 0 27 54'%3E%3Cpath d='M196 0C201.885 0 206.691 4.6217 206.985 10.4336L207 11C215.837 11 223 18.1634 223 27C223 35.8366 215.837 43 207 43C207 49.0751 202.075 54 196 54H27C20.9249 54 16 49.0751 16 43C7.16344 43 0 35.8366 0 27C0 18.1634 7.16344 11 16 11L16.0146 10.4336C16.3094 4.6217 21.1148 0 27 0H196Z' fill='black'/%3E%3C/svg%3E"),
    linear-gradient(#000, #000);
  mask-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='27' height='54' viewBox='0 0 27 54'%3E%3Cpath d='M196 0C201.885 0 206.691 4.6217 206.985 10.4336L207 11C215.837 11 223 18.1634 223 27C223 35.8366 215.837 43 207 43C207 49.0751 202.075 54 196 54H27C20.9249 54 16 49.0751 16 43C7.16344 43 0 35.8366 0 27C0 18.1634 7.16344 11 16 11L16.0146 10.4336C16.3094 4.6217 21.1148 0 27 0H196Z' fill='black'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='27' height='54' viewBox='196 0 27 54'%3E%3Cpath d='M196 0C201.885 0 206.691 4.6217 206.985 10.4336L207 11C215.837 11 223 18.1634 223 27C223 35.8366 215.837 43 207 43C207 49.0751 202.075 54 196 54H27C20.9249 54 16 49.0751 16 43C7.16344 43 0 35.8366 0 27C0 18.1634 7.16344 11 16 11L16.0146 10.4336C16.3094 4.6217 21.1148 0 27 0H196Z' fill='black'/%3E%3C/svg%3E"),
    linear-gradient(#000, #000);
  -webkit-mask-size: 27px 54px, 27px 54px, calc(100% - 54px) 54px;
  mask-size: 27px 54px, 27px 54px, calc(100% - 54px) 54px;
  -webkit-mask-position: left top, right top, 27px top;
  mask-position: left top, right top, 27px top;
}


/* ============================================================
   HERO

   Two unrelated carriers share the `.block--hero` class, and get two
   unrelated treatments:

   - `.block-bg`: a background image or video. base.css positions it
     absolutely over the whole section. The only one on the site is the home
     hero, which pulls it into the right half instead - see "Home hero"
     below.
   - `.hero-media`: an image or video picked as the block's own field, meant
     to sit BESIDE the copy. hero.php emits it as a SIBLING of `.hero-inner`
     rather than a child, so base.css's split grid never reaches it - ENGINE
     WORKAROUNDS 1. Worked around by gridding the section instead. This is
     the carrot cake hero, out of scope this pass and left as before.
   ============================================================ */

.block--hero {
  background: var(--color-soft-apricote);
  display: flex;
  align-items: center;
}

/* base.css grids `.hero-inner` unconditionally on a split layout, with
   auto-fit columns sized for hero-inner and hero-media to share. Past
   roughly 48rem of container width that auto-fit also slices the COPY's own
   children - eyebrow, heading, subheading, body, actions - into separate
   columns, which a full-width hero reaches easily. Locked to one column
   always; the two-column carrier below grids the SECTION, not this.

   Bare `.hero-inner` is (0,1,0); base.css's `[data-layout="split"]
   .hero-inner` is (0,2,0) and was winning outright, not losing a tie - this
   split hero on a `.block-bg` carrier (content.background, not
   content.image) still has `data-layout="split"` set even though there is
   no `.hero-media` to split with, so the auto-fit rule was live and did
   slice the copy into columns whenever `.hero-inner` got wide enough,
   confirmed by forcing its width past 48rem. It only read as fine because
   `.block--hero:has(> .block-bg) > .hero-inner` below caps this hero's copy
   at 30rem - comfortably under the threshold - so this was a bug waiting
   on a wider `--wrap` (admin-configurable since 1.11.x) or on the
   `.hero-media` carrier's own copy column being pushed past 48rem, neither
   of which has happened yet. Matched at (0,2,0) with `.block--hero
   .hero-inner` instead of restated at (0,1,0), so it actually wins rather
   than appearing to. */
.block--hero .hero-inner {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  justify-items: start;
}

/* Fallback measure for a hero with neither carrier, if one is ever built. */
.block--hero > .hero-inner { max-inline-size: var(--measure); }

.block--hero .hero-actions { margin-block-start: var(--space-l); }

/* ---- Striped band ----------------------------------------------------------
   Vertical stripes of sandy-brown and a softened action, the second mixed
   toward sandy-brown to match design/hero-concept.png's softer contrast.
   Worn by the interior pages' title bands (`.page-hero`) and the
   homepage's newsletter band (`.newsletter`, on the section itself, since
   its block is the sign); with no photo, the stripes fill the band. Set
   once here from the --stripe-* properties, so a tweak lands on all of
   them. */
:is(.page-hero > .block--hero, .sw-section.newsletter) {
  --stripe-width: 4rem;
  --stripe-a: var(--color-sandy-brown);
  --stripe-b: color-mix(in srgb, var(--color-action) 45%, var(--color-sandy-brown));
  background: repeating-linear-gradient(
    to right,
    var(--stripe-a) 0 var(--stripe-width),
    var(--stripe-b) var(--stripe-width) calc(var(--stripe-width) * 2)
  );
}

/* ---- Home hero: full-bleed photo, copy in the wide sign -----------------
   Scoped to `.home-hero`, the class set in the admin on the homepage's first
   section, not to the `.block-bg` carrier, so a photo hero added to any
   other page does not inherit it. The photo fills the band (base.css's
   `inset: 0`) and the copy sits over it in the wide sign (below). `.block--hero` is a flex row and `.block`'s inline padding uses
   the same formula as the header's, so the sign's outermost point, its
   left tip, starts at the logo's left edge at every width. */

.home-hero > .block--hero {
  min-block-size: min(78vh, 40rem);
  /* Clip the photo as it zooms (below). */
  overflow: hidden;
}

/* On ink (#363436): accent-green eyebrow 7.8:1, paper heading and
   subheading 12.3:1, softened-paper body (the hero's copy, a page's intro)
   8.5:1. AA everywhere, AAA at body size. The primary button keeps its
   site-wide style; its orange edge is 4.8:1 against the ink. */
:is(.home-hero, .page-hero) .block-eyebrow { color: var(--color-accent-green); }
:is(.home-hero, .page-hero) :is(.block-heading, .block-subheading) { color: var(--color-paper); }
:is(.home-hero, .page-hero) .block-body {
  color: color-mix(in srgb, var(--color-paper) 80%, var(--color-ink));
}

/* ---- Wide sign: the home hero and every page hero -------------------------
   The `.carrots-shape--sign` outline from design/brand-shapes.css, used as
   a shape reference only: that file's markup can't come out of the CMS,
   and its masks stretch one fixed-ratio drawing with
   preserveAspectRatio="none", which would distort the curves around copy
   this tall. Rebuilt so the curves never stretch. One pattern for three
   bands: `.home-hero` over its photo, `.page-hero`, the interior pages'
   title bands on the stripes, and the homepage's `.newsletter`, also on
   stripes, where the form block itself is the sign. Each band sets only
   its own size and spacing (with its own rules); a new page hero picks
   the sign up from the class. The :is() list takes its most specific
   member, (0,3,0), which is what every one of these rules had before.

   The sign's path is cut into fixed-size pieces, traced from the file's
   own coordinates (its 1000-unit viewBox, drawn at .64px a unit on
   desktop, .48 and .36 on smaller signs): four rounded corners and the two
   pointed tips. Straight runs join them and are the only parts that
   stretch: the top and bottom edges across, and the sides between each
   corner and the tip. The file's sides run exactly vertical where a
   corner meets a tip, so a straight run slotted in there adds no kink,
   and the sign grows in height as well as width. Like the file, only the
   decoration is masked, on the pseudo-elements; the copy and its focus
   rings are never clipped.

   ::before is the ink ground: the six pieces plus two rectangles.
   ::after is the inset line, in --panel-line-colour: on the curves, the
   outline itself stroked inside the shape, an exact parallel; on the
   straight runs, 1px bars. The file's three strokes are one line here.
   The pieces are alpha masks only: every colour comes from the tokens.

   Why it is cut this way, not as one nine-slice (mask-border or
   border-image): a nine-slice stretches each whole side, and the tips sit
   in the middle of the sides, so they would stretch with them; and
   Firefox has no mask-border. So pieces, built so their joins can't show:
   - Every tile size, tile offset and straight edge is a whole pixel. The
     earlier pieces sat at fractional offsets (27 units = 17.28px, the line
     at 25.28px), so the browser rounded each tile and each straight run
     separately, and at a join the two landed on different pixels: the
     hairline seams in the ground, and the steps and broken pixels in the
     line where a curve met a straight run. --sign-s, -r, -v and -e are
     the drawing's offsets rounded to whole pixels (the side, the line's
     inset, where a corner has turned vertical and where it has turned
     horizontal); --sign-c is the corner tile, 100 units, also whole at
     each size. The tip tiles are drawn in pixels, with the side at
     exactly --sign-s (the tip narrowed by under 2% to get there).
   - Every edge where one piece ends sits inside another piece's solid
     area, at least 4px in, never edge to edge. Two soft edges meeting at
     the same fractional pixel each let some of the background through,
     which is what a seam is.
   - The line is 1px at whole-pixel positions everywhere, so the bars and
     the curves' straight ends sit on the same pixel column or row. On
     the smallest signs the line is 5px in (12.5 units would be 4.5px,
     which put a 1px line across two pixels at half strength).
   The pieces come in sets, one per size (--sign-fill-64-l, --sign-ring-
   48-tl, ...), each picked into --sign-fill-* and --sign-ring-* with its
   five measurements wherever the size changes. A new size needs a new set
   (and --sign-line has to stay 1px). The corner fills are one set.

   Room inside the line: the side 17px in from the tip, the line 8px
   inside that and 1px wide, then --sign-gap to the text: 39px at the
   sides on desktop, and 63px top and bottom from 4.5rem of padding.
   (The line used to sit twice as far in at 2px; the gap took up the
   difference, so the text did not move.) The corners clear easily.
   Without mask support the panel falls back to an ink rectangle with a
   straight inset line. */
:is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form) {
  --sign-line: 1px;
  --sign-gap: 2.4375rem;
  --sign-pad-block: 4.5rem;
  --sign-fill-tl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M100 0 L88 0 C29 0 0 40 0 87 L0 100 L100 100 Z'/%3E%3C/svg%3E");
  --sign-fill-tr: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1)'%3E%3Cpath d='M100 0 L88 0 C29 0 0 40 0 87 L0 100 L100 100 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-fill-bl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(0 100) scale(1 -1)'%3E%3Cpath d='M100 0 L88 0 C29 0 0 40 0 87 L0 100 L100 100 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-fill-br: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1) translate(0 100) scale(1 -1)'%3E%3Cpath d='M100 0 L88 0 C29 0 0 40 0 87 L0 100 L100 100 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-fill-64-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 128'%3E%3Cpath d='M64 0 L17 0 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 128 L64 128 Z'/%3E%3C/svg%3E");
  --sign-fill-64-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 128'%3E%3Cg transform='translate(64 0) scale(-1 1)'%3E%3Cpath d='M64 0 L17 0 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 128 L64 128 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-64-tl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='28.125'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-64-tr: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='28.125'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-64-bl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='28.125'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-64-br: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1) translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='28.125'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-64-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 128'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-256' y='-256' width='1024' height='1024'%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192' fill='none' stroke='white' stroke-width='18'/%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192' fill='none' stroke='black' stroke-width='16'/%3E%3C/mask%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192 L256 192 L256 -64 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-64-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 128'%3E%3Cg transform='translate(64 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-256' y='-256' width='1024' height='1024'%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192' fill='none' stroke='white' stroke-width='18'/%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192' fill='none' stroke='black' stroke-width='16'/%3E%3C/mask%3E%3Cpath d='M17 -64 L17 3.2 C17 30.72 9.444 48 0 64 C9.444 80 17 97.28 17 124.8 L17 192 L256 192 L256 -64 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-fill-48-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 96'%3E%3Cpath d='M48 0 L13 0 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 96 L48 96 Z'/%3E%3C/svg%3E");
  --sign-fill-48-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 96'%3E%3Cg transform='translate(48 0) scale(-1 1)'%3E%3Cpath d='M48 0 L13 0 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 96 L48 96 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-48-tl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='29.167'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-48-tr: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='29.167'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-48-bl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='29.167'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-48-br: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1) translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='29.167'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='25'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-48-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 96'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-192' y='-192' width='768' height='768'%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144' fill='none' stroke='white' stroke-width='14'/%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144' fill='none' stroke='black' stroke-width='12'/%3E%3C/mask%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144 L192 144 L192 -48 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-48-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 96'%3E%3Cg transform='translate(48 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-192' y='-192' width='768' height='768'%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144' fill='none' stroke='white' stroke-width='14'/%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144' fill='none' stroke='black' stroke-width='12'/%3E%3C/mask%3E%3Cpath d='M13 -48 L13 2.4 C13 23.04 7.222 36 0 48 C7.222 60 13 72.96 13 93.6 L13 144 L192 144 L192 -48 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-fill-36-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 72'%3E%3Cpath d='M36 0 L10 0 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 72 L36 72 Z'/%3E%3C/svg%3E");
  --sign-fill-36-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 72'%3E%3Cg transform='translate(36 0) scale(-1 1)'%3E%3Cpath d='M36 0 L10 0 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 72 L36 72 Z'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-36-tl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='33.333'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='27.778'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-36-tr: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='33.333'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='27.778'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-36-bl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='33.333'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='27.778'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-36-br: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg transform='translate(100 0) scale(-1 1) translate(0 100) scale(1 -1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-300' y='-300' width='1200' height='1200'%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='white' stroke-width='33.333'/%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 88' fill='none' stroke='black' stroke-width='27.778'/%3E%3C/mask%3E%3Cpath d='M200 0 L88 0 C29 0 0 40 0 87 L0 200 L200 200 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-ring-36-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 72'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-144' y='-144' width='576' height='576'%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108' fill='none' stroke='white' stroke-width='12'/%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108' fill='none' stroke='black' stroke-width='10'/%3E%3C/mask%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108 L144 108 L144 -36 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --sign-ring-36-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 72'%3E%3Cg transform='translate(36 0) scale(-1 1)'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-144' y='-144' width='576' height='576'%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108' fill='none' stroke='white' stroke-width='12'/%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108' fill='none' stroke='black' stroke-width='10'/%3E%3C/mask%3E%3Cpath d='M10 -36 L10 1.8 C10 17.28 5.556 27 0 36 C5.556 45 10 54.72 10 70.2 L10 108 L144 108 L144 -36 Z' mask='url(%23m)'/%3E%3C/g%3E%3C/svg%3E");
  --sign-s: 17px;
  --sign-r: 8px;
  --sign-c: 64px;
  --sign-v: 56px;
  --sign-e: 57px;
  --sign-fill-l: var(--sign-fill-64-l);
  --sign-fill-r: var(--sign-fill-64-r);
  --sign-ring-tl: var(--sign-ring-64-tl);
  --sign-ring-tr: var(--sign-ring-64-tr);
  --sign-ring-bl: var(--sign-ring-64-bl);
  --sign-ring-br: var(--sign-ring-64-br);
  --sign-ring-l: var(--sign-ring-64-l);
  --sign-ring-r: var(--sign-ring-64-r);
  position: relative;
  isolation: isolate;
  /* Never smaller than both ends: two corners and a tip tall, two ends
     wide. Below that the fixed-size pieces would overlap, so a short
     title (a heading alone) gets a sign of full-height ends with the copy
     centred in it, the spare height split evenly above and below, rather
     than squashed ends. */
  min-block-size: calc((var(--sign-c) + var(--sign-v)) * 2);
  min-inline-size: calc((var(--sign-s) + var(--sign-e)) * 2);
  align-content: center;
  padding-block: var(--sign-pad-block);
  padding-inline: calc(var(--sign-s) + var(--sign-r) + var(--sign-line) + var(--sign-gap));
}

:is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::before,
:is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  pointer-events: none;
}

:is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::before { background: var(--panel-ground); }

:is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::after {
  inset: var(--sign-r);
  border: var(--sign-line) solid var(--panel-line-colour);
}

@supports (mask-image: linear-gradient(black, black)) {
  :is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::before {
    mask-image:
      var(--sign-fill-tl),
      var(--sign-fill-tr),
      var(--sign-fill-bl),
      var(--sign-fill-br),
      var(--sign-fill-l),
      var(--sign-fill-r),
      linear-gradient(black, black),
      linear-gradient(black, black);
    mask-position:
      left var(--sign-s) top 0,
      right var(--sign-s) top 0,
      left var(--sign-s) bottom 0,
      right var(--sign-s) bottom 0,
      left 0 top 50%,
      right 0 top 50%,
      left var(--sign-s) top var(--sign-v),
      left calc(var(--sign-s) + var(--sign-e)) top 0;
    mask-size:
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) calc(var(--sign-c) * 2),
      var(--sign-c) calc(var(--sign-c) * 2),
      calc(100% - var(--sign-s) * 2) calc(100% - var(--sign-v) * 2),
      calc(100% - (var(--sign-s) + var(--sign-e)) * 2) 100%;
    mask-repeat: no-repeat;
  }

  :is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form)::after {
    inset: 0;
    border: 0;
    background: var(--panel-line-colour);
    mask-image:
      var(--sign-ring-tl),
      var(--sign-ring-tr),
      var(--sign-ring-bl),
      var(--sign-ring-br),
      var(--sign-ring-l),
      var(--sign-ring-r),
      linear-gradient(black, black),
      linear-gradient(black, black),
      linear-gradient(black, black),
      linear-gradient(black, black),
      linear-gradient(black, black),
      linear-gradient(black, black);
    mask-position:
      left var(--sign-s) top 0,
      right var(--sign-s) top 0,
      left var(--sign-s) bottom 0,
      right var(--sign-s) bottom 0,
      left 0 top 50%,
      right 0 top 50%,
      left calc(var(--sign-s) + var(--sign-e)) top var(--sign-r),
      left calc(var(--sign-s) + var(--sign-e)) bottom var(--sign-r),
      left calc(var(--sign-s) + var(--sign-r)) top var(--sign-v),
      left calc(var(--sign-s) + var(--sign-r)) bottom var(--sign-v),
      right calc(var(--sign-s) + var(--sign-r)) top var(--sign-v),
      right calc(var(--sign-s) + var(--sign-r)) bottom var(--sign-v);
    mask-size:
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) var(--sign-c),
      var(--sign-c) calc(var(--sign-c) * 2),
      var(--sign-c) calc(var(--sign-c) * 2),
      calc(100% - (var(--sign-s) + var(--sign-e)) * 2) var(--sign-line),
      calc(100% - (var(--sign-s) + var(--sign-e)) * 2) var(--sign-line),
      var(--sign-line) calc(50% - var(--sign-c) - var(--sign-v) + 1px),
      var(--sign-line) calc(50% - var(--sign-c) - var(--sign-v) + 1px),
      var(--sign-line) calc(50% - var(--sign-c) - var(--sign-v) + 1px),
      var(--sign-line) calc(50% - var(--sign-c) - var(--sign-v) + 1px);
    mask-repeat: no-repeat;
  }
}

/* Tablet and mobile: the sign steps down to the .48px set: the line 6px
   in, still 1px, and 23px of room inside it at the sides. */
@media (max-width: 850px) {
  :is(.home-hero > .block--hero > .hero-inner,
    .page-hero > .block--hero > .hero-inner,
    .newsletter > .block--form) {
    --sign-gap: 1.4375rem;
    --sign-pad-block: 3.375rem;
    --sign-s: 13px;
    --sign-r: 6px;
    --sign-c: 48px;
    --sign-v: 42px;
    --sign-e: 43px;
    --sign-fill-l: var(--sign-fill-48-l);
    --sign-fill-r: var(--sign-fill-48-r);
    --sign-ring-tl: var(--sign-ring-48-tl);
    --sign-ring-tr: var(--sign-ring-48-tr);
    --sign-ring-bl: var(--sign-ring-48-bl);
    --sign-ring-br: var(--sign-ring-48-br);
    --sign-ring-l: var(--sign-ring-48-l);
    --sign-ring-r: var(--sign-ring-48-r);
  }
}

/* The home hero's sign: 38.75rem keeps the text column the width it had
   in the earlier ink panel. */
.home-hero > .block--hero > .hero-inner {
  inline-size: min(38.75rem, 100%);
  max-inline-size: none;
  margin-inline: 0;
}

/* Clip the zoomed photo at the background layer too, not only at the
   section - a child combinator, so this holds even where the section's
   :has() rule does not apply. */
.block--hero > .block-bg { overflow: hidden; }

.block--hero > .block-bg img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transform-origin: center;
}

/* Gentle zoom, 1 to 1.08, as the hero scrolls out of view. A CSS
   scroll-driven animation, so no script or CDN download; a browser without
   `animation-timeline` support just shows the photo still. The timeline is
   named on the section rather than taken from `view()` on the img, because
   `view()` binds to the nearest scroll container and `.block-bg`'s
   `overflow: hidden` makes it one. `animation-timeline` comes after the
   `animation` shorthand, which would otherwise reset it. Not declared at
   all under reduced motion, so the photo stays still. */
.home-hero { view-timeline: --home-hero block; }

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .home-hero > .block--hero > .block-bg img {
      animation: home-hero-zoom linear both;
      animation-timeline: --home-hero;
      animation-range: exit;
    }
  }
}

@keyframes home-hero-zoom {
  from { transform: scale(1); }
  to   { transform: scale(1.08); }
}

/* Phones (below 46rem, where the homepage's cards become a swipe row):
   the sign covered the whole photo, so the two stack instead, the way
   the "Visit the Bake Shop" band does below 1024px. The photo comes out
   of the background and into the flow as the band's first item, 280px
   tall and bled out through the band's side padding (the gutter, at these
   widths); the sign follows, pulled up 4rem over the photo's bottom
   edge. Everything is laid out top down from the band's own top edge,
   and `overflow: clip` keeps it all inside the band. From the photo down
   the band is paper, not the hero's apricot, so the sign sits on white,
   and it has no padding of its own below the sign: the next section's
   top padding (COLOUR BOUNDARIES restores it after a hero) is then the
   whole gap, one --space-block, as between any two sections. The crop is
   anchored right, where the bread basket is; the photo zoom on scroll
   carries on.

   The sign is shorter too: the address paragraph is hidden (the
   subheading and the buttons say enough), and the heading drops a step
   to the h2 size, line height and tracking from Branding. */
@media (max-width: 46rem) {
  .home-hero > .block--hero {
    flex-direction: column;
    align-items: stretch;
    min-block-size: 0;
    padding-block: 0;
    overflow: clip;
    background: var(--color-paper);
  }

  .home-hero > .block--hero > .block-bg {
    position: relative;
    inset: auto;
    z-index: 0;
    inline-size: auto;
    block-size: 17.5rem;
    margin-inline: calc(var(--gutter) * -1);
  }

  .home-hero > .block--hero > .block-bg img { object-position: right center; }

  .home-hero > .block--hero > .hero-inner {
    z-index: 1;
    margin-block-start: -4rem;
  }

  .home-hero > .block--hero > .hero-inner > .block-body { display: none; }

  .home-hero > .block--hero > .hero-inner > .block-heading {
    font-size: var(--h2-size, var(--step-3));
    line-height: var(--h2-line, 1.1);
    letter-spacing: var(--h2-tracking, -.015em);
  }
}

/* Below 380px the sign's text box is under 230px, narrower than "Visit the
   Bake Shop" at the buttons' full --space-l side padding (that label
   can't wrap), so it ran into the room inside the sign's line. Both
   buttons take --space-m at the sides instead, which is about 200px. */
@media (max-width: 23.75rem) {
  .home-hero .hero-actions .block-cta { --button-padding: var(--space-2xs) var(--space-m); }
}

/* ---- Hero entrance: the sign arrives once ---------------------------
   Every sign hero, home and page alike: the sign (`.hero-inner`, which
   draws the nameplate in its own ::before and ::after) fades in and rises
   16px over 900ms, ease-out; its copy follows, 150ms apart in source order
   (on a page hero the heading, then the intro; on the home hero the
   eyebrow, heading, subheading, body and buttons), each over the same
   900ms with an 8px rise of its own, so the words settle just after the
   sign does.

   Nothing is hidden by default. The resting state is the visible one and
   only the keyframes' `from` is transparent, with `backwards` fill to hold
   it through each delay; with no forwards fill, the end of the animation
   hands back to that same visible state. A browser that does not run the
   animation shows everything as it is. CSS on purpose, not gated on `.js`:
   it starts on the first frame with no script or network wait. Under
   reduced motion it is not declared at all. Replaces the home hero's
   900ms copy stagger, so the two never stack. The photo zoom on scroll
   above is a separate animation on the photo and is untouched. */
@media (prefers-reduced-motion: no-preference) {
  :is(.home-hero, .page-hero) > .block--hero > .hero-inner {
    animation: hero-sign-in 900ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }

  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > * {
    animation: hero-copy-in 900ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }

  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > :nth-child(1) { animation-delay: 150ms; }
  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > :nth-child(2) { animation-delay: 300ms; }
  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > :nth-child(3) { animation-delay: 450ms; }
  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > :nth-child(4) { animation-delay: 600ms; }
  :is(.home-hero, .page-hero) > .block--hero > .hero-inner > :nth-child(5) { animation-delay: 750ms; }
}

@keyframes hero-sign-in {
  from { opacity: 0; transform: translateY(16px); }
}

@keyframes hero-copy-in {
  from { opacity: 0; transform: translateY(8px); }
}


/* ---- Image-beside-copy hero: the "Carrot cake" feature ------------------
   `.hero-media` is a sibling of `.hero-inner`, not a child, so base.css's
   split grid never reaches it - ENGINE WORKAROUNDS 1. Gridded on the
   section instead. This is also where the dark feature-band treatment
   lives: graphite ground, green eyebrow, paper heading. It belongs to the
   CARRIER, not to a particular block, so ANY hero with an image picked on
   the block itself gets it - the homepage's "Carrot cake" is the one that
   is meant to, and every other image-beside-copy hero (the two on
   /our-story/) opts out by sitting in a section with its own class; see
   "story-split" under STORY PAGE. Padding is symmetric here rather than
   base.css's follows-another-block zero, since this is now a colour
   change on both sides. */

.block--hero:has(> .hero-media) {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  gap: var(--space-xl);
  padding-block: var(--space-block);
  background: var(--color-ink);
}

.block--hero:has(> .hero-media) > .hero-inner { order: 0; max-inline-size: none; }
.block--hero:has(> .hero-media) > .hero-media { order: 1; }
.block--hero.is-reverse:has(> .hero-media) > .hero-media { order: -1; }

.block--hero:has(> .hero-media) .block-eyebrow { color: var(--color-accent-green); }
.block--hero:has(> .hero-media) .block-heading { color: var(--color-paper); }
.block--hero:has(> .hero-media) .block-body {
  color: color-mix(in srgb, var(--color-paper) 78%, var(--color-ink));
}

/* The photo fills `.hero-media` edge to edge - no mat. `.hero-media` itself
   carries no background or padding of its own now, so this is scoped by
   what it selects (only the `.hero-media` carrier, never the homepage
   hero's `.block-bg`) rather than by anything stated here; nothing needs
   repeating against the other hero for this to stay out of its way. The
   rounding lives on the image alone, via its own border-radius below. */

.block--hero > .hero-media img {
  inline-size: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

@media (max-width: 700px) {
  .block--hero:has(> .hero-media) {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-l);
  }
  /* Photo first, then the copy, as story-split does below 800px. */
  .block--hero:has(> .hero-media) > .hero-media,
  .block--hero.is-reverse:has(> .hero-media) > .hero-media { order: -1; }
}


/* ---- Page-title hero: a heading and nothing else ------------------------
   The third carrier sharing `.block--hero`, and the only one holding
   neither a `.block-bg` nor a `.hero-media`: the masthead at the top of an
   interior page ("Our Products"). base.css hands it the same --space-block
   padding as a full hero, so a band carrying one line of type opens the
   page as a mostly empty apricote slab.

   Proportioned to what it actually holds instead - still clearly a band,
   but sized to the heading rather than to a photo. Type size is left to
   Branding; only the rhythm changes. */

.block--hero:not(:has(> .block-bg)):not(:has(> .hero-media)) {
  padding-block: var(--space-xl);
}

/* ---- Page hero: the title band on stripes, in the wide sign -------------
   `.page-hero`, set in the admin on the section, puts an interior page's
   title band on the stripes, with heading and intro in the wide sign (see
   "Striped band" and "Wide sign" under HERO). Adding the class to a new
   page's hero section is all it takes. The band keeps the
   page-title padding above, so stripes show above and below the sign.
   `.block--hero` is a flex row, centred here, and the sign shrinks to fit
   its content: a bare "Our Products" makes a small sign, not a wide empty
   one, and never shorter than its own ends. It grows to 38.75rem and no
   further, the home hero's width. The heading and intro are centred in
   it. */
.page-hero > .block--hero { justify-content: center; }

.page-hero > .block--hero > .hero-inner {
  max-inline-size: min(38.75rem, 100%);
  justify-items: center;
  text-align: center;
}

/* The intro at most 46ch, so a one-sentence intro breaks into two lines
   rather than running the sign's full width, and balanced, so the two
   come out about even: Contact's 57 characters and Wholesale's 96 both
   make two. The cap is the box the sign fits, so a longer intro widens
   the sign only to 46ch. An intro with its own line break (Our Story)
   keeps it. */
.page-hero > .block--hero .block-body {
  max-inline-size: 46ch;
  text-wrap: balance;
}

/* Phones: the sign is the full content width, still centred inside. */
@media (max-width: 600px) {
  .page-hero > .block--hero > .hero-inner { flex: 1 1 auto; }
}

/* Shorter than the home hero's sign, since a title band holds a line or
   two. The top and bottom padding is half the home hero's, so the copy
   sits close to the line: 30px clear of it on desktop, 22px below 850px.
   The sign's ends scale down to suit, every piece at once so the curves
   stay round: the .48px set (.36px below 850px), which lowers the
   shortest sign the ends allow to 180px (136px). /our-products/, a
   heading alone, is shorter than that, so it takes the minimum with the
   heading centred: about 18px of extra space above and below on a wide
   screen, 14-25px elsewhere. Getting closer would mean shrinking the ends again. The side
   padding stays what it was (65px, 43px below 850px), so the text keeps
   clear of the pointed ends. */
.page-hero > .block--hero > .hero-inner {
  --sign-pad-block: 2.25rem;
  --sign-s: 13px;
  --sign-r: 6px;
  --sign-c: 48px;
  --sign-v: 42px;
  --sign-e: 43px;
  --sign-fill-l: var(--sign-fill-48-l);
  --sign-fill-r: var(--sign-fill-48-r);
  --sign-ring-tl: var(--sign-ring-48-tl);
  --sign-ring-tr: var(--sign-ring-48-tr);
  --sign-ring-bl: var(--sign-ring-48-bl);
  --sign-ring-br: var(--sign-ring-48-br);
  --sign-ring-l: var(--sign-ring-48-l);
  --sign-ring-r: var(--sign-ring-48-r);
  padding-inline: 4.08rem;
}

/* The band's own padding: 3rem, down from the page-title band's 4rem, so
   the hero as a whole takes less of the screen; stripes still show above
   and below the sign. The page-title rule's selector plus this class,
   so it outranks it. */
.page-hero > .block--hero:not(:has(> .block-bg)):not(:has(> .hero-media)) {
  padding-block: calc(var(--space-xl) * .75);
}

@media (max-width: 850px) {
  .page-hero > .block--hero > .hero-inner {
    --sign-pad-block: 1.6875rem;
    --sign-s: 10px;
    --sign-r: 5px;
    --sign-c: 36px;
    --sign-v: 32px;
    --sign-e: 32px;
    --sign-fill-l: var(--sign-fill-36-l);
    --sign-fill-r: var(--sign-fill-36-r);
    --sign-ring-tl: var(--sign-ring-36-tl);
    --sign-ring-tr: var(--sign-ring-36-tr);
    --sign-ring-bl: var(--sign-ring-36-bl);
    --sign-ring-br: var(--sign-ring-36-br);
    --sign-ring-l: var(--sign-ring-36-l);
    --sign-ring-r: var(--sign-ring-36-r);
    padding-inline: 2.685rem;
  }
}


/* ============================================================
   CARDS
   ============================================================ */

.card,
.info-cards > .block {
  background: var(--color-paper);
  border: var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 150ms ease, box-shadow 150ms ease;
}

/* Feedback: a card that carries a link lifts on hover. Only when it has a
   cta - a card with nothing to click stays put. Under reduced motion
   base.css collapses the transition, so the shadow still appears, just
   without the lift. */
.card:has(.card-cta):hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -8px color-mix(in srgb, var(--color-ink) 16%, transparent);
}

/* base.css bleeds the media to the card edges with a negative margin and then
   rounds its top corners. The card clips now, so that second radius would
   only double up. */
.card-media img {
  aspect-ratio: 4 / 3;
  border-radius: 0;
}

.card-title { font-size: var(--step-0); }

.card-badge {
  background: var(--color-accent-green);
  color: var(--color-ink);
  font-weight: 700;
}

/* Same contrast fix as the hero eyebrow: plain --color-action on the card's
   paper ground is 2.6:1 and fails AA at this size. Darkened toward ink to
   ~4.8:1, still clearly the brand orange. */
.card-cta {
  color: color-mix(in srgb, var(--color-ink) 45%, var(--color-action));
  font-weight: 600;
  font-size: var(--step--1);
  text-decoration: none;
}

.card-cta::after { content: "\00a0\2192"; }

.card-cta:hover,
.card-cta:focus-visible { text-decoration: underline; }

/* ---- Homepage "Every day at the Bake Shop": `.daily-cards` ---------------
   The card links ("Cakes and cookies \2192") as ink text with an orange
   underline - the same treatment as the tertiary CTA - instead of the
   orange-tinted text. The arrow is part of the link, so it keeps the
   underline too. The underline thickens on hover and focus. */
.daily-cards .card-cta,
.where-to-buy .map-place a,
.products-cards .block--collection-grid > .block-cta,
:is(.products-feature, .product-category) .block--collection-list > .block-cta,
.products-feature .block--list > .block-cta {
  color: var(--color-ink);
  text-decoration: underline;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
  text-underline-offset: .25em;
  transition: text-decoration-thickness var(--transition);
}

.daily-cards .card-cta:hover,
.daily-cards .card-cta:focus-visible,
.where-to-buy .map-place a:is(:hover, :focus-visible),
.products-cards .block--collection-grid > .block-cta:is(:hover, :focus-visible),
:is(.products-feature, .product-category) .block--collection-list > .block-cta:is(:hover, :focus-visible),
.products-feature .block--list > .block-cta:is(:hover, :focus-visible) {
  color: var(--color-ink);
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 4px;
}

/* Card titles were --step-0, the same size as the description under them.
   Raised two steps and to the heaviest weight so the title leads; the
   description keeps body size and --muted (70% ink, ~4.9:1 on the card's
   paper, AA for body text). */
.daily-cards .card-title {
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.15;
}

/* A soft shadow instead of the grey border. With no edge at all the text
   half of each card would float on the white page with nothing to hold
   it; the shadow gives the card its shape without a hard line against the
   photo. Hover keeps the existing lift with a slightly deeper shadow. */
.daily-cards .card {
  border: 0;
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--color-ink) 6%, transparent),
    0 8px 24px -12px color-mix(in srgb, var(--color-ink) 22%, transparent);
}

.daily-cards .card:has(.card-cta):hover {
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--color-ink) 6%, transparent),
    0 14px 32px -12px color-mix(in srgb, var(--color-ink) 28%, transparent);
}

/* Heading to cards. base.css puts --space-l above the grid; tightened to
   --space-m. And while the section heading still lives in its own block
   (the "featured" grid above, which renders no cards), that block's
   bottom padding is the bulk of the gap, so it is dropped when the block
   holds no cards and the next section is this one. Once that heading
   moves into this block, the first rule does nothing and the second
   alone sets the gap. */
.sw-section:has(+ .sw-section.daily-cards) > .block--collection-grid:not(:has(.card)) {
  padding-block-end: 0;
}

.daily-cards .grid { margin-block-start: var(--space-m); }

/* Phones: the cards as a swipe row instead of a stack, on the homepage's
   "Every day at the Bake Shop" and the photo-card sections on Our
   Products (`products-cards`: Bread and Cakes & Cupcakes), but not the
   category pages' full product grids (`product-category`), which stay
   stacked. 46rem is where grid-2's 20rem columns stop fitting side by
   side, so on the homepage the row takes over exactly where the stack
   would have started; on Our Products it also replaces the two-column
   step between 40rem and 46rem. Each card is 85% of the
   screen, so the next one shows at the right edge; the row bleeds out
   through the block's side padding (the gutter, at these widths) and pads
   it back, so the first card starts on the page's left edge and the last
   can scroll fully in. Each swipe snaps to a card. No scrollbar; the
   peeking card is the cue. The row's padding top and bottom leaves room
   for the cards' shadows, which the scroller would otherwise clip, and
   the negative margin below takes it back out of the spacing. site.js
   (section 4) makes the row a labelled, focusable region while it
   scrolls, so it can be scrolled with the arrow keys; the ring is drawn
   inside it. */
@media (max-width: 46rem) {
  :is(.daily-cards, .products-cards:not(.product-category)) .block--collection-grid > .grid {
    display: flex;
    gap: var(--space-s);
    margin-inline: calc(var(--gutter) * -1);
    margin-block-end: calc(var(--space-l) * -1);
    padding: var(--space-2xs) var(--gutter) var(--space-l);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }

  :is(.daily-cards, .products-cards:not(.product-category)) .block--collection-grid > .grid::-webkit-scrollbar { display: none; }

  :is(.daily-cards, .products-cards:not(.product-category)) .block--collection-grid > .grid > .card {
    flex: 0 0 85vw;
    scroll-snap-align: start;
  }

  :is(.daily-cards, .products-cards:not(.product-category)) .block--collection-grid > .grid:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
  }
}

/* ---- Photo-top card, "Every day at the Bake Shop" -----------------------
   Scoped by `data-layout="grid-2"` rather than by a block class or id. That
   layout is no longer this section's alone: the four grids on /our-products/
   are grid-2 as well, so every rule below reaches them too, and anything
   else later set to grid-2 inherits them. The products page overrides them
   inside `.product-grid` (see "Products page" under COLLECTION GRID) instead
   of these being narrowed - touch them with that in mind. */

/* base.css zeroes a block's own top padding whenever it follows another
   block (`.block + .block`), on the assumption that the block ABOVE it
   already supplies the gap via its own bottom padding. That holds for two
   plain white blocks stacked in a row, but the hero above this one is a
   colour change: its bottom padding is apricote, not white, so it reads as
   part of the band rather than as space before this heading. Restated here
   so this section gets its own breathing room after the colour boundary,
   rather than starting flush against it. */
.block[data-layout="grid-2"] { padding-block-start: var(--space-block); }

[data-layout="grid-2"] .card-media img { aspect-ratio: 5 / 4; }

/* The badge floats over the photo's top-left corner instead of sitting in
   its own row above it, which is base.css's default (it is the first
   child, in source order ahead of .card-media). `.card` becomes the
   badge's containing block, which base.css never sets it up to be, and
   `.card-media`'s negative-margin bleed (also base.css) means the two
   boxes already share a top-left corner - an inset against one is the
   same inset against the other, so nothing needs measuring against the
   photo specifically. Without `position: relative` here, the badge would
   anchor to whichever positioned ancestor it found instead, well outside
   where it visually reads as "over the card" - and `.card`'s own
   `overflow: hidden` would then clip whatever fell outside its own box.
   That was the clipping. */
[data-layout="grid-2"] .card { position: relative; }

[data-layout="grid-2"] .card-badge {
  position: absolute;
  inset-block-start: var(--space-s);
  inset-inline-start: var(--space-s);
  z-index: 1;
}


/* ============================================================
   COLLECTION GRID
   The product listings on "Our Products". Four of these stack on that
   page, one per product family (Bread, Pastry & Muffins, Cakes & Cupcakes,
   Cookies & Squares).
   ============================================================ */

/* base.css sizes every grid with `auto-fit`, so the declared column count is
   a minimum track width and not a count: at the full 1200px wrap, `grid-3`
   fits four 16rem tracks and renders four across. The number in the admin's
   layout field is the one thing an editor can see, so it should be the
   number they get. Raising the track floor to 20rem makes three the most
   that fit inside the wrap, while auto-fit still drops to two and then one
   as the screen narrows. Nothing else on the site uses `grid-3`. */
[data-layout="grid-3"] .grid {
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}

/* A product tile carries a photo and a name, no body and no link, so the
   photo is the whole sell. Squared up from base.css's 1200/630 letterbox
   (site.css already pulls it to 4/3 globally) to nearer a square, which
   crops a loaf to fill the frame rather than leaving it small in a wide
   band. */
[data-layout="grid-3"] .card-media img { aspect-ratio: 5 / 4; }

/* Nothing in these cards is clickable, so the border is doing all the work
   of separating tile from page. Dropped in favour of the photo's own edge
   plus a soft ground behind the caption, which reads lighter across a grid
   of five and puts the product name on something rather than floating it
   on white. */
[data-layout="grid-3"] .card {
  border: 0;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--color-ink) 10%, transparent);
}

[data-layout="grid-3"] .card-title { padding-block-start: var(--space-2xs); }

/* The product's own description. Stored in `data.body`, so since 1.16.0 it
   renders as `.card-field--body` - but only on a block whose binding has
   Body ticked; blocks saved earlier hide it by default. Same muted small
   copy base.css gives `.card-body`, which this class does not inherit. */
[data-layout="grid-3"] .card-field--body {
  color: var(--muted);
  font-size: var(--step--1);
}

/* An unbound or empty collection still emits its `.grid`, which carries
   base.css's `margin-block-start: var(--space-l)` - so a section with a
   heading and no products reserves 40px of nothing beneath it. ENGINE
   WORKAROUNDS 4 only collapses sections that would draw literally nothing;
   these have a real heading, so they stay visible on purpose. Collapsing
   just the empty grid removes the phantom gap without hiding the heading an
   editor published. */
.block--collection-grid .grid:not(:has(.card)) { display: none; }

/* ---- Products page: `.product-grid` --------------------------------------
   The four category grids on /our-products/ sit in sections carrying the CSS
   class `product-grid`. They are `grid-2` in the admin, which the rules above
   (the 5/4 photo, the badge overlay) also reach; everything here overrides
   those inside `.product-grid` rather than editing them.

   Fixed two columns: base.css's grid-2 is `auto-fit` with a 20rem floor,
   which is a minimum width and not a count - at the 1200px wrap it fits
   three. (It is not an admin setting: the Layout choices are all minimum
   widths.) Fixed columns also keep a lone card in the left half instead of
   stretching it across the row.

   A card is itself a two-column grid: title and text on the left, a fixed
   square photo (`--product-img`) on the right spanning the card's height.
   Below 1100px a card is too narrow for both, so the photo stacks above the
   text; below 800px the grid drops to one column. */
.product-grid { --product-img: 10rem; }

.product-grid .block--collection-grid .grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-m);
}

.product-grid .card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--product-img);
  /* The last row is the flexible one, so a photo taller than the text adds
     its extra height there instead of pushing the text rows apart. */
  grid-template-rows: auto auto 1fr;
  gap: var(--space-2xs) var(--space-m);
  align-items: start;
  /* Cards in a row are stretched to the tallest one; without this the extra
     height is shared out across the auto rows and the text of the shorter
     card sits lower than its neighbour's. */
  align-content: start;
  padding: var(--space-m);
}

/* No photo: one column and no template rows, so empty tracks do not leave
   their row gaps behind under a title-only card. */
.product-grid .card:not(:has(> .card-media)) {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: none;
}

.product-grid .card > :not(.card-media) { grid-column: 1; }

.product-grid .card > .card-media {
  grid-column: 2;
  grid-row: 1 / span 3;
  inline-size: var(--product-img);
  aspect-ratio: 1;
  margin: 0;
}

.product-grid .card .card-media img {
  inline-size: 100%;
  block-size: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius);
}

.product-grid .card-title {
  font-size: var(--step-1);
  font-weight: 700;
}

/* The engine emits one <p> per line of the description, and the plain-text
   stopgap below turns the newline between each pair into a blank line:
   measured at 24px between paragraphs, 0 once `white-space` is normal. Off
   here, with the one gap that is wanted put back on purpose - after the
   description, before the bullet lines, which then sit tight together. */
.product-grid .card-field--body {
  color: color-mix(in srgb, var(--color-ink) 85%, var(--color-paper));
  font-size: var(--step-0);
  white-space: normal;
}

.product-grid .card-field--body > p:first-child:not(:last-child) {
  margin-block-end: var(--space-m);
}

/* Same doubling in the section intro, where the gap between paragraphs was
   the accident rather than a margin - so it gets a real one. */
.product-grid .block--collection-grid > .block-body { white-space: normal; }

.product-grid .block--collection-grid > .block-body > p + p {
  margin-block-start: var(--space-s);
}

@media (max-width: 1100px) {
  .product-grid .card,
  .product-grid .card:not(:has(> .card-media)) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }

  .product-grid .card > .card-media {
    grid-column: 1;
    grid-row: auto;
    inline-size: min(100%, 16rem);
    margin-block-end: var(--space-xs);
  }
}

@media (max-width: 800px) {
  .product-grid .block--collection-grid .grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Products page, restyled: photo cards, and the split feature bands -----
   Section classes, set in the admin alongside `product-grid`:

   - `products-cards`: Bread and Cakes & Cupcakes, on white. Heading (and
     the intro, where there is one) as one row, then the products as
     photo-top cards, four across.
   - `products-feature`: a two-column section holding a gallery block (the
     category photo) and a collection-list, on a full-width linen band.
     The products are a plain list of names. The photo's side follows the
     section's block order unless `products-feature--photo-left` or
     `--photo-right` sets it, so the bands can alternate down the page
     whatever order their blocks were added in.
   - `products-feature--white`: add beside `products-feature` for the same
     split layout on white. Unused for now: the white sections between the
     linen bands are photo-card sections.

   Every one of these sections ends the same way: the collection grid's
   button, which the engine renders before the products, moved after them
   and turned into a text link - the daily-cards link (ink, 2px orange
   underline, an arrow; see "Every day at the Bake Shop"), added to that
   rule's selector list. The link is the only focusable thing in these
   blocks, so moving it after the products changes no tab order.

   Written after "Products page: .product-grid" above and at least at its
   specificity, so these win over it here. */

/* ---- The section-ending link ---------------------------------------------- */
/* The site-wide button, switched off through its own tokens and box:
   no fill, no padding, no pill, no hover lift. Text and underline come from
   the daily-cards link rule. */
.products-cards .block--collection-grid > .block-cta,
:is(.products-feature, .product-category) .block--collection-list > .block-cta,
.products-feature .block--list > .block-cta {
  --button-bg: transparent;
  --button-bg-hover: transparent;
  --button-border: transparent;
  --button-fg: var(--color-ink);
  order: 99;
  justify-self: start;
  align-self: start;
  inline-size: auto;
  min-block-size: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  font-size: var(--step-0);
  margin-block-start: var(--space-l);
}

.products-cards .block--collection-grid > .block-cta::after,
:is(.products-feature, .product-category) .block--collection-list > .block-cta::after,
.products-feature .block--list > .block-cta::after { content: "\00a0\2192"; }

.products-cards .block--collection-grid > .block-cta:is(:hover, :focus-visible),
:is(.products-feature, .product-category) .block--collection-list > .block-cta:is(:hover, :focus-visible),
.products-feature .block--list > .block-cta:is(:hover, :focus-visible) { transform: none; }

/* ---- Photo cards: Bread, Cakes & Cupcakes -----------------------------------
   The block is a grid: heading in the left column, intro in the right,
   top-aligned, as one row; the products span both columns below, then the
   link. */
.products-cards .block--collection-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  column-gap: var(--space-xl);
  align-items: start;
}

.products-cards .block--collection-grid > .block-heading { grid-column: 1; margin: 0; }
.products-cards .block--collection-grid > .block-body {
  grid-column: 2;
  margin-block-start: 0;
}

.products-cards .block--collection-grid > :is(.grid, .block-cta) { grid-column: 1 / -1; }

/* The note at the end of the intro, a blockquote in the copy: smaller,
   muted (ink at 75%, 5.6:1 on white), with a thin rule down its left. */
.products-cards .block-body blockquote {
  margin: var(--space-s) 0 0;
  padding-inline-start: var(--space-s);
  border-inline-start: 2px solid var(--hairline);
  font-size: var(--step--1);
  color: color-mix(in srgb, var(--color-ink) 75%, var(--color-paper));
}

/* Photo-top cards, four across. The photo bleeds to the card's edges and is
   cropped square; the card's own rounding clips its top corners. */
.products-cards .block--collection-grid .grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-block-start: var(--space-l);
}

.products-cards .card,
.products-cards .card:not(:has(> .card-media)) {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--space-m);
}

.products-cards .card > .card-media {
  inline-size: auto;
  aspect-ratio: auto;
  margin: calc(var(--space-m) * -1) calc(var(--space-m) * -1) var(--space-2xs);
}

.products-cards .card .card-media img {
  aspect-ratio: 1;
  border-radius: 0;
}

/* Hover zoom: hovering anywhere on the card eases the photo up to 1.05
   over 700ms, and back the same way. A gentle ease-out (easeOutQuad) rather
   than the hero entrance's steep one, which covered most of the distance
   in the first 100ms and read as a jump; this one starts and settles
   softly. `will-change` keeps the photo on its own layer while it scales,
   so it doesn't snap or blur as the zoom ends. `.card-media` clips it, so
   it zooms inside its frame - the card's rounded top corners and the
   frame's own bottom edge - and the card, title and text never move. These
   cards have no link, so the card lift (`.card:has(.card-cta):hover`)
   never applies to them. Only on a device that can hover, so a tap on a
   phone doesn't leave it stuck, and never under reduced motion. */
.products-cards .card > .card-media { overflow: hidden; }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .products-cards .card .card-media img {
    transition: transform 700ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
    will-change: transform;
  }

  .products-cards .card:hover .card-media img { transform: scale(1.05); }
}

.products-cards .card-field--description > p:first-child:not(:last-child) {
  margin-block-end: var(--space-xs);
}

@media (max-width: 1024px) {
  .products-cards .block--collection-grid .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  .products-cards .block--collection-grid { grid-template-columns: minmax(0, 1fr); }
  .products-cards .block--collection-grid > :is(.block-heading, .block-body) { grid-column: 1; }
  .products-cards .block--collection-grid > .block-body { margin-block-start: var(--space-s); }
  .products-cards .block--collection-grid .grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Category pages: /our-products/bread/ and the other three ------------
   Each is a page hero, then one section carrying `product-category`, set
   in the admin: the photo cards (`products-cards` with `product-grid`, a
   collection-grid) for Bread and Cakes & Cupcakes, and the engine's own
   collection-list, unstyled here, for Cookies & Squares and Pastry &
   Muffins. Neither block has a heading or intro (the hero has both), so
   the photo cards start at the block's own top padding. Either block's
   button is the way back to Our Products, in the same text-link style as
   the section-ending links above, with the arrow on the left, pointing
   back. The engine renders it before the products; the link rule's
   `order` moves it after them, which takes a grid or flex parent: the
   photo cards' block is already a grid, and the list's block is made a
   flex column for it, with no other change to the list. The arrow is
   decoration, so it is given empty alt text where the browser supports
   it. */
.product-category .block--collection-grid .grid { margin-block-start: 0; }

.product-category .block--collection-list { display: flex; flex-direction: column; }

/* The list, as a menu rather than FAQ questions: COLLECTION LIST's row
   rhythm (--space-l of padding under each row and --space-l of gap)
   halved, and the list held to about 40rem from the page edge, its
   hairlines with it. */
.product-category .stack-list {
  gap: calc(var(--space-l) / 2);
  max-inline-size: 40rem;
}

.product-category .list-row:not(:last-child) {
  padding-block-end: calc(var(--space-l) / 2);
}

/* The link after the list: --space-xl above it, clearly more than the
   2.5rem from one product to the next, so it reads as apart from the
   list. A block box rather than the button's inline-flex: as a flex
   container its arrow (::before) was a flex item of its own, so its
   underline was drawn separately from the words' and broke between
   them. In plain text flow the arrow and words share one line and one
   underline, as on the daily cards' links, whose arrow is inline text
   too. The section-ending "See all" links are still inline-flex. */
.product-category .block--collection-list > .block-cta {
  display: inline-block;
  margin-block-start: var(--space-xl);
}

.product-category :is(.block--collection-grid, .block--collection-list) > .block-cta::after { content: none; }

.product-category :is(.block--collection-grid, .block--collection-list) > .block-cta::before {
  content: "\2190\00a0";
  content: "\2190\00a0" / "";
}

/* ---- Feature bands: Pastry & Muffins, Cookies & Squares ----------------------
   The section is base.css's two-column grid, already the width of the
   content column. The band's colour runs the full width of the page from a
   border-image outset, which paints past the box without widening the page
   or adding a scrollbar. --feature-ground is the colour; the white
   modifier only changes that. */
.sw-section.products-feature {
  --feature-ground: var(--color-linen);
  --section-gap: var(--space-xl);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  border-image: conic-gradient(var(--feature-ground) 0 0) fill 0 / / 0 100vw;
}

.sw-section.products-feature.products-feature--white { --feature-ground: var(--color-paper); }

/* The category photo: one image, rounded, a steady 4:3 whatever its file. */
.products-feature .gallery-grid {
  grid-template-columns: minmax(0, 1fr);
  margin-block-start: 0;
}

.products-feature .gallery-item img {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
}

/* Which side the photo sits on, when a modifier says. The gallery holds
   nothing focusable, so moving it changes no tab order. */
.sw-section.products-feature--photo-left > .block--gallery { order: -1; }
.sw-section.products-feature--photo-right > .block--gallery { order: 1; }

/* Heading, then the list, then the link. The products are a
   collection-list (stacked, title only in its display settings); the list
   can also be a list block, typed in by hand (Wholesale's "What we
   make"), which takes the same plain list below. Each pair of selectors
   below is the product list's and the list block's, side by side. */
.products-feature :is(.block--collection-list, .block--list) {
  display: flex;
  flex-direction: column;
}

.products-feature :is(.block--collection-list, .block--list) > .block-heading { margin: 0; }

/* The products as a plain list of names: no borders, images or bullets, a
   hairline above, between and below. The lines top and bottom are what
   make a one-product list read as a list rather than a stray line. */
.products-feature .block--collection-list > .stack-list,
.products-feature .block--list > .feature-list {
  display: block;
  margin-block-start: var(--space-m);
  border-block: var(--border);
}

.products-feature .block--collection-list > .stack-list {
  padding: 0;
  list-style: none;
}

/* The `:not(:last-child)` row selector matches COLLECTION LIST's and
   base.css's own, which pad and rule every row but the last. */
.products-feature .list-row,
.products-feature .list-row:not(:last-child),
.products-feature .feature-item {
  display: block;
  padding: var(--space-s) 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.products-feature :is(.list-row + .list-row, .feature-item + .feature-item) { border-block-start: var(--border); }

.products-feature :is(.list-title, .feature-body) {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--color-ink);
}

/* COLLECTION LIST caps a list title at 40ch for the FAQ. */
.products-feature .list-title { max-inline-size: none; }

/* Phone and small tablet: one column, the photo always on top. */
@media (max-width: 800px) {
  .sw-section.products-feature { grid-template-columns: minmax(0, 1fr); }
  .sw-section.products-feature > .block--gallery { order: -1; }
}


/* ============================================================
   COLLECTION LIST
   The Q&A stack on "FAQ". One block, one row per question.

   Since 1.16.0 a listing draws whichever of the bound collection's own
   fields its binding ticks (src/Content/ListingFields.php). A column field
   keeps its old class (`.list-body` for an excerpt); a `data.*` field gets
   `.list-field .list-field--<name>`. The FAQ answer lives in `data.answer`,
   so it arrives as `.list-field--answer`, and is styled as the row's body.
   ============================================================ */

/* base.css grids every row as `auto 1fr`, reserving the first track for
   `.list-media`. FAQ entries have no image field at all, so `.list-text`
   falls into that auto track, shrinks to fit it, and the 1fr beside it is
   dead space - a question wrapping early with half the row empty next to
   it. Collapsed to one column for rows that carry no media; a list that
   does have images keeps base.css's two tracks. */
.list-row:not(:has(.list-media)) {
  grid-template-columns: minmax(0, 1fr);
}

/* base.css opens the list with `margin-block-start: var(--space-l)`, which
   is the gap under a block heading. This block has neither heading nor
   subheading, so that 40px is space away from nothing - it stacks onto the
   section's own 96px and drops the first question 136px below the header
   with nothing in between. Removed only when there is genuinely nothing
   above it to clear. */
.block--collection-list:not(:has(.block-heading)):not(:has(.block-subheading)) > .stack-list {
  margin-block-start: 0;
}

/* Question and answer want to read as one unit with air between units, so
   the row gap tightens and the gap between rows opens up. The divider is
   the only structure on the page, so it earns a little more room than
   base.css's shared list rhythm gives it. */
.stack-list { gap: var(--space-l); }

.list-row:not(:last-child) { padding-block-end: var(--space-l); }

.list-title { max-inline-size: 40ch; }

.list-row :is(.list-body, .list-field--answer) {
  margin-block-start: var(--space-xs);
  max-inline-size: var(--measure);
  color: var(--muted);
}


/* ---- FAQ accordion --------------------------------------------------------
   /faq/ is a page-hero band, then three sections with the class `faq-group`,
   each a collection-list block bound to one FAQ category, under its
   category's name as the block heading. site.js turns each question into a
   disclosure button (section 3 there) and adds `.faq-accordion` to the list
   once it has, so everything below that class is the enhanced state only:
   with no script, every answer shows as the plain list above. */

/* The column: about 50rem, centred under the sign, so the dividers run
   the width of the text. The block's padding is the wrap formula (see
   SECTION HEADERS) with 50rem in place of --wrap. */
.sw-section.faq-group > .block {
  padding-inline: max(var(--gutter), calc((100% - 50rem) / 2));
}

/* Small group headings in the action orange, in the eyebrow's tracked
   capitals. */
.faq-group .block--collection-list > .block-heading {
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-action);
  max-inline-size: none;
}

/* Questions one step down from base.css's --step-1, semi-bold, and set
   with plain wrapping: base.css balances every heading, which broke even
   short questions across two lines. No 40ch cap here, so a question runs
   the column's width before it wraps. With or without the accordion. */
.faq-group .list-title {
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.4;
  text-wrap: wrap;
  max-inline-size: none;
}

/* COLOUR BOUNDARIES zeroes the top padding of a section that follows
   another plain one (0,9,0), so the space between groups is a margin on
   the section instead of fighting that rule. */
.sw-section.faq-group + .sw-section.faq-group { margin-block-start: var(--space-xl); }

.faq-group .block--collection-list > .stack-list.faq-accordion {
  gap: 0;
  margin-block-start: var(--space-s);
  border-block-start: var(--border);
}

.faq-accordion > .list-row,
.faq-accordion > .list-row:not(:last-child) {
  padding-block: 0;
  border-block-end: var(--border);
}

.faq-accordion .list-title {
  max-inline-size: none;
  margin: 0;
}

/* The question: the whole row is the button, the chevron at the row's end.
   The h3's own size and weight carry over. The focus ring is drawn inside
   the button (negative offset) so it stays between the dividers; the
   question and answer sit --space-2xs in from the row's edges to give the
   ring room. */
.faq-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  inline-size: 100%;
  padding: var(--space-s) var(--space-2xs);
  background: none;
  border: 0;
  border-radius: var(--radius);
  color: inherit;
  font: inherit;
  text-align: start;
  text-wrap: wrap;
  cursor: pointer;
}

.faq-toggle:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}

/* Hover and focus: the question and its chevron deepen to a darker
   action orange (about 3.7:1 against paper). */
.faq-accordion { --faq-hover: color-mix(in srgb, var(--color-action) 75%, var(--color-ink)); }

.faq-question { transition: color var(--transition); }

.faq-toggle:is(:hover, :focus-visible) .faq-question { color: var(--faq-hover); }

/* The field border's mix: quiet, still 3.6:1 against paper. The deeper
   orange on hover and focus, with the question. */
.faq-chevron {
  flex: none;
  inline-size: 1rem;
  block-size: 1rem;
  background: color-mix(in srgb, var(--color-ink) 60%, var(--color-paper));
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4.25l4 4 4-4'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4.25l4 4 4-4'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform 250ms cubic-bezier(0.16, 1, 0.3, 1), background var(--transition);
}

.faq-toggle:is(:hover, :focus-visible) .faq-chevron { background: var(--faq-hover); }

.faq-accordion .is-open .faq-chevron { transform: rotate(180deg); }

/* The answer eases open: a one-row grid whose row goes from 0fr to 1fr,
   the inner box clipping it in between. Closed, it is also hidden, so its
   links are out of the tab order and away from screen readers; visibility
   switches at the end of a close and at the start of an open. The inner
   box carries a few pixels of padding, cancelled by a negative margin, so
   the clip never cuts a link's focus ring. */
.faq-panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 300ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 300ms;
}

.faq-accordion .is-open .faq-panel {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 300ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}

.faq-panel-inner {
  min-block-size: 0;
  overflow: hidden;
  padding: .375rem;
  margin: -.375rem;
}

.faq-accordion .list-field--answer { margin: 0 var(--space-2xs) var(--space-m); }

@media (prefers-reduced-motion: reduce) {
  .faq-panel,
  .faq-accordion .is-open .faq-panel,
  .faq-chevron,
  .faq-question { transition: none; }
}

@media (forced-colors: active) {
  .faq-chevron,
  .faq-toggle:is(:hover, :focus-visible) .faq-chevron {
    forced-color-adjust: none;
    background: CanvasText;
  }
}


/* ============================================================
   PLAIN-TEXT BODY FIELDS
   Collection body fields arrive as plain text with blank lines between
   paragraphs, dropped into a single div - so HTML collapses the lot into
   one run-on paragraph. `pre-line` honours the breaks the editor actually
   typed while still collapsing runs of spaces.
   -> Scott: these fields want nl2br (or markdown) in the renderer. This is
   a stopgap and should come out when that lands - at which point `pre-line`
   would start doubling the breaks in real markup.
   That has already started to happen on /our-products/, where the card
   descriptions and the section intros arrive as one <p> per line; the
   `.product-grid` rules under COLLECTION GRID switch it back off there.
   ============================================================ */

.block--collection-grid > .block-body,
.card-body,
.list-body,
.card-field--body,
.list-field--answer {
  white-space: pre-line;
}


/* ============================================================
   PROSE
   ============================================================ */

.prose :is(h2, h3) { margin-block-start: var(--space-m); }

/* ---- Panelled list, opt in with `has-panel` ----------------------------- */

.has-panel .prose ul {
  background: var(--surface);
  border: var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-m) var(--space-l);
  padding-inline-start: calc(var(--space-l) + 1.25rem);
  margin-block-start: var(--space-m);
}

.has-panel .prose li + li { margin-block-start: var(--space-xs); }

.has-panel .prose li::marker { color: var(--color-action); }


/* ============================================================
   FORMS
   The baseline is a plain form on the page's own paper ground: base.css's
   stacked grid, ink labels, and white rounded fields with a visible border;
   the submit button is a button like any other (see BUTTONS). Anything specific to one place is scoped to the class on the
   `.sw-section` that marks that place - `.newsletter` for the striped band,
   `.contact-columns` for the contact page - and never to `.block--form`,
   which every form block carries. (css_class on a section lands on the
   `.sw-section` wrapper, not on the block inside it, so those are descendant
   selectors.)
   ============================================================ */

.cms-form .field > span,
.where-to-buy .map-block__search label {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--color-ink);
}

/* One light field for every form, one shape for all of them (--field-radius);
   checkboxes and radios are left to the browser, which the radius and 24px
   padding would mangle. */
.cms-form :is(input:not([type="checkbox"], [type="radio"]), textarea, select),
.where-to-buy .map-block__search input {
  background: var(--color-paper);
  color: var(--color-ink);
  border: var(--field-border);
  border-radius: var(--field-radius);
  padding: var(--field-padding);
  min-block-size: var(--control-height);
  transition: border-color var(--transition);
}

/* Element-count above the rule before it, so it wins without depending on
   order. */
.cms-form label.field textarea {
  padding: var(--field-padding-tall);
  min-block-size: 8rem;
}

/* Selects: the browser's arrow goes, a chevron goes in its place.
   `appearance: none` also drops the native control's inner inset, which put
   the select's text a few pixels right of the inputs' above it; the left
   padding is now the inputs' own, so the two line up. The right padding
   clears the chevron - its 1.25rem inset, its .75rem width and .5rem of
   air - so a long option ("Halifax / Dartmouth / Bedford / Sackville")
   is cut off before it, never under it.

   A select takes no ::before or ::after, and a chevron in a background SVG
   would need its colour typed into the SVG, so it is drawn on the label
   that wraps the select instead (`label.field`, the form block's markup):
   an inline-SVG mask filled with --color-ink, set over the select's right
   end. Absolutely positioned, so it is not a row in the label's grid, and
   it lets clicks through to the select. The select is the label's last
   child and --control-height tall (its content is ~36px, so the floor sets
   it), which places the chevron at its vertical centre. */
/* (0,2,2), the shared field rule's weight, after it, so its padding gives way. */
.cms-form label.field select {
  appearance: none;
  padding-inline: var(--select-padding-start) var(--select-padding-end);
}

.cms-form {
  --select-padding-start: var(--space-m);
  --select-chevron-inset: 1.25rem;
  --select-chevron-size: .75rem;
  --select-padding-end: calc(var(--select-chevron-inset) + var(--select-chevron-size) + var(--space-2xs));
}

.cms-form label.field:has(> select) { position: relative; }

.cms-form label.field:has(> select)::after {
  content: "";
  position: absolute;
  inset-inline-end: var(--select-chevron-inset);
  inset-block-end: calc((var(--control-height) - var(--select-chevron-size)) / 2);
  inline-size: var(--select-chevron-size);
  block-size: var(--select-chevron-size);
  background: var(--color-ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4.25l4 4 4-4'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4.25l4 4 4-4'/%3E%3C/svg%3E") center / contain no-repeat;
  pointer-events: none;
}

/* High contrast: forced colours would repaint the chevron's fill as the
   page ground and take it out of sight, so it opts out and takes the
   theme's own text colour. */
@media (forced-colors: active) {
  .cms-form label.field:has(> select)::after {
    forced-color-adjust: none;
    background: CanvasText;
  }
}

/* The border follows the ring colour on focus, and the ring itself is the
   shared one in BUTTONS - never at the cost of it. base.css documents having
   removed an `outline: none` here for exactly that reason; the previous
   version of this file had put it back. */
.cms-form :is(input, textarea, select):focus-visible,
.where-to-buy .map-block__search input:focus-visible {
  border-color: var(--focus-ring);
}

/* The submit button is styled with every other button, in BUTTONS. */

.form-success,
.form-error {
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-lg);
  font-weight: 600;
}

.form-success {
  background: color-mix(in srgb, var(--color-accent-green) 25%, transparent);
  border-color: var(--color-accent-green);
}

/* base.css's own #ff6b6b is 2.8:1 on paper; the same ink-toward-orange mix
   the card and eyebrow text use clears 4.5:1. */
.form-error {
  background: color-mix(in srgb, var(--color-action) 15%, transparent);
  color: color-mix(in srgb, var(--color-ink) 45%, var(--color-action));
}


/* ---- Newsletter: the wide sign on stripes -----------------------------------
   The section carries the stripes (see "Striped band" under HERO) and the
   form block is the wide sign ("Wide sign"), with the ink on the sign,
   not the band. The block's margins, not its padding, take the wrap
   formula `.block` normally pads with, so the sign spans the content
   column, its left tip on the logo's left edge; the section's padding
   shows the stripes above and below it, 3rem as on the page heroes.

   Inside the sign, two columns: copy left, form right. _chrome.php renders the eyebrow,
   heading and body (and an optional cta, unused on this block today) as
   flat siblings of the form rather than inside a wrapper - there is no
   markup change available here, so the "left column" is four explicit grid
   rows rather than one box. Four rows are declared whether or not this
   block's content fills all of them: an author who never adds a chrome cta
   leaves that row's cell empty, and an empty `auto` row track collapses to
   0 - no gap left behind. The form spans every row of that explicit
   template (`1 / -1` needs one to span reliably) and centers itself
   vertically inside it, so it reads as one column beside the other
   regardless of how tall the copy ends up.

   The sign's top and bottom padding is set here at (0,6,0): COLOUR
   BOUNDARIES' restore (0,5,0) would otherwise put a full --space-block
   back on top of the sign. The sides get --space-xl of room inside the
   line (90px from the edge on desktop), well clear of the pointed ends;
   below 850px they take the sign's own step-down. */
.sw-section.newsletter { padding-block: calc(var(--space-xl) * .75); }

.sw-section.newsletter[data-sw-layout="one"] > .block.block--form:first-child {
  --sign-gap: var(--space-xl);
  margin-inline: max(var(--gutter), calc((100% - var(--wrap)) / 2));
  padding-block: var(--sign-pad-block);
}

@media (max-width: 850px) {
  .sw-section.newsletter[data-sw-layout="one"] > .block.block--form:first-child {
    --sign-gap: 1.4375rem;
  }
}

.newsletter .block--form {
  color: var(--color-paper);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: repeat(4, auto);
  column-gap: var(--space-xl);
  align-items: center;
}

.newsletter .block--form > .block-eyebrow { grid-column: 1; grid-row: 1; }
.newsletter .block--form > .block-heading { grid-column: 1; grid-row: 2; }
.newsletter .block--form > .block-body    { grid-column: 1; grid-row: 3; }
.newsletter .block--form > .block-cta     { grid-column: 1; grid-row: 4; }

/* On the sign's ink: accent-green eyebrow 7.8:1, paper heading 12.3:1,
   softened body 8.2:1, labels 7.0:1. Fields keep their white fill, and the
   focus ring is paper here (see CONTROL TOKENS), 12.3:1 on the ink. */
.newsletter .block--form .block-eyebrow { color: var(--color-accent-green); }
.newsletter .block--form .block-heading { color: var(--color-paper); }

.newsletter .block--form .block-body {
  color: color-mix(in srgb, var(--color-paper) 78%, var(--color-ink));
  max-inline-size: 46ch;
}

.newsletter .cms-form {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: center;
  /* base.css puts a margin-block-start on every .cms-form, meant to space it
     below chrome copy stacked directly above it. There is no "above" here -
     the form sits in its own column, not under one - and left alone that
     margin was both nudging it off-centre in its spanned cell and, because
     it spans into the empty fourth row, inflating that row's auto track
     past 0. */
  margin-block-start: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-s);
}

.newsletter .cms-form .field {
  flex: 1 1 12rem;
  min-inline-size: 0;
}

.newsletter .cms-form .field--consent {
  flex: 1 1 100%;
  justify-content: flex-start;
}

/* The mockup shows placeholder-only fields. The engine has no placeholder on
   a form field, so the label stays visible - two unlabelled fields would be
   guesswork. Quiet, small, and above the input. */
.newsletter .cms-form .field > span {
  color: color-mix(in srgb, var(--color-paper) 70%, var(--color-ink));
}

/* White fields on the dark ground: only the border goes (the white fill
   is its own boundary there). Radius, padding and height are the shared
   tokens. */
.newsletter .cms-form :is(input, textarea, select) { border-color: transparent; }

.newsletter .cms-form button[type="submit"] { flex: 0 0 auto; }

.newsletter .form-success,
.newsletter .form-error {
  flex: 1 1 100%;
  text-align: center;
  color: var(--color-paper);
}


/* ============================================================
   CTA BAND
   ============================================================ */

/* Full-bleed storefront photo with a white panel over it, offset right.
   Written for the homepage band, which always carries a background photo;
   a cta-band without one (the Wholesale page's) gets the fallback after
   the photo rules below. */
.block--cta-band {
  display: flex;
  align-items: center;
  min-block-size: min(75vh, 38rem);
  /* A photo band, not a text block - it takes its rhythm from min-block-size
     and the panel's own padding, not from `.block`'s vertical padding. Left
     on, that padding is also asymmetric here (base.css zeroes only the top
     when a block follows another, and this one follows a hidden embed), so
     `align-items: center` was centring the panel in a lopsided box. Two
     classes to override `.block + .block` on source order. */
}

/* The second selector beats COLOUR BOUNDARIES' blanket restore
   (`.sw-section + .sw-section > .block`, 0,3,0): without it, a cta-band
   that happens to follow a different-background block would win its top
   padding back, fighting the min-block-size this band actually uses for
   rhythm. */
.block.block--cta-band,
.sw-section + .sw-section > .block.block--cta-band {
  padding-block: 0;
}

/* base.css sizes `.block-bg` itself via inset: 0 - which, as the hero
   confirms, fills the whole section box including its padding - but it
   never sizes the <img> inside that box. Left to itself the photo renders
   at its own aspect ratio (1200:675) scaled to full width, which here came
   out ~200px taller than the section and ran down into the footer. */
.block--cta-band > .block-bg img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* No background photo: nothing to fill the photo band's height, which left
   the panel alone in ~600px of empty page. Only as tall as its panel, then,
   with the usual space below it; the space above comes from the section
   before. Outranks the zero-padding rule above (0,4,0) on source order. A
   stopgap until the band is designed. */
.sw-section > .block.block--cta-band:not(:has(> .block-bg)) {
  min-block-size: 0;
  padding-block-end: var(--space-block);
}

/* base.css already makes .cta-band-inner a rounded, padded panel; this only
   swaps its tint for solid paper, lifts it off the photo with a shadow,
   caps its width, and lets `margin-inline-start: auto` push it to the end
   of the flex row. Left-aligned inside (base's own `justify-items: start`),
   so the old centred override is dropped rather than restated. */
.cta-band-inner {
  background: var(--color-paper);
  box-shadow: 0 20px 50px color-mix(in srgb, var(--color-ink) 22%, transparent);
  max-inline-size: 26rem;
  margin-inline-start: auto;
  padding: var(--space-l) var(--space-xl);
}

.cta-band-inner .block-heading { max-inline-size: none; }

/* ---- Badge -----------------------------------------------------------------
   The `.carrots-shape--badge` outline from design/brand-shapes.css, used
   as a shape reference only: its markup can't come out of the CMS. One
   shared pattern, read from the --badge-* properties: the fill, the inset
   line and the copy colour. Worn by the homepage's "Visit the Bake Shop"
   band (`.visit-shop`, the class set in the admin on its section), paper
   with an ink line. Another panel joins by going into the :is()
   list in each rule below and setting its own --badge-* values.

   The badge never changes shape: it keeps the drawing's own 10:9 at every
   size, and both masks are `contain`, so even copy that outgrows it can't
   stretch the curves. ::before is the fill, masked by the outline;
   ::after is the line, a mask over --badge-line, so the line takes any
   colour from the tokens. The file bakes three stroke colours into its
   SVG, which would vanish on a white fill; here the SVGs carry shape only.
   The line is the outline itself stroked inside the shape, an exact
   parallel 13 units in (of the drawing's 1000), about 7px, and 1.953
   units wide, which is exactly 1px at the badge's 512px: a line just under
   a pixel wide (it was .82px) is drawn as a faint, broken run of partial
   pixels on the curves. No texture.

   The masks fill the box (100% 100%, not `contain`) and the box's height
   is rounded to a whole pixel (461px, not 460.8), so the shape is never
   resampled off the pixel grid. That stretches it by under .05%.

   The badge is one size, 32rem (512px). The copy keeps 16px clear of the
   line all round, measured line by line against the outline rather than
   as one rectangle: the copy is centred, so only the hours run the full
   width, and they sit across the badge's widest part, while the eyebrow
   and the button, nearer the narrow top and bottom, are shorter. The text
   box is 340px wide (padding 16.8% of the badge's width at the sides);
   top and bottom, the copy is centred in the badge's height and the
   padding only stops it reaching the points. Padding in % reads the
   PARENT's width, not the badge's, so the padding is worked out from
   --badge-w. Measured against the current copy ("Kempt Road, Halifax" /
   "Visit the Bake Shop" / the short hours / the button), body text at full
   size and the heading one step down: about 7px to spare beyond the 16px
   with the hours on three lines, more on two. The button, which cannot
   wrap, needs about 250px of the box, and the body text's height is what
   keeps the badge from getting smaller. Longer copy needs a bigger badge,
   or it spills over the edge.

   Below 32rem of band (a container query, so it switches exactly when the
   badge would have to shrink: about a 570px screen) the panel is a plain
   rounded rectangle in the same fill: the large radius, a soft shadow,
   and a faint 1px line 6px in whose corners are concentric with the
   panel's. Equal padding top and bottom, so the copy sits centred. That
   is also the fallback without mask support. */
.visit-shop .block--cta-band { container-type: inline-size; }

:is(.visit-shop .cta-band-inner) {
  --badge-fill: var(--color-paper);
  --badge-line: var(--color-ink);
  --badge-text: var(--color-ink);
  --badge-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 900'%3E%3Cpath d='M500 10 C478 70 440 92 390 88 C290 78 210 105 205 185 C115 200 75 260 80 335 C85 410 55 435 20 450 C55 465 85 490 80 565 C75 640 115 700 205 715 C210 795 290 822 390 812 C440 808 478 830 500 890 C522 830 560 808 610 812 C710 822 790 795 795 715 C885 700 925 640 920 565 C915 490 945 465 980 450 C945 435 915 410 920 335 C925 260 885 200 795 185 C790 105 710 78 610 88 C560 92 522 70 500 10 Z'/%3E%3C/svg%3E");
  --badge-ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 900'%3E%3Cmask id='m' maskUnits='userSpaceOnUse' x='-100' y='-100' width='1200' height='1100'%3E%3Cpath d='M500 10 C478 70 440 92 390 88 C290 78 210 105 205 185 C115 200 75 260 80 335 C85 410 55 435 20 450 C55 465 85 490 80 565 C75 640 115 700 205 715 C210 795 290 822 390 812 C440 808 478 830 500 890 C522 830 560 808 610 812 C710 822 790 795 795 715 C885 700 925 640 920 565 C915 490 945 465 980 450 C945 435 915 410 920 335 C925 260 885 200 795 185 C790 105 710 78 610 88 C560 92 522 70 500 10 Z' fill='none' stroke='white' stroke-width='29.906'/%3E%3Cpath d='M500 10 C478 70 440 92 390 88 C290 78 210 105 205 185 C115 200 75 260 80 335 C85 410 55 435 20 450 C55 465 85 490 80 565 C75 640 115 700 205 715 C210 795 290 822 390 812 C440 808 478 830 500 890 C522 830 560 808 610 812 C710 822 790 795 795 715 C885 700 925 640 920 565 C915 490 945 465 980 450 C945 435 915 410 920 335 C925 260 885 200 795 185 C790 105 710 78 610 88 C560 92 522 70 500 10 Z' fill='none' stroke='black' stroke-width='26'/%3E%3C/mask%3E%3Cpath d='M500 10 C478 70 440 92 390 88 C290 78 210 105 205 185 C115 200 75 260 80 335 C85 410 55 435 20 450 C55 465 85 490 80 565 C75 640 115 700 205 715 C210 795 290 822 390 812 C440 808 478 830 500 890 C522 830 560 808 610 812 C710 822 790 795 795 715 C885 700 925 640 920 565 C915 490 945 465 980 450 C945 435 915 410 920 335 C925 260 885 200 795 185 C790 105 710 78 610 88 C560 92 522 70 500 10 Z' mask='url(%23m)'/%3E%3C/svg%3E");
  --badge-w: min(100cqi, 32rem);
  position: relative;
  isolation: isolate;
  inline-size: var(--badge-w);
  max-inline-size: none;
  margin-inline: auto;
  padding: var(--space-l) 1.875rem;
  gap: var(--space-xs);
  justify-items: center;
  align-content: center;
  text-align: center;
  color: var(--badge-text);
  background: none;
  box-shadow: none;
  border-radius: 0;
}

/* The grid gap spaces the copy; base.css's own top margins would stack on
   it. On paper every line of copy is ink, 12.3:1: heading, hours and the
   eyebrow too, since accent green would be 1.6:1 there. The line and the
   focus ring are ink as well. The button is the site's own primary. */
:is(.visit-shop .cta-band-inner) > * { margin-block: 0; }
:is(.visit-shop .cta-band-inner) :is(.block-eyebrow, .block-heading, .block-body) {
  color: var(--badge-text);
}

/* The heading one step down, to the h3 size and line height from Branding,
   so it fits the smaller badge on one line; the plain panel matches.
   Balanced lines, so if it ever wraps it doesn't leave "Shop" on its own. */
:is(.visit-shop .cta-band-inner) .block-heading {
  font-size: var(--h3-size, var(--step-2));
  line-height: var(--h3-line, 1.2);
  text-wrap: balance;
}

:is(.visit-shop .cta-band-inner)::before,
:is(.visit-shop .cta-band-inner)::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  pointer-events: none;
}

:is(.visit-shop .cta-band-inner)::before {
  background: var(--badge-fill);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 32px -12px color-mix(in srgb, var(--color-ink) 28%, transparent);
}

/* The plain panel's line: faint (ink at 20%), so the panel reads as one
   box, and concentric with its corners, the panel's radius less the
   inset. The badge (below) draws its own line in full ink. */
:is(.visit-shop .cta-band-inner) { --panel-inset: .375rem; }

:is(.visit-shop .cta-band-inner)::after {
  inset: var(--panel-inset);
  border: 1px solid color-mix(in srgb, var(--badge-line) 20%, transparent);
  border-radius: calc(var(--radius-lg) - var(--panel-inset));
}

@container (min-width: 32rem) {
  @supports (mask-image: linear-gradient(black, black)) {
    :is(.visit-shop .cta-band-inner) {
      aspect-ratio: 10 / 9;
      block-size: round(calc(var(--badge-w) * .9), 1px);
      padding: calc(var(--badge-w) * .08) calc(var(--badge-w) * .168);
    }

    :is(.visit-shop .cta-band-inner)::before {
      border-radius: 0;
      mask: var(--badge-shape) 0 0 / 100% 100% no-repeat;
    }

    :is(.visit-shop .cta-band-inner)::after {
      inset: 0;
      border: 0;
      border-radius: 0;
      background: var(--badge-line);
      mask: var(--badge-ring) 0 0 / 100% 100% no-repeat;
    }
  }
}

/* ---- "Visit the Bake Shop": the badge over or below the photo -------------
   1024px and up: centred over the photo (the badge's own
   `margin-inline: auto`). Narrower: the photo across the top at a fixed
   height and the badge centred below it on the page, full width up to its
   32rem, where it has the room it needs.

   The band's own padding is set here at (0,6,0). COLOUR BOUNDARIES'
   top-padding restore (`.sw-section + .sw-section[data-sw-layout="one"] >
   .block:first-child`, 0,5,0) outranks the general cta-band rule's
   `padding-block: 0` (0,4,0) above, so the band was taking a full
   --space-block on top only and centring the panel off-centre. */
.sw-section.visit-shop[data-sw-layout="one"] > .block.block--cta-band:first-child {
  padding-block: var(--space-l);
}

/* Below 1024px the photo is in the flow, not behind the band: the band's
   first child, bled out through the side padding (the gutter, at these
   widths), with the panel after it. Everything is laid out top down from
   the band's own top edge, so nothing depends on the band's top padding,
   which COLOUR BOUNDARIES' zero rule (0,9,0) takes to 0 here anyway (the
   section before is a plain one). The earlier version reserved the photo's
   height as top padding and pulled the photo up into it; with that padding
   zeroed the photo went up over the sections above. `overflow: clip` keeps
   anything from reaching above the band's top edge or out at its sides.
   Below it, the usual --space-block before the footer.

   The badge (or, below the badge switch, the plain panel) comes first, a
   section's space below the section above, then the photo, with the
   badge or panel over its top 3rem. The panel is later in the source, so
   it paints over the photo; the z-index makes sure. The photo runs on
   through the band's bottom padding to the footer, as the photo band
   does on wide screens. Down to the badge switch (a 32rem container,
   about a 570px screen) the photo is a strip of fixed height; below it,
   16:10. The container query matches the badge's own, so the two change
   together. */
@media (max-width: 1023px) {
  .sw-section.visit-shop[data-sw-layout="one"] > .block.block--cta-band:first-child {
    flex-direction: column;
    align-items: stretch;
    min-block-size: 0;
    padding-block: 0 var(--space-block);
    overflow: clip;
  }

  .visit-shop .block--cta-band > .block-bg {
    position: relative;
    inset: auto;
    z-index: 0;
    order: 1;
    inline-size: auto;
    block-size: clamp(14rem, 50vw, 24rem);
    margin-inline: calc(var(--gutter) * -1);
    margin-block-end: calc(var(--space-block) * -1);
    overflow: hidden;
  }

  .visit-shop .block--cta-band > .cta-band-inner {
    z-index: 1;
    margin-block: var(--space-block) -3rem;
  }
}

@container (max-width: 31.999rem) {
  .visit-shop .block--cta-band > .block-bg {
    block-size: auto;
    aspect-ratio: 16 / 10;
  }
}


/* ============================================================
   WHERE TO BUY
   The homepage store locator, in the section with the class
   `where-to-buy`: a rich-text intro, then the engine's map block in
   "nearest" mode (templates/blocks/map.php). The engine's script shows the
   search row, then the search form and the map once Google Maps has
   loaded; with no key, or a refused one, the list stands alone. The
   buttons, field, label, focus rings and links are the site-wide ones,
   added to the selector lists in BUTTONS, FORMS and the daily-cards link
   rule rather than restyled here: Search is primary, "Use my location"
   secondary. Laid out as one card: a linen column holding the search, the
   status line and the list, beside the map. Layout only below.
   ============================================================ */

.where-to-buy {
  /* Slim scrollbar on the list. Thumb 3.5:1 on the track, past the 3:1 a
     control needs. */
  --wtb-thumb: color-mix(in srgb, var(--color-action) 70%, var(--color-ink));
  --wtb-track: color-mix(in srgb, var(--color-soft-apricote) 45%, var(--color-paper));
  /* The card's height on desktop, which is the map's; the linen column's
     padding; the map's height on phones. */
  --wtb-height: 36rem;
  --wtb-pad: var(--space-l);
  --wtb-map-phone: clamp(16rem, 60vw, 22rem);
  /* Secondary text (the status line, a store's distance) on linen.
     base.css's --muted is 3.4:1 there, under AA for small text; this is
     5.2:1. */
  --wtb-quiet: color-mix(in srgb, var(--color-ink) 75%, var(--color-paper));
}

/* ENGINE WORKAROUND: base.css gives `.map-block__find` and
   `.map-block__search` `display: flex`, which beats the browser's own
   `[hidden] { display: none }`, so a search box the script hides (no key,
   or a refused one) still shows, and does nothing. Put back for this
   section; flagged to Scott. Remove once base.css honours `hidden`. */
.where-to-buy :is(.map-block__find, .map-block__search, .map-block__map)[hidden] {
  display: none;
}

/* Intro to search row. base.css zeroes the map block's top padding (it
   follows the intro in the same section), so the intro's bottom padding is
   the whole gap: --space-block down to --space-m. */
.where-to-buy > .block--rich-text { padding-block-end: var(--space-m); }

/* ---- The card ------------------------------------------------------------
   The search row, the status line, the map and the list are all direct
   children of `.map-block` (templates/blocks/map.php), so the card is a
   grid on it, with no wrapper and no template change. Linen is the card's
   own ground; the map covers its column edge to edge and the card clips
   it to the corners (`isolation` keeps Safari clipping the map's own
   composited layers). The linen column's padding is grid tracks - the dots
   in the areas - not padding on the card, so the map can still reach the
   card's edges.

   One column first, for phones and for a card with no map (no key, or a
   refused one: the map's `auto` row is then empty and collapses): the map
   on top, then search, status and list flowing with the page.

   Every selector here carries `.block--map`: from 48rem base.css lays the
   block out side by side at (0,3,0) and places its children and caps the
   list at (0,4,0), and these have to outrank those, not just follow
   them. */
.where-to-buy .block--map .map-block--nearest {
  grid-template-columns: var(--wtb-pad) minmax(0, 1fr) var(--wtb-pad);
  grid-template-rows: auto var(--wtb-pad) auto auto auto auto var(--wtb-pad);
  grid-template-areas:
    "map map    map"
    ".   .      ."
    ".   find   ."
    ".   status ."
    ".   list   ."
    ".   more   ."
    ".   .      .";
  gap: 0;
  align-items: stretch;
  background: var(--color-linen);
  border: var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  isolation: isolate;
}

.where-to-buy .block--map .map-block--nearest > .map-block__find { grid-area: find; }

.where-to-buy .block--map .map-block--nearest > .map-block__status {
  grid-area: status;
  margin-block: var(--space-s) var(--space-xs);
  color: var(--wtb-quiet);
}

/* Square: the card's corners do the rounding now. */
.where-to-buy .block--map .map-block--nearest > .map-block__map {
  grid-area: map;
  block-size: var(--wtb-map-phone);
  min-block-size: 0;
  border-radius: 0;
}

.where-to-buy .block--map .map-block--nearest > .map-block__list {
  grid-area: list;
  max-block-size: none;
  overflow: visible;
}

/* ---- Phone: the first three stores -----------------------------------------
   Where the card stacks to one column (below 48rem, base.css's own
   breakpoint for the side-by-side layout), the list shows the first three
   stores and a "Show all stores" button after it opens the rest; it then
   reads "Show fewer". site.js builds the button and adds `.is-collapsed` to
   the list only once the button is in place, so with no script - or one
   that fails first - nothing is hidden. The cut is `nth-child`, so when the
   engine's map script re-sorts the list by distance it is the three nearest
   that show; site.js also folds the list back up when that happens. The
   button has its own row in the one-column grid (`more`), empty and so
   0 tall without it. From 48rem the list is whole and the button gone, as
   it was. */
.where-to-buy .block--map .map-block--nearest > .map-block__more {
  grid-area: more;
  justify-self: start;
  margin-block-start: var(--space-s);
}

@media (max-width: 47.99rem) {
  .where-to-buy .map-block__list.is-collapsed > .map-place:nth-child(n + 4) { display: none; }
}

@media (min-width: 48rem) {
  .where-to-buy .map-block__more { display: none; }
}

/* ---- Desktop: linen column beside the map ----------------------------------
   Only while the map is showing. The linen column is 40% - its padding
   tracks plus the content - and the map the other 60%, the full height of
   the card. The card is the map's height, and the list takes whatever the
   search row and status line leave and scrolls inside it. The list's
   padding keeps a focus ring on a link at its edge from being clipped by
   the scroll box, and its negative start margin keeps the store names in
   line with the search row above. */
@media (min-width: 48rem) {
  .where-to-buy .block--map .map-block--nearest:has(> .map-block__map:not([hidden])) {
    grid-template-columns: var(--wtb-pad) minmax(0, 1fr) var(--wtb-pad) 60%;
    grid-template-rows: var(--wtb-pad) auto auto minmax(0, 1fr) var(--wtb-pad);
    grid-template-areas:
      ".   .      .   map"
      ".   find   .   map"
      ".   status .   map"
      ".   list   .   map"
      ".   .      .   map";
    block-size: var(--wtb-height);
  }

  .where-to-buy .block--map .map-block--nearest:has(> .map-block__map:not([hidden])) > .map-block__map {
    block-size: auto;
  }

  .where-to-buy .block--map .map-block--nearest:has(> .map-block__map:not([hidden])) > .map-block__list {
    min-block-size: 0;
    overflow-y: auto;
    margin-inline-start: -.375rem;
    padding: .375rem var(--space-s) .375rem .375rem;
    scrollbar-width: thin;
    scrollbar-color: var(--wtb-thumb) var(--wtb-track);
  }

  /* Safari before scrollbar-color. */
  @supports not (scrollbar-color: auto) {
    .where-to-buy .map-block__list::-webkit-scrollbar { inline-size: 8px; width: 8px; }
    .where-to-buy .map-block__list::-webkit-scrollbar-track {
      background: var(--wtb-track);
      border-radius: 999px;
    }
    .where-to-buy .map-block__list::-webkit-scrollbar-thumb {
      background: var(--wtb-thumb);
      border-radius: 999px;
    }
  }
}

/* ---- Search row ----------------------------------------------------------
   In the linen column: "Use my location", then the labelled field with
   Search beside it. That is the template's own order, and so the tab
   order; putting the field first by CSS would send focus backwards, so
   the order is kept.

   "Use my location" is the site's secondary button, given a visible edge
   here: a white pill on linen is 1.1:1 and read as plain text. The edge is
   the field border's mix (--field-border), 3.4:1 on linen, so the button
   and the field read as one set of controls. */
.where-to-buy .map-block__find {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-s);
}

.where-to-buy :is(.map-block__locate, .map-block__more) {
  --button-border: color-mix(in srgb, var(--color-ink) 60%, var(--color-paper));
}

.where-to-buy .map-block__search {
  align-self: stretch;
  gap: var(--space-3xs) var(--space-xs);
}

/* `appearance: none` lets Safari take the field radius: it otherwise draws
   a search field its own way and ignores the radius and padding. */
.where-to-buy .map-block__search input {
  flex: 1 1 8rem;
  min-inline-size: 0;
  appearance: none;
}

/* Phone: everything full width - the location button, then the label,
   field and Search. */
@media (max-width: 30rem) {
  .where-to-buy .map-block__locate { align-self: stretch; }
  .where-to-buy .map-block__search :is(input, button) { flex-basis: 100%; }
}

/* ---- List ----------------------------------------------------------------
   Store name bold, address under it, then the links. A hairline between
   stores replaces base.css's gap. The address steps down a size for the
   narrower column. */
.where-to-buy .map-block__list { gap: 0; }

.where-to-buy .map-place { padding-block: var(--space-s); }
.where-to-buy .map-place:first-child { padding-block-start: 0; }
.where-to-buy .map-place + .map-place { border-block-start: var(--border); }

.where-to-buy .map-place__name { font-weight: 700; }
.where-to-buy .map-place__address { font-size: var(--step--1); }
.where-to-buy .map-place__distance { color: var(--wtb-quiet); }
.where-to-buy .map-place__links { margin-block-start: var(--space-3xs); }


/* ============================================================
   FOOTER
   ============================================================ */

.site-footer {
  background: var(--color-ink);
  border-block-start: 0;
  margin-block-start: 0;
}

/* The class is on the <img> itself, not a wrapper - base.css writes this as
   `.footer-logo img, img.footer-logo` for that reason. */
img.footer-logo { block-size: 3rem; }

.footer-inner {
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: var(--space-xl) var(--space-l);
  padding-block: var(--space-xl) var(--space-l);
}

.footer-blurb {
  color: color-mix(in srgb, var(--color-paper) 75%, var(--color-ink));
  max-inline-size: 32ch;
  margin-block-start: var(--space-s);
}

/* ---- Footer nav --------------------------------------------------------- */

.footer-nav-heading {
  color: var(--color-paper);
  font-weight: var(--h6-weight, 600);
  letter-spacing: .12em;
  margin-block-end: var(--space-s);
}

.footer-list { gap: var(--space-2xs); }

.footer-list .nav-item > a {
  color: color-mix(in srgb, var(--color-paper) 80%, var(--color-ink));
  text-decoration: none;
  transition: color var(--transition);
}

.footer-list .nav-item > a:hover,
.footer-list .nav-item > a:focus-visible {
  color: var(--color-paper);
  text-decoration: underline;
  text-underline-offset: .2em;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
}

.footer-list .nav-heading { color: var(--color-paper); font-weight: 700; }

/* ---- Social ------------------------------------------------------------- */
/* Rendered as text labels, not icons (no Font Awesome kit set under
   Integrations). Pills keep that from looking unfinished.

   The profile-links block's own markup - `.profile-list` / `.profile-item`
   - replaced `.footer-social` / `.footer-social-item` somewhere around
   1.12.0-1.17.0; the old classes are never emitted and this whole block was
   dead. `.profile-list` is generic (any profile-links block, any region),
   so this is scoped to `.footer-inner` rather than written bare - this
   paper-on-transparent pill is a dark-footer look and would read as broken
   white boxes if a profile-links block ever landed somewhere light, like a
   header bar. If a Font Awesome kit is ever set, base.css's
   `.profile-list--icons` takes over and sizes each link into a circle;
   these rules aren't scoped around that case. */

.footer-inner .profile-list {
  gap: var(--space-2xs);
  align-content: start;
}

.footer-inner .profile-item > a {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  color: var(--color-paper);
  padding: var(--space-3xs) var(--space-s);
  border: 1px solid color-mix(in srgb, var(--color-paper) 35%, transparent);
  border-radius: var(--control-radius);
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.footer-inner .profile-item > a:hover,
.footer-inner .profile-item > a:focus-visible {
  background: var(--color-paper);
  border-color: var(--color-paper);
  color: var(--color-ink);
}

/* ---- Legal bar ---------------------------------------------------------- */

.footer-legal {
  border-block-start: 1px solid color-mix(in srgb, var(--color-paper) 18%, transparent);
  color: color-mix(in srgb, var(--color-paper) 65%, var(--color-ink));
  padding-block: var(--space-m);
}

/* ---- Block-built footer ------------------------------------------------- */
/* The Footer region (Header & Footer > Footer) replaces the classic footer
   above while it has blocks; the classic rules stay for when it is emptied.
   One `cards` section with the class `footer-columns`, four one-column
   sections in it: Brand (logo, blurb, profile links), Explore and Products
   (menu blocks; the second is the "Footer products" menu), Visit (a
   rich-text block, typed, since location blocks only render on a location
   page or in multi-location mode). The lower footer is outside the region
   and keeps its own styling. */

.site-footer .footer-blocks {
  border-block-start: 0;
  color: color-mix(in srgb, var(--color-paper) 80%, var(--color-ink));
}

/* Outranks base.css's region rhythm (0,3,0) and its cards grid. */
.site-footer .footer-blocks > .sw-section.footer-columns {
  grid-template-columns: 1.3fr 1fr 1fr 1.3fr;
  gap: var(--space-xl) var(--space-l);
  padding-block: var(--space-xl) var(--space-l);
}

/* The section-boundary rule in SPACING gives the first block of every
   section after the first a --space-block top pad (0,5,0); in a column that
   only pushes Explore, Products and Visit down. Same weight, later. */
.site-footer .footer-blocks .footer-columns > .sw-section > .block {
  padding-block: 0;
}

/* The Brand column's three blocks, closer than the section gap. */
.footer-columns > .sw-section > .block + .block { margin-block-start: var(--space-s); }

.footer-columns .logo-block img { block-size: 3rem; }

.footer-columns .block--rich-text .block-body { max-inline-size: 32ch; }

.footer-columns .prose p { margin: 0; }
.footer-columns .prose p + p { margin-block-start: var(--space-2xs); }

/* Column headings: the menu blocks' <p> and the Visit block's <h2>, made
   to match. */
.footer-columns :is(.menu-block-heading, .block-heading) {
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-accent-green);
  max-inline-size: none;
  margin: 0 0 var(--space-s);
}

.footer-columns .menu-block--stacked > .menu-list { gap: var(--space-2xs); }

.footer-columns :is(.menu-block a, .prose a) {
  color: color-mix(in srgb, var(--color-paper) 80%, var(--color-ink));
  text-decoration: none;
  transition: color var(--transition);
}

.footer-columns :is(.menu-block a, .prose a):is(:hover, :focus-visible) {
  color: var(--color-paper);
  text-decoration: underline;
  text-underline-offset: .2em;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
}

/* The phone number, the one link in the Visit column. */
.footer-columns .prose a[href^="tel:"] { color: var(--color-paper); font-weight: 600; }

/* ---- Footer profile icons ---------------------------------------------- */
/* No Font Awesome kit is set, so the profile-links block renders each
   network's name. For the networks with an icon here, the name stays in the
   link, visually hidden, as its accessible name, and the icon is a mask
   filled with currentColor. A network added later without an icon here
   keeps its visible name rather than becoming an empty circle. */

.footer-columns .profile-list { gap: var(--space-xs); }

.footer-columns :is(.profile-item--instagram, .profile-item--facebook) > a {
  inline-size: 2.5rem;
  block-size: 2.5rem;
  justify-content: center;
  color: var(--color-paper);
  border: 1px solid color-mix(in srgb, var(--color-paper) 35%, transparent);
  border-radius: 50%;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.footer-columns :is(.profile-item--instagram, .profile-item--facebook) > a::before {
  content: "";
  inline-size: 1.125rem;
  block-size: 1.125rem;
  background: currentColor;
  -webkit-mask: var(--profile-icon) center / contain no-repeat;
  mask: var(--profile-icon) center / contain no-repeat;
}

.footer-columns :is(.profile-item--instagram, .profile-item--facebook) .profile-name {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.footer-columns :is(.profile-item--instagram, .profile-item--facebook) > a:is(:hover, :focus-visible) {
  background: var(--color-paper);
  border-color: var(--color-paper);
  color: var(--color-ink);
}

/* Masks only show their alpha, so the fill colour in the SVG is irrelevant. */
.footer-columns .profile-item--instagram {
  --profile-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Crect x='3' y='3' width='18' height='18' rx='5'/%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3Ccircle cx='17.5' cy='6.5' r='1.25' fill='black' stroke='none'/%3E%3C/svg%3E");
}

.footer-columns .profile-item--facebook {
  --profile-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.5 21v-8.2h2.8l.4-3.2h-3.2V7.6c0-.9.3-1.6 1.6-1.6h1.7V3.1c-.3 0-1.3-.1-2.5-.1-2.5 0-4.2 1.5-4.2 4.3v2.3H7.3v3.2h2.8V21z'/%3E%3C/svg%3E");
}

/* Forced colours drops backgrounds, which would take the icon with it. */
@media (forced-colors: active) {
  .footer-columns :is(.profile-item--instagram, .profile-item--facebook) > a::before {
    forced-color-adjust: none;
    background: CanvasText;
  }
}

/* Two columns, then one. */
@media (max-width: 62rem) {
  .site-footer .footer-blocks > .sw-section.footer-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 34rem) {
  .site-footer .footer-blocks > .sw-section.footer-columns {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-l);
  }
}

/* ---- Anchored sections -------------------------------------------------- */
/* A section with an id (the footer's links to /our-products/#cakes-cupcakes
   and the rest) lands below the sticky header, not under it: the logo, the
   header's padding, and a little air. */
.sw-section[id] { scroll-margin-block-start: calc(2.75rem + var(--space-s) * 3); }


/* ============================================================
   CONTACT PAGE
   /contact/ is a one-column title band, then a two-column section carrying
   the CSS class `contact-columns` - a `list` block on the left (heading,
   intro, a "Get directions" CTA, then Address / hours / Phone as items) and
   the contact form block on the right - then the map in a section of its own.
   Layout only: its buttons and fields are the site-wide ones in BUTTONS and
   FORMS, with nothing overridden here.
   ============================================================ */

.contact-columns { --section-gap: var(--space-xl); }

/* ---- Left column: the list block as contact details ---------------------
   A flex column so the chrome CTA - which _chrome.php renders BEFORE the
   list - can sit after it. base.css gives every direct child of a block in a
   column `inline-size: 100%`, which turns the button into a full-width bar. */
.contact-columns .block--list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.contact-columns .block--list > .feature-list { order: 1; inline-size: 100%; }

.contact-columns .block--list > .block-cta {
  order: 2;
  inline-size: auto;
  margin-block-start: var(--space-m);
}

/* base.css lays each item out as `auto 1fr` (icon/image, then text), which
   puts the heading and the text side by side when there is no icon, and
   draws a divider under every item but the last. */
.contact-columns .feature-list { gap: var(--space-m); margin-block-start: var(--space-m); }

.contact-columns .feature-item {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3xs);
  padding-block-end: 0;
  border-block-end: 0;
}

.contact-columns .feature-heading { font-size: var(--step-0); font-weight: 700; }

.contact-columns .feature-cta {
  margin-block-start: 0;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
  text-underline-offset: .2em;
}

/* Opening hours as a two-column table without a table: each line is a pair
   of class-only spans (the list description allows those), the first a fixed
   width so the times line up and right-align. 9.5rem + 6.5rem is 16rem,
   which still fits the narrowest the column gets (a 320px phone). */
.hours-day,
.hours-time { display: inline-block; }

.hours-day { min-inline-size: 9.5rem; }

.hours-time { min-inline-size: 6.5rem; text-align: end; }

/* ---- Map ----------------------------------------------------------------
   base.css: `.block iframe { aspect-ratio: 16 / 9 }`, which is 675px tall at
   full content width. A banner-shaped map instead - scoped to Google Maps
   rather than every embed, so a video embed keeps its 16:9. */
.block--embed iframe[src*="google.com/maps"] {
  aspect-ratio: auto;
  block-size: clamp(16rem, 22vw, 24rem);
  border-radius: var(--radius-lg);
}


/* ============================================================
   STORY PAGE
   /our-story/ is a title band, then sections each marked by a CSS class on
   the section itself:

   - `story-split`: an image beside eyebrow, heading and copy, on white.
     Used twice. Which side the image sits on is the hero block's own
     Direction field (Right puts it on the left); nothing here decides it.
   - `values-cards`: a heading, then a list block as three apricote cards.
   - `community`: a full-width apricote band, two columns.
   ============================================================ */

/* ---- story-split ---------------------------------------------------------
   Both blocks are `hero`s with an image picked on the block, which is the
   carrier behind the homepage's dark "Carrot cake" band - so they rendered
   as dark bands too (graphite ground, green eyebrow, paper text) until put
   back here. Every override is prefixed `.sw-section.story-split`, which
   puts it above the carrier's own rules without touching them, and keeps
   the carrot cake section (which has no such class) exactly as it was. */
.sw-section.story-split { --section-gap: var(--space-xl); }

.sw-section.story-split .block--hero { background: var(--color-paper); }

.sw-section.story-split .block--hero .block-eyebrow { color: var(--color-ink); }

.sw-section.story-split .block--hero .block-heading {
  color: var(--color-ink);
  font-size: var(--step-3);
}

.sw-section.story-split .block--hero .block-body { color: var(--color-ink); }

/* The hero's body is not `.prose`, so its paragraphs have no gap between
   them. */
.sw-section.story-split .block-body > p + p { margin-block-start: var(--space-s); }

.sw-section.story-split .hero-media img { aspect-ratio: 4 / 3; }

.sw-section.story-split .hero-actions { margin-block-start: var(--space-m); }

/* A CTA here is a text link, whatever level it was saved at: the button
   colours go transparent and the label ink through the same custom
   properties BUTTONS reads (the label has to be set: a primary's is white),
   so hover, focus and the ring stay the shared ones. Same look as
   `.cta--tertiary`. */
.sw-section.story-split .hero-actions > .block-cta {
  --button-bg: transparent;
  --button-bg-hover: transparent;
  --button-border: transparent;
  --button-fg: var(--color-ink);
  min-block-size: 0;
  padding: var(--space-2xs) 0;
  text-decoration: underline;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: 2px;
  text-underline-offset: .2em;
}

/* One-column sections put a block's own padding either side of it; a
   two-column section carries its own. Back to back they double up. */
.sw-section.values-cards + .sw-section.story-split { padding-block-start: 0; }

@media (max-width: 800px) {
  /* Stacked, photo first. Past this the two columns are under ~280px each. */
  .sw-section.story-split .block--hero {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-l);
  }

  .sw-section.story-split .block--hero > .hero-media,
  .sw-section.story-split .block--hero.is-reverse > .hero-media { order: -1; }
}

/* ---- values-cards --------------------------------------------------------
   The list block's items as equal cards in a row. */
.values-cards .block--rich-text { padding-block-end: 0; }

.values-cards .feature-list {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-m);
  margin-block-start: var(--space-l);
}

/* base.css lays an item out as `auto 1fr` (icon, then text) and rules a line
   under every item but the last. */
.values-cards .feature-item {
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: var(--space-xs);
  padding: var(--space-m);
  background: var(--color-soft-apricote);
  border: 0;
  border-radius: var(--radius);
}

.values-cards .feature-heading { font-size: var(--step-1); font-weight: 700; }

/* --muted is 2.7:1 on apricote; ink is 10:1. */
.values-cards .feature-body { color: var(--color-ink); max-inline-size: none; }

/* Four cards (Wholesale's "Why 24 Carrots") go four across, and two by two
   on a tablet, where four would be under 200px each. */
.values-cards .feature-list:has(> .feature-item:nth-child(4)) {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 1100px) {
  .values-cards .feature-list:has(> .feature-item:nth-child(4)) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .values-cards .feature-list,
  .values-cards .feature-list:has(> .feature-item:nth-child(4)) { grid-template-columns: minmax(0, 1fr); }
}

/* ---- community -----------------------------------------------------------
   A full-width band, but on a TWO-column section, which base.css caps at
   `--wrap` and centres. Made full width with the wrap put back as padding -
   the technique the header uses - so it never needs a 100vw bleed. */
.sw-section.community[data-sw-layout] {
  --section-gap: var(--space-xl);
  inline-size: 100%;
  margin-inline: 0;
  padding-inline: max(var(--gutter), calc((100% - var(--wrap)) / 2));
  background: var(--color-soft-apricote);
}

/* The right-hand block is the supporting list: a smaller heading. */
.community > .block + .block .block-heading { font-size: var(--step-1); }

/* rich-text has no eyebrow field, but its heading takes class-only spans
   and newlines: `<span class="kicker">Community support</span>` then a new
   line, then the heading text, gives the small label above the heading. The
   <br> the newline becomes would add a blank line under a block span. */
.community .block-heading .kicker {
  display: block;
  margin-block-end: var(--space-2xs);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--color-ink);
}

.community .block-heading .kicker + br { display: none; }

.community .prose li + li { margin-block-start: var(--space-2xs); }


/* ============================================================
   WHOLESALE PAGE
   /wholesale/ reuses other pages' patterns, by class in the admin:
   `values-cards` (Our Story) for "Why 24 Carrots", `products-feature
   products-feature--photo-left` (Our Products) for "What we make", and
   `contact-columns` (Contact) for the coordinator beside the form. Only
   what those don't cover is here:

   - `customer-tags`: a full-width linen band, the block centred (its own
     Align), the customers as a list in the copy, shown as pill tags.
   - `info-cards`: a two-column section whose blocks are plain cards (the
     card ground and hairline are in CARDS). Bold in the copy is the
     facts to find at a glance.
   - `contact-actions`: on the coordinator's list block. Its phone and
     email are the main actions, so larger and bolder than Contact's.
   ============================================================ */

/* ---- customer-tags ---------------------------------------------------- */
.sw-section.customer-tags { background: var(--color-linen); }

.customer-tags .prose ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2xs);
  margin-block-start: var(--space-m);
  padding: 0;
  list-style: none;
}

/* Paper on linen, a hairline round each: the shape of the site's pill
   buttons, but nothing to click, so no fill colour or hover. */
.customer-tags .prose li {
  margin: 0;
  padding: var(--space-3xs) var(--space-s);
  background: var(--color-paper);
  border: var(--border);
  border-radius: var(--control-radius);
  white-space: nowrap;
}

/* ---- info-cards --------------------------------------------------------- */
.sw-section.info-cards > .block {
  padding: var(--space-l);
  border-radius: var(--radius-lg);
}

/* Both cards the same height when side by side. */
.sw-section.info-cards { align-items: stretch; }

.info-cards .block-heading { font-size: var(--step-2); }

.info-cards .prose strong { font-weight: 700; color: var(--color-ink); }

/* ---- contact-actions ---------------------------------------------------- */
.contact-columns .contact-actions .feature-cta {
  font-size: var(--step-1);
  font-weight: 700;
  /* The email is one long word: on a phone it breaks rather than running
     past the column. */
  overflow-wrap: anywhere;
}

/* Phones: the cards' padding steps down so the copy keeps its width, and
   the email back to body size (still bold), where at the larger size it
   would no longer fit a 320px screen on one line. */
@media (max-width: 30rem) {
  .sw-section.info-cards > .block { padding: var(--space-m); }

  .contact-columns .contact-actions .feature-cta[href^="mailto:"] { font-size: var(--step-0); }
}


/* ============================================================
   SMALL SCREENS
   ============================================================ */

@media (max-width: 800px) {
  .site-header {
    padding-block: var(--space-xs);
    gap: var(--space-s);
    row-gap: var(--space-xs);
  }

  .site-logo img { block-size: 2.25rem; }

  .site-nav .nav-list {
    gap: var(--space-s) var(--space-m);
    justify-content: flex-start;
  }

  .site-nav .nav-list--sub {
    box-shadow: none;
    border-radius: 0;
  }

  /* ---- Interim mobile nav toggle -------------------------------------
     site.js injects a `.nav-toggle` button and adds `.js` to <html>.
     Everything below is gated on `.js`, so with the script absent - or
     failed - the nav keeps the fallback above: a full-width row under
     the logo, always visible. A header/footer rework will replace this
     whole block. */

  /* The menu drops from the bottom of the header as an overlay panel -
     bled to the viewport edges like the header's own band, the link list
     held to the content width inside it - so it covers the page rather
     than pushing it down. `.site-header` is `position: sticky`, which is
     a positioned value, so this anchors to it. */
  .js .site-nav {
    position: absolute;
    inset-block-start: 100%;
    /* The header is full width now, so its padding box already spans the
       viewport - `inset-inline: 0` reaches both edges with no vw, and no
       scrollbar overshoot. */
    inset-inline: 0;
    inline-size: auto;
    z-index: 20;                       /* over the page; sits inside the header's own z:50 context */
    background: var(--color-paper);
    border-block-end: var(--border);
    box-shadow: 0 16px 24px -8px color-mix(in srgb, var(--color-ink) 20%, transparent);
    padding-block: var(--space-s) var(--space-m);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-0.5rem);
    transition: opacity var(--transition), transform var(--transition),
                visibility var(--transition);
  }

  .js .site-header.nav-open .site-nav {
    opacity: 1;
    visibility: visible;
    transform: none;
  }

  .js .site-nav .nav-list {
    inline-size: min(100% - (var(--gutter) * 2), var(--wrap));
    margin-inline: auto;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .js .site-nav .nav-item > a {
    display: block;
    padding-block: var(--space-xs);
  }

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    margin-inline-start: auto;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-ink);
    cursor: pointer;
  }

  .nav-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

  /* Three bars from one element plus its two pseudo-elements. */
  .nav-toggle-bar,
  .nav-toggle-bar::before,
  .nav-toggle-bar::after {
    content: "";
    display: block;
    inline-size: 1.4rem;
    block-size: 2px;
    background: currentColor;
    transition: transform var(--transition), background var(--transition);
  }

  .nav-toggle-bar { position: relative; }
  .nav-toggle-bar::before { position: absolute; inset-inline: 0; inset-block-start: -.42rem; }
  .nav-toggle-bar::after  { position: absolute; inset-inline: 0; inset-block-start: .42rem; }

  /* Open: the middle bar clears, the outer two cross into an X. */
  .nav-open .nav-toggle-bar { background: transparent; }
  .nav-open .nav-toggle-bar::before { transform: translateY(.42rem) rotate(45deg); }
  .nav-open .nav-toggle-bar::after  { transform: translateY(-.42rem) rotate(-45deg); }

  /* Both hero carriers get their own mobile handling next to their own
     rules, above under HERO - there is nothing generic left to say here. */

  /* The two-column form band collapses to one, and the explicit row/column
     placement resets to auto so copy and form fall back to plain source
     order: eyebrow, heading, body, form - the sensible stacked reading
     order, and the same as every other block on the page. */
  .newsletter .block--form {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }

  .newsletter .block--form > :is(.block-eyebrow, .block-heading, .block-body, .block-cta),
  .newsletter .cms-form {
    grid-column: 1;
    grid-row: auto;
  }

  .newsletter .cms-form .field,
  .newsletter .cms-form button[type="submit"] { flex: 1 1 100%; }
}
