/* The soil was a plain tonal ramp with a 1px hairline every 92px over it,
   meant to read as strata. It did not: at rgba(255,255,255,.02) over near-black
   the lines are too faint to be rock and too regular to be anything but a
   rendering artefact, and they stopped dead at the burrow, adding to the seam
   below. Removed 2026-07-29. If strata are wanted, they need irregular spacing
   and a warm tint, not a repeat. */
/* Where the soil bottoms out. Named because two rules a screen apart have to
   agree on it — see the burrow below. */
.enhanced { --soil-floor: #0A0808; }

/* The top of the soil is the bottom edge of the near band, sampled from the
   art and written by main.js as the day turns — so the seam between the last
   row of painted ground and the first row of soil is not a match that has to be
   maintained by hand, it is the same colour by construction. The fallback is
   the fixed brown this ramp used until 2026-07-29, which is what shows for the
   one frame before the clock's first tick.

   The middle stops are mixes rather than literals because the top is no longer
   a known colour: three of the four sampled values are DARKER than the browns
   that used to sit below them, and a ramp with fixed middles would have lit up
   under its own start and read as a band at the seam. At 62% and 31% of the way
   down to the floor these reproduce the old #1A1410 and #120E0C to within a
   unit per channel, so with the fallback in force the section is unchanged.

   No color-mix means the whole declaration drops and .underground falls back to
   flat --soil from base.css — the same place it lands with JS off. */
/* Three layers, top to bottom in the list. The soil ramp is the bottom one and
   is described above; the two over it are the ground line, which sampling
   --soil-top removed along with the seam it was hiding in. The seam was an
   accidental colour mismatch and deserved to go. The ground line was never
   drawn at all — it was being faked by that mismatch — so it comes back here as
   light rather than as an edge. See docs/ground-line-spec.md.

   Neither needs measuring. This element's own top edge IS the ground line, so
   both anchor at 0%, and the mouth is always at horizontal centre — geometry.js
   puts the first vertex at xMid and a test asserts it — so the pool anchors at
   50%. No JS touches these beyond the colour.

   --sky-ground is the sky at the horizon, written by main.js as the day turns.
   Its fallback is the midnight horizon, the same colour surface.css shows
   before the clock's first tick, so the unlit frame is lit consistently. */
.enhanced .underground {
  position: relative;
  padding: 0;
  background:
    /* the pool: brightest at the hole, because that is where light gets in */
    radial-gradient(32% 340px at 50% 0%,
      color-mix(in srgb, var(--sky-ground, #2A3348) 22%, transparent), transparent),
    /* the wash: faint, full width, so the ground line reads at the edges too */
    linear-gradient(180deg,
      color-mix(in srgb, var(--sky-ground, #2A3348) 10%, transparent) 0%, transparent 180px),
    linear-gradient(180deg,
      var(--soil-top, #241C13) 0%,
      color-mix(in srgb, var(--soil-top, #241C13) 62%, var(--soil-floor)) 26%,
      color-mix(in srgb, var(--soil-top, #241C13) 31%, var(--soil-floor)) 60%,
      var(--soil-floor) 100%);

  /* Baked by tools/build-crust.py — paste its output here, do not hand-edit. */
  --crust-tile: url("../assets/terrain/crust-earth.webp?v=1");
  --crust-aspect: 9.6970;  /* tile w/h */
  --crust-rise: 0.3333;    /* fraction of the asset that is ragged top */

  --crust-h: clamp(30px, 3.4vw, 52px);
  --crust-light: 26%;
  --crust-show: 1;
}

/* The crust: what stops the ground line reading as a clean horizontal chord.
   8f gave the line its own light and that was the right fix for the COLOUR;
   what was left is that nothing crosses the line, so the eye reads a boundary.
   A ragged band of lit earth sitting astride it breaks the straightness across
   the width. The tree does the same job properly, at one x — this does it
   everywhere the tree cannot reach. See docs/foreground-spec.md.

   The colour is NOT in the asset. It is --soil-top lit toward --sky-ground,
   both of which main.js already lerps as the day turns, so one file is correct
   at all four times and cannot drift out of match with the soil the way four
   painted crusts would. Same argument as 8e, one layer up.

   The asset is alpha only, so it is a mask rather than a picture, and it
   repeats on x. Nothing measures anything: this element's top edge IS the
   ground line, so --crust-rise — the fraction of the art above the solid body,
   measured by the build tool — is all it takes to sit the teeth above the line
   and the body below it in the soil. No JS at all.

   z-index 1 puts it over the terrain band (z 0) and under the tunnel (z 2), so
   the mouth paints over the crust rather than the crust bridging the hole.
   --crust-show: 0 is the off switch; --crust-light alone is not, because the
   teeth would still break the silhouette even when they match the soil. */
.enhanced .underground::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(-1 * var(--crust-rise) * var(--crust-h));
  height: var(--crust-h);
  z-index: 1;
  pointer-events: none;
  opacity: var(--crust-show);
  background-color: color-mix(in srgb,
    var(--soil-top, #241C13), var(--sky-ground, #2A3348) var(--crust-light));
  -webkit-mask-image: var(--crust-tile);
          mask-image: var(--crust-tile);
  -webkit-mask-repeat: repeat-x;
          mask-repeat: repeat-x;
  -webkit-mask-size: calc(var(--crust-h) * var(--crust-aspect)) var(--crust-h);
          mask-size: calc(var(--crust-h) * var(--crust-aspect)) var(--crust-h);
}

/* Kept next to the rule above because of a constraint that spans both: the
   burrow's FIRST stop must equal the underground's LAST one, or the section
   boundary draws a line across the page. That is now --soil-floor in both
   places rather than the same literal twice. It is deliberately NOT `--soil`,
   which is what the unenhanced rule in base.css matches instead.
   The ramp then warms downward rather than darkening. Descending soil that
   keeps getting darker and then stops is a dead end; a chamber that lifts
   slightly at the bottom reads as somewhere to arrive. Nothing follows the
   burrow, so its last stop answers to no other colour — and nothing above it
   reaches this far, so the burrow stays fixed while the soil above it turns. */
.enhanced .burrow {
  background: linear-gradient(180deg, var(--soil-floor) 0%, #100C0A 46%, #17110D 100%);
}

/* The carrot at the end of the dig descends behind this heading, and "behind"
   is doing exact work: it has to pass IN FRONT OF the gradient above and BEHIND
   the words. The section itself stays unpositioned, so its background sits under
   the overlay's z-index 2 and the carrot paints over it; the children are lifted
   past the overlay so the type stays on top. Lifting the section instead would
   hide the carrot entirely behind that gradient, which is the obvious edit and
   the wrong one. */
.enhanced .burrow > * { position: relative; z-index: 5; }

/* ------------------------------------------------------------- the prize */

/* One image, drawn twice, muted here and lit in .prize-lit. This copy is buried:
   it paints before the tunnel, so the wall covers whatever crosses it, and what
   is left is the part standing in earth. Dim enough to read as a shape sensed
   through soil rather than an object in a room, but not so dim that the carrot
   is a surprise when the rabbit reaches it — you should half-see it coming.

   --prize-buried is the dial. 1 would make both copies identical and lose the
   effect entirely; 0 hides the buried half and leaves only the lit shoulder. */
.enhanced .prize-buried {
  --prize-buried: .3;
  opacity: var(--prize-buried);
  filter: saturate(.4) brightness(.6);
}

/* The overlay spans the whole document so that geometry's page-space
   coordinates land correctly. It sits above the soil but below the pockets. */
.enhanced main { position: relative; }

.enhanced .overlay {
  display: block;
  position: absolute;
  inset: 0 0 auto 0;
  width: 100%;
  z-index: 2;
  pointer-events: none;
}

.enhanced .stage {
  display: block;
  position: absolute;
  inset: 0 0 auto 0;
  width: 100%;
  overflow: visible;
}

/* The foreground object. Everything else in this scene sits BEHIND the rabbit,
   which is most of why the picture reads flat, and nothing crosses the ground
   line, which is the rest of why the line reads as a boundary. One object
   fixes both. See docs/foreground-spec.md.

   Always two elements — one above the line, one below — because the two halves
   want different fills. They meet at the ground line and line up for free: the
   build tools pad each asset symmetrically about its own measured join centre,
   so centring both on --fore-x is the whole of the alignment and there is no
   offset constant to go stale at a viewport nobody tested.

   THE FIRST ATTEMPT WAS A WHOLE TREE AND IT FAILED ON THE PAGE (2026-07-29).
   Franck's reading: the bare upper branches read as a dead tree, the limbs got
   clipped by the sides of the viewport, and the composition was not improved —
   "that tree should probably have been integrated from the start for this to
   work". The lesson is about scope, not craft. A full canopy is a composition
   decision about the whole frame and cannot be dropped into a finished one;
   what works instead is an object small enough to be a detail. Both variants
   below are that. tree.webp is still built and still baked, deliberately
   unwired — the swap is here if it is ever wanted with the scene recomposed.

   PARALLAX RATE IS ZERO, and that is forced rather than chosen. Every instinct
   says a nearer plane should move faster than the near band's 0.08. It cannot:
   whatever is below the line is buried in soil that moves with the page, and
   any rate at all slides it through the dirt it is planted in. So this is a
   z-order change, not a new plane, and it needs no JS and no frame-loop work. */
.enhanced .fore {
  /* Between where the rabbit starts and the hole he runs at, which is always
     horizontal centre. He passes behind it on the way in. Moved out from 32%
     on 2026-07-29: at 32% it crowded his starting mark. */
  --fore-x: 40%;

  --fore-tint: 22%;   /* how much sky is in the above-ground silhouette */
  --fore-show: 1;     /* 0 takes the whole thing off the page */

  display: block;
  position: absolute;
  inset: 0 0 auto 0;
  width: 100%;
  pointer-events: none;
  opacity: var(--fore-show);
}

/* --ground-y IS the ground line: geometry works in page space where y = 0 is
   the top of the document and groundY equals the viewport height, and this
   element starts at the document top.

   It was 100vh, with a note saying the assumption was soft on mobile because
   100vh and window.innerHeight disagree while the URL bar moves. It was not
   soft, it was wrong: 100vh is the viewport with the bar HIDDEN and innerHeight
   is the viewport as it stands, so the foreground sat a bar-height below the
   line the geometry had been given. main.js now writes --ground-y from exactly
   the number it passes to buildGeometry, so there is no second source left to
   drift. Keep the 100vh fallback: it is what a frame before JS gets, and it is
   also the old behaviour, so nothing regresses if the property goes missing. */
.enhanced .fore > * {
  display: block;
  position: absolute;
  left: var(--fore-x);
  transform: translateX(-50%);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

.enhanced .fore-above {
  top: calc(var(--ground-y, 100vh) - var(--above-h));
  width: var(--above-w);
  height: var(--above-h);
  /* Above the line everything in this scene is a silhouette — the cabin, the
     treeline, the near band. Anything carrying its own colour up here would be
     the one lit object on the surface and would read as pasted on. So it is a
     mask, filled from --sky-ground, which main.js already lerps: it retints
     continuously across the day rather than in four steps, and one mask cannot
     violate 8c the way four relights of a generated silhouette would. */
  background-color: color-mix(in srgb, var(--sky-ground, #2A3348) var(--fore-tint), #0A0808);
  -webkit-mask-image: var(--above-tile);
          mask-image: var(--above-tile);
}

.enhanced .fore-below {
  top: var(--ground-y, 100vh);
  width: var(--below-w);
  height: calc(var(--below-w) / var(--below-aspect));
}

/* ------------------------------------------------------------------ carrot */

/* The shipping variant, and the one that pays off the headline: *Most of the
   work is under the surface* is that sentence as an object — a small tuft
   above the line and the whole body below it. It also drops the collision the
   tree roots could not: a root fan is wide and shallow and wants pocket 1's
   space on the left, where a carrot is narrow and vertical and can go deep
   without ever reaching the copy.

   Sized from the BODY, not the tops: the carrot is the hero and the leaves are
   its garnish, so --carrot-w is the one artistic number and the tops follow. */
.enhanced .fore--carrot {
  /* Swap -e for -f to try the other render: glossier, and closer to celery.
     Both are built every run of build-carrot.py. */
  --above-tile: url("../assets/terrain/carrot-tops-e.webp?v=4");

  /* Baked by tools/build-carrot.py — paste its output, do not hand-edit.
     -f is 0.7429 if you swap the tile above. */
  --above-aspect: 0.7084;  /* w/h */
  --below-aspect: 0.1280;  /* w/h */

  /* Each half is sized by the dimension you actually notice about it: the
     carrot by its WIDTH, because it is the hero and its length follows, and
     the tuft by its HEIGHT, because that is what the rabbit passes behind.

     --carrot-w is the BOX, not the vegetable: the asset is padded to centre
     its shoulder, so the carrot itself is about 76% of this (the tool prints
     that figure). 85px of box is roughly 65px of carrot and 660px of length —
     a third shorter than the first version, which is what Franck asked for.

     BOTH ARE FIXED PIXELS, WHICH IS THE WHOLE PROBLEM ON A PHONE. They do not
     vary with the viewport, so the same carrot that reads as a detail on a
     1440px desktop takes a quarter of a 390px screen. --carrot-scale is the one
     dial that fixes it, at the foot of this file. Do not retune the two lengths
     below to change the phone: they are the approved desktop sizes, they were
     arrived at on the page, and moving them moves both screens at once. */
  --carrot-scale: 1;
  --carrot-w: calc(85px * var(--carrot-scale, 1));
  --tops-h: calc(205px * var(--carrot-scale, 1));

  --below-w: var(--carrot-w);
  --above-h: var(--tops-h);
  --above-w: calc(var(--above-h) * var(--above-aspect));
}

/* The one thing on the surface that is not a silhouette. Everything else up
   here is, and that is the right default, but at noon the scene is blue sky
   and white mountains with nothing alive in it — so the leaves earn the
   exception (Franck, 2026-07-29).

   THREE ATTEMPTS, AND THE FIRST TWO ARE WORTH KNOWING ABOUT.

   1. Flat green computed inside the fill:

          color-mix(in srgb, var(--tops-green) calc(var(--daylight) * 94%), ...)

      It shipped and rendered plain grey. A calc() percentage built from a
      var(), nested inside a second color-mix, is enough to lose: when any part
      of it fails to resolve the WHOLE declaration is dropped and .fore-above's
      own silhouette fill wins. **The failure mode is a plausible picture, not
      a missing one** — it looks exactly like a correctly working green layer
      at midnight, so it reads as a colour that needs tuning, and no amount of
      picking greener hexes moves it. Anything driven by --daylight now goes
      through a plain `opacity`, which cannot fail this way.

   2. Flat green, correctly applied. Legible, and still wrong: a flat
      silhouette standing next to a fully rendered carrot root looked pasted
      on, because one object was being drawn in two languages at once. The
      surface silhouette rule lost to a stronger one — **an object that crosses
      the ground line has to agree with itself before it agrees with the plane
      it is standing in.**

   3. What is here: a rendered plate, painterly and matte to match the root,
      with the day baked into it. Colour is in the asset again, which is the
      opposite of what the crust and the tree do — justified because this is
      half an underground object, not a piece of the surface.

   The mask lives on the parent and masks its whole painted subtree, so the
   overlay below is clipped to the leaves without knowing anything about them. */
.enhanced .fore--carrot .fore-above {
  /* The render carries its own colour, so the silhouette fill inherited from
     .fore-above has to go. --above-tile serves twice — as the picture and as
     the mask that clips the overlay below to the leaves — which is one file
     and one request either way. */
  background-color: transparent;
  background-image: var(--above-tile);
}

/* Night is subtraction now, not addition. The plate is lit for noon, so what
   varies is how much of the scene's darkness is laid back over it: nothing at
   midday, most of it at midnight, where the leaves settle onto the same
   silhouette colour every other foreground object uses.

   PROVISIONAL. Franck's plan is to validate the day render and then bake four
   recoloured versions. This is the placeholder that keeps the other twenty
   hours honest in the meantime, and it may turn out to be enough on its own —
   if dawn and dusk read correctly with one plate dimmed, four files buy
   nothing. Judge that before generating them. */
.enhanced .fore--carrot .fore-above::after {
  --tops-night: 0.82;   /* how far toward silhouette the leaves go at midnight */

  content: "";
  position: absolute;
  inset: 0;
  background-color: color-mix(in srgb,
    var(--sky-ground, #2A3348) var(--fore-tint), #0A0808);
  opacity: calc((1 - var(--daylight, 0)) * var(--tops-night));
}

/* Real colour, not a mask, and the exception is deliberate. Underground there
   is no silhouette language to match and nothing else down there is coloured
   at all — which is exactly why this should be. It is the one warm object in
   the soil and the reward for scrolling. Computing it from --soil-top like
   everything else would produce a brown carrot, which is not a carrot.
   The join needs no hiding: leaves are dark and the root is orange, and real
   carrots change colour at soil level too.
   --carrot-sat is the dial if the orange shouts — 0 desaturates it to earth. */
.enhanced .fore--carrot .fore-below {
  --carrot-sat: 1;
  background-image: url("../assets/terrain/carrot-body.webp?v=1");
  filter: saturate(var(--carrot-sat));
}

/* ------------------------------------------------------ broken trunk (alt) */

/* The safe alternative: a snapped trunk, short enough not to claim vertical
   space, solid enough for the rabbit to pass behind. Swap the class in
   index.html from fore--carrot to fore--trunk. Its root fan is WIDE — 7.1x the
   trunk, which is what matching the two trunks costs with this pair of plates
   — so it reaches further toward pocket 1 than the carrot does. */
.enhanced .fore--trunk {
  --above-tile: url("../assets/terrain/trunk.webp?v=1");
  --below-tile: url("../assets/terrain/tree-roots.webp?v=1");

  /* Baked by tools/build-tree.py — paste its output, do not hand-edit. */
  --above-aspect: 0.3810;  /* w/h */
  --below-aspect: 1.7391;  /* w/h */
  --root-scale: 7.0990;    /* root width in trunk widths, so the trunks meet */

  --above-h: 34vh;
  --above-w: calc(var(--above-h) * var(--above-aspect));
  --below-w: calc(var(--above-w) * var(--root-scale));

  --root-light: 18%;  /* how far the roots are lit toward --sky-ground */
}

/* The gradient is what hides the join, and it is here because the crust cannot
   do it: the crust sits at z-index 1, under the tunnel, and this layer is
   inside .overlay at z-index 2, so the trunk paints OVER the crust rather than
   behind it. What would show is the fill changing colour abruptly across the
   trunk at the exact line. Starting the roots on the trunk's own dark and
   warming into lit earth over the first third removes the step entirely, and
   reads as roots coming out of the tree's shadow. */
.enhanced .fore--trunk .fore-below {
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--sky-ground, #2A3348) var(--fore-tint), #0A0808) 0%,
    color-mix(in srgb, var(--soil-top, #241C13), var(--sky-ground, #2A3348) var(--root-light)) 38%);
  -webkit-mask-image: var(--below-tile);
          mask-image: var(--below-tile);
}

/* ------------------------------------------------------------ tunnel walls */

/* The tunnels were the last thing on the page still reading as a website while
   everything around them had become a rendered world (Franck, 2026-07-30).
   Four causes, and only one of them was the missing texture:

   1. The bore was a two-stop gradient stretched over the whole descent — the
      last pure-vector fill on a page where the crust and the carrot both carry
      grain.
   2. NOT A CAUSE AT ALL, and this entry is kept because getting it wrong cost
      the rest of the evening. `.tunnel-shade` drew black at 0.8 bore OVER a
      full-width bore, darkening the MIDDLE and leaving a pale rim at each edge.
      That was called an inside-out cylinder and removed, on the sound-sounding
      grounds that a tube lit from its mouth is bright in the middle. Nobody was
      reading it as a lit tube: the dark middle read as EMPTY SPACE and the pale
      rim read as GROUND, which is how a cross-section should read, and the
      lower rim was the floor the rabbit stands on. Removing it made him float,
      and three passes of offset bands went into trying to put back what one
      concentric stroke had been doing. It is now `air` in LAYERS.
   3. `.tunnel-glow` stroked --accent around the whole thing. A brand-coloured
      outline on a shape is a UI affordance, not a hole in the ground.
   4. Constant width, perfect round caps, crisp vector boundary. Nothing about
      the silhouette had been dug.

   See docs/tunnel-wall-spec.md. The stack that replaces it is in LAYERS in
   src/js/tunnel.js, which sizes and dashes every layer from one list.

   THE LIGHT STAYS IN CSS AND THE DETAIL STAYS IN THE ASSET. That split is why
   the tile is generated flat-lit and multiplied rather than painted and
   dropped in: the depth fade, the throat and the mouth pool all still come
   from here, so one tile is correct the whole way down and cannot introduce a
   second light direction. Same argument as the crust one layer up, and the
   failure it avoids is carrot-tops attempt 2 at the scale of the whole page. */
.enhanced .tunnel {
  --wall-grain: 1;   /* 0 -> the bore is flat again, as it shipped before */

  /* How dark the empty middle of the tunnel goes, toward the soil's own colour.
     This is what leaves a band of lit ground at the top and bottom of the bore,
     and the lower band is the floor the rabbit stands on — at 0 he runs down the
     middle of a uniform ribbon and floats. See the note on `air` in LAYERS for
     why this is structure rather than lighting, and why removing it once cost
     three passes of trying to replace it with something more elaborate. */
  --air-dark: 0.42;

  /* THE CRUST AND A SEPARATE FLOOR TEXTURE ARE NOT HERE. Both were asked for on
     2026-07-30 and built three ways out of vertically-offset strokes — ceiling
     and floor bands, thin linings, a mask to contain them, six dials — and every
     version read on the page as painted stripes. The reason applies to all of
     them: **a stroke has no soft edge, so any band built from one is a stripe**,
     and more or smaller steps do not converge on a gradient. Concentric strokes
     like `air` are fine; it is the OFFSET ones that fail.

     AND THE TUNNEL DOES NOT GET ROUNDNESS. That is a settled answer now, not an
     outstanding task, because BOTH routes are closed at this object size.
     Strokes cannot do it, for the reason just given. A filter cannot either:
     url(#tubeShade), the textbook inner shadow off the group's own alpha, was
     built, switched on and rendered on 2026-07-30, and it showed **vertical
     banding** down the descent along with other artefacts. That is inherent
     rather than tunable — a page-height filter region is rasterised in TILES, and
     a blur reading across a tile boundary cannot see the neighbouring tile's
     pixels, so seams appear at the joins. It was deleted rather than left off.

     What remains in the slot is url(#wallCrumble) for the undug silhouette. It is
     a displacement map rather than a blur, so it is not obviously subject to the
     same seams, but it has never been rendered either — and after tubeShade the
     safe assumption is that any page-height filter on this object is suspect.
     One slot rather than a variable per effect, because `filter: none none` is
     invalid and two variables would produce exactly that whenever both were off. */
  --tunnel-filter: none;

  /* Confines the material's multiply to the strokes underneath it. Without
     this it would also multiply against the soil through the feathered outer
     edge of its own stroke, which darkens the rim by an amount that depends on
     how deep down the page you are. */
  isolation: isolate;
  filter: var(--tunnel-filter);
}

/* The full width of the hole, in lit earth. Everything else sits on this. */
.tunnel-bore { stroke: url(#tunnelGradient); }

/* The earth. Multiply rather than replace: the tile is normalised to a fixed
   mean of about 0.47 of white, so it darkens the bore beneath it and its grain
   modulates around that — the gradient underneath keeps supplying the depth
   fade, and the tile supplies only material. */
.tunnel-material {
  stroke: url(#soilWall);
  mix-blend-mode: multiply;
  opacity: var(--wall-grain);
}

/* The empty middle. Narrower than the bore, so what it does NOT cover is a band
   of lit ground at the top and bottom — and the lower band is the floor.

   Painted in the soil's own colour rather than black, because the tunnel is a
   hole in THIS earth: the darkness inside it is that earth with the light taken
   away, and black over warm brown greys it instead. Over the material rather than
   under it, so it darkens finished earth and the grain still reads through. */
.tunnel-air {
  stroke: var(--soil-floor, #0A0808);
  stroke-opacity: var(--air-dark);
}

/* Last, so it lies over everything else rather than under it. Its own gradient
   carries the fade, so there is no stroke-opacity here to multiply against. */
.tunnel-throat { stroke: url(#tunnelThroat); }

.enhanced .rabbit-layer {
  display: block;
  position: absolute;
  inset: 0 0 auto 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.rabbit {
  position: absolute;
  top: 0;
  left: 0;
  background-repeat: no-repeat;
  will-change: transform;
  transform-origin: 50% 50%;
}

.enhanced .depth-readout {
  display: block;
  position: fixed;
  left: clamp(18px, 4vw, 44px);
  bottom: 26px;
  z-index: 60;
  margin: 0;
  font: 700 11px var(--mono);
  letter-spacing: .14em;
  color: var(--accent);
  opacity: 0;
  transition: opacity .4s ease;
  pointer-events: none;
}
.enhanced .depth-readout.visible { opacity: 1; }

/* ------------------------------------------------------------------ narrow */

/* One dial, one breakpoint. The carrot is sized in fixed pixels because its
   proportions are the point — it is one object, not a layout — and fixed
   pixels are exactly what stops it shrinking with the screen. 0.68 puts it back
   at roughly the share of the width it holds on a laptop.

   To judge it, change this number and nothing else. The tops follow the body by
   construction, so they cannot be left behind. */
@media (max-width: 767px) {
  .enhanced .fore--carrot { --carrot-scale: .68; }
}
