/* ═══════════════════════════════════════════════════════════════════════════
   Bonsai landing — "The Potting Bench"

   The page is a surface, not a document: a gardener-student's work bench, seen
   from above. Everything on it is an object — specimen cards, seed packets,
   pressed leaves, a ruled ledger sheet, a taped letter — laid out by hand and,
   in the hero, actually draggable.

   The rule the whole thing follows: EVERY SURFACE CATCHES LIGHT ON ITS TOP EDGE.
   `inset 0 1px 0 rgba(255,255,255,.7)` is on the card, the tag, the button, the
   packet and the pill. That single repeated highlight is what makes flat CSS
   read as physical paper, and it is why the page feels touchable rather than
   drawn.

   Extracted to a file rather than a <style> block because index.html serves
   style-src 'self' with no 'unsafe-inline' — see the CSP comment in the head.
   Nothing in the markup may carry a style="" attribute for the same reason;
   positions for the scattered ephemera are therefore authored here as classes
   (.e-leaf, .e-note …), and only JS-set custom properties vary at runtime.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── paper & ink ── */
  --paper:        #efe6d3;
  --paper-lit:    #f7f0df;
  --paper-panel:  #f6efdf;
  --paper-deep:   #e5d9bd;
  --ink:          #2b2618;
  --ink-soft:     #5b5340;
  --ink-faint:    #6f6450;
  --ink-ghost:    #8d8168;
  --line:         #d6c8a8;
  --line-strong:  #c3b290;

  /* ── the garden ── */
  --forest:       #34503a;
  --forest-deep:  #25402b;
  --forest-dark:  #1e3423;
  --leaf:         #4c6b46;

  /* ── the desk drawer: things that are not paper ──
     Two inks only. The ruled lines on the ledger and the notepaper are the
     same blue-grey the app uses for water (#6d949b), written as rgba where
     they appear because they are always used at low alpha. The highlighter
     (#d9d76a) and the tape (#ded0a4) are likewise mixed at the point of use. */
  --seal:         #8f3b2c;   /* wax, stamp ink, the ledger's margin rule */
  --seal-deep:    #6f2c20;

  /* ── type scale ── */
  --t-wordmark: clamp(3.4rem, 10vw, 7.5rem);
  --t-display:  clamp(2.4rem, 5.4vw, 3.9rem);
  --t-h2:       clamp(2rem, 4vw, 3rem);
  --t-lead:     1.1875rem;
  --t-body:     1.0625rem;
  --t-small:    0.9375rem;
  --t-tag:      0.6875rem;
  --t-typed:    0.75rem;
  --t-hand:     1.25rem;

  --maxw: 1180px;
  --maxw-wide: 1340px;
  --measure: 60ch;
  --rail-h: 4rem;

  /* ── the light on every surface ── */
  --lip:       inset 0 1px 0 rgba(255,255,255,.72);
  --lip-soft:  inset 0 1px 0 rgba(255,255,255,.5);
  --lift-1:    0 1px 2px rgba(43,38,24,.10);
  --lift-2:    0 2px 4px rgba(43,38,24,.10), 0 14px 26px -18px rgba(43,38,24,.42);
  --lift-3:    0 3px 6px rgba(43,38,24,.12), 0 26px 46px -26px rgba(43,38,24,.5);

  --ease:      cubic-bezier(.22,.8,.32,1);
  --ease-rule: cubic-bezier(.6,.05,.2,1);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
html, body { overflow-x: clip; }

/* The bench surface itself: paper grain, a faint field-notebook grid, and a
   vignette so the middle of the page reads as the lit part of the desk.

   The grid and the grain scroll with the paper, so they live here and are
   rastered once into the scrolling layer. The two lights — the glow at the top
   of the viewport and the vignette at the bottom — are anchored to the WINDOW
   rather than to the page, and they used to say so with
   `background-attachment: fixed` on this same declaration. That is the most
   expensive thing a background can say: a fixed layer must be re-rastered
   against the viewport on every scrolled frame, and because attachment is a
   per-layer list on one property, the grid and the grain were dragged into
   that repaint with it — the whole viewport, five gradient layers deep,
   redrawn once per frame for the length of every scroll. That is what made the
   page stutter uniformly from top to bottom.

   The lights are a fixed pseudo-element now (below). Identical to look at — a
   fixed background box and a fixed element box are both the viewport — but it
   is one composited layer that scrolling simply leaves alone. */
body {
  background-color: var(--paper);
  background-image:
    repeating-linear-gradient(0deg, rgba(120,100,60,.055) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(90deg, rgba(120,100,60,.045) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg, rgba(120,100,60,.02) 0 1px, transparent 1px 3px);
  color: var(--ink);
  font-family: 'Spectral', Georgia, serif;
  font-size: var(--t-body);
  line-height: 1.7;
  font-feature-settings: "kern" 1, "liga" 1;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* The light on the bench. z-index:-1 puts it above the body's own background —
   which is where these two layers sat in the stack before — and below every
   line of content, and `fixed` registers it to the window exactly as
   `background-attachment: fixed` did. It never moves, never changes and is
   never written to, so the compositor keeps it as one static tile and scrolling
   past it costs nothing. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(255,252,242,.55), transparent 60%),
    radial-gradient(100% 60% at 50% 100%, rgba(90,74,53,.06), transparent 70%);
}
::selection { background: rgba(52,80,58,.2); }

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(1.25rem, 4vw, 2rem); }
.wrap-wide { max-width: var(--maxw-wide); }
section[id] { scroll-margin-top: 5.75rem; }
h1, h2, h3 { overflow-wrap: break-word; font-weight: 500; }
img, svg, canvas { display: block; max-width: 100%; }
.num { font-variant-numeric: tabular-nums lining-nums; }
.sr { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
      clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

a { color: inherit; }
p a, li a, .foot-copy a {
  text-decoration: underline; text-decoration-color: var(--line-strong);
  text-decoration-thickness: 1px; text-underline-offset: 3px;
  transition: color .18s ease, text-decoration-color .18s ease;
}
p a:hover, li a:hover {
  color: var(--forest-deep); text-decoration-color: var(--forest); text-decoration-thickness: 2px;
}
:focus-visible { outline: 2px solid var(--leaf); outline-offset: 3px; border-radius: 2px; }


/* ═══ MATERIALS ══════════════════════════════════════════════════════════════
   The three primitives every object on the bench is assembled from, plus the
   one rule they all obey: a stack of a vertical gradient (the sheet), a
   hairline border (the cut edge), a top-edge highlight (--lip, the light) and
   a cast shadow (the air underneath).
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. TAG — the small typed label. Bonsai's section kicker, and the caption
      strip on every specimen card. The ::before is a punched hole. */
.tag {
  display: inline-flex; align-items: center; gap: .5rem;
  height: 27px; padding: 0 .7rem 0 .55rem;
  background: #f4edda;
  border: 1px solid var(--line-strong); border-radius: 2px;
  box-shadow: var(--lip), var(--lift-1);
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-tag); font-weight: 700; letter-spacing: .17em;
  text-transform: uppercase; color: var(--ink-faint); white-space: nowrap;
  text-shadow: 0 1px 0 rgba(255,255,255,.85);
}
.tag::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--paper-deep);
  box-shadow: inset 0 1px 1.5px rgba(43,38,24,.5), 0 1px 0 rgba(255,255,255,.6);
}
.tag-leaf::before {
  background: var(--leaf); box-shadow: inset 0 1px 1px rgba(255,255,255,.35);
}

/* 2. TYPED — the field-journal typewriter voice. Filenames, units, folios. */
.typed {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-typed); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-ghost);
}

/* 3. HAND — marginalia. Never load-bearing; always an aside a person added. */
.hand {
  font-family: 'Caveat', 'Segoe Script', cursive;
  font-size: var(--t-hand); line-height: 1.3; color: var(--leaf);
  letter-spacing: .01em;
}

/* ── buttons: letterpress, not plastic. Ink pressed into paper. ── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-family: inherit; font-size: 1rem; font-weight: 600; letter-spacing: .005em;
  padding: .8rem 1.6rem; border-radius: 3px; text-decoration: none;
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  transition: transform .12s var(--ease), box-shadow .18s ease,
              background-color .18s ease, border-color .18s ease, color .18s ease;
}
.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--forest);
  color: #f5f0e3; border-color: #1f3324;
  text-shadow: 0 1px 0 rgba(0,0,0,.3);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), inset 0 -2px 0 rgba(0,0,0,.2),
              0 2px 3px rgba(43,38,24,.16), 0 14px 26px -16px rgba(37,64,43,.8);
}
.btn-primary:hover { background: #37553e; }
.btn-primary:active { box-shadow: inset 0 2px 5px rgba(0,0,0,.34), 0 1px 1px rgba(43,38,24,.2); }

.btn-ghost {
  background: #f4ecd9;
  color: var(--forest-deep); border-color: var(--line-strong);
  text-shadow: 0 1px 0 rgba(255,255,255,.9);
  box-shadow: var(--lip), var(--lift-1), 0 10px 20px -16px rgba(43,38,24,.5);
}
.btn-ghost:hover { border-color: var(--ink-ghost); color: var(--forest-dark); }
.btn-ghost:active { box-shadow: inset 0 2px 4px rgba(43,38,24,.2); }

.btn-lg { font-size: 1.0625rem; padding: .95rem 1.9rem; }
.btn svg { width: 1.05em; height: 1.05em; }


/* ═══ THE RAIL ═══════════════════════════════════════════════════════════════
   Not a nav bar — the lip of the bench. It carries the brand at one end, the
   chapter links in the middle, and a live conditions readout at the other:
   the visitor's own season, clock and sky glyph, filled in by bench-desk.js
   from their own Date object. It is the smallest possible demonstration of the
   claim made further down the page — that the garden knows where you are.
   ═══════════════════════════════════════════════════════════════════════════ */
.rail {
  position: sticky; top: 0; z-index: 100;
  /* No backdrop-filter. It used to blur(9px) saturate(1.1) whatever was passing
     underneath — but the gradient above it is .92–.96 opaque, so the backdrop
     only ever contributed four to eight percent of the colour, and blurring
     that fraction is not something an eye can find. What it cost was real: the
     rail spans the window and is pinned to it, so the region under it is new on
     every scrolled frame, and each one meant reading that strip back and
     running a separable blur over it before the header could be composited.
     A paid-for pass, every frame, for an effect that was never visible. */
  background: rgba(243,235,217,.94);
  border-bottom: 1px solid var(--line);
  transition: box-shadow .25s ease, border-color .25s ease;
}
.rail::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 2px;
  background: linear-gradient(180deg, rgba(43,38,24,.07), transparent);
  pointer-events: none;
}
.rail.scrolled { box-shadow: 0 10px 22px -18px rgba(43,38,24,.6); border-bottom-color: var(--line-strong); }

.rail-inner {
  display: flex; align-items: center; gap: 1.85rem;
  height: var(--rail-h);
}
.brand {
  display: inline-flex; align-items: center; gap: .6rem;
  font-size: 1.25rem; font-weight: 600; letter-spacing: .005em;
  color: var(--forest-deep); text-decoration: none; flex: none;
}
.brand-leaf { width: 27px; height: 27px; object-fit: contain; }

.rail-links { display: flex; align-items: center; gap: 1.75rem; margin-inline: auto; }
.rail-links a {
  font-size: 1.0625rem; color: var(--ink-soft); text-decoration: none;
  position: relative; padding-block: .35rem; transition: color .18s ease;
}
.rail-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--leaf); transform: scaleX(0); transform-origin: left;
  transition: transform .3s var(--ease);
}
.rail-links a:hover { color: var(--forest-deep); }
.rail-links a:hover::after, .rail-links a.is-current::after { transform: scaleX(1); }
.rail-links a.is-current { color: var(--forest-deep); }

.rail-end { display: flex; align-items: center; gap: 1.05rem; flex: none; }

/* the conditions cluster — the rail's answer to a menu bar's clock */
.conditions {
  display: inline-flex; align-items: center; gap: .55rem;
  padding-right: 1rem; margin-right: .1rem;
  border-right: 1px solid var(--line);
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .8125rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-ghost); white-space: nowrap;
}
.conditions svg { width: 16px; height: 16px; color: var(--leaf); flex: none; }
.cond-sep { opacity: .45; }
.rail-signin { font-size: 1.0625rem; color: var(--ink-soft); text-decoration: none; }
.rail-signin:hover { color: var(--forest-deep); }
.rail .btn { padding: .62rem 1.3rem; font-size: 1rem; }


/* ═══ THE BENCH (hero) ═══════════════════════════════════════════════════════
   A 300vh scroll-scene with a pinned surface. The tree in the middle is the
   real one — trees/classic.js on the shared engine — and it grows itself once
   on load, then hands over to scroll. Everything else on the bench is loose:
   [data-drag] items can be picked up and moved, and stay where you drop them.
   ═══════════════════════════════════════════════════════════════════════════ */
.grow-scene {
  position: relative; height: 300vh;
  /* The bench is a surface you drag things around on, so a press-and-move is a
     grab, never a text selection. Without this every drag paints the wordmark,
     the canvas and the call blue and leaves the selection behind afterwards.
     Scoped to the scene on purpose — the prose further down the page stays
     selectable, because people copy it. */
  -webkit-user-select: none; user-select: none;
}
/* Native image/canvas dragging fights the custom one: Chrome starts its own
   ghost-image drag from the polaroids the moment the pointer moves. */
.grow-scene img, .grow-scene canvas { -webkit-user-drag: none; user-drag: none; }
.grow-scene.is-static { height: auto; }
.grow-scene.is-static .grow-sticky { position: static; }
.grow-sticky {
  position: sticky; top: var(--rail-h);
  height: calc(100vh - var(--rail-h)); min-height: 620px;
  display: flex; flex-direction: column; overflow: hidden;
}

/* the masthead: tagline, wordmark, tree, then the call. */
.grow-head {
  position: relative; z-index: 4; text-align: center;
  padding: clamp(1.5rem, 4vh, 2.75rem) 1rem 0;
  pointer-events: none;
}
.masthead {
  display: inline-flex; align-items: center; gap: .75rem;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-tag); font-weight: 700; letter-spacing: .3em;
  text-transform: uppercase; color: var(--ink-ghost);
}
.masthead::before, .masthead::after {
  content: ""; width: clamp(18px, 5vw, 54px); height: 1px; background: var(--line-strong);
}
.grow-head h1 {
  font-size: var(--t-wordmark); font-weight: 500; line-height: .92;
  letter-spacing: -.035em; color: var(--forest-deep);
  margin-top: clamp(.25rem, 1vh, .6rem);
}
.grow-head h1 .mark {
  display: inline-block; width: .78em; height: .78em; vertical-align: -.04em;
  margin-right: .12em; object-fit: contain;
}

/* the tree, potted, centred, sitting on the bench line */
.grow-tree {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: flex-end; justify-content: center;
  padding: clamp(9rem, 20vh, 13rem) 1rem clamp(10rem, 22vh, 13rem);
  pointer-events: none;
}
.grow-stage { position: relative; width: min(100%, 1060px); height: 100%; }
.grow-stage canvas { width: 100%; height: 100%; }

/* the bench line the pot stands on — a hairline with its own cast shadow */
.bench-line {
  position: absolute; left: 50%; translate: -50% 0; z-index: 1;
  bottom: clamp(9.4rem, 21.4vh, 12.4rem);
  width: min(94vw, 1180px); height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong) 12%, var(--line-strong) 88%, transparent);
  pointer-events: none;
  opacity: clamp(0, var(--grow, 0) * 3, .9);
}
.bench-line::after {
  content: ""; position: absolute; left: 8%; right: 8%; top: 1px; height: 22px;
  background: radial-gradient(60% 100% at 50% 0%, rgba(43,38,24,.10), transparent 70%);
}

/* the growth ruler: a boxwood rule stood on end beside the tree, filling as the
   tree grows, with the carbon figure riding the fill line. Driven entirely by
   --grow, which landing-grow-tree.js writes on <html>.
   Deliberately narrow: it is an instrument at the edge of the bench, not a
   progress bar. Anything wider competes with the tree for the eye. */
.grow-ruler {
  position: absolute; right: clamp(.6rem, 2vw, 1.6rem); top: 17%; bottom: 13%;
  width: 19px; z-index: 3; pointer-events: none;
}
.grow-ruler::before {
  content: ""; position: absolute; inset: 0;
  background: #f0e6cb;
  border: 1px solid var(--line-strong); border-radius: 2px;
  box-shadow: var(--lip), var(--lift-1);
}
/* the tick marks, every 5% with a longer rule every 25% */
.grow-ruler::after {
  content: ""; position: absolute; inset: 1px;
  background:
    repeating-linear-gradient(0deg, rgba(43,38,24,.38) 0 1px, transparent 1px 5%),
    repeating-linear-gradient(0deg, rgba(43,38,24,.5) 0 1px, transparent 1px 25%);
  background-size: 6px 100%, 11px 100%;
  background-position: left top, left top;
  background-repeat: no-repeat, no-repeat;
  opacity: .5;
}
.grow-ruler-fill {
  position: absolute; left: 1px; right: 1px; bottom: 1px; top: 1px;
  background: rgba(94,131,76,.29);
  border-top: 1.5px solid var(--leaf);
  transform: scaleY(var(--grow, 0)); transform-origin: bottom;
}
/* the figure rides the fill line — stacked so the tag stays narrow enough to
   keep the whole instrument inside the right-hand margin */
.grow-ruler-tag {
  position: absolute; right: calc(100% + .5rem);
  bottom: calc(var(--grow, 0) * 100%); translate: 0 50%;
  display: flex; flex-direction: column; align-items: flex-end; gap: .05rem;
  padding: .3rem .5rem; border-radius: 2px; white-space: nowrap; text-align: right;
  background: #f4ecd9;
  border: 1px solid var(--line-strong);
  box-shadow: var(--lip), var(--lift-1);
  opacity: clamp(0, var(--grow, 0) * 6, 1);
}
.grow-ruler-tag::after {
  content: ""; position: absolute; left: 100%; top: 50%; translate: 0 -50%;
  border: 5px solid transparent; border-left-color: var(--line-strong);
}
.grow-ruler-val {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .875rem; font-weight: 700; color: var(--forest-deep);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.grow-ruler-unit {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .5rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-ghost); line-height: 1.1;
}

/* the call, which arrives only once the tree has actually grown */
.grow-foot {
  position: relative; z-index: 4; margin-top: auto;
  padding: 0 1rem clamp(1.5rem, 4vh, 2.5rem); text-align: center;
  pointer-events: none;
}
.grow-cta {
  display: flex; flex-direction: column; align-items: center; gap: .9rem;
  opacity: clamp(0, (var(--grow, 0) - .7) * 4.2, 1);
  transform: translateY(calc((1 - clamp(0, (var(--grow,0) - .7) * 4.2, 1)) * 14px));
  visibility: hidden;
}
.is-grown .grow-cta { visibility: visible; }
.grow-sub {
  font-size: var(--t-lead); line-height: 1.55;
  /* A step closer to ink than --ink-soft, plus a paper halo so the copy
     still reads over the tree's oval rather than sinking into it. */
  color: color-mix(in srgb, var(--ink) 48%, var(--ink-soft));
  max-width: 46ch; text-wrap: pretty;
  text-shadow: 0 1px 10px var(--paper), 0 0 22px var(--paper);
}
.grow-sub b { color: var(--ink); font-weight: 600; }
/* The foot is full-width; if the whole CTA catches the pointer, it blankets
   the watering can and the rest of the bottom-left scatter. Only the buttons
   need to be clickable — the copy is not a control. */
.hero-actions {
  display: flex; flex-wrap: wrap; gap: .75rem; justify-content: center;
  pointer-events: auto;
}
.trust-line {
  display: inline-flex; align-items: center; gap: .4rem;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-typed); letter-spacing: .09em; text-transform: uppercase; color: var(--ink-ghost);
}
.trust-line svg { width: 14px; height: 14px; color: var(--leaf); }

/* The scroll invitation, which retires the moment the tree starts moving.
   Kept off-centre because the trust line lands in the middle of that same
   strip once the tree is grown, and for a second or two both are on screen. */
.grow-hint {
  position: absolute; left: clamp(1rem, 4vw, 2.75rem); bottom: .75rem; z-index: 5;
  display: inline-flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  opacity: clamp(0, 1 - var(--grow, 0) * 8, 1); transition: opacity .25s ease;
}
.grow-hint.is-hidden { pointer-events: none; }
.grow-hint .hand { color: var(--ink-ghost); }
.grow-hint svg { width: 16px; height: 16px; color: var(--ink-ghost); animation: nudge 2.2s var(--ease) infinite; }
@keyframes nudge { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }


/* ═══ EPHEMERA ═══════════════════════════════════════════════════════════════
   The loose objects on the bench. Positioned as a percentage of the sticky
   surface so the composition survives any viewport; picked up and moved by
   bench-desk.js, which writes --dx/--dy and nothing else.
   ═══════════════════════════════════════════════════════════════════════════ */
.bench-scatter { position: absolute; inset: 0; z-index: 3; pointer-events: none; }

.eph {
  position: absolute; left: var(--x); top: var(--y);
  translate: var(--dx, 0px) var(--dy, 0px);
  rotate: var(--r, 0deg);
  pointer-events: auto;
  animation: eph-in .5s var(--ease) var(--enter, .1s) backwards;
}
@keyframes eph-in { from { opacity: 0; translate: var(--dx, 0px) calc(var(--dy, 0px) + 10px); } }

.eph[data-drag] {
  cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none;
  transition: filter .2s ease, opacity .2s ease;
}
.eph[data-drag]:active, .eph.is-dragging { cursor: grabbing; }
/* will-change is a promise to the compositor and it is kept for as long as it
   is declared: nineteen loose objects each saying "my translate is about to
   change" meant nineteen permanently promoted layers, held and composited on
   every frame of every scroll, for a property that only ever changes while a
   finger is on one of them. It is asked for at the two moments it is true
   instead — .is-dragging is set on pointerdown, before the first move, and
   .is-returning for the animated trip back to the item's place on the bench. */
.eph.is-dragging, .eph.is-returning { will-change: translate; }
.eph.is-dragging { z-index: 40; }
.eph.is-returning { transition: translate .6s var(--ease), rotate .6s var(--ease), filter .2s ease, opacity .2s ease; }

/* Picking something up has to lift it in the shape it actually is.
   A card is a rectangle, so it casts a box-shadow. A pressed leaf or an ink
   sketch is a bare <svg> over transparent paper, and a box-shadow on one of
   those draws a hard rectangle around a leaf-shaped hole — so those lift with
   a drop-shadow, which follows the alpha, and brighten slightly instead. */
.eph[data-drag] > :not(svg) { transition: box-shadow .2s ease, rotate .3s var(--ease); }
.eph[data-drag]:hover > :not(svg) { box-shadow: var(--lip), var(--lift-3); }
.eph.is-dragging > :not(svg) { box-shadow: var(--lip), 0 6px 12px rgba(43,38,24,.16), 0 40px 60px -30px rgba(43,38,24,.6); }

.eph[data-drag].pressed:hover  { filter: drop-shadow(0 7px 8px rgba(43,38,24,.3)); }
.eph.is-dragging.pressed       { filter: drop-shadow(0 15px 16px rgba(43,38,24,.34)); }
.eph[data-drag].doodle:hover   { opacity: .68; filter: drop-shadow(0 5px 5px rgba(43,38,24,.22)); }
.eph.is-dragging.doodle        { opacity: .72; filter: drop-shadow(0 13px 13px rgba(43,38,24,.26)); }

/* In-flow objects (tool plates, bubbles, the notebook nudge) use the same
   --dx/--dy drag as the hero scatter, but they keep their place in the
   layout rather than sitting on an absolute map. */
[data-drag]:not(.eph) {
  cursor: grab; touch-action: none;
  -webkit-user-select: none; user-select: none;
  /* `transform`, not `translate`: .reveal already owns the translate property
     for its entrance, and the two must be able to stack. */
  transform: translate(var(--dx, 0px), var(--dy, 0px));
}
[data-drag]:not(.eph):active, [data-drag]:not(.eph).is-dragging { cursor: grabbing; }
[data-drag]:not(.eph).is-dragging { z-index: 40; }
[data-drag]:not(.eph).is-returning { transition: transform .6s var(--ease); }

/* a specimen card: a photograph of the garden, mounted, with a typed caption */
.spec {
  width: var(--w, 170px); padding: 7px 7px 0;
  background: #f3ebd8;
  border: 1px solid var(--line); border-radius: 3px;
  box-shadow: var(--lip), var(--lift-2);
}
.spec img { width: 100%; height: auto; aspect-ratio: 16/10; object-fit: cover; border-radius: 1px;
            box-shadow: inset 0 0 0 1px rgba(43,38,24,.12); }
.spec figcaption {
  display: flex; align-items: baseline; justify-content: space-between; gap: .5rem;
  padding: .4rem .1rem .45rem; white-space: nowrap;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .5625rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-ghost);
}
/* the specimen name gives way before the date does — a card whose caption
   wrapped to two lines stopped looking like a label and started looking broken */
.spec figcaption span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* a torn scrap of note paper, jitter-cut along the bottom */
.scrap {
  position: relative; width: var(--w, 200px); padding: .85rem .95rem 1.15rem;
  background: #f8f2e1;
  border: 1px solid var(--line); border-top-width: 1px;
  box-shadow: var(--lip), var(--lift-2);
  clip-path: polygon(0 0, 100% 0, 100% 92%, 93% 97%, 86% 92%, 78% 98%, 69% 93%, 60% 99%,
                     51% 93%, 43% 98%, 34% 93%, 26% 99%, 17% 94%, 9% 98%, 0 93%);
}
.scrap .hand { color: var(--ink-soft); }
.scrap .hand em { color: var(--leaf); font-style: normal; }

/* The brand line, set as two written lines rather than one wrapped paragraph.
   Caveat is a handwriting face, so it wants the tighter leading a person
   actually writes at — 1.3 left a gap between the halves wide enough to read
   as two separate notes. The second clause steps in a little, the way a
   continued line does on paper, and it carries the leaf colour so the pair
   reads as a turn rather than a repeat. */
.scrap-note { font-size: calc(var(--t-hand) * 1.15); line-height: 1.12; text-wrap: balance; }
.scrap-note span,
.scrap-note em { display: block; }
.scrap-note em { margin-left: .55em; }

/* a paper luggage tag on twine — the carbon figure hangs off the bench */
.hangtag {
  position: relative; width: var(--w, 170px); padding: .6rem .65rem .55rem 1.2rem;
  background: #f0e4c7;
  border: 1px solid var(--line-strong);
  border-radius: 2px 3px 3px 2px;
  box-shadow: var(--lip), var(--lift-2);
  clip-path: polygon(0 50%, 9% 0, 100% 0, 100% 100%, 9% 100%);
  text-align: center;
}
.hangtag::before {
  content: ""; position: absolute; left: 7px; top: 50%; translate: 0 -50%;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--paper); box-shadow: inset 0 1px 2px rgba(43,38,24,.55);
}
.hangtag-num {
  display: block; font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: 1.1875rem; font-weight: 700; color: var(--forest-deep); line-height: 1.1;
}
.hangtag-lab {
  display: block; margin-top: .12rem;
  font-size: .5625rem; letter-spacing: .05em; line-height: 1.5; white-space: nowrap;
}

/* a seed packet, crimped along the top the way a real one is torn open */
.seedpacket {
  position: relative;
  width: var(--w, 108px); padding: .95rem .55rem .6rem; text-align: center;
  background: #f2e9d2;
  border: 1px solid var(--line-strong); border-radius: 2px;
  box-shadow: var(--lip), var(--lift-2);
}
.seedpacket .sp-crimp {
  position: absolute; left: 0; right: 0; top: 8px; height: 1px;
  border-top: 1px dashed var(--line-strong); opacity: .8;
}
.seedpacket .sp-name {
  display: block; font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .5rem; font-weight: 700; letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-faint); text-shadow: 0 1px 0 rgba(255,255,255,.8);
}
.seedpacket .sp-art {
  display: block; margin: .4rem auto .45rem; padding: .3rem 0; width: 100%;
  background: #f9f4e6;
  box-shadow: inset 0 0 0 1px rgba(43,38,24,.12), inset 0 2px 4px -3px rgba(43,38,24,.25);
  border-radius: 1px;
}
.seedpacket .sp-art svg { width: 44px; height: auto; margin-inline: auto; }
.seedpacket .sp-kind {
  display: block; font-size: .8125rem; font-weight: 600; line-height: 1.15;
  color: var(--forest-deep);
}
.seedpacket .sp-note {
  display: block; margin-top: .12rem;
  font-family: 'Caveat', cursive; font-size: .875rem; color: var(--leaf);
}

/* a rubber stamp, pressed unevenly */
.stamp {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .32rem .6rem .28rem; border: 2px solid var(--seal); border-radius: 3px;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .625rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--seal); opacity: .82;
  -webkit-mask-image: radial-gradient(120% 140% at 30% 20%, #000 60%, rgba(0,0,0,.78) 100%);
  mask-image: radial-gradient(120% 140% at 30% 20%, #000 60%, rgba(0,0,0,.78) 100%);
}
.stamp svg { width: 11px; height: 11px; pointer-events: none; }
/* Hollow ink: without a fill, a click in the middle of the badge falls
   through the box. The film is invisible; it only makes the interior a surface. */
.e-stamp .stamp { position: relative; }
.e-stamp .stamp::before {
  content: ""; position: absolute; inset: 0;
}

/* ink doodles and pressed leaves sit loose, no card behind them */
.doodle { color: var(--leaf); opacity: .5; }
/* Default SVG hit-testing is visiblePainted: a 1.9px stroke is the only
   pixel that counts, so the hollow of a can (or snail, or sprout) is a
   hole you click straight through. Hand the pointer to the parent box. */
.doodle svg { width: 100%; height: auto; pointer-events: none; }
.pressed { filter: drop-shadow(0 4px 6px rgba(43,38,24,.22)); }
.pressed svg { width: 100%; height: auto; }

/* The fern and the packet live in the 20-28% lane, between the scrap note and
   the canopy. That lane only exists above ~1340px: the scrap is a fixed 226px
   wide while its position is a percentage, so below that it and the canopy
   close on the lane from both sides and the packet lands on the note. */
@media (max-width: 1339px) {
  .e-fern, .e-packet { display: none; }
}

/* ── the hero's own scatter placement (desktop) ──
   Read this block as a map of the bench. Three zones are kept clear and
   everything is placed around them:
     · the masthead      — centre, 4%–24%
     · the tree and pot  — centre third, all the way down
     · the call          — centre, 76%–98%
     · the ruler         — the right-hand margin, x > 84%
   --enter staggers the arrival left-to-right, roughly 45ms apart, so the bench
   assembles itself rather than appearing all at once. */
.e-spec-sunset { --x: 3%;    --y: 17%; --r: -4deg;  --enter: .10s; --w: 172px; }
.e-scrap-ask   { --x: 2%;    --y: 50%; --r: 2.5deg; --enter: .19s; --w: 206px; }
.e-leaf-a      { --x: 14%;   --y: 77%; --r: -14deg; --enter: .28s; width: 60px; }
.e-snail       { --x: 25.5%; --y: 89%; --r: 0deg;   --enter: .37s; width: 46px; }
.e-hangtag     { --x: 69%;   --y: 11%; --r: 5deg;   --enter: .14s; }
.e-spec-rain   { --x: 74.5%; --y: 41%; --r: 3.5deg; --enter: .23s; --w: 152px; }
.e-stamp       { --x: 69%;   --y: 72%; --r: -8deg;  --enter: .32s; }
.e-leaf-b      { --x: 81%;   --y: 79%; --r: 22deg;  --enter: .41s; width: 44px; }
.e-sprout      { --x: 8%;    --y: 34%; --r: 0deg;   --enter: .45s; width: 34px; }
/* The left-hand band between the edge and the canopy was the one empty stretch
   of the bench. The fern and the packet sit in the 20-28% lane — right of the
   scrap note, which runs to 18%, and left of the canopy, which starts at 33%
   on the narrowest screen these three are shown on. The can tucks into the
   bottom corner under the scrap. */
.e-fern        { --x: 20%; --y: 26%; --r: -9deg; --enter: .52s; width: 58px; }
.e-packet      { --x: 20%; --y: 52%; --r: 6deg;  --enter: .24s; }
.e-can         { --x: 5%;  --y: 70%; --r: -3deg; --enter: .58s; width: 84px; }
/* The drawing stays 84px; the grab box is a padded square around it so you
   do not have to land on the spout. Invisible — the can itself does not grow. */
.e-can::before {
  content: "";
  position: absolute;
  inset: -28px -24px;
}
.e-rain-doodle { --x: 65%;   --y: 25%; --r: 0deg;   --enter: .49s; width: 30px; }

/* "put it all back" — appears only after something has been moved */
.tidy {
  position: absolute; right: clamp(1rem, 4vw, 2.75rem); bottom: .75rem; z-index: 6;
  display: inline-flex; align-items: center; gap: .35rem;
  background: none; border: 0; cursor: pointer;
  font-family: 'Caveat', cursive; font-size: 1.05rem; color: var(--ink-ghost);
  opacity: 0; visibility: hidden; transition: opacity .3s ease, color .18s ease;
}
.tidy.is-on { opacity: 1; visibility: visible; }
.tidy:hover { color: var(--forest); }
.tidy svg { width: 15px; height: 15px; }


/* ═══ THE TICKER ═════════════════════════════════════════════════════════════
   A strip of gummed tape run across the bench with a list of things people
   actually bring to a tutor. Deliberately not a claim about users — it is a
   list of subjects, and it says so on the tag.
   ═══════════════════════════════════════════════════════════════════════════ */
.ticker {
  position: relative; border-block: 1px solid var(--line);
  background: rgba(222,208,164,.26);
  padding-block: .9rem;
}
.ticker::before, .ticker::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: clamp(48px, 12vw, 160px); z-index: 2;
  pointer-events: none;
}
.ticker::before { left: 0;  background: linear-gradient(90deg, var(--paper), transparent); }
.ticker::after  { right: 0; background: linear-gradient(270deg, var(--paper), transparent); }
/* The clipping lives here and NOT on .ticker, because the tag is pinned across
   the strip's top edge and an overflow:hidden on the parent cut it in half. */
.ticker-rail { overflow: hidden; }
.ticker-tag {
  position: absolute; left: 50%; top: -13px; translate: -50% 0; z-index: 3;
}
.ticker-track {
  display: flex; width: max-content; gap: 0;
  /* 77s keeps the strip at the ~40px/s it was tuned at: the duration is one
     copy's width, so it has to be re-timed whenever items are added or removed
     or the whole thing speeds up. */
  animation: roll 77s linear infinite;
}
.ticker-track span {
  display: inline-flex; align-items: center; gap: 1.9rem;
  padding-right: 1.9rem; white-space: nowrap;
  font-size: 1.0625rem; color: var(--ink-ghost);
  /* The lit subject fades in and out rather than switching. Long, because the
     strip is already moving — a fast change on top of the scroll reads as a
     flicker, and the point is that your eye is drawn over, not startled. */
  transition: color .9s ease;
}
.ticker-track span::after {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--line-strong); flex: none;
  transition: background-color .9s ease;
}
/* Colour only, and deliberately no italic. font-style cannot be transitioned,
   and swapping the roman face for the italic one changes the word's width —
   which shoves every item after it along the track, mid-scroll. */
.ticker-track span.lit { color: var(--forest-deep); }
.ticker-track span.lit::after { background: var(--leaf); }
@keyframes roll { to { transform: translateX(-50%); } }


/* ═══ CHAPTERS ═══════════════════════════════════════════════════════════════
   Every section opens the same way: tag → title → lead. Same rhythm each time,
   so the page has a beat you can feel without reading the words.
   ═══════════════════════════════════════════════════════════════════════════ */
/* 4.5rem top AND bottom put 144px of empty paper between every two chapters at
   desktop, on top of each chapter's own internal gap. Trimmed to 3.5rem: the
   beat is still clearly a beat, without the page reading as a slideshow of
   well-spaced slides. */
.chapter { padding-block: clamp(2.5rem, 4.6vw, 3.5rem); position: relative; }
.chapter-head { display: flex; flex-direction: column; align-items: flex-start; gap: 1rem; }
.chapter-head.centred { align-items: center; text-align: center; }
/* Title left, lead right. The lead is bottom-aligned with the title's last line
   so the two blocks sit on a shared baseline rather than both hanging from the
   top, which is what makes a split head look like a layout rather than a gap. */
.chapter-head.split {
  display: grid; align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas: "rule rule" "tag  lead" "h2   lead";
  gap: 1rem clamp(2rem, 5vw, 4rem);
}
.chapter-head.split > .rule { grid-area: rule; }
/* justify-self, because a grid item stretches to its column by default and an
   inline-flex tag becomes a half-page-wide box the moment it is gridded. */
.chapter-head.split > .tag  { grid-area: tag; justify-self: start; }
.chapter-head.split > h2    { grid-area: h2; max-width: none; }
.chapter-head.split > .lead { grid-area: lead; align-self: end; max-width: none; }
.chapter-head h2 {
  font-size: var(--t-h2); line-height: 1.08; letter-spacing: -.02em; color: var(--forest-deep);
  max-width: 20ch; text-wrap: balance;
}
.chapter-head.centred h2 { max-width: 24ch; }
.accent { color: var(--leaf); font-style: italic; }
.chapter-head .lead {
  font-size: var(--t-lead); line-height: 1.62; color: var(--ink-soft);
  max-width: var(--measure); text-wrap: pretty;
}
.chapter-head .lead b { color: var(--ink); font-weight: 600; }
.chapter-gap { margin-top: clamp(1.75rem, 3.5vw, 2.5rem); }

/* a hairline that draws itself in as the chapter arrives */
.rule {
  height: 1px; background: var(--line-strong); position: relative;
  transform-origin: left; transition: transform .8s var(--ease-rule);
}
.rule::after { content: ""; position: absolute; left: 0; right: 0; top: 3px; height: 1px; background: var(--line); }
.reveal .rule, .reveal.rule { transform: scaleX(0); }
.rvg-in .rule, .rvg-in.rule { transform: scaleX(1); }


/* ═══ THE TOOLS ══════════════════════════════════════════════════════════════
   Three rows, alternating. Each pairs a real exchange — the actual examples
   from the tool pages, set as hand/typed marginalia — with a plate showing the
   thing itself. The row you are looking at is lit; the others step back.
   ═══════════════════════════════════════════════════════════════════════════ */
.tool-rows { display: flex; flex-direction: column; gap: clamp(2.25rem, 4.5vw, 3.75rem); }
.tool-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: clamp(2rem, 5vw, 4.5rem); align-items: center;
  transition: opacity .45s var(--ease), filter .45s var(--ease);
}
.tool-row.alt { grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr); }
.tool-row.alt .tool-copy { order: 2; }
.tool-row.alt .tool-plate { order: 1; }
.tool-row.dim { opacity: .34; filter: saturate(.65); }
/* The opener row: chapter title on the left, the all-time tally on the right.
   Aligned to the top so the tally hangs from the same line as the tag rather
   than floating in the middle of a tall heading.

   The dividing rule used to sit on .chapter-head, because the Learn copy was
   directly beneath it in the same column and the rule separated the two. With
   the Learn row moved out to its own row the rule would have been a dangling
   underline under one column, so it moved to the row itself, where it spans
   the full measure and separates what it actually separates. */
.tool-row-open {
  align-items: start;
  padding-bottom: clamp(1.15rem, 2.4vw, 1.7rem);
  border-bottom: 1px solid var(--line);
}
.tool-row-open .chapter-head { width: 100%; }
.tool-row-open .chapter-head h2 { max-width: none; }
.tool-row-open .chapter-head .lead { max-width: none; }

/* ── THE TALLY ──
   An index card someone has filled in, not a stat strip. The big three-figure
   treatment belongs to the ledger chapter further down; if this matched it the
   page would make its loudest numeric statement twice and the second one would
   land flat. So this is a record card: typed header, a red rule under it the way
   a filing card is printed, ruled entries with dot leaders, and the receipts
   stamp pressed into the bottom corner the way a card gets signed off.

   It sits on graph paper, so the card stock is deliberately PLAIN — no rules or
   texture of its own. Every other paper object here is patterned against a plain
   ground; this one is the reverse, which is what keeps it from reading as one
   more sheet in the pile. */
.tally {
  align-self: start;
  padding: clamp(1.15rem, 2.5vw, 1.6rem) clamp(1.25rem, 2.8vw, 1.8rem) clamp(1rem, 2.2vw, 1.35rem);
  background: #faf4e6;
  border: 1px solid var(--line); border-radius: 2px;
  box-shadow: var(--lip), var(--lift-2);
}

/* Header. The date moved up here from the foot: down there it sat opposite the
   stamp, and a faint typed line is no counterweight to a red rubber stamp — the
   row read as lopsided. Paired with the title it is a printed card's header
   instead, and it leaves the stamp alone in the corner where it belongs. */
.tally-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .75rem 1rem; flex-wrap: wrap;
  padding-bottom: .7rem;
  border-bottom: 1px solid rgba(143,59,44,.32);   /* the index card's red rule */
}
/* Same leaf pip the chapter tags carry, so the card is visibly part of the same
   printed system rather than a panel that wandered in. */
.tally-head { display: inline-flex; align-items: center; gap: .5rem; color: var(--ink-soft); }
.tally-head::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--leaf); box-shadow: inset 0 1px 1px rgba(255,255,255,.35);
}
.tally-when { color: var(--ink-faint); }

/* The list is closed at the bottom as well as ruled between entries. Without
   the last rule the stamp had nothing to sit on and floated in the corner. */
.tally-rows { margin-top: .35rem; border-bottom: 1px solid var(--line); }
.tally-row {
  display: flex; align-items: baseline; gap: 1rem;
  padding-block: clamp(.7rem, 1.6vw, .95rem);
  border-top: 1px solid var(--line);
}
.tally-row:first-child { border-top: none; }
.tally-row dt {
  display: flex; align-items: baseline; gap: .7rem; flex: 1; min-width: 0;
  font-size: var(--t-small); color: var(--ink-soft); white-space: nowrap;
}
/* The leader. Dotted rather than solid so it reads as a guide rather than an
   underline, and stopped a little short of the figure so the number keeps its
   own air. It is decoration: a pseudo-element, so it is never announced. */
.tally-row dt::after {
  content: ""; flex: 1 1 auto; min-width: 1.5rem;
  /* flex-end puts it on the bottom of the label's line box — which is under the
     descenders, not on the baseline, and it read as a rule sliding off the row.
     The margin lifts it back onto the baseline. */
  align-self: flex-end; margin-bottom: .46em; margin-right: .3rem;
  border-bottom: 1px dotted var(--ink-ghost); opacity: .5;
}
.tally-fig {
  display: flex; align-items: baseline; gap: .22em; flex: none;
  font-size: clamp(1.55rem, 2.9vw, 2rem); font-weight: 500; line-height: 1;
  letter-spacing: -.025em; color: var(--forest-deep);
}
/* No text-transform here. "L" is the symbol for litres and lowercasing it gives
   "16.5 l", which in a serif is a straight stroke barely distinguishable from a
   1 — the figure would read as 16.51. */
.tally-fig .unit {
  font-size: .46em; font-weight: 600; color: var(--leaf); letter-spacing: .06em;
}

/* The stamp alone, right-hand corner — a card that has been checked and signed
   off. No rule above it: the last entry's own hairline already closes the list,
   and a second line 1rem below it made the card look like it had a footer. */
.tally-foot { display: flex; justify-content: flex-end; margin-top: clamp(.75rem, 1.8vw, 1.05rem); }
.tally-foot .stamp { text-decoration: none; transition: opacity .18s ease, background-color .18s ease; }
.tally-foot .stamp:hover { opacity: 1; background: rgba(143,59,44,.07); }

.tool-copy { display: flex; flex-direction: column; gap: 1rem; align-items: flex-start; }
.tool-folio {
  display: flex; align-items: center; gap: .6rem;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-tag); font-weight: 700; letter-spacing: .2em;
  text-transform: uppercase; color: var(--ink-ghost);
}
.tool-folio svg { width: 17px; height: 17px; color: var(--leaf); }
.tool-copy h3 {
  font-size: clamp(1.6rem, 3vw, 2.15rem); line-height: 1.12; letter-spacing: -.015em;
  color: var(--forest-deep); text-wrap: balance; max-width: 18ch;
}
.tool-copy p { color: var(--ink-soft); max-width: 44ch; text-wrap: pretty; }
.tool-copy p b { color: var(--ink); font-weight: 600; }
.tool-more {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: var(--t-small); font-weight: 600; color: var(--forest);
  text-decoration: none; padding-bottom: 2px;
  border-bottom: 1px solid var(--line-strong); transition: gap .2s var(--ease), border-color .2s ease;
}
.tool-more:hover { gap: .7rem; border-bottom-color: var(--forest); }
.tool-plate { position: relative; z-index: 1; }
.tool-plate:has(.is-dragging) { z-index: 6; }

/* the plate: a mounted sheet with a caption strip, like a plate in a field guide */
.plate {
  position: relative; padding: 10px 10px 0;
  background: #f2ead7;
  border: 1px solid var(--line); border-radius: 4px;
  box-shadow: var(--lip), var(--lift-3);
}
.plate-body {
  position: relative; border-radius: 2px; overflow: hidden;
  background: #faf5e7;
  box-shadow: inset 0 0 0 1px rgba(43,38,24,.1), inset 0 2px 6px -3px rgba(43,38,24,.2);
}
/* Bubbles and the notebook nudge can be picked up; they must be able to leave
   the mount without being clipped by the plate's inner window. */
.tool-plate .plate-body { overflow: visible; }
.plate-cap {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: .55rem .2rem .6rem;
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .625rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-ghost);
}
.plate-cap em { font-style: normal; color: var(--ink-soft); }

/* ── the transcript inside a plate: a real exchange, set as chat on paper ── */
.script { display: flex; flex-direction: column; gap: .85rem; padding: 1.25rem 1.25rem 1.4rem; }
.script-line { display: flex; flex-direction: column; gap: .3rem; max-width: 88%; }
.script-line.them { align-self: flex-start; }
.script-line.you  { align-self: flex-end; align-items: flex-end; text-align: right; }
.script-line[data-drag] { position: relative; }
.script-who {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .5625rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-ghost);
}
.script-bubble {
  padding: .6rem .85rem; border-radius: 3px; font-size: .9375rem; line-height: 1.5;
  border: 1px solid var(--line); box-shadow: var(--lip-soft), var(--lift-1);
}
.script-line.them .script-bubble { background: #eff2e7; color: var(--ink); }
.script-line.you  .script-bubble { background: #f6eedc; color: var(--ink-soft); }
.script-bubble b { color: var(--forest-deep); font-weight: 600; }

/* ── the notebook plate: ruled paper with working on it ── */
.sketch {
  position: relative; aspect-ratio: 16/11;
  background-color: #faf6e9;
  background-image:
    repeating-linear-gradient(0deg, transparent 0 25px, rgba(109,148,155,.3) 25px 26px);
}
.sketch svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.sketch-nudge {
  position: absolute; right: 4%; bottom: 6%; max-width: 54%;
  display: flex; align-items: center; gap: .5rem;
  padding: .5rem .75rem; border-radius: 3px;
  background: #edf1e5;
  border: 1px solid var(--line); box-shadow: var(--lip-soft), var(--lift-2);
  font-size: .875rem; line-height: 1.4; color: var(--ink);
}
.sketch-nudge svg { position: static; width: 15px; height: 15px; flex: none; color: var(--leaf); }

/* ── the quiz plate: a marked test ── */
.quiz { padding: 1.15rem 1.25rem 1.35rem; display: flex; flex-direction: column; gap: .7rem; }
.quiz-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding-bottom: .55rem; border-bottom: 1px solid var(--line);
}
.quiz-title { font-weight: 600; color: var(--forest-deep); font-size: .9375rem; }
.quiz-score { font-family: 'Courier Prime', ui-monospace, monospace; font-size: .8125rem; font-weight: 700; color: var(--forest); }
.quiz-q { display: flex; gap: .6rem; align-items: flex-start; font-size: .875rem; line-height: 1.45; }
.quiz-q > svg { width: 16px; height: 16px; flex: none; margin-top: .18rem; }
.quiz-q.ok > svg { color: var(--leaf); }
.quiz-q.no > svg { color: var(--seal); }
.quiz-q p { color: var(--ink); }
.quiz-q .picked { color: var(--ink-ghost); display: block; font-size: .8125rem; }
.quiz-note {
  margin-left: 2.2rem; padding: .5rem .7rem; border-radius: 3px; font-size: .8125rem; line-height: 1.45;
  background: rgba(143,59,44,.07); border: 1px solid rgba(143,59,44,.18); color: var(--ink-soft);
}


/* ═══ THE GARDEN ═════════════════════════════════════════════════════════════ */
/* The left column now opens with the chapter head, so the two columns start on
   the same line and `start` is the only correct alignment — centred would float
   the heading down the page away from the rule that introduces it. */
.garden-grid {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2rem, 5vw, 4rem); align-items: start;
}
.garden-copy { display: flex; flex-direction: column; gap: clamp(1.25rem, 2.6vw, 1.75rem); }
.garden-stage { display: flex; flex-direction: column; }
.garden-note {
  padding: 1.15rem 1.25rem; border-radius: 3px;
  background: rgba(239,242,231,.83);
  border: 1px solid var(--line); box-shadow: var(--lip-soft), var(--lift-1);
}
.garden-note .tag { margin-bottom: .65rem; }
.garden-note p { color: var(--ink-soft); font-size: var(--t-small); }

/* Numbered field notes: a three-up strip across the foot of the section, each
   entry hung under its own rule like a column head. The folio numeral does the
   work three identical green ticks were doing badly, and is aria-hidden — an
   <ol> already announces the order. */
.field-notes {
  list-style: none; grid-column: 1 / -1;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2.25rem);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}
.field-notes li {
  display: flex; gap: .7rem; align-items: baseline;
  color: var(--ink-soft); font-size: var(--t-small); line-height: 1.55;
  padding-top: .85rem; border-top: 1px solid var(--line-strong);
}
.field-notes .fn-no { flex: none; color: var(--leaf); font-weight: 700; }
.field-notes li b { color: var(--ink); font-weight: 600; }

.check-list { list-style: none; }
.check-list li {
  display: flex; gap: .75rem; align-items: flex-start;
  color: var(--ink-soft); font-size: var(--t-small);
  padding-block: .8rem; border-top: 1px solid var(--line);
}
.check-list li:first-child { border-top: none; }
.check-list li svg { width: 1.05rem; height: 1.05rem; color: var(--leaf); flex: none; margin-top: .22rem; }
.check-list li b { color: var(--ink); font-weight: 600; }

.sky-stage {
  position: relative;
  aspect-ratio: 16/10;
  overflow: hidden;
  /* If the live canvas is a frame late, this is sky-dust, not cream paper. */
  background: #c4b7a0;
}
/* PINNED TO THE TOP, and the direction matters more than it looks.

   The engine's buffer is ~13% taller than the stage: resize() pads it against a
   pond lip sized from an older layout (groundY at 0.94H, ry of W*0.12). The live
   draw puts groundY at 0.9H and caps the pond's ry at 0.088H, so the scene
   finishes by ~0.99H and that pad holds nothing but more sky gradient.

   Pinned to the BOTTOM, the empty pad was the one part of the buffer guaranteed
   to be on screen. Everything drawn sat 13% of the frame too high: the canopy
   was cut off by the top of the plate, and the height it vacated showed up as
   bare ground under the pond. Both complaints, one cause.

   Pinned to the top, drawing y maps 1:1 onto the stage, the ground lands at the
   0.9H the engine intends, and the strip `overflow: hidden` clips is the empty
   pad it was always meant to be.

   (width:100% + height:auto rather than height:100% + object-fit — object-fit
   fought the bitmap and left a cream strip down one side.) */
.sky-stage canvas {
  position: absolute; left: 0; right: 0; top: 0; bottom: auto;
  width: 100% !important; height: auto !important;
  max-width: none;
}
#skyCaption {
  display: inline-block; min-width: 12ch;
  transition: opacity .45s ease;
}

/* the weather dial — same buttons landing-sky-plate.js already drives */
.sky-controls {
  display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center;
  margin-top: .85rem;
}
.sky-pill {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .625rem; letter-spacing: .12em; text-transform: uppercase;
  padding: .42rem .7rem; border-radius: 999px; cursor: pointer;
  color: var(--ink-faint);
  background: #f4ecd9;
  border: 1px solid var(--line-strong);
  box-shadow: var(--lip), var(--lift-1);
  text-shadow: 0 1px 0 rgba(255,255,255,.8);
  transition: transform .12s var(--ease), color .35s ease, background-color .45s ease, border-color .45s ease, box-shadow .35s ease, text-shadow .35s ease;
}
.sky-pill:hover { color: var(--forest-deep); }
.sky-pill:active { transform: translateY(1px); }
.sky-pill.is-active {
  background: var(--forest);
  border-color: #1f3324; color: #f2ecdd;
  text-shadow: 0 1px 0 rgba(0,0,0,.3);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), var(--lift-1);
}


/* ═══ THE LEDGER (impact) ════════════════════════════════════════════════════
   Accounting paper. The carbon claim is a set of books, so it is presented as
   one: a red margin rule, ruled lines, tabular figures, and a stamp at the foot
   that links to the page where every constant is shown its working.
   ═══════════════════════════════════════════════════════════════════════════ */
.ledger {
  position: relative; padding: clamp(1.5rem, 3.2vw, 2.4rem) clamp(1.5rem, 4vw, 3rem);
  padding-left: clamp(2.5rem, 6vw, 4.5rem);
  background: #faf5e7
    repeating-linear-gradient(0deg, transparent 0 35px, rgba(109,148,155,.3) 35px 36px);
  border: 1px solid var(--line); border-radius: 3px;
  box-shadow: var(--lip), var(--lift-3);
  overflow: hidden;
}
.ledger::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: clamp(1.4rem, 3.4vw, 2.6rem);
  width: 1px; background: rgba(143,59,44,.55);
}
.ledger::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: calc(clamp(1.4rem, 3.4vw, 2.6rem) + 4px);
  width: 1px; background: rgba(143,59,44,.28);
}

/* The gap under the figures and the gap above the rows used to be 2.25rem each,
   which stacked into 72px of blank ruled paper in the middle of the card — the
   one place a set of books should look full. Both trimmed; the rule between
   them is what separates the two halves, not the distance. */
.ledger-figures {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-bottom: clamp(1.15rem, 2.2vw, 1.6rem);
  border-bottom: 1px solid var(--line-strong);
}
.figure .fig-num {
  display: flex; align-items: baseline; gap: .1rem;
  font-size: clamp(2.5rem, 5.4vw, 3.75rem); font-weight: 500; line-height: 1;
  letter-spacing: -.03em; color: var(--forest-deep);
}
.figure .fig-num .unit { font-size: .42em; font-weight: 500; color: var(--leaf); letter-spacing: 0; }
.figure .fig-lab {
  margin-top: .65rem; font-size: var(--t-small); line-height: 1.5; color: var(--ink-soft);
  max-width: 30ch;
}

.ledger-rows { display: grid; gap: 0; margin-top: clamp(1.15rem, 2.2vw, 1.6rem); }
.ledger-row {
  display: grid; grid-template-columns: auto minmax(0, 15ch) minmax(0, 1fr);
  gap: clamp(.85rem, 2.5vw, 1.5rem); align-items: baseline;
  padding-block: 1.05rem; border-top: 1px solid var(--line);
}
.ledger-row:first-child { border-top: none; }
.ledger-row .rowmark { width: 22px; height: 22px; align-self: center; flex: none; }
.ledger-row.debit  .rowmark { color: var(--seal); }
.ledger-row.credit .rowmark { color: var(--leaf); }
.ledger-row h3 {
  font-size: 1.0625rem; font-weight: 600; color: var(--forest-deep); line-height: 1.3;
}
.ledger-row.debit h3 { color: var(--seal-deep); }
.ledger-row p { font-size: var(--t-small); color: var(--ink-soft); line-height: 1.55; }

.ledger-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.25rem;
  margin-top: clamp(1.15rem, 2.2vw, 1.5rem); padding-top: 1rem;
  border-top: 1px solid var(--line-strong);
}
.ledger-foot p { font-size: .8125rem; color: var(--ink-faint); max-width: 62ch; flex: 1 1 40ch; }
.ledger-foot .stamp {
  text-transform: none; letter-spacing: .06em; text-decoration: none;
  cursor: pointer; opacity: 1;
  transition: background-color .18s ease, color .18s ease;
}
.ledger-foot .stamp:hover {
  color: var(--seal-deep);
  background: rgba(143,59,44,.08);
}


/* ═══ PRICING — TWO SEED PACKETS ═════════════════════════════════════════════
   A plan is a thing you are handed and then plant. The packets are built like
   real ones: a printed head, an illustration window, a torn-open top edge on
   the free one, and the sowing instructions on the back as a check-list.
   ═══════════════════════════════════════════════════════════════════════════ */
.packets {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
  /* stretch, not start: the two packets carry different numbers of lines and
     were ending 42px apart — one row of the check-list — which threw the
     window, the button and the note out of line across the pair */
  align-items: stretch;
}
.packet {
  position: relative; display: flex; flex-direction: column;
  border-radius: 3px; overflow: hidden;
  border: 1px solid var(--line-strong);
  background: #f1e9d5;
  box-shadow: var(--lip), var(--lift-3);
  transition: transform .3s var(--ease), box-shadow .3s ease;
}
.packet:hover { transform: translateY(-4px); box-shadow: var(--lip), 0 4px 8px rgba(43,38,24,.12), 0 34px 54px -28px rgba(43,38,24,.55); }
.packet-crease {
  height: 26px; flex: none;
  background: #ede3c9;
  border-bottom: 1px dashed var(--line-strong);
  box-shadow: inset 0 -3px 5px -3px rgba(43,38,24,.2);
}
.packet-head {
  padding: 1.15rem 1.35rem 1rem; text-align: center;
  border-bottom: 1px solid var(--line);
}
.packet-pro .packet-head {
  background: rgba(52,80,58,.06);
}
.packet-name {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: .6875rem; font-weight: 700; letter-spacing: .28em; text-transform: uppercase;
  color: var(--ink-faint);
}
.packet-price {
  display: flex; align-items: baseline; justify-content: center; gap: .15rem;
  margin-top: .35rem; font-size: clamp(2.75rem, 6vw, 3.5rem); font-weight: 500;
  line-height: 1; letter-spacing: -.035em; color: var(--forest-deep);
}
.packet-price .per { font-size: .9rem; font-weight: 400; letter-spacing: 0; color: var(--ink-ghost); font-family: 'Courier Prime', monospace; }
.packet-sub { margin-top: .5rem; font-size: .8125rem; color: var(--ink-faint); }
/* A corner ribbon has to be wider than the corner it crosses, or the clip on
   .packet cuts the words. 190px at 45° comfortably spans a 3px-radius corner. */
.packet-badge {
  position: absolute; top: 32px; right: -52px; z-index: 2;
  display: block; width: 190px; padding: .32rem 0;
  transform: rotate(45deg); transform-origin: center; text-align: center;
  background: var(--forest); color: #f2ecdd;
  font-family: 'Courier Prime', monospace; font-size: .625rem; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase;
  box-shadow: 0 2px 5px rgba(43,38,24,.35), inset 0 1px 0 rgba(255,255,255,.16);
}
/* The window takes up the slack. Whichever packet lists fewer things gets the
   difference as paper at the foot of its window, which leaves the button and
   the note on the same line in both — the two places the eye actually compares. */
.packet-window {
  flex: 1 1 auto;
  margin: 1.15rem 1.35rem; padding: .9rem 1rem; border-radius: 3px;
  background: #f9f3e5;
  box-shadow: inset 0 0 0 1px rgba(43,38,24,.12), inset 0 2px 5px -3px rgba(43,38,24,.25);
}
.packet-window .typed { display: block; margin-bottom: .5rem; }
.packet-window .check-list li { padding-block: .55rem; font-size: .875rem; }
.packet-foot { padding: 0 1.35rem 1.35rem; display: flex; flex-direction: column; gap: .6rem; align-items: stretch; }
.packet-foot .btn { width: 100%; }
/* Two lines are reserved whether or not the note needs them. Pro's note wraps
   at narrower column widths and Free's does not, which pushed the two buttons
   17px out of line — the note is the last thing in the card, so its height is
   the one difference the flexing window above it cannot absorb. */
.packet-note {
  font-size: .75rem; color: var(--ink-ghost); text-align: center;
  line-height: 1.5; min-height: 2.25rem;
}


/* ═══ COLOPHON & FOOTER ══════════════════════════════════════════════════════ */
.colophon {
  padding-block: clamp(2.5rem, 4.6vw, 3.5rem);   /* the chapters' rhythm */
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(229,217,189,.4), transparent 60%);
  text-align: center;
}
/* A flat 1rem between every element gave the mark, the line, the hand and the
   button equal weight, which is what made the page's last word feel like a
   stack of centred items rather than a sign-off. The gaps are graded instead:
   the hand line belongs to the headline it follows, the button stands apart. */
.colophon-inner { display: flex; flex-direction: column; align-items: center; gap: .85rem; }
.colophon .hand { margin-top: -.15rem; }
.colophon .btn { margin-top: .6rem; }
.colophon-note {
  font-size: var(--t-small); color: var(--ink-faint);
}
.colophon img { width: 62px; height: 62px; object-fit: contain; opacity: .92; }
.colophon h2 {
  font-size: var(--t-display); font-weight: 300; line-height: 1.06; letter-spacing: -.025em;
  color: var(--forest-deep); max-width: 16ch;
}
.colophon h2 .accent { color: var(--leaf); font-style: italic; font-weight: 400; }
.colophon .hand { font-size: 1.5rem; }

footer { padding-block: clamp(2.5rem, 5vw, 3.5rem); border-top: 1px solid var(--line-strong); }
footer::before { content: ""; display: block; height: 1px; background: var(--line); margin-top: -3px; margin-bottom: 1.5rem; }
.foot-grid {
  display: grid; grid-template-columns: minmax(0, 1.2fr) repeat(3, minmax(0, 1fr));
  gap: 2rem clamp(1.5rem, 4vw, 3rem); align-items: start;
}
.foot-brand { display: flex; flex-direction: column; gap: .6rem; align-items: flex-start; }
.foot-brand .typed { max-width: 30ch; text-transform: none; letter-spacing: .04em; line-height: 1.7; }
/* The one thing in the footer that is not navigation.
   It sits in the brand column rather than among the three link lists because a
   button dropped into a column of links reads as a link that has been shouted;
   here it has that column's empty space to itself.
   Ghost rather than primary: "Start free" sits in the colophon a few
   centimetres above this, and two filled buttons that close together argue
   about which one is the page's call to action. Sized down from the default
   .btn to the footer's own smaller type, the way .rail .btn is. */
.foot-donate { margin-top: .5rem; padding: .6rem 1.25rem; font-size: var(--t-small); }
.foot-col h4 {
  font-family: 'Courier Prime', ui-monospace, monospace;
  font-size: var(--t-tag); font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: .85rem;
}
.foot-col a { display: block; font-size: var(--t-small); color: var(--ink-soft); text-decoration: none; padding-block: .28rem; }
.foot-col a:hover { color: var(--forest-deep); }
.foot-end {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: 1.75rem; padding-top: 1.35rem; border-top: 1px solid var(--line);
}
.foot-copy { font-size: .8125rem; color: var(--ink-ghost); }
.foot-social { display: flex; gap: .35rem; }
.soc {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 3px; color: var(--ink-faint);
  border: 1px solid transparent; transition: color .18s ease, background-color .18s ease, border-color .18s ease;
}
.soc:hover { color: var(--forest-deep); background: var(--paper-panel); border-color: var(--line); }
.soc svg { width: 17px; height: 17px; }


/* ═══ MOTION ═════════════════════════════════════════════════════════════════
   One reveal, applied by group: the container gets .rvg-in and every .reveal
   inside it lands, offset by --rd. Nothing is ever hidden without JS, because
   the class that hides is only added by JS.
   ═══════════════════════════════════════════════════════════════════════════ */
.js-reveal .reveal { opacity: 0; translate: 0 18px; scale: .975; }
.js-reveal .rvg-in .reveal, .js-reveal .reveal.rvg-in {
  opacity: 1; translate: 0; scale: 1;
  transition: opacity .6s ease var(--rd, 0s),
              translate .75s var(--ease) var(--rd, 0s),
              scale .75s var(--ease) var(--rd, 0s);
}
.rv-1 { --rd: .08s; } .rv-2 { --rd: .16s; } .rv-3 { --rd: .24s; } .rv-4 { --rd: .32s; }

.grow-head .masthead { animation: fade-up .9s var(--ease) backwards; }
.grow-head h1 { animation: fade-up .9s var(--ease) .1s backwards; }
@keyframes fade-up { from { opacity: 0; transform: translateY(14px); } }


/* ═══ RESPONSIVE ═════════════════════════════════════════════════════════════
   The bench thins out rather than shrinking: ephemera are dropped in order of
   how little they carry, and the layout goes to one column before the type
   ever has to get small.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1180px) {
  .e-spec-sunset { --x: 2%;  --w: 150px; }
  .e-spec-rain   { --x: 85%; --w: 136px; }
  .e-scrap-ask   { --x: 1.5%; --w: 190px; }
  /* The can keeps its corner all the way down; the fern and the packet are
     already gone by here, gated above. */
  .e-sprout, .e-rain-doodle { display: none; }
  .e-can { --x: 3%; --y: 71%; width: 72px; }
}
@media (max-width: 1000px) {
  .rail-links { display: none; }
  .conditions { display: none; }
  .tool-row, .tool-row.alt { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  /* Stacked, the tally would otherwise run the full measure and strand its
     labels a screen-width away from their own figures. Capped near the width it
     has beside the title, so the row still reads left-to-right as one line. */
  .tally { max-width: 34rem; }
  .tool-row.alt .tool-copy { order: 1; }
  .tool-row.alt .tool-plate { order: 2; }
  .tool-row.dim { opacity: 1; filter: none; }
  .garden-grid { grid-template-columns: minmax(0, 1fr); }
  /* One column, so the strip becomes a list again. */
  .field-notes { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .field-notes li { padding-bottom: .85rem; }
  .field-notes li:last-child { padding-bottom: 0; }
  /* Stacked, a split head is just a head — one column, in source order. */
  .chapter-head.split {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "rule" "tag" "h2" "lead";
  }
  .chapter-head.split > .lead { align-self: start; max-width: var(--measure); }
  .ledger-figures { grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
  .figure .fig-lab { max-width: none; }
  .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Below 1000px the bench runs out of margin: the ruler owns the right-hand
     strip and the tree owns the middle, so anything that would have to sit
     between them is dropped rather than squeezed. */
  .e-leaf-b, .e-stamp, .e-snail, .e-spec-rain, .e-leaf-a, .e-packet, .e-can { display: none; }
  /* the hang tag goes too: between the wordmark, the canopy and the ruler
     there is nowhere left for it, and the ruler is already showing grams */
  .e-hangtag { display: none; }
  .e-spec-sunset { --x: 1%; --y: 15%; --w: 136px; }
  .e-scrap-ask   { --x: 1%; --y: 52%; --w: 194px; }
  /* with the chapter links gone the rail has nothing pushing its right-hand
     group over, so give that group the auto margin instead */
  .rail-end { margin-left: auto; }
}
@media (max-width: 760px) {
  :root { --t-wordmark: clamp(2.9rem, 17vw, 4.6rem); }
  .grow-scene { height: 250vh; }
  .grow-sticky { min-height: 560px; }
  .grow-tree { padding: 8.5rem .75rem 12rem; }
  .grow-ruler { display: none; }
  .bench-line { bottom: 11.5rem; }
  /* On a phone the bench clears completely. There is one screen's worth of
     room, the tree needs most of it, and every object left on the surface ends
     up sitting on a branch. The paper, the grid, the masthead rules and the
     hand-written line carry the character here; the props come back the moment
     there is a margin to put them in. */
  .eph { display: none; }
  .tidy { display: none; }
  .ledger-row { grid-template-columns: auto minmax(0, 1fr); }
  .ledger-row p { grid-column: 2; }
  .foot-grid { grid-template-columns: minmax(0, 1fr); }
  .script-line { max-width: 100%; }
}
@media (max-width: 420px) {
  .hero-actions { flex-direction: column; align-self: stretch; }
  .hero-actions .btn { width: 100%; }
}

/* Very short viewports (a laptop with every toolbar open): the pinned scene
   would otherwise crop the tree, so let the page scroll normally instead. */
@media (max-height: 620px) and (min-width: 761px) {
  .grow-scene { height: auto; }
  .grow-sticky { position: static; height: auto; min-height: 0; padding-bottom: 3rem; }
  .grow-tree { position: relative; inset: auto; height: 62vh; padding: 1rem; }
  .eph, .grow-hint, .bench-line { display: none; }
  .grow-cta { visibility: visible; opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .js-reveal .reveal { opacity: 1; translate: 0; scale: 1; }
  .ticker-track { animation: none; }
  .grow-cta { opacity: 1; visibility: visible; transform: none; }
  .grow-hint { display: none; }
}
