/* TANK · Nightwater tokens · codified 2026-09-02 from design/concepts/nightwater.html
   Rule: no component declares a raw colour, size, radius or duration. It reads a token.
   The Three.js layer reads the scene tokens by name through getComputedStyle → 2D canvas.
   <link> equivalent of the @import below:
   <link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&family=Geist+Mono:wght@400..600&display=swap" rel="stylesheet">
   Google Fonts axes for Archivo are ital, wdth (62..125), wght (100..900); Geist Mono has wght only. */
@import url("https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&family=Geist+Mono:wght@400..600&display=swap");

:root {
  color-scheme: dark;

  /* ── scene (read by tank.js by name) ─────────────────────────────── */
  --ground: #0A0C10;                 /* the lobby at 2 a.m.; cold near-black with a blue cast; the page and the room are one value */
  --water: #3688A0;                  /* BRIGHTENED with --water-hi, same reason: the mid water is most of the tank's area, so it is most of what "too dark" was about. */
  --water-hi: #8AD2E2;               /* BRIGHTENED 2026-09-07 late (Jeff: "still way too dark"). The water was darkened while the fish were clamped BELOW it, when the only way to make a fish read was to make its background dim. That clamp is gone (--fish-value-cap), so the water can be lit water again and the fish stay brighter than it. */
  --water-deep: #12404F;             /* BRIGHTENED, but still clearly below the mid water, which is what keeps the box reading as deep rather than as a flat panel. */
  --glass: #F2F4F6;                  /* the glass edge, drawn as a hairline of the text colour at --glass-alpha; the locked LP */
  --glass-alpha: .45;                /* how much of the glass is visible; higher reads as frost, lower as no tank */
  --fish: #0A0C10;                   /* fish ink: the shoal, every pupil, the share card's fish, and the fallback body if the palette below is ever missing; same value as the ground so the tank still reads as a hole of light */
  --whale: #0A0C10;                  /* the capped top holders: same ink; size does the work and the palette's rich end does the rest, so nobody invents a whale colour */
  --food: #F2F4F6;                   /* feeder pellets: the text colour falling; a feed is a plain event, not an accent event */
  --crumb: #D8B77A;                  /* the crumb a viewer drops on their OWN fish in focus. Deliberately not --food and deliberately warm: the white pellets falling from the hopper are a buyback run the chain paid for, and a crumb is decoration that costs nothing and touches nothing. They must never be mistaken for each other, so they share no colour, no word and no hardware (hue 38, nowhere near --accent's 225) */
  --caustic: #F2F4F6;                /* light ripples on the sand and the upper glass at --caustic-alpha; the proof the light is real */
  --caustic-alpha: .3;               /* how much caustic rides on the surface under it; above ~.45 the sand reads as tinsel */
  --cone: #C9EEF5;                   /* the light entering the water, spent on the in-water shafts (amended 2026-09-03: it was a CEILING cone, which bar #16's framing puts off-frame) */
  --shaft-intensity: .09;            /* CUT again from .13. The shafts are drawn over the school, so every point of intensity is a cyan veil laid on top of the fish. With the fish dark they were the light in the tank; with the fish bright they are only what greys them. */

  /* ── the tank's interior (read by tank.js; the seabed invents no colour, it MIXES these) ───────── */
  --sand-mix: .55;                   /* .86 was a grey slab tuned to vanish under a dark school; .42 overcorrected to near-white and the open front of the floor read as a bare band of paper. .55 is sand. */
  --rock-mix: .34;                   /* RAISED from .06. At .06 a boulder was very nearly --plinth, so the rocks read as charcoal lumps rather than as stone and were the darkest thing in a now-bright tank. Lifted toward the surface light until they read as wet rock, still clearly below the sand so the floor keeps its form. */
  --plant-mix: .18;                  /* how far the planting is muted from its own colours toward --plinth. It was .62, and that was RIGHT while the school was dark and grey: at full chroma the floor read as a garden with fish above it, so the planting was pushed down until the eye landed on the school first. The school is bright and saturated now, so the same mute only makes the floor look dead beside it, and a reef is allowed to be a reef. */
  /* THE REEF'S OWN COLOURS. The planting used to borrow three of the fish palette's entries, which was
     tidy while both were muted and is wrong now: coral that is a fish colour makes the floor read as
     more school. These are reef colours, chosen warm and deep so they sit UNDER a bright school rather
     than competing with it, and they are the tank's only saturated non-fish surfaces. */
  --coral-1: #E2606B;                /* soft red, the classic reef body */
  --coral-2: #E79A4C;                /* warm orange */
  --coral-3: #C9556E;                /* magenta-rose */
  --coral-4: #7F5AA8;                /* deep violet, the cool note that stops the reef going all-warm */
  --anemone: #E8B0C4;                /* pale rose, for the tentacle tufts that catch the light */
  /* THE PLANTING. A planted tank is not one green: it runs from a bright fresh green through deep
     bottle greens to the reds and burgundies that every real aquascape uses as its accent. Four, so a
     bed can be mixed rather than uniform, which is most of what makes planting look grown rather than
     placed. */
  --plant-1: #5FBF7A;                /* bright fresh green, the new growth */
  --plant-2: #2F8C63;                /* deep bottle green, the mass */
  --plant-3: #9CC24A;                /* yellow-green, the light-catching tips */
  --plant-4: #A8484F;                /* burgundy, the aquascaper's accent — sparing */
  --kelp-1: #4E9E7A;                 /* green, kept a little grey so the reds stay the colour event */
  --kelp-2: #8AA84B;
  --fish-haze: .20;                  /* CUT from .30. Depth fog was doing a second job it should never have had: with the fish clamped dark, haze was most of what separated near from far. Now the fish carry their own colour and heavy fog only greys the school out. */
  --scene-haze: .70;                 /* CUT from .86. The seabed is the background and had been pushed hard into the water to keep a dark tank legible; in a lit tank that just made the floor mud. */
  --fish-value-cap: 1.30;            /* A FISH MAY NOW BE BRIGHTER THAN ITS WATER. This was .78, a hard ceiling that forced every fish pixel below the luminance of the water behind it, and it is why raising the light on the school did nothing: the clamp took it straight back. It was the right rule for flat solid fish, whose only separation came from being darker. It is the wrong rule for the look Jeff asked for with three reference images — glossy, saturated, bold — where the fish is the bright thing and the water is the deep thing behind it. Kept rather than deleted, and above 1.0, so it still catches a runaway highlight blowing a fish to white; the separation now comes from the water being darker, below. */
  --focus-recede: .72;               /* with one fish in focus, how far EVERY other fish travels toward the water it is seen against. At .86 the tank emptied: your fish floated in blank water and stopped reading as a fish IN a tank, which is the only reason the close-up is worth having. The crowd has to go quiet, not go away. It is spent through the fish's own depth haze, not a second dimming path, so the recession is the same operation distance already performs and the value cap still governs every pixel. The shoal recedes with them, in the same proportion */
  --lamp-min: .80;                   /* RAISED again from .55. Read off the live feed 2026-09-08: the agent holds $0.11 against a $5 daily cap, so the lamp term is 0.02 and the tank ships at 56% brightness on a completely healthy day — which is most of what "dull and dark" has been about, and it is invisible locally because there is no /api/feed to dim it. A day's spend is a denominator the agent will never hold; until the agent lane reconsiders the mapping, the FLOOR is what the page is actually lit by and it should not be a 44% cut. .80 still reads as clearly dimmed against a full lamp, and the dark state says what it is in words too. */
  --focus-rim: .28;                  /* extra rim light on the subject of a focus, and on nothing else. Tinted by the fish's own body like every other rim here, so a focused fish separates from the water without gaining a colour, and the value cap is applied after it, so it cannot buy separation by breaking the one rule */
  --plinth: #0E1116;                 /* what the tank stands on; one tonal step up from the ground, never a shadow. Since 2026-09-06 a real slab (site/vessel.js): wider than the frame, with a lit line round its top edge in --frame-glow */
  --frame: #CFE9EF;                  /* the vessel's frame: rounded tubes along the twelve edges of the glass and the mount of the rail, a pale of the same cold light as --water-hi, lit by the ROOM (a hemisphere of --water-hi over --plinth, and one directional light down the fish's own light direction) and never by the lamp. Just under --water-hi so a shaded tube in front of lit water reads as a surface; measured against the fish-value rule with tools/agent-light.js, because a fish is clamped against the WATER and a tube darker than the water at that height would let the fish read brighter than what it is seen against */
  --frame-glow: #C9EEF5;             /* what the vessel gives off: the lit rail over the water (the luminaire the shafts hang from) and the line round the plinth's top edge. --water-hi itself, emitted rather than seen through water; unlit, so it is the same value on a dark tank, and the object still reads as one on the ground */
  --feeder: #1A1F27;                 /* the feeder body on the rim and the filter box: hardware, two steps up from the ground, outlined in --glass */
  --drain: #2B5F70;                  /* the sell drain, the one piece of hardware that sits UNDERWATER: dark enough to read as metal on a lit floor, light enough not to punch a hole in the tank the way --feeder did */
  --accent: #4272FF;                 /* "arc": the blue-white of an arc lamp struck in a dark lobby, one step colder than the water; it means exactly "a buy just landed" — the chip, the arriving fish, its drop trail, its splash — and nothing else on the page */
  --text: #F2F4F6;                   /* primary text, the "you" outline, dock active fill, CTA fill */

  /* ── the fish palette (read by tank.js by name, --fish-1-* … --fish-10-*) ──────────────────────
     Ten species, each a body + a lighter belly panel + a contrasting fin colour, so the school is a
     bright illustration seen through water rather than a bag of sweets. A wallet keeps one entry
     for the session, drawn from its address (fish.js paletteFor); the order is PALE → RICH and a
     big holder's roll is pushed toward the rich end, so a whale is the biggest body AND the deepest
     colour. Bodies are darker than --water on purpose: value carries the silhouette at 20 px and
     hue does the character work above that.
     ACCENT CONTRACT: --accent #4272FF is hue 225 at full saturation and it means "a buy just
     landed". No fish colour comes within 25° of it. The closest thing in this block is teal's body
     and belly at hue 191, 34° away; every other value is 50°+ away and most are 150°+. There is no
     cornflower and no mid-blue fish, which is why the arriving fish is unmistakable. */
  --fish-1-body:  #B9C2C4;           /* THE PALETTE WAS A RAINBOW UNTIL 2026-09-07, and two cross-family reviews said so independently on the same day: ten unrelated hues made the tank read as candy and made every fish compete with every other. It is one family now, silver and steel through olive, with exactly two warms and one near-black. A belly is a PALE OF ITS OWN BODY rather than a new colour, which is countershading and is what makes a fish read as lit rather than as painted.  gold: the palest species, a warm mid-yellow; the small-holder end of the ladder */
  --fish-1-belly: #E4E9E8;           /* the same yellow lifted, for the belly panel across the lower body */
  --fish-1-fin: #9FA9AB;           /* terracotta fins: a darker warm against a light warm body */
  --fish-2-body:  #93A7B2;           /* coral: the reference school's signature red-orange */
  --fish-2-belly: #D6E0E4;
  --fish-2-fin: #7C8E99;           /* gold fins on a red body */
  --fish-3-body:  #A3A88F;           /* ochre: burnt orange, the workhorse warm */
  --fish-3-belly: #DCDECB;
  --fish-3-fin: #8B9179;           /* plum fins: the one cool fin in the set, so warms are not all one note */
  --fish-4-body:  #7E8C93;           /* rose: pink-red, the only magenta in the school */
  --fish-4-belly: #C3CED2;
  --fish-4-fin: #6A777E;           /* leaf-green fins on a pink body: the reference's boldest pairing */
  --fish-5-body:  #8A8459;           /* fern: a mid leaf green, far from both the water and the accent */
  --fish-5-belly: #C9C49B;
  --fish-5-fin: #726D46;           /* orange fins on a green body: the reference's warm-fin-on-cool-body move */
  --fish-6-body:  #5F7684;           /* jade: blue-green; cool, but 56° off the accent */
  --fish-6-belly: #A7B8C1;
  --fish-6-fin: #4E626E;           /* coral fins on jade */
  --fish-7-body:  #6B6F5A;           /* olive: the dark yellow-green; reads almost black at depth, which is the point */
  --fish-7-belly: #A8AC96;
  --fish-7-fin: #575A47;
  --fish-8-body:  #A5752F;           /* THE ONE AMBER. Both reviews asked for a single warm in an otherwise cool school; a second one would make it a scheme rather than an accent.  rust: deep oxide red, a rich colour for a big holder */
  --fish-8-belly: #D9B478;
  --fish-8-fin: #8A5F22;           /* sand fins: the one light fin in the set, against the deepest red, but still darker than the water so the tail keeps its edge */
  --fish-9-body:  #8C3A2F;           /* THE ONE DEEP RED, and the only other warm. Kept dark so it reads as an animal and not as a warning.  teal: the deepest cool; the closest hue to the accent in the whole set and still 34° off */
  --fish-9-belly: #C08070;
  --fish-9-fin: #732C22;           /* gold fins so a teal fish never reads as a blue one */
  --fish-10-body:  #3D4650;           /* THE WHALE END. The richest holders get the darkest body, which is the opposite of the old palette, where the biggest fish were the brightest and shouted loudest in a bright tank. In dark water a near-black fish is a silhouette, and a silhouette is what a whale should be.  plum: the richest and darkest body; the whale end of the ladder */
  --fish-10-belly: #7E8A94;
  --fish-10-fin: #313943;
  --fish-eye: #F4F1E8;               /* the eyeball ring: warm off-white, the one non-token-blue light on a fish; the pupil is --fish ink */
  --earning: #D98A2B;                /* the glint along the top of the back of a fish whose wallet is AT OR ABOVE the token's own earning floor (site/config.js `earning`, site/ledger.js earns; on $ORBIO, 1,000 tokens or more earns credits out of the trade fees every hour). A saturated warm amber, hue 32: 193° from --accent's 225 so it can never read as "a buy just landed", and deliberately not --guest's pale sand (hue 41, unsaturated) because the two marks mean different things and sit on different parts of the fish. Drawn on the BODY only, along the very top of the back ridge (t > 0.90, above and thinner than the king's stripe at 0.72), at half strength, and put on BEFORE the light and the value clamp like every other fish pixel. MEASURED with tools/agent-light.js, three runs at 650 marked fish (`earning=650`): at lamp 1.0, 48 to 71 fish pixels brighter than the water behind them of ~95,700 covered (0.05% to 0.07%), worst excess .144 to .161; at lamp 0.0, 0 to 80 of ~105,700 (0.00% to 0.08%). The same bench with no marks measures 0 at both levels, so the glint is the whole of the difference and it stays under the 0.1% bar at every setting of the lamp, which is the point of putting it on before the clamp rather than after */
  --guest: #D2B77C;               /* the band on a guest's tail, a wallet that sent the tank's own wallet at least one whole token (site/ledger.js GUEST_MIN): a warm sand like the ring on a ringed bird's leg, hue 41, nowhere near --accent's 225 so it can never read as "a buy just landed". Drawn on the body at the peduncle only (fish.js guestBandFor) and put on BEFORE the value clamp like every other fish pixel. Measured with tools/agent-light.js, 650 banded fish at full lamp: 60 fish pixels brighter than the water behind them against 35 with no bands (of 132,000 covered), worst excess .154 against .124; the paler #EFE6C8 measured 78 and .185, which is why the sand won */

  /* ── text and chrome ─────────────────────────────────────────────── */
  --text-2: #8C96A3;                 /* secondary text: eyebrows, stat labels, idle dock tabs, status lines */
  --text-3: #4A5461;                 /* tertiary text: the footer block number and nothing louder */
  --text-on-accent: #0A0C10;         /* ink on the accent chip; 4.74:1 */
  --text-on-cta: #0A0C10;            /* ink on the white CTA and the active dock tab */
  --line: rgba(242,244,246,.12);     /* the hairline INSIDE a pane: the status block, the find field, the share card */
  --line-2: rgba(242,244,246,.22);   /* the rim: every pane's 1 px edge, an action inside a pane, and the sheet handle. Lighter than any fill it edges, at every depth */
  --ring: rgba(255,255,255,.06);     /* the 1 px inset highlight on the pane's top edge; the only elevation cue besides the rim */

  /* ── glass: the one chrome material (2026-09-03) ───────────────────
     Every HUD surface is the same pane: the scene BEHIND it dimmed and blurred by --hud-veil, then a
     fill that is a whisper of --text so the pane reads LIGHTER than what it covers (the room and the
     water both), then the --line-2 rim, lighter still. The fills were 70 % / 92 % ground — opaque dark
     boxes that read as cut-outs on a lit tank rather than as glass over it. What makes a light fill
     legal over a lit tank is the veil: it knocks the light shafts down BEFORE the fill goes on, so the
     brightest pixel the text can sit over is the veil's output, not the shaft (design/bar.md #12, #15). */
  --hud: rgba(242,244,246,.07);      /* pane fill: stat strip, dock, pills, chips, the CA lines */
  --hud-2: rgba(242,244,246,.10);    /* the reading pane: panel and sheet, one step lighter because copy sits on it */
  --hud-3: rgba(242,244,246,.05);    /* a block INSIDE a pane: the status block, the find field. No veil of its own; it is already behind one */
  --hud-veil: blur(18px) saturate(1.05) brightness(.28);  /* the pane's backdrop: 18 px of blur so the scene reads as depth not detail, and .28 of brightness so a light shaft behind the chrome cannot carry the text with it */
  --hud-veil-2: blur(28px) saturate(1.02) brightness(.16); /* the reading pane's backdrop: darker and softer, because a paragraph needs a quieter field than a four-word pill */
  --pill-on-glass: rgba(242,244,246,.90); /* the annotation's backing: the one label on the page that inverts to light with ink text */
  --hud-text-shadow: 0 1px 2px rgba(10,12,16,.6); /* legibility for HUD text that crosses the water; replaces blur */
  --body-text: #C9CFD6;              /* paragraph text inside a panel; one step below --text so the h3 leads */

  /* ── type ─────────────────────────────────────────────────────────── */
  --font-display: "Archivo", system-ui, sans-serif;   /* one grotesk family; hierarchy by width and weight */
  --font-body: "Archivo", system-ui, sans-serif;      /* the same family at width 100 */
  --font-mono: "Geist Mono", ui-monospace, Menlo, monospace; /* labels, stats, addresses, status; diegetic only */
  --wdth-display: 125%;              /* font-stretch of the display line and the brand */
  --wdth-h3: 112%;                   /* font-stretch of a panel h3 */
  --wdth-note: 90%;                  /* font-stretch of the italic annotation */
  --wdth-body: 100%;
  --w-display: 800;                  /* the only heavy weight; display, h3, brand */
  --w-semibold: 600;                 /* CTA, active dock tab, stat values */
  --w-medium: 500;                   /* mono labels, pills, idle dock */
  --w-body: 400;                     /* paragraphs, hero sub */

  /* ── THE LADDER: THREE TIERS, and every name below is an alias onto one of them ─────────────────
     There were 20 token names carrying 16 distinct sizes on one screen, which is not a hierarchy, it
     is a gradient. Four pairs were exact duplicates wearing different names (sub-m = dock, body = cta,
     dock-m = stat-m, brand = stat) and nine more pairs differed by 1.5px or less — differences nobody
     can see, so they cost a rung each without buying one. Specified by GPT-6 Astra Pro against the
     rendered surface, 2026-09-08, after two seats independently named the ladder as the reason the
     page reads unresolved.

     The names are kept so no component has to change: a component still asks for the size that suits
     its job, and the ladder decides what that means. That is the tokens file's whole contract. */
  --tier-metric: 56px;               /* a number that IS the fact: the holder count, and nothing else */
  --tier-heading: 24px;              /* the promise on a panel */
  --tier-ui: 14px;                   /* EVERYTHING else. One interface size, one line-height */

  /* THE HERO NUMERAL WAS 128px AND IS NOW THE METRIC TIER. It was a second hero competing with the
     tank, which is the actual product: 3,235 fish that are 3,235 real wallets. The number stays larger
     than a panel heading, so it is still the loudest text on the page, but it stops being an
     illustration. Deliberately not compensated for elsewhere: no badge, no container, no background. */
  --size-display: var(--tier-metric);
  --size-display-m: 40px;            /* the metric tier at 390 */

  --size-h3: var(--tier-heading);
  --size-h3-m: 20px;                 /* the heading tier at 390 */

  /* The interface tier. Sixteen names, one size. `note` was 19px and outranked `body` at 14.5px, so a
     floating annotation was typographically louder than the page's primary explanation. The 9–11.5px
     labels are promoted rather than kept small: they sit over moving WebGL, which is the hardest
     possible background to read against, and the fix for that is never lower opacity. */
  --size-sub: var(--tier-ui);
  --size-sub-m: var(--tier-ui);
  --size-body: var(--tier-ui);
  --size-body-m: var(--tier-ui);
  --size-note: var(--tier-ui);
  --size-note-m: var(--tier-ui);
  --size-cta: var(--tier-ui);
  --size-dock: var(--tier-ui);
  --size-dock-m: var(--tier-ui);
  --size-brand: var(--tier-ui);
  --size-stat: var(--tier-ui);
  --size-stat-m: var(--tier-ui);
  --size-label: var(--tier-ui);
  --size-status: var(--tier-ui);
  --size-eyebrow: var(--tier-ui);
  --size-stat-label: var(--tier-ui);
  --size-est: var(--tier-ui);        /* was 8.5px, over a moving 3D scene; folded into the tier */

  --lead-display: .84;               /* display leading drops below 1 */
  --lead-h3: .95;
  --lead-sub: 1.3;
  --lead-body: 1.55;                 /* body leading goes up */
  --lead-status: 1.7;
  --track-display: -.045em;          /* tracking ladder: tighter as size grows */
  --track-h3: -.03em;
  --track-sub: -.01em;
  --track-body: -.005em;
  --track-mono: .02em;               /* mono values */
  --track-label: .08em;              /* mono uppercase labels */
  --track-stat-label: .16em;
  --track-eyebrow: .18em;            /* the widest tracking on the page */

  /* ── shape ────────────────────────────────────────────────────────── */
  --r-content: 6px;                  /* panel, stat bar, status, sheet top corners, EST tag, brand mark */
  --r-action: 999px;                 /* dock, tabs, pills, CTA, COPY, event chip, sheet handle */
  /* there is no third radius; scene geometry (tank corners) is not UI and is set in tank.js */

  /* ── space ────────────────────────────────────────────────────────── */
  --s-1: 4px;                        /* dock gap, tag padding */
  --s-2: 8px;                        /* chip gap, icon gap */
  --s-3: 12px;                       /* chip padding, status padding */
  --s-4: 16px;                       /* stat cell padding, panel inner rhythm */
  --s-5: 24px;                       /* panel padding, HUD inset from the frame edge on desktop */
  --s-6: 32px;                       /* brand and hero left inset at 1440 */
  --s-7: 48px;                       /* the large step; the 390 hero's offset under the brand row (the dock has its own measured --dock-h) */
  --s-8: 64px;                       /* hero top offset from the brand */
  --hud-inset: 32px;                 /* 1440 edge inset */
  --hud-inset-m: 16px;               /* 390 edge inset */
  --panel-w: 358px;                  /* right-docked panel; camera views are solved against it */
  --agent-w: 300px;                  /* the receipts pane at 1440, left column above the footer. Narrower than the panel on purpose: it is a readout, not a reading surface, and three panes of one width would be the three-equal-cards tell (design/bar.md #14). It exists only while there is a feed, so the stage it takes is taken only when the agent is real */
  --stage-aspect-m: .72;             /* how tall a PORTRAIT stage may be as a share of its own width. In portrait the tank is width-bound: a 2:1 box in a narrow stage draws the same height whether the stage is 260px or 340px tall, so everything above this is dead band split either side of the glass. Read by hud.js measureStage, which caps the stage and hands the difference to the footer as --foot-lift. .72 leaves ~30px of scene above and below the tank at 390 (the pills hang there and need it) and evens the three gaps: 46 above / 46 below / 44 from the dock to the sheet */
  /* ── the keeper (site/keeper.js; read by hud.js measureStage and placeKeeper) ──────────────── */
  --keeper-h: .12;                   /* his height as a share of the STAGE's, used only by the plinth fallback: a viewport under 900px, or any that has no hero band to stand in. .18 put a 209px box in the 147px gap between the page edge and the glass and keeperBox shrank him every frame; .12 is the largest share that fits it. Above 900px he stands in the band instead and --keeper-hero-h governs. A share of the stage rather than of the vessel, so the camera orbiting the tank never changes his size and the stage can reserve his room before the tank is fitted. */
  --keeper-hero-h: .85;              /* his height as a share of the HERO BAND: the strip between the hero number and the stats panel, above the tank, which is where he stands above 900px. Moving him there gave him his size back and gave the tank its column back (docs/plans/2026-09-07-tank-density-and-seabed.md). Must be its own token: sharing --keeper-h drew him a few pixels across at 390, because that value is measured against a different box. .62 read as small once the art was painterly rather than blocky. */
  --keeper-room: .06;                /* the room the fit leaves beside the plinth anyway (the fit margin's slack plus the plinth's pad past the frame: 38 px of 637 at 1440, measured), as a share of the stage height. His column reserves his width LESS this, so the tank gives up only what he actually needs */
  --keeper-bubble-w: 320px;          /* his bubble's measure: wide enough for a narrated line in two or three rows, narrower than the hero's, because it is a speech and not a paragraph */
  --keeper-breathe: 7px;             /* the idle float's travel. 2px was a breath nobody could see: at his size on the page it is under half a percent of his height, and Jeff's read was that the body barely moved at all. 7px reads as something hanging in water rather than pinned to the page, and the drift multiplies it. */
  --sheet-h: 62vh;                   /* mobile bottom sheet maximum */
  --sheet-peek: 276px;               /* mobile sheet resting height */
  --stats-h: 60px;                   /* the stat strip's measured height at 1440: --s-3 above and below a label, its --s-2 gap and a value, plus the two rims. The docked panel hangs off it (--panel-top), so it is a token and not a guess */
  --ca-h: 30px;                      /* the CA pane's measured height: --s-2 above and below one --size-label line, plus its two rims. The hero hangs off it, so it is a token and not a guess */
  --dock-h: 56px;                   /* measured, not chosen: --s-2 of pane padding either side of a --s-3/--s-4 tab, plus the two rims. It was 48 when the dock's padding was --s-1 and the row sat tight inside its own rim */
  --cta-h: 46px;
  --cta-h-m: 44px;
  --hero-max-w: 420px;               /* hero sub-line measure */
  --bp-mobile: 520px;                /* below: sheet, horizontal dock, one-line hero (documented; media queries repeat the number) */
  --bp-tablet: 900px;                /* below: panel narrows to the sheet */

  /* ── motion (narrative only: arrive, eat, flush, fly) ─────────────── */
  --t-arrive: 900ms;                 /* a buy: fish falls from the surface light and settles */
  --t-splash: 700ms;                 /* the two accent rings expand and fade after the fish enters */
  --t-eat: 600ms;                    /* pellets fall and the nearest fish turn to them */
  --t-flush: 800ms;                  /* a sell: fish drawn to the drain and gone */
  --t-fly: 750ms;                    /* camera to a station (~45 frames at 60); labels hide while flying */
  --t-focus: 900ms;                  /* the push in to one fish and the pull back out. Longer than --t-fly because it is a much larger distance and the tank has to be seen receding, which is the half that says "this one is yours" */
  --t-crumb: 2200ms;                 /* a crumb drop: they fall, the fish turns and takes them. Cosmetic, so it is off entirely under reduced motion rather than shortened */
  --t-line: 6000ms;                  /* how long a narrated line stays under its fish before it moves to the receipts pane. Long enough to read a sentence twice, short enough that the tank is never a page of text; it is a HOLD, not a motion, so reduced motion leaves it alone */
  --t-keeper-fade: 200ms;            /* the crossfade between two of the keeper's poses; one pose at a time, so the fade is the whole transition */
  --t-keeper-breathe: 3.1s;          /* one float, up and back. Coprime with the drift and the tail so the three never line up. */
  --t-chip: 220ms;                   /* event chip and panel slide in */
  --t-chip-hold: 8s;                 /* how long a buy chip stays before it fades; the accent is gone after this */
  --t-level: 1200ms;                 /* water level easing to a new depth reading */
  --ease-fall: cubic-bezier(.4, 0, 1, 1);      /* accelerating: things that drop */
  --ease-settle: cubic-bezier(.16, 1, .3, 1);  /* decelerating: things that land, chips, panels */
  --ease-fly: cubic-bezier(.65, 0, .35, 1);    /* symmetric: the camera */
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --t-arrive: 0ms; --t-splash: 0ms; --t-eat: 0ms; --t-flush: 0ms; --t-fly: 0ms; --t-level: 0ms;
    /* Focus still WORKS under reduced motion, it just does not travel: the camera cuts to the close-up
       and the tank is already receded when it gets there. Crumbs are a motion and nothing else, so they
       are not offered at all rather than offered and inert. */
    --t-focus: 0ms; --t-crumb: 0ms;
    /* The keeper still changes pose (a pose is a state, and the fade is how one state becomes the next),
       but he does not breathe: the bob is a motion and nothing else. */
    --t-keeper-breathe: 0s;
    /* fish are still; events are chips; the chip itself still slides (--t-chip) */
  }
}
