/* ==========================================================================
   CocoSeller — Crop pages
   Loaded only on a crop page template. Shared by Tomato and, in a later phase,
   every other crop. Personality here comes from one signature device — the
   growth rail — not from new colours, fonts or container widths.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Progress custom property
   Registering it makes the value a real number rather than a token stream,
   which lets it interpolate smoothly and guarantees the calc() below can never
   receive something invalid. Browsers without @property still work: the value
   is written on every frame anyway.
   -------------------------------------------------------------------------- */

@property --crop-progress {
    syntax: "<number>";
    inherits: true;
    initial-value: 0;
}

/* --------------------------------------------------------------------------
   1. Page rhythm
   The crop page is a long editorial scroll beside a standing plant, so it runs
   a tighter vertical rhythm than the marketing pages: roughly 72-100px of
   section padding on desktop rather than 136px. Nothing here is a new token —
   the page re-values the shared section tokens for itself, and every component
   keeps reading the same names.
   -------------------------------------------------------------------------- */

.is-crop-page {
    --space-section:    clamp(2.75rem, 1.9rem + 2.9vw, 6.25rem);
    --space-section-sm: clamp(2rem,    1.6rem + 1.4vw, 3.25rem);
    --space-section-lg: clamp(3.5rem,  2.4rem + 3.6vw, 7.5rem);
}

/* --------------------------------------------------------------------------
   2. Story region
   ONE region, ONE rail. The wrapper is the parent of every section the plant
   travels — requirements, blends, root zone, customisation, manufacturing — so
   the sticky rail pins once, at the top of the region, and releases once, at
   the end of it. A rail per section would restart the story five times.

   Reading column ~74%, crop rail ~26% (a 24-24.5% column plus the 32px gutter
   the plant leans into), from 64em only. Below that the layout is a single
   column and the rail is removed from the document entirely, so none of its
   frames are ever fetched.
   -------------------------------------------------------------------------- */

.l-crop-story {
    --crop-progress: 0;
    --crop-sticky-offset: calc(var(--header-height) + var(--space-md));

    /*
     * The height of the sticky rail, as a length rather than a percentage,
     * because it is needed in two calculations: the rail's own block-size, and
     * the width the plant occupies at that height (section 4). Roughly 75-88%
     * of the viewport at ordinary desktop heights.
     */
    --crop-rail-height: clamp(24rem, calc(100vh - var(--header-height) - var(--space-2xl)), 60rem);

    /* The gutter the plant leans into. It is dead space, never content. */
    --crop-rail-lean: var(--space-lg);

    /* How far the progress line sits into the page's own right margin. */
    --crop-progress-inset: 1.5rem;

    position: relative;
    width: 100%;
    max-width: calc(var(--container-2xl) + (var(--container-gutter) * 2));
    margin-inline: auto;
    padding-inline: var(--container-gutter);
    /* The plant crosses section boundaries. Nothing in the region may clip it. */
    overflow: visible;
}

.l-crop-story__content {
    min-width: 0;
}

/* The rail is decorative narration; it never holds a link or a control. */
.l-crop-story__rail {
    display: none;
}

@media (min-width: 64em) {
    .l-crop-story {
        display: grid;
        /*
         * Reading column ~74%, rail column 24%, and the 32px gap between them
         * is the lean the plant is allowed to take. The gap belongs to the
         * rail visually — it is dead space the plant grows into, never content
         * — so the crop zone reads as 26% of the region and the reading column
         * as the remaining 74%.
         */
        grid-template-columns: minmax(0, 1fr) minmax(0, 24%);
        gap: var(--crop-rail-lean);
        /*
         * Deliberately NOT align-items: start. A sticky element can only
         * travel inside its own containing block, so the rail column has to
         * stretch to the full height of the region — which is exactly what the
         * default stretch alignment does.
         */
        align-items: stretch;
    }

    .l-crop-story__rail {
        display: block;
        position: relative;
        /* Above section backgrounds, below the header and any dialog. */
        z-index: 1;
        pointer-events: none;
    }
}

/* Wide desktop: the plant takes the last half-point it can without narrowing
   the reading column below 74%. */
@media (min-width: 90em) {
    .l-crop-story {
        grid-template-columns: minmax(0, 1fr) minmax(0, 24.5%);
    }
}

/*
 * Sections inside the story column carry their own rhythm but no container:
 * the story wrapper is the container, and the plant has to be able to cross
 * every section boundary inside it.
 */
.l-crop-story__content > .l-section {
    padding-inline: 0;
    overflow: visible;
}

/*
 * Between 64em and 75em the reading column is narrow enough that the inner
 * two-column splits stop working. They collapse; the rail stays.
 */
@media (min-width: 64em) and (max-width: 74.99em) {
    .l-crop-story__content .c-crop-custom__layout,
    .l-crop-story__content .c-crop-maker__layout {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-lg);
    }

    /*
     * Only where the levers are sharing the row with a product photograph. With
     * the media column absent the layout is already full width, and dropping
     * the levers to one column there would leave a long thin list beside the
     * rail. See template-parts/crop/crop-custom.php.
     */
    .l-crop-story__content .c-crop-custom__layout:not(.c-crop-custom__layout--full) .c-lever-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .l-crop-story__content .c-blend-card__ratio {
        font-size: var(--text-3xl);
    }
}

/* --------------------------------------------------------------------------
   3. The rail
   A sticky block the height of most of the viewport. The plant canvas fills it
   and leans left into the column gutter; the progress line sits outside it, in
   the page's own right margin.
   -------------------------------------------------------------------------- */

.c-crop-rail__sticky {
    position: sticky;
    top: var(--crop-sticky-offset);
    block-size: var(--crop-rail-height);
}

.c-crop-rail__canvas {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    /* The lean: the plant occupies the rail plus the gutter beside it. */
    inset-inline-start: calc(var(--crop-rail-lean) * -1);
}

/* --------------------------------------------------------------------------
   4. Growth
   The four frames are photographs of four different plants and every one of
   them fills its canvas top to bottom. Cross-fading them therefore reads as
   what it is — swapping photographs. Height has to be MADE, and it is made
   from four things layered on one shared box:

     a  a locked root      the substrate mound, drawn once, never transformed
     b  a growth scale     the canopy rises out of that mound, 0.66 -> 1.00
     c  a bottom-up mask   each frame unmasks upward on a soft edge
     d  a late hand-over   a frame only leaves after its successor is whole

   All of (b), (c) and (d) hang off ONE number, --crop-progress, which the
   script eases rather than sets, so the whole thing moves like growth instead
   of like a scrollbar.

   The line everything is built on is --crop-root-band: the bottom slice of the
   frame that is mound rather than plant, published per crop by the rail
   component. Every transform origin sits on that line, which makes it a fixed
   point of every transform. That is the whole trick behind the root not
   moving, and it is the thing to check first if it ever does.
   -------------------------------------------------------------------------- */

.c-crop-rail__canvas {
    /* Overridden per crop by the rail component; this is the tomato value. */
    --crop-root-band: 26%;

    /*
     * The line itself, measured from the top, which is the form both
     * transform-origin and the top edge of an inset() clip want.
     */
    --crop-root-line: calc(100% - var(--crop-root-band));

    /*
     * How much of the rail's height the mature plant is allowed to occupy.
     * At the wider desktop sizes the plant is height-constrained rather than
     * width-constrained, and at 1.0 it would stand from the exact top of the
     * rail to the exact bottom with nothing to breathe into — and with the top
     * progress label sitting on its highest leaves. This keeps it dominant,
     * 86-94% of the rail depending on width, without letting it touch out.
     */
    --crop-plant-fill: 0.94;

    /*
     * How soft the growth edge is. A hard line reads as a wipe; this is the
     * band over which the new growth fades in above it. Roughly 64px, held
     * within the 40-80px the plant can carry without looking out of focus.
     */
    --crop-feather: 4rem;

    /*
     * The growth curve: how tall the canopy stands, as a multiple of the
     * artwork, at the current progress. Two straight segments rather than one,
     * because a plant puts on height fastest when it is young:
     *
     *   0.00 -> 0.35   0.66 -> 0.86
     *   0.35 -> 0.80   0.86 -> 1.00
     *
     * It reaches full height at 0.80 and holds it to 1.00, so the mature plant
     * is standing at its true size beside the manufacturing section rather
     * than still creeping upward there.
     *
     * The range matters more than it looks. The mask opens the first frame by
     * about 0.15, so from there on this scale is the ONLY thing still adding
     * height: too narrow a range and the plant is full-grown a sixth of the
     * way down the page and merely changes its fruit after that.
     */
    --crop-grow: calc(
        0.66
        + 0.20 * clamp(0, calc(var(--crop-progress) / 0.35), 1)
        + 0.14 * clamp(0, calc((var(--crop-progress) - 0.35) / 0.45), 1)
    );
}

/*
 * ONE SHARED BOX, sized to the artwork and to nothing else.
 *
 * Width is the narrower of the canvas and the width the frame occupies at the
 * rail's full height; aspect-ratio derives the height from it. So the box is
 * exactly the rectangle the plant is drawn in, object-fit: contain adds no
 * letterboxing, and a percentage in this box is the same percentage of the
 * photograph. That equivalence is what lets --crop-root-band be a real line on
 * the plant rather than an approximation.
 *
 * There is deliberately no max-block-size: the width is already capped at
 * rail-height x aspect, so the height can never exceed the rail. A block-size
 * cap would shrink the height without the width, reintroduce letterboxing, and
 * quietly move the root line off the mound.
 */
.c-crop-plant {
    position: absolute;
    inset-block-end: 0;
    inset-inline-end: 0;
    inline-size: min(
        100%,
        calc(var(--crop-rail-height) * var(--crop-plant-fill) * var(--crop-stage-aspect, 0.5))
    );
    aspect-ratio: var(--crop-stage-ratio, 1 / 2);
}

/*
 * a. The root.
 *
 * The mound, clipped out of one frame and left completely alone: no transform,
 * no mask, no opacity, no transition, nothing that reads --crop-progress. It
 * is the only element in the rail of which that is true, and it is why the
 * substrate and the base of the stem sit on the same pixel from the first
 * scroll to the last.
 */
.c-crop-root {
    position: absolute;
    inset: 0;
    clip-path: inset(var(--crop-root-line) 0 0 0);
}

/*
 * b. The growth scale, and the sway.
 *
 * Two wrappers rather than one because they run on different clocks — grow is
 * driven by scroll, sway by time — and a single element cannot carry two
 * independent transforms. Both share the box and therefore the origin, so they
 * compose about the same point on the plant.
 */
.c-crop-plant__grow,
.c-crop-plant__sway {
    position: absolute;
    inset: 0;
    transform-origin: 50% var(--crop-root-line);
}

/*
 * Deliberately no will-change: transform here. Pinning a raster while the
 * layer's own scale is what changes is how a scaled-up plant goes soft — and
 * the browser composites an animated transform without the hint anyway.
 */
.l-crop-story.is-live .c-crop-plant__grow {
    transform: scale(var(--crop-grow));
}

.c-crop-stage {
    position: absolute;
    inset: 0;

    /*
     * Everything below the root line belongs to .c-crop-root, so it is cut
     * away here. The cut sits exactly on the transform origin, which makes it
     * a fixed point of the scale below: the stem stays planted in the mound at
     * every scale, and no gap and no overlap can open at the join.
     */
    clip-path: inset(0 0 var(--crop-root-band) 0);

    transform-origin: 50% var(--crop-root-line);
    opacity: 0;

    /* Raw position within this stage's own range. */
    --r: clamp(0, calc((var(--crop-progress) - var(--from)) / (var(--to) - var(--from))), 1);

    /*
     * Three ramps off that one number, deliberately in this order:
     *
     *   --s  scale, 0.92 -> 1.00, complete at 45% of the range. The frame
     *        reaches full size BEFORE it is fully revealed, so nothing creeps
     *        after the reveal has finished.
     *   --g  the growth line, complete at 53% of the range. It overshoots to
     *        1.12 so the soft edge is pushed clear off the top of the frame
     *        rather than parking as permanent blur across the highest leaves.
     *   --in opacity in, complete at 29% of the range.
     */
    --s:  calc(0.92 + 0.08 * clamp(0, calc(var(--r) * 2.2), 1));
    --g:  clamp(0, calc(var(--r) * 1.9), 1.12);
    --in: clamp(0, calc(var(--r) * 3.4), 1);

    /* The growth line as a distance up from the foot of the box. */
    --line: calc(var(--crop-root-band) + (100% - var(--crop-root-band)) * var(--g));

    /*
     * The hand-over. --out and --gone come from the data and sit AFTER the
     * successor's own --in has completed, so the older plant is still solid
     * underneath while the newer one establishes over it. That overlap is what
     * absorbs the structural differences between four separately photographed
     * plants; without it the join is a jump.
     */
    --exit: clamp(0, calc((var(--crop-progress) - var(--out, 1.5)) / (var(--gone, 2) - var(--out, 1.5))), 1);
}

.c-crop-stage__image {
    display: block;
    inline-size: 100%;
    block-size: 100%;
    /*
     * contain, bottom centre — never cover. Cropping a frame to fill the box
     * would move the base of the plant between stages, which is the one thing
     * this sequence cannot do.
     */
    object-fit: contain;
    object-position: bottom center;
}

/* Static state: no JS, or JS not yet run. The mature frame is shown whole. */
.c-crop-stage--last {
    opacity: 1;
}

/*
 * c. and d. Live state: only once the script has measured and marked the
 * wrapper.
 */
.l-crop-story.is-live .c-crop-stage {
    opacity: calc(var(--in) * (1 - var(--exit)));
    transform: scale(var(--s));
    -webkit-mask-image: linear-gradient(to top,
        #000 var(--line),
        transparent calc(var(--line) + var(--crop-feather)));
    mask-image: linear-gradient(to top,
        #000 var(--line),
        transparent calc(var(--line) + var(--crop-feather)));
    will-change: opacity;
}

/*
 * Fallback where mask-image is unavailable. Same growth line, hard edge: the
 * clip does both jobs at once, cutting the root band away at the bottom and
 * the ungrown canopy away at the top. --g is re-clamped to 1 here because an
 * inset() cannot take a negative offset the way a mask position can.
 */
@supports not ((mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000))) {
    .l-crop-story.is-live .c-crop-stage {
        --line: calc(var(--crop-root-band) + (100% - var(--crop-root-band)) * clamp(0, var(--g), 1));

        clip-path: inset(calc(100% - var(--line)) 0 var(--crop-root-band) 0);
    }
}

/*
 * Secondary motion. Not a sway — a drift, 0.36 degrees end to end about the
 * root line, which is under two pixels at the top of the plant and invisible
 * as an event. It exists so the canopy is not frozen while the page moves, and
 * it is the first thing to delete if it ever draws attention to itself.
 */
@keyframes cocoseller-crop-drift {
    0%,
    100% { transform: rotate(-0.18deg); }
    50%  { transform: rotate(0.18deg); }
}

.l-crop-story.is-live .c-crop-plant__sway {
    /*
     * ease-in-out, not --ease-standard. That token is an entrance curve —
     * fast out, slow in — and on a loop it reads as a twitch at each end.
     */
    animation: cocoseller-crop-drift 13s ease-in-out infinite;
}

/*
 * Reduced motion, or the script standing down: the mature frame, whole and
 * still, on its mound. Only the photographic stages are touched — the drawn
 * stand-in is left alone, because its own static rules already draw the fully
 * grown plant.
 */
.l-crop-story.is-static .c-crop-stage {
    opacity: 0;
    transform: none;
    -webkit-mask-image: none;
    mask-image: none;
    will-change: auto;
}

.l-crop-story.is-static .c-crop-stage--last {
    opacity: 1;
}

.l-crop-story.is-static .c-crop-plant__grow {
    transform: none;
}

.l-crop-story.is-static .c-crop-plant__sway {
    animation: none;
}

/* --------------------------------------------------------------------------
   5. Drawn stand-in
   Used only while the four transparent frames are missing. It is deliberately
   a diagram — flat brand colour, no texture — so it can never be mistaken for
   product photography.
   -------------------------------------------------------------------------- */

.c-crop-draw {
    /*
     * Local to the stand-in, deliberately not a design token: it exists only
     * until the four photographic frames arrive, and it disappears with them.
     */
    --crop-ripe: #C0442F;

    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: var(--space-xs);
}

.c-crop-draw__svg {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    overflow: visible;
}

.c-crop-draw__bag {
    fill: var(--coco-cream-100);
    stroke: var(--coco-green-700);
    stroke-width: 2;
}

.c-crop-draw__bag-line {
    fill: none;
    stroke: var(--coco-green-700);
    stroke-width: 2;
    opacity: 0.45;
}

.c-crop-draw__stem {
    fill: none;
    stroke: var(--coco-green-700);
    stroke-width: 4;
    stroke-linecap: round;
    /*
     * The path carries pathLength="1" as an SVG attribute, so its length is
     * normalised: one unit of scroll progress is one unit of stem. It has to be
     * an attribute — pathLength is not a CSS property.
     */
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

.c-crop-draw__leaf {
    fill: var(--coco-green-500);
    opacity: 0;
    transform-box: fill-box;
    transform-origin: 0% 100%;
    transform: scale(0.4);
}

.c-crop-draw__flower {
    fill: var(--coco-coir-300);
    opacity: 0;
}

.c-crop-draw__fruit {
    fill: var(--coco-green-400);
    opacity: 0;
    transform-box: fill-box;
    transform-origin: 50% 50%;
    transform: scale(0.3);
}

/* Static fallback: the whole plant, drawn, no animation. */
.c-crop-draw__stem { stroke-dashoffset: 0; }
.c-crop-draw__leaf { opacity: 1; transform: none; }
.c-crop-draw__fruit { opacity: 1; transform: none; fill: var(--crop-ripe); }
.c-crop-draw__flower { opacity: 0; }

/* Live state: reveal against progress. */
.l-crop-story.is-live .c-crop-draw__stem {
    stroke-dashoffset: calc(1 - clamp(0, calc(var(--crop-progress) * 1.05), 1));
}

.l-crop-story.is-live .c-crop-draw__leaf {
    --p: clamp(0, calc((var(--crop-progress) - var(--at)) * 14), 1);
    opacity: var(--p);
    transform: scale(calc(0.4 + (var(--p) * 0.6)));
}

.l-crop-story.is-live .c-crop-draw__flower {
    /* Flowers rise, then give way to fruit. */
    --p: clamp(0, calc((var(--crop-progress) - var(--at)) * 14), 1);
    --gone: clamp(0, calc((var(--crop-progress) - var(--at) - 0.16) * 10), 1);
    opacity: calc(var(--p) - var(--gone));
}

.l-crop-story.is-live .c-crop-draw__fruit {
    --p: clamp(0, calc((var(--crop-progress) - var(--at)) * 12), 1);
    --r: clamp(0, calc((var(--crop-progress) - var(--ripe)) * 12), 1);
    opacity: var(--p);
    transform: scale(calc(0.3 + (var(--p) * 0.7)));
    fill: color-mix(in srgb, var(--crop-ripe) calc(var(--r) * 100%), var(--coco-green-400));
}

.c-crop-draw__caption {
    display: flex;
    flex-direction: column;
    gap: 0.15em;
    padding: var(--space-2xs) var(--space-xs);
    border: 1px dashed var(--coco-coir-400);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

.c-crop-draw__caption-title {
    font-weight: 600;
    color: var(--color-heading);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
}

.c-crop-draw__caption-file { opacity: 0.7; word-break: break-all; }

/* --------------------------------------------------------------------------
   6. Progress indicator
   A hairline at the far right edge of the page, standing in the page's own
   margin rather than over the reading column, carrying one node per section of
   the story and a fill that tracks scroll. It runs the full height of the
   rail — it is a measure of where the reader is in the region, so anything
   shorter than the region reads as decoration.

   The two editorial labels are captions on the rail, not part of the line:
   putting them in the line's own column would have eaten half its length.
   -------------------------------------------------------------------------- */

.c-crop-progress {
    position: absolute;
    inset-block: 0;
    /*
     * Into the page gutter, never past it: the inset is smaller than
     * --container-gutter at every width the rail is shown at (33px at 64em),
     * so the line can never create horizontal overflow.
     */
    inset-inline-end: calc(var(--crop-progress-inset) * -1);
    inline-size: var(--crop-progress-inset);
}

.c-crop-progress__track {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    inline-size: 2px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--coco-green-500) 26%, transparent);
}

.c-crop-progress__fill {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 100%;
    border-radius: inherit;
    background-color: var(--coco-green-600);
    transform-origin: top;
    transform: scaleY(1);
}

.l-crop-story.is-live .c-crop-progress__fill {
    transform: scaleY(var(--crop-progress));
}

.c-crop-progress__dot {
    position: absolute;
    inset-inline-start: 50%;
    inset-block-start: calc(var(--at) * 100%);
    inline-size: 0.4375rem;
    block-size: 0.4375rem;
    margin-inline-start: -0.21875rem;
    margin-block-start: -0.21875rem;
    border-radius: var(--radius-circle, 50%);
    background-color: var(--color-bg);
    box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--coco-green-500) 36%, transparent);
}

.l-crop-story.is-live .c-crop-progress__dot {
    --reached: clamp(0, calc((var(--crop-progress) - var(--at)) * 40 + 1), 1);
    background-color: color-mix(in srgb, var(--coco-green-600) calc(var(--reached) * 100%), var(--color-bg));
    box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--coco-green-600) calc(var(--reached) * 100%), color-mix(in srgb, var(--coco-green-500) 36%, transparent));
}

/*
 * The bookend captions: set against the line, reading back into the rail. A
 * halo keeps them legible where they cross the plant, which is the only thing
 * they can ever sit over — they are inside the rail, never over the prose.
 */
.c-crop-progress__label {
    position: absolute;
    inset-inline-end: var(--space-md);
    /*
     * An explicit width, NOT just a max. The label is absolutely positioned
     * inside .c-crop-progress, which is only --crop-progress-inset wide (33px
     * at 64em). With a max-inline-size alone the available width is the
     * containing block's, so the label shrink-wrapped to about 42px, broke to
     * one word per line, and grew tall enough to run straight down through the
     * stage label sitting 2.6rem below it — two text runs overprinting each
     * other on every animated crop page at every desktop width.
     *
     * 12rem, right-aligned, reading back into the rail: the same width
     * .c-crop-progress__stage already uses, which is why that one never
     * collided with anything.
     */
    inline-size: 12rem;
    font-size: var(--text-2xs);
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-wide);
    text-align: end;
    text-transform: uppercase;
    color: var(--color-text-muted);
    text-shadow: 0 0 6px var(--color-bg), 0 1px 3px var(--color-bg);
    text-wrap: balance;
}

.c-crop-progress__label--top    { inset-block-start: 0; }
.c-crop-progress__label--bottom { inset-block-end: 0; }

/* --------------------------------------------------------------------------
   7. Editorial annotations
   Handwriting look from system faces only — no font is downloaded. One at a
   time: each fades in at its own milestone and out again before the next
   arrives, so the rail never carries three notes at once. Desktop only, and
   inside the rail, so they can never sit over reading content.
   -------------------------------------------------------------------------- */

.c-crop-note {
    position: absolute;
    inset-inline-start: 0;
    inset-block-end: var(--y, 50%);
    max-width: 10rem;
    margin: 0;
    font-family: var(--font-hand);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--coco-green-800);
    /* A light halo, so the note stays legible where it crosses foliage. */
    text-shadow: 0 1px 3px var(--color-bg), 0 0 8px var(--color-bg);
    opacity: 0;
    transform: rotate(-3deg) translateY(0.5rem);
}

.l-crop-story.is-live .c-crop-note {
    --in: clamp(0, calc((var(--crop-progress) - var(--at)) * 12), 1);
    --out: clamp(0, calc((var(--crop-progress) - var(--until)) * 12), 1);
    opacity: calc((var(--in) - var(--out)) * 0.94);
    transform: rotate(-3deg) translateY(calc((1 - var(--in)) * 0.5rem));
}

/* Static: the last note only, so the rail is annotated but not crowded. */
.l-crop-story.is-static .c-crop-note { opacity: 0; }
.l-crop-story.is-static .c-crop-note:last-of-type { opacity: 0.94; transform: rotate(-3deg); }

/* --------------------------------------------------------------------------
   8. Tonal band inside the story region
   The plant has to be seen crossing a change of ground, or the rail reads as a
   picture pinned beside the text rather than one element travelling the page.
   The root-zone section takes a full-bleed tonal ground for that: the band is
   drawn by a pseudo-element behind the section, the rail paints above it, and
   the plant carries straight over both of its edges.

   100vw is safe here because html and body carry overflow-x: clip, so the
   scrollbar's width can never open a horizontal scrollbar.
   -------------------------------------------------------------------------- */

.l-crop-story__content > .c-rootzone::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 50%;
    z-index: -1;
    inline-size: 100vw;
    margin-inline-start: -50vw;
    background-color: var(--color-bg-subtle);
}

/* --------------------------------------------------------------------------
   9. Inline stage figures — tablet and mobile only
   display:none from 64em, where the rail takes over, and the images are lazy,
   so a frame shown in the rail is never also fetched here.
   -------------------------------------------------------------------------- */

/*
 * Contained, not floating. The frames are cut-outs on a transparent ground, so
 * dropped straight onto the page they read as clip art adrift between two
 * paragraphs. A panel makes each one a figure: a stated edge, its own ground,
 * and the caption sitting on the same card as the picture.
 */
.c-crop-inline {
    margin: 0 0 var(--space-xl);
    padding: var(--space-lg) var(--space-md) var(--space-md);
    border-radius: var(--radius-xl);
    background-color: var(--color-bg-subtle);
    text-align: center;
}

/*
 * Sized by both axes, so the box is the picture and nothing else. A frame is
 * 1:2, so constraining width alone would letterbox a tall plant inside a wide
 * empty box — exactly the blank area the rail is meant to avoid leaving behind
 * when it is switched off.
 */
.c-crop-inline__image {
    display: block;
    inline-size: auto;
    max-inline-size: 100%;
    block-size: auto;
    /*
     * Height-led, because a 1:2 frame constrained by width alone would either
     * tower over a phone or sit lost in the middle of a tablet column. The
     * viewport term keeps the plant a comfortable read on a short screen.
     */
    max-block-size: clamp(20rem, 52vh, 30rem);
    margin-inline: auto;
}

.c-crop-inline__caption {
    margin-block-start: var(--space-2xs);
    font-size: var(--text-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

@media (min-width: 64em) {
    .c-crop-inline { display: none; }
}

/* --------------------------------------------------------------------------
   10. Hero
   -------------------------------------------------------------------------- */

.c-crop-hero { padding-block-start: var(--space-xl); }

.c-crop-hero__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-xl);
    align-items: center;
}

.c-crop-hero__title {
    margin-block: var(--space-xs) 0;
    font-family: var(--font-display);
    font-size: var(--text-5xl);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    color: var(--color-heading);
    text-wrap: balance;
}

.c-crop-hero__standfirst {
    margin-block-start: var(--space-sm);
    font-size: var(--text-xl);
    line-height: var(--leading-snug);
    color: var(--color-heading);
}

.c-crop-hero__lead {
    margin-block-start: var(--space-sm);
    max-width: 42rem;
    font-size: var(--text-lg);
    line-height: var(--leading-relaxed);
    color: var(--color-text-muted);
}

.c-crop-hero__actions { margin-block-start: var(--space-lg); }

.c-crop-hero__figure { border-radius: var(--radius-xl); overflow: hidden; }
.c-crop-hero__figure.c-placeholder { --placeholder-ratio: var(--ratio-tall); }
.c-crop-hero__image { width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 62em) {
    .c-crop-hero__inner {
        grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
        gap: var(--space-2xl);
    }

    .c-crop-hero__figure.l-media,
    .c-crop-hero__figure.c-placeholder {
        --media-ratio: var(--ratio-portrait);
        --placeholder-ratio: var(--ratio-portrait);
    }
}

/* --------------------------------------------------------------------------
   11. Reduced motion
   Nothing scroll-dependent survives here. The script never attaches a listener
   in this mode; these rules are the belt to its braces.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .l-crop-story .c-crop-stage,
    .l-crop-story .c-crop-plant__grow,
    .l-crop-story .c-crop-plant__sway,
    .l-crop-story .c-crop-note,
    .l-crop-story .c-crop-draw__leaf,
    .l-crop-story .c-crop-draw__fruit,
    .l-crop-story .c-crop-draw__stem,
    .l-crop-story .c-crop-progress__fill {
        transition: none !important;
        animation: none !important;
        will-change: auto;
    }

    /* The mature plant, whole and still, standing on its untouched mound. */
    .l-crop-story .c-crop-stage {
        opacity: 0;
        transform: none;
        -webkit-mask-image: none;
        mask-image: none;
    }

    .l-crop-story .c-crop-stage--last { opacity: 1; }

    .l-crop-story .c-crop-plant__grow,
    .l-crop-story .c-crop-plant__sway { transform: none; }

    .l-crop-story .c-crop-draw__stem { stroke-dashoffset: 0; }
    .l-crop-story .c-crop-draw__leaf { opacity: 1; transform: none; }
    .l-crop-story .c-crop-draw__fruit { opacity: 1; transform: none; }
    .l-crop-story .c-crop-draw__flower { opacity: 0; }

    .l-crop-story .c-crop-progress__fill { transform: scaleY(1); }

    .l-crop-story .c-crop-note { opacity: 0; }
    .l-crop-story .c-crop-note:last-of-type { opacity: 0.94; transform: rotate(-3deg); }
}

/* --------------------------------------------------------------------------
   12. Slot variant — the six Phase 4 crop pages
   Same region, same sticky behaviour, but the second column holds a Phase 5
   animation slot rather than the photographic rail. The slot is content, not
   narration: it has a stage list a screen reader can read, it is wider than the
   rail because a schematic needs width to stay legible, and it survives on
   small screens as a contained figure instead of being removed.

   The slot is first in the document and placed into the second column here, so
   the collapsed single-column layout leads with the drawing rather than ending
   with it. See page-crop.php.
   -------------------------------------------------------------------------- */

.l-crop-story--slot .l-crop-story__slot {
    margin-block-end: var(--space-xl);
}

@media (min-width: 64em) {
    .l-crop-story--slot {
        grid-template-columns: minmax(0, 1fr) minmax(0, 34%);
        gap: var(--space-2xl);
    }

    .l-crop-story--slot .l-crop-story__slot {
        grid-column: 2;
        grid-row: 1;
        margin-block-end: 0;
    }

    .l-crop-story--slot .l-crop-story__content {
        grid-column: 1;
        grid-row: 1;
    }
}

@media (min-width: 90em) {
    .l-crop-story--slot { grid-template-columns: minmax(0, 1fr) minmax(0, 32%); }
}

/*
 * The slot column is narrower than a full container, so the inner two-column
 * splits in the reading column collapse a breakpoint later than they do beside
 * the photographic rail.
 */
@media (min-width: 64em) and (max-width: 81.24em) {
    .l-crop-story--slot .c-crop-custom__layout,
    .l-crop-story--slot .c-crop-maker__layout {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-lg);
    }
}

/* --------------------------------------------------------------------------
   13. Phase 5 mount point
   The empty div the motion engine writes the procedural tomato SVG into. It
   occupies no space while it is empty, so the interim photographic system
   below it behaves exactly as it did before the mount existed. The moment it
   holds a child, it takes the canvas and the frame stack is hidden — which is
   how Phase 5 replaces the drawing without a template edit.
   -------------------------------------------------------------------------- */

.c-crop-rail__mount:empty { display: none; }

.c-crop-rail__mount:not(:empty) {
    position: absolute;
    inset: 0;
    z-index: 2;
}

.c-crop-rail__sticky:has(.c-crop-rail__mount:not(:empty)) .c-crop-plant,
.c-crop-rail__sticky:has(.c-crop-rail__mount:not(:empty)) .c-crop-draw {
    display: none;
}

/* --------------------------------------------------------------------------
   14. Photography beats inside the story region
   PHASE 5C. Two image sections punctuate the reading column so a crop page is
   not a single unbroken run of white cards beside a drawing: a wide band after
   the blends, and an image beside a short lever list after the customisation
   section. Both sit INSIDE the story wrapper, so the growth rail keeps running
   beside them.

   Neither carries an argument. They are looked at, not read — which is why the
   band's caption is one line and the lever list beside the second is three
   items lifted from the section above it rather than new copy.
   -------------------------------------------------------------------------- */

.c-crop-band__figure {
    margin: 0;
    display: grid;
    gap: var(--space-sm);
}

/*
 * Edge to edge of the reading column, and flat: the band's job is to break the
 * card rhythm, and a rounded, inset image reads as one more card.
 */
.c-crop-band__media {
    inline-size: 100%;
    --placeholder-ratio: var(--ratio-ultrawide);
    border-radius: var(--radius-md);
}

.c-crop-band__caption {
    display: grid;
    gap: var(--space-3xs);
    max-inline-size: 62ch;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

.c-crop-use__layout {
    display: grid;
    gap: var(--space-lg);
}

.c-crop-use__media {
    margin: 0;
    --placeholder-ratio: var(--ratio-landscape);
}

.c-crop-use__caption {
    margin-block: var(--space-2xs) var(--space-sm);
    font-size: var(--text-base);
    line-height: var(--leading-relaxed);
    color: var(--color-text);
}

.c-crop-use__levers {
    display: grid;
    gap: var(--space-2xs);
    margin: 0;
    padding: 0;
    list-style: none;
    border-block-start: var(--border-hairline);
    padding-block-start: var(--space-sm);
}

.c-crop-use__levers li {
    display: grid;
    gap: 0.125rem;
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
}

.c-crop-use__levers b {
    font-weight: var(--weight-semibold);
    color: var(--color-text);
}

.c-crop-use__levers span {
    color: var(--color-text-muted);
}

/*
 * From 48em the second beat becomes an image BESIDE technical content, which is
 * the whole reason it is a separate section from the band: two identical wide
 * images on one page would be a pattern rather than a rhythm.
 */
@media (min-width: 48em) {
    .c-crop-use__layout {
        grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
        align-items: center;
    }
}

/* Inside the narrower reading column the split would crowd; stack it back. */
@media (min-width: 64em) and (max-width: 78em) {
    .l-crop-story__content .c-crop-use__layout {
        grid-template-columns: 1fr;
    }
}

/* --------------------------------------------------------------------------
   16. The root-zone cutaway
   A drawn section through the crop's own container, standing where the
   root-zone photograph would. It occupies exactly the space the photographic
   slot occupied — --ratio-wide, full width of the reading column — so a
   supplied photograph replaces it with no layout change at all.

   The drawing carries no text. Its key is the HTML beneath it, which is what
   keeps the figure legible at 320px: a label sized to be readable inside a
   288px-wide SVG would be absurd inside a 1100px one.
   -------------------------------------------------------------------------- */

.c-cutaway {
    margin: 0;
    display: grid;
    gap: var(--space-md);
}

.c-cutaway__stage {
    position: relative;
    overflow: hidden;
    aspect-ratio: var(--ratio-wide);
    border-radius: var(--radius-lg);
    border: var(--border-hairline);
    /*
     * A warm technical ground rather than white: the figure is a section
     * through a substrate, and a pure white field made the coir read as dirt on
     * paper. The wash is the same coir family the drawing is built from.
     */
    background-image:
        radial-gradient(120% 96% at 50% 6%, var(--coco-cream-50) 0%, transparent 62%),
        linear-gradient(176deg, var(--coco-coir-50) 0%, var(--coco-cream-100) 58%, var(--coco-coir-100) 100%);
}

.c-cutaway__svg {
    display: block;
    inline-size: 100%;
    block-size: 100%;
}

.c-cutaway__key {
    display: grid;
    gap: var(--space-sm);
}

/*
 * The ratio badge. The only figure anywhere near the drawing, and it is the
 * published blend ratio — printed as the ratio, never converted into a depth,
 * a percentage or a volume. See inc/crop-cutaway.php.
 */
.c-cutaway__ratio {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2xs) var(--space-sm);
    margin: 0;
    padding-block-end: var(--space-sm);
    border-block-end: var(--border-hairline);
}

.c-cutaway__ratio-value {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    line-height: 1;
    color: var(--color-heading);
    font-variant-numeric: tabular-nums;
}

.c-cutaway__ratio-note {
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

.c-cutaway__legend {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: var(--space-sm) var(--space-lg);
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-cutaway__item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "swatch label" "swatch note";
    column-gap: var(--space-xs);
    row-gap: 0.15rem;
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
}

.c-cutaway__item b {
    grid-area: label;
    font-weight: var(--weight-semibold);
    color: var(--color-text);
}

.c-cutaway__item span:not(.c-cutaway__swatch) {
    grid-area: note;
    color: var(--color-text-muted);
}

/*
 * The swatches are the drawing's own fills, so the key is readable as a key
 * rather than as three decorative dots. Each is a shape as well as a colour —
 * a round speck, an angular flake, a line — because colour alone would not
 * distinguish them for a reader who cannot separate the two browns.
 */
.c-cutaway__swatch {
    grid-area: swatch;
    inline-size: 0.95rem;
    block-size: 0.95rem;
    margin-block-start: 0.2em;
    border: 1px solid rgba(63, 42, 25, 0.28);
}

.c-cutaway__item--pith .c-cutaway__swatch {
    border-radius: 50%;
    background-color: var(--coco-coir-900);
}

.c-cutaway__item--chip .c-cutaway__swatch {
    border-radius: 2px;
    background-color: var(--coco-coir-300);
    clip-path: polygon(14% 0, 100% 22%, 86% 100%, 0 74%);
}

.c-cutaway__item--root .c-cutaway__swatch {
    border-radius: 50%;
    border-color: var(--coco-green-300);
    background-image: linear-gradient(150deg, var(--coco-cream-50) 0 46%, var(--coco-green-500) 46% 100%);
}

/*
 * Below the rail breakpoint the reading column is the viewport, and a 16:9
 * section through a deep planter becomes a thin strip. The taller ratio gives
 * the vertical vessels room without letting the slab crops grow a void.
 */
@media (max-width: 47.99em) {
    .c-cutaway__stage { aspect-ratio: var(--ratio-landscape); }
}
