@charset "utf-8";

/* ==========================================================================
   MIRACLE FRUIT STORE

   The visual system is grounded in one reference: a cream-canvas fruit-brand
   landing page built on heavy condensed capitals in deep forest green, food
   photography held in circles, colour chips overlapping those circles, and a
   single terracotta band with an organic top edge.

   Two things carry the personality here. First, all the photography is shot
   on black, so every image sits inside a circle on cream — the darkness reads
   as deliberate rather than as a mistake, and the circle becomes the motif
   that ties hero, tasting rail and product together. Second, the type is
   condensed grotesque rather than the high-contrast serif a cream palette
   usually attracts.

   Everything else stays quiet: no card grids, no drop shadows, one accent
   band, and body copy set at 19px because the people buying this are often
   over sixty and readability outranks fashion.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts --- */

@font-face {
    font-family: "Anton";
    src: url("../fonts/anton-400.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Fraunces";
    src: url("../fonts/fraunces-var.woff2") format("woff2-variations");
    font-weight: 300 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Instrument Sans";
    src: url("../fonts/instrument-sans-var.woff2") format("woff2-variations");
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

/* --------------------------------------------------------------- tokens --- */

:root {
    /* Sampled from the reference rather than guessed. */
    --paper:        #fff9ef;
    --paper-warm:   #f6ead4;
    --paper-edge:   #ece0c8;

    --forest:       #142b17;
    --forest-mid:   #3f5740;
    --forest-line:  #c9d3c2;

    /* The reference's terracotta is #cc5f38, which gives only 3.5:1 against
       light body text — under AA, and this site is read by people who need
       contrast more than most. Deepened until white body copy clears 4.5:1
       while the hue stays recognisably the same burnt orange. */
    --terracotta:   #b04a28;
    --berry:        #a81628;

    /* Clay and rose are lifted a shade from the reference's #de6b3e / #c66d67
       so dark text clears AA on them comfortably rather than marginally. */
    --chip-mustard: #e3af3f;
    --chip-sage:    #92a47e;
    --chip-clay:    #e47c4c;
    --chip-plum:    #673c43;
    --chip-rose:    #d98a84;

    /* Body copy starts at 19px. Large type is fluid but never below the
       comfortable minimum. */
    --text-xs:   0.8125rem;
    --text-sm:   0.9375rem;
    --text-base: 1.1875rem;
    --text-lg:   1.375rem;
    --text-xl:   clamp(1.5rem, 1.2rem + 1.4vw, 2rem);
    --display-s: clamp(2rem, 1.4rem + 3vw, 3.25rem);
    --display-m: clamp(2.75rem, 1.6rem + 5.4vw, 5rem);
    --display-l: clamp(3.25rem, 1.5rem + 8vw, 7rem);

    --measure: 62ch;

    --gap:      clamp(1.25rem, 3vw, 2.5rem);
    --section:  clamp(4.5rem, 9vw, 8.5rem);
    --shell:    min(100% - 2.5rem, 78rem);

    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---------------------------------------------------------------- reset --- */

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

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

body {
    margin: 0;
    background: var(--paper);
    color: var(--forest);
    font-family: "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: var(--text-base);
    line-height: 1.62;
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;
}

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

h1, h2, h3, h4 { margin: 0; font-weight: 400; }

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

a { color: inherit; }

button, input, select, textarea { font: inherit; color: inherit; }

/* One focus treatment everywhere, and it is impossible to miss. */
:focus-visible {
    outline: 3px solid var(--berry);
    outline-offset: 3px;
    border-radius: 2px;
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--chip-mustard); color: var(--forest); }

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.skip-link {
    position: absolute;
    left: 1rem;
    top: -4rem;
    z-index: 100;
    padding: 0.85rem 1.4rem;
    background: var(--forest);
    color: var(--paper);
    text-decoration: none;
    transition: top 0.18s var(--ease);
}
.skip-link:focus { top: 1rem; }

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

.display {
    font-family: "Anton", "Arial Narrow", sans-serif;
    font-weight: 400;
    text-transform: uppercase;
    line-height: 0.9;
    letter-spacing: 0.004em;
    color: var(--forest);
}

.display--hero { font-size: var(--display-l); }
.display--l    { font-size: var(--display-m); }
.display--m    { font-size: var(--display-s); }
.display--s    { font-size: var(--text-xl); line-height: 1.02; }

/* Editorial prose. Fraunces gets a touch more leading than the sans. */
.prose {
    font-family: "Fraunces", Georgia, "Times New Roman", serif;
    font-optical-sizing: auto;
    font-size: var(--text-lg);
    line-height: 1.68;
    color: var(--forest-mid);
}
.prose--tight { font-size: var(--text-base); }

.lede {
    font-family: "Fraunces", Georgia, serif;
    font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.6rem);
    line-height: 1.55;
    color: var(--forest-mid);
    max-width: 34ch;
}

.muted { color: var(--forest-mid); }

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

.shell {
    width: var(--shell);
    margin-inline: auto;
}

.section { padding-block: var(--section); }
.section--warm { background: var(--paper-warm); }
.section--tight { padding-block: calc(var(--section) * 0.6); }

/* A centred heading flanked by short rules, straight from the reference. */
.rule-heading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.15rem;
    margin-bottom: 0.6rem;
    text-align: center;
}
.rule-heading::before,
.rule-heading::after {
    content: "";
    width: clamp(1.5rem, 5vw, 3.25rem);
    height: 2px;
    background: var(--forest);
    flex: none;
}
.section-intro {
    text-align: center;
    max-width: 46ch;
    margin: 0 auto clamp(2.5rem, 5vw, 4rem);
    color: var(--forest-mid);
}

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

/* The reference's CTA is a pill, so the pill stays — but only for real
   actions, never for navigation or decoration. Tap targets clear 48px. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 3.25rem;
    padding: 0.75rem 2rem;
    border: 2px solid var(--forest);
    border-radius: 999px;
    background: var(--forest);
    color: var(--paper);
    font-size: var(--text-base);
    font-weight: 600;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.btn:hover { background: var(--berry); border-color: var(--berry); }

.btn--ghost { background: transparent; color: var(--forest); }
.btn--ghost:hover { background: var(--forest); color: var(--paper); border-color: var(--forest); }

.btn--berry { background: var(--berry); border-color: var(--berry); }
.btn--berry:hover { background: var(--forest); border-color: var(--forest); }

.btn--light { background: var(--paper); border-color: var(--paper); color: var(--forest); }
.btn--light:hover { background: transparent; color: var(--paper); border-color: var(--paper); }

.btn--wide { width: 100%; }

.btn[disabled], .btn[aria-disabled="true"] {
    opacity: 0.45;
    pointer-events: none;
}

/* A quieter action for destructive or secondary things. */
.link-btn {
    background: none;
    border: 0;
    padding: 0.5rem 0;
    min-height: 2.75rem;
    color: var(--forest-mid);
    text-decoration: underline;
    text-underline-offset: 4px;
    cursor: pointer;
}
.link-btn:hover { color: var(--berry); }

/* ---------------------------------------------------------------- chips --- */

/* The label that overlaps the bottom of a circle.

   The reference sets every chip label in white. Four of its five chip colours
   are too light for that to clear 4.5:1, so the light chips take the forest
   text instead and only the genuinely dark ones stay white. The palette is
   unchanged; only the ink on it is. */
.chip {
    display: inline-block;
    padding: 0.5rem 1.35rem;
    border-radius: 999px;
    background: var(--chip-sage);
    color: var(--forest);
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Light chips — dark ink. */
.chip--mustard { background: var(--chip-mustard); color: var(--forest); }
.chip--sage    { background: var(--chip-sage);    color: var(--forest); }
.chip--clay    { background: var(--chip-clay);    color: var(--forest); }
.chip--rose    { background: var(--chip-rose);    color: var(--forest); }

/* Dark chips — light ink. */
.chip--plum    { background: var(--chip-plum);    color: var(--paper); }
.chip--forest  { background: var(--forest);       color: var(--paper); }

/* -------------------------------------------------------------- circles --- */

/* The recurring device: dark photography contained by a cream-set circle. */
.circle {
    position: relative;
    aspect-ratio: 1;
    border-radius: 50%;
    overflow: hidden;
    background: var(--forest);
}
.circle > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ------------------------------------------------------------- botanical -- */

/* Leaf sprigs bleeding in from the corners, as in the reference. Purely
   decorative, so hidden from assistive tech and dropped on small screens
   where they would crowd the type. */
.sprig {
    position: absolute;
    pointer-events: none;
    color: var(--forest);
    opacity: 0.9;
}
.sprig--tl { top: -1.5rem; left: -3.5rem; width: clamp(7rem, 14vw, 12rem); transform: rotate(-8deg); }
.sprig--br { bottom: -2rem; right: -3rem; width: clamp(6rem, 11vw, 9.5rem); transform: rotate(168deg); }

@media (max-width: 60rem) {
    .sprig { display: none; }
}

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

.masthead {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--paper);
    border-bottom: 1px solid var(--paper-edge);
}

.masthead__bar {
    display: flex;
    align-items: center;
    gap: var(--gap);
    min-height: 4.75rem;
}

.wordmark {
    font-family: "Anton", sans-serif;
    font-size: clamp(1.3rem, 1.1rem + 0.6vw, 1.75rem);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    text-decoration: none;
    color: var(--forest);
    line-height: 1;
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding-block: 0.7rem;
}
.wordmark__dot {
    width: 0.55em;
    height: 0.55em;
    border-radius: 50%;
    background: var(--berry);
    flex: none;
}

.nav {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2.4vw, 2.25rem);
}

.nav__link {
    position: relative;
    padding: 0.9rem 0;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--forest);
}
.nav__link::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0.45rem;
    height: 2px;
    background: var(--forest);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.22s var(--ease);
}
.nav__link:hover::after { transform: scaleX(1); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); background: var(--berry); }

.nav__cart {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    padding: 0.5rem 1.1rem;
    border: 2px solid var(--forest);
    border-radius: 999px;
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.nav__cart:hover { background: var(--forest); color: var(--paper); }
.nav__count {
    display: inline-grid;
    place-items: center;
    min-width: 1.6rem;
    height: 1.6rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--berry);
    color: #fff;
    font-size: var(--text-xs);
}

.nav-toggle {
    display: none;
    margin-left: auto;
    align-items: center;
    gap: 0.6rem;
    min-height: 2.75rem;
    padding: 0.5rem 1rem;
    background: none;
    border: 2px solid var(--forest);
    border-radius: 999px;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    cursor: pointer;
}

@media (max-width: 56rem) {
    .nav-toggle { display: inline-flex; }
    .nav {
        display: none;
        position: absolute;
        left: 0; right: 0; top: 100%;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        padding: 0.5rem 1.25rem 1.5rem;
        background: var(--paper);
        border-bottom: 1px solid var(--paper-edge);
    }
    .nav[data-open="true"] { display: flex; }
    .nav__link {
        padding: 1.1rem 0;
        font-size: var(--text-base);
        border-bottom: 1px solid var(--paper-edge);
    }
    .nav__link::after { display: none; }
    .nav__link[aria-current="page"] { color: var(--berry); }
    .nav__cart { margin-top: 1.25rem; justify-content: center; }
}

/* --------------------------------------------------------------- flashes --- */

.flashes { margin-top: 1.5rem; }
.flash {
    display: flex;
    gap: 0.85rem;
    padding: 1rem 1.35rem;
    margin-bottom: 0.75rem;
    border-left: 4px solid var(--chip-sage);
    background: var(--paper-warm);
    font-size: var(--text-base);
}
.flash--error   { border-left-color: var(--berry); }
.flash--notice  { border-left-color: var(--chip-mustard); }
.flash--success { border-left-color: var(--chip-sage); }

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

/* The hero is one full-bleed photograph with the copy laid over the empty
   half of it. Everything in this section is set in cream rather than forest
   green: the table in the picture is dark walnut in low light, and dark green
   type on it is barely type at all. */
.hero {
    position: relative;
    isolation: isolate;
    display: grid;
    align-items: center;
    min-height: clamp(30rem, 66vh, 44rem);
    padding-block: clamp(3rem, 7vw, 6rem);
    color: var(--paper);
    overflow: hidden;
}

.hero__media {
    position: absolute;
    inset: 0;
    z-index: 0;
}
/* <picture> is an inline box by default, which leaves the image's percentage
   height with nothing to resolve against. */
.hero__media picture {
    display: block;
    height: 100%;
}
.hero__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Hold the bowl to the right as the frame narrows, so the crop eats the
       empty wood rather than the fruit. */
    object-position: 62% 50%;
}

/* Heavy on the left where the words are, clear on the right where the bowl
   is. The tint is the site's forest rather than neutral black, so the
   photograph warms into the palette instead of greying out.

   Lighter than it looks like it should be, because the photograph is dark
   already — the wood behind the headline sits around a quarter brightness on
   its own, where the previous banner's sunlit oak sat at three fifths. Piling
   the old opacities on top of this one only flattened the blurred branch and
   leaves on the left into a single murky field. The angle is a degree or two
   off the horizontal rather than seven, so the falloff is even top to bottom:
   the notes line crosses the bright burlap near the foot of the frame, and a
   gradient that thinned out downwards left exactly that line short. */
.hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(93deg,
        rgba(9, 20, 11, 0.70) 0%,
        rgba(9, 20, 11, 0.63) 28%,
        rgba(9, 20, 11, 0.41) 52%,
        rgba(9, 20, 11, 0.12) 76%,
        rgba(9, 20, 11, 0.03) 100%);
}

.hero__inner {
    position: relative;
    z-index: 2;
}

/* Wide enough to keep the four notes on one line; the headline breaks on its
   own <br>s and the lede holds its own 34ch measure, so nothing else in the
   column stretches to meet it. */
.hero__copy { max-width: 46rem; }

/* Red on cream reads; red on a dark photograph does not. The hero's accents
   move to the mustard, which is the one chip colour that holds up on both. */
.hero :focus-visible { outline-color: var(--chip-mustard); }

.hero__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    margin-bottom: 1.6rem;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--forest-mid);
}
.hero__eyebrow::before {
    content: "";
    width: 2.25rem;
    height: 2px;
    background: var(--berry);
}

/* The hero byline is set in the editorial serif rather than as another
   tracked-out capital label — the nav and the buttons already carry the
   uppercase, and a third stack of it above the headline would flatten the
   hierarchy instead of building it. */
.hero__byline {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    margin-bottom: 1.5rem;
    font-family: "Fraunces", Georgia, serif;
    font-size: var(--text-base);
    font-style: italic;
    color: var(--paper-warm);
}
.hero__byline::before {
    content: "";
    width: 2.5rem;
    height: 2px;
    background: var(--chip-mustard);
    flex: none;
}

.hero__title {
    margin-bottom: 1.5rem;
    color: var(--paper);
}

.hero__lede {
    margin-bottom: 2.25rem;
    color: var(--paper-warm);
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 2.5rem;
}

/* The buttons invert for the same reason the type does: a forest fill that
   reads as solid on cream turns into a hole in a dark photograph. */
.hero .btn {
    background: var(--paper);
    border-color: var(--paper);
    color: var(--forest);
}
.hero .btn:hover {
    background: var(--berry);
    border-color: var(--berry);
    color: var(--paper);
}
.hero .btn--ghost {
    background: transparent;
    color: var(--paper);
}
.hero .btn--ghost:hover {
    background: var(--paper);
    border-color: var(--paper);
    color: var(--forest);
}

.hero__note {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.75rem;
    font-size: var(--text-sm);
    color: var(--paper-warm);
}
.hero__note span { display: inline-flex; align-items: center; gap: 0.5rem; }
.hero__note span::before {
    content: "";
    width: 0.45rem; height: 0.45rem;
    border-radius: 50%;
    background: var(--chip-mustard);
}

/* Narrow frames lose the wood on the left, so the scrim stops being a
   left-to-right wipe and covers the whole frame instead. Lightened on the
   same grounds as the desktop one: covering the whole frame at the opacities
   the old sunlit photograph needed takes this one down to near-black, and the
   bowl the crop is built around stops being worth cropping to. */
@media (max-width: 60rem) {
    .hero {
        min-height: clamp(26rem, 78vh, 38rem);
    }
    .hero::after {
        background: linear-gradient(178deg,
            rgba(9, 20, 11, 0.46) 0%,
            rgba(9, 20, 11, 0.58) 45%,
            rgba(9, 20, 11, 0.66) 100%);
    }
    .hero__img { object-position: 68% 42%; }
    .lede { max-width: var(--measure); }
    .hero__lede { max-width: 34ch; }
}

/* --------------------------------------------------------- tasting rail --- */

/* Five circles in a row, each with its label chip overlapping the bottom
   edge — the reference's clearest single device. */
.rail {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: clamp(0.75rem, 2vw, 1.75rem);
    align-items: start;
}

.rail__item {
    position: relative;
    padding-bottom: 2.25rem;
    text-align: center;
}
.rail__item .circle { border: 5px solid var(--paper); }
.rail__chip {
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
}
.rail__note {
    margin: 3rem auto 0;
    max-width: 46ch;
    font-size: var(--text-base);
    color: var(--forest-mid);
}

@media (max-width: 56rem) {
    .rail { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem 0.75rem; }
    .rail__item:nth-child(4) { grid-column: 1 / 2; }
    .rail__item:nth-child(5) { grid-column: 2 / 3; }
}
@media (max-width: 30rem) {
    .rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .rail__item:nth-child(4) { grid-column: auto; }
    .rail__item:nth-child(5) { grid-column: auto; }
    /* Tighter box, same type size. The chip is a label people actually read,
       so the padding and tracking give way before the font size does. */
    .chip { padding: 0.4rem 0.85rem; letter-spacing: 0.06em; }
}
/* Below ~352px the longest label ("Strawberry") no longer clears its column
   at 15px, and the chips overhang into each other. This is the one place the
   size has to drop. */
@media (max-width: 22rem) {
    .chip { font-size: var(--text-xs); }
}

/* ------------------------------------------------------------ strengths --- */

/* Two columns on a shared baseline, so the four reasons read as one set
   rather than a drifting stagger.

   These are four independent reasons, not a sequence, so they carry no
   numerals — numbering is reserved for "How it works", where the order is
   real. Each reason gets its own stamped badge instead, which keeps the
   circle motif the photography already uses. */
.strengths {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(2.25rem, 5vw, 3.75rem) clamp(2.5rem, 6vw, 5.5rem);
    align-items: start;
}

.strength {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 1.35rem;
}

/* Each badge is a finished stamp in its own ink on a transparent matte, so
   it sits straight on the paper — a tinted disc behind one would only fight
   it. They carry their own rim lettering too, which needs more room to stay
   legible than the line icons they replaced did. */
.strength__mark {
    align-self: start;
    width: clamp(4.5rem, 3.5rem + 3.2vw, 5.5rem);
    height: auto;
}

.strength__title { margin-bottom: 0.6rem; }
.strength__body { font-size: var(--text-base); color: var(--forest-mid); margin: 0; }

.strengths-figure {
    position: relative;
    margin-top: clamp(2.5rem, 5vw, 4rem);
}

@media (max-width: 52rem) {
    .strengths { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ how it works */

/* A genuine sequence, so it is numbered and it reads left to right. */
.steps {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(2rem, 4vw, 3.5rem);
    counter-reset: step;
}

.step { position: relative; }
.step__marker {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.25rem;
}
.step__index {
    font-family: "Anton", sans-serif;
    font-size: var(--text-xl);
    color: var(--berry);
    line-height: 1;
}
.step__marker::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--forest-line);
}
.step__title { margin-bottom: 0.65rem; }
.step__body { font-size: var(--text-base); color: var(--forest-mid); margin: 0; }

@media (max-width: 52rem) {
    .steps { grid-template-columns: 1fr; gap: 2.25rem; }
}

/* ----------------------------------------------------------- story band --- */

/* The one strongly coloured surface on the site, with an organic top edge. */
.band {
    position: relative;
    background: var(--terracotta);
    color: var(--paper);
    margin-top: clamp(3rem, 6vw, 6rem);
}
.band__wave {
    position: absolute;
    bottom: 100%;
    left: 0;
    width: 100%;
    height: clamp(3rem, 6vw, 6rem);
    display: block;
    fill: var(--terracotta);
}
.band__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: center;
    padding-block: clamp(3rem, 6vw, 5.5rem);
}
.band .display { color: var(--paper); margin-bottom: 1.5rem; }
.band__body { color: var(--paper); font-size: var(--text-lg); }

.band__stats {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem 2.5rem;
    margin-top: 2.25rem;
    padding-top: 2rem;
    border-top: 1px solid rgba(255, 249, 239, 0.35);
}
.band__stat strong {
    display: block;
    font-family: "Anton", sans-serif;
    font-size: var(--text-xl);
    font-weight: 400;
    line-height: 1;
}
/* Kept secondary by size and tracking rather than by fading the colour, which
   is what pushed these labels under the contrast threshold. */
.band__stat span {
    font-size: var(--text-sm);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--paper);
}

.band__figure { border-radius: 1rem; overflow: hidden; }

@media (max-width: 56rem) {
    .band__inner { grid-template-columns: 1fr; }
    .band__figure { order: -1; }
}

/* ------------------------------------------------------------- editorial --- */

.editorial {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: clamp(2rem, 5vw, 5rem);
    align-items: center;
}
.editorial--reverse .editorial__figure { order: 2; }

.editorial__figure { position: relative; }

.pull-quote {
    margin: 2rem 0 0;
    padding-left: 1.5rem;
    border-left: 3px solid var(--berry);
    font-family: "Fraunces", Georgia, serif;
    font-size: clamp(1.15rem, 1rem + 0.7vw, 1.45rem);
    line-height: 1.5;
    color: var(--forest);
}

@media (max-width: 56rem) {
    .editorial { grid-template-columns: 1fr; }
    .editorial--reverse .editorial__figure { order: 0; }
}

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

.product-lead {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: center;
}

.price {
    font-family: "Anton", sans-serif;
    font-size: var(--display-s);
    line-height: 1;
    color: var(--berry);
}
.price__unit {
    display: block;
    margin-top: 0.5rem;
    font-family: "Instrument Sans", sans-serif;
    font-size: var(--text-sm);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--forest-mid);
}

.stock {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-size: var(--text-base);
    color: var(--forest-mid);
}
.stock::before {
    content: "";
    width: 0.6rem; height: 0.6rem;
    border-radius: 50%;
    background: var(--chip-sage);
}
.stock--out::before { background: var(--berry); }

/* Quantity stepper. Big targets, and it works as a plain number input if the
   JavaScript never arrives. */
.qty {
    display: inline-flex;
    align-items: center;
    border: 2px solid var(--forest);
    border-radius: 999px;
    overflow: hidden;
}
.qty__btn {
    width: 3rem;
    height: 3.25rem;
    background: none;
    border: 0;
    font-size: 1.35rem;
    line-height: 1;
    cursor: pointer;
    color: var(--forest);
}
.qty__btn:hover { background: var(--paper-warm); }
.qty__input {
    width: 3.5rem;
    height: 3.25rem;
    border: 0;
    border-inline: 2px solid var(--forest);
    background: transparent;
    text-align: center;
    font-size: var(--text-base);
    font-weight: 600;
    -moz-appearance: textfield;
}
.qty__input::-webkit-outer-spin-button,
.qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.buy-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin: 1.75rem 0;
}

.product-meta {
    margin-top: 2rem;
    padding-top: 1.75rem;
    border-top: 1px solid var(--forest-line);
    font-size: var(--text-base);
    color: var(--forest-mid);
}
.product-meta dl { display: grid; grid-template-columns: auto 1fr; gap: 0.6rem 1.5rem; margin: 0; }
.product-meta dt { font-weight: 600; color: var(--forest); }
.product-meta dd { margin: 0; }

@media (max-width: 56rem) {
    .product-lead { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ shop grid --- */

.catalogue {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    gap: clamp(2.5rem, 5vw, 4rem);
}

/* Not a card: no border, no shadow, no panel. The circle carries it. */
.catalogue__item { text-align: center; }
.catalogue__figure {
    position: relative;
    margin-bottom: 2.5rem;
}
.catalogue__figure .circle {
    border: 5px solid var(--paper);
    transition: transform 0.4s var(--ease);
}
.catalogue__item:hover .circle { transform: scale(1.02); }
.catalogue__badge {
    position: absolute;
    left: 50%;
    bottom: -1.1rem;
    transform: translateX(-50%);
}
.catalogue__title { margin-bottom: 0.5rem; }
.catalogue__title a { text-decoration: none; }
.catalogue__title a:hover { color: var(--berry); }
.catalogue__sub { font-size: var(--text-sm); color: var(--forest-mid); margin: 0 auto 1rem; max-width: 30ch; }
.catalogue__price {
    font-family: "Anton", sans-serif;
    font-size: var(--text-xl);
    color: var(--berry);
    margin-bottom: 1.25rem;
}

/* ----------------------------------------------------------------- cart --- */

.cart-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: start;
}

.cart-line {
    display: grid;
    grid-template-columns: 6rem minmax(0, 1fr) auto;
    gap: 1.5rem;
    align-items: center;
    padding: 1.75rem 0;
    border-bottom: 1px solid var(--forest-line);
}
.cart-line:first-child { border-top: 1px solid var(--forest-line); }
.cart-line__thumb { border-radius: 50%; overflow: hidden; aspect-ratio: 1; background: var(--forest); }
.cart-line__thumb img { width: 100%; height: 100%; object-fit: cover; }
.cart-line__name { margin-bottom: 0.3rem; }
.cart-line__name a { text-decoration: none; }
.cart-line__meta { font-size: var(--text-sm); color: var(--forest-mid); }
.cart-line__end { text-align: right; display: grid; gap: 0.6rem; justify-items: end; }
.cart-line__total { font-family: "Anton", sans-serif; font-size: var(--text-lg); }

.summary {
    padding: clamp(1.75rem, 3vw, 2.5rem);
    background: var(--paper-warm);
    border-radius: 1rem;
    position: sticky;
    top: 6.5rem;
}
.summary__row {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.7rem 0;
    font-size: var(--text-base);
}
.summary__row--total {
    margin-top: 0.5rem;
    padding-top: 1.25rem;
    border-top: 2px solid var(--forest);
    font-family: "Anton", sans-serif;
    font-size: var(--text-xl);
}
.summary__note { margin-top: 1.25rem; font-size: var(--text-sm); color: var(--forest-mid); }

.empty {
    text-align: center;
    padding-block: clamp(3rem, 7vw, 6rem);
}
.empty .display { margin-bottom: 1rem; }

@media (max-width: 60rem) {
    .cart-layout { grid-template-columns: 1fr; }
    .summary { position: static; }
}
@media (max-width: 34rem) {
    .cart-line { grid-template-columns: 4.5rem minmax(0, 1fr); }
    .cart-line__end { grid-column: 1 / -1; justify-items: start; text-align: left; }
}

/* ---------------------------------------------------------------- forms --- */

.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
}
.field--full { grid-column: 1 / -1; }

/* align-content matters here. A field stretches to the height of the tallest
   one in its row, and without this its own rows absorb that extra height —
   so a hint or an error message under one field pushes its neighbour's input
   down and stretches it. Packing to the top keeps every input on the same
   line regardless of what sits underneath it. */
.field { display: grid; gap: 0.5rem; align-content: start; }

.field label {
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.04em;
}
.field__hint { font-size: var(--text-sm); color: var(--forest-mid); }

.field input,
.field select,
.field textarea {
    width: 100%;
    min-height: 3.25rem;
    padding: 0.85rem 1.1rem;
    border: 2px solid var(--forest-line);
    border-radius: 0.6rem;
    background: #fff;
    font-size: var(--text-base);
    transition: border-color 0.18s var(--ease);
}
/* Chrome forces line-height:normal on <select> from its UA sheet, so a select
   left to size itself lands ~6px shorter than the input beside it. Pinning
   both to one height is the only version of this that survives a font change. */
.field input,
.field select { height: 3.5rem; }

.field textarea { min-height: 10rem; resize: vertical; line-height: 1.55; }
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--forest-mid); }
.field input:focus,
.field select:focus,
.field textarea:focus { border-color: var(--forest); }

.field--error input,
.field--error select,
.field--error textarea { border-color: var(--berry); }

.field__error {
    font-size: var(--text-sm);
    color: var(--berry);
    font-weight: 600;
}

/* The honeypot. Off-screen rather than display:none, because some bots skip
   hidden inputs but not positioned ones. */
.trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form-actions { margin-top: 2rem; }

@media (max-width: 46rem) {
    .form-grid { grid-template-columns: 1fr; }
}

/* -------------------------------------------------------------- contact --- */

.contact-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
    gap: clamp(2.5rem, 6vw, 5rem);
    align-items: start;
}
.contact-detail { margin-bottom: 2rem; }
.contact-detail dt {
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--forest-mid);
    margin-bottom: 0.35rem;
}
.contact-detail dd { margin: 0 0 1.5rem; font-size: var(--text-lg); }
.contact-detail a { text-decoration-thickness: 1px; text-underline-offset: 3px; }

@media (max-width: 56rem) {
    .contact-layout { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------- order --- */

.receipt {
    max-width: 44rem;
    margin-inline: auto;
}
.receipt__number {
    font-family: "Anton", sans-serif;
    font-size: var(--display-s);
    color: var(--berry);
    line-height: 1;
    margin: 1rem 0 1.5rem;
}
.receipt__table { width: 100%; border-collapse: collapse; margin: 2rem 0; }
.receipt__table th,
.receipt__table td {
    padding: 0.9rem 0;
    text-align: left;
    border-bottom: 1px solid var(--forest-line);
    font-size: var(--text-base);
}
.receipt__table th { font-size: var(--text-sm); letter-spacing: 0.09em; text-transform: uppercase; color: var(--forest-mid); }
.receipt__table td:last-child,
.receipt__table th:last-child { text-align: right; }

/* -------------------------------------------------------------- disclaimer */

.disclaimer {
    padding: clamp(1.5rem, 3vw, 2rem);
    border: 2px solid var(--forest-line);
    border-radius: 1rem;
    background: var(--paper);
    font-size: var(--text-base);
    color: var(--forest-mid);
}
.disclaimer strong { color: var(--forest); }

/* The stamped variant, for the cards that carry a real disclaimer rather than
   a plain aside. The mark sits beside the words instead of above them so the
   card still reads as one block of text with a seal on it. It says in art
   exactly what the paragraph says outright, which is what makes it
   decorative — the badge macro leaves its alt empty. */
.disclaimer--stamped {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(1.1rem, 2.5vw, 1.75rem);
    align-items: start;
}

/* Drawn a little larger than the strengths badges even though it matters
   less, because its rim carries two long words where those carry two short
   ones — at the same diameter the letters come out half the size. */
.disclaimer__mark {
    width: clamp(4.5rem, 3.5rem + 3.2vw, 6rem);
    height: auto;
}

/* Two of this card's homes are narrow — the contact sidebar and any phone —
   and neither leaves enough beside the stamp to set a readable line, so there
   the mark moves on top. */
@media (max-width: 34rem) {
    .disclaimer--stamped {
        grid-template-columns: 1fr;
        gap: 1.1rem;
    }
}

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

.footer {
    background: var(--forest);
    color: var(--paper);
    padding-block: clamp(3rem, 6vw, 5rem) 2.5rem;
    margin-top: 0;
}
.footer__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
    gap: clamp(2rem, 5vw, 4rem);
    padding-bottom: 3rem;
    border-bottom: 1px solid rgba(255, 249, 239, 0.2);
}
.footer .wordmark { color: var(--paper); margin-bottom: 1.25rem; }
.footer__blurb { color: #d3ddd2; font-size: var(--text-base); max-width: 34ch; }
.footer__heading {
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    margin-bottom: 1.1rem;
    color: #a8bda6;
}
.footer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
/* Padded to a comfortable 44px target rather than left as bare 23px text.
   Half this site's visitors are over sixty and reading on a phone. */
.footer__list a {
    display: inline-block;
    padding: 0.6rem 0;
    text-decoration: none;
}
.footer__list a:hover { text-decoration: underline; text-underline-offset: 4px; }
.footer__base {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 1rem;
    padding-top: 2rem;
    font-size: var(--text-sm);
    color: #a8bda6;
}

@media (max-width: 52rem) {
    .footer__grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------- motion --- */

/* Cards travel along the scroll axis. Where an element sits relative to the
   viewport decides its state, which means the scroll direction never has to be
   tracked: going down a card runs default -> is-visible -> is-above, so it
   rises in from below and lifts away out of the top; coming back up it runs
   is-above -> is-visible -> is-below, so it drops in from above and sinks away
   out of the bottom.

   Leaving is deliberately softer than arriving — a quarter of the opacity and
   under half the travel. A card half out of frame stays readable, which counts
   for more than symmetry on a site whose customers are frequently over sixty.

   Scoped to .js on purpose. Hiding content by default and relying on script to
   bring it back means a blocked or broken script leaves half the page blank,
   which is too high a price for a fade. */

:root {
    --reveal-travel: 4.5rem;   /* arriving: the full distance, from off-stage */
    --reveal-drift:  2rem;     /* leaving: a shorter push, still clearly seen */
    --reveal-in:     0.72s;
    --reveal-out:    0.4s;

    /* Flatter and later-braking than --ease, which is what makes the arrival
       read as a slide rather than as a fade that happens to move. */
    --reveal-ease:   cubic-bezier(0.16, 1, 0.3, 1);
}

/* The first-arrival state: never seen, so it comes the whole way. */
.js .reveal {
    opacity: 0;
    transform: translate3d(0, var(--reveal-travel), 0) scale(0.94);
    transition:
        opacity var(--reveal-out) var(--ease),
        transform var(--reveal-out) var(--ease);
}

/* Settled. The longer duration and the expo-ish ease belong to arrival alone —
   an exit that lingers reads as lag rather than as motion. */
.js .reveal.is-visible {
    opacity: 1;
    transform: none;
    transition:
        opacity var(--reveal-in) var(--reveal-ease),
        transform var(--reveal-in) var(--reveal-ease);
}

/* Out of the top, going down; and back out of the bottom, coming up. Same
   softness, opposite directions. */
.js .reveal.is-above {
    opacity: 0.25;
    transform: translate3d(0, calc(var(--reveal-drift) * -1), 0) scale(0.97);
}
.js .reveal.is-below {
    opacity: 0.25;
    transform: translate3d(0, var(--reveal-drift), 0) scale(0.97);
}

/* Rows arrive one after another. nth-child is enough on its own: every card in
   a grid is a direct child of that grid, and a card standing alone is its own
   first child and so waits for nobody. Arrival only — a staggered exit just
   looks like the row is struggling to keep up.

   In the stylesheet rather than set as a custom property from script, because
   the Content-Security-Policy here carries no 'unsafe-inline' for styles. */
.js .reveal:nth-child(2).is-visible { transition-delay: 80ms; }
.js .reveal:nth-child(3).is-visible { transition-delay: 160ms; }
.js .reveal:nth-child(4).is-visible { transition-delay: 240ms; }
.js .reveal:nth-child(5).is-visible { transition-delay: 320ms; }
.js .reveal:nth-child(6).is-visible { transition-delay: 400ms; }

@media (max-width: 52rem) {
    /* Less room to travel through, and a narrow screen scrolls past each card
       faster, so the whole gesture tightens rather than being dropped. */
    :root {
        --reveal-travel: 2.75rem;
        --reveal-drift:  1.25rem;
        --reveal-in:     0.6s;
    }
}

/* Honoured both ways: the OS setting, and the class the pre-paint script sets
   from it, so nothing has already started animating before CSS catches up.
   Every state is listed because .js .reveal.is-above outweighs a bare
   .reveal, and a half-faded card is exactly what reduced motion is asking us
   not to produce. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
    .js .reveal,
    .js .reveal.is-above,
    .js .reveal.is-below { opacity: 1; transform: none; }
}

.reduce-motion .reveal,
.reduce-motion .reveal.is-above,
.reduce-motion .reveal.is-below {
    opacity: 1;
    transform: none;
    transition: none;
}
.reduce-motion { scroll-behavior: auto; }

/* ------------------------------------------------------------ utilities --- */

/* A small, closed set of spacing helpers. These exist because the Content-
   Security-Policy has no 'unsafe-inline' for styles, so a style attribute in a
   template is silently dropped by the browser — the rhythm of the page has to
   live in the stylesheet, which is where it belongs anyway. */

.mt-1 { margin-top: 0.85rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.25rem; }
.mt-4 { margin-top: 1.5rem; }
.mt-5 { margin-top: 1.75rem; }
.mt-6 { margin-top: 2rem; }
.mt-7 { margin-top: 2.5rem; }
.mt-8 { margin-top: 3rem; }
.mt-9 { margin-top: clamp(2.5rem, 5vw, 4rem); }

.mb-0  { margin-bottom: 0; }
.mb-sm { margin-bottom: 0.75rem; }
.mb-2  { margin-bottom: 1rem; }
.mb-3  { margin-bottom: 1.25rem; }
.mb-4  { margin-bottom: 1.5rem; }
.mb-6  { margin-bottom: 2rem; }

.pt-0 { padding-top: 0; }

.text-center { text-align: center; }
.justify-center { justify-content: center; }
.mx-auto { margin-inline: auto; }

.measure { max-width: var(--measure); }
.measure-narrow { max-width: 48ch; }

.heading-block { margin: 2.75rem 0 1.5rem; }

/* A row of buttons is not body copy, so the 62ch measure on <p> must not
   apply — left on, it pins a "centred" row to the left of its container. */
.inline-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    max-width: none;
}
.inline-actions--center { justify-content: center; }

.remove-list {
    margin-top: 1.5rem;
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
}

.summary__row--divide {
    margin-top: 0.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--forest-line);
}

.textarea--short { min-height: 6rem; }

.debug-dump {
    margin-top: 3rem;
    padding: 1.5rem;
    text-align: left;
    overflow-x: auto;
    background: var(--paper-warm);
    border-radius: 0.75rem;
    font-size: 0.8rem;
}

/* ---------------------------------------------------------------- print --- */

@media print {
    .masthead, .footer, .btn, .nav-toggle { display: none !important; }
    body { background: #fff; color: #000; font-size: 12pt; }
}
