/* Finform
   ----------------------------------------------------------------------------
   A lit planted tank seen from the front. The ground is a pale mint, the panels
   are white, and the colour is spent deliberately rather than sprayed: one vivid
   green carries every positive signal, an aqua blue carries the headings and the
   far end of every gradient, and amber and coral carry the two negative verdicts.

   Verdict colours stay green / amber / red because they are the hobby's own
   vocabulary -- published compatibility charts are green/amber/red grids and
   every keeper reads them fluently.

   --gap is deliberately colourless. A missing fact is not a middling verdict,
   it is off the scale (the Verdict enum has no UNKNOWN member), so a gap never
   borrows a point on the green-red ramp.
   ---------------------------------------------------------------------------- */

/* --------------------------------------------------------------------- palette

   Every colour in this file is defined HERE and nowhere else. Two repaints have
   now gone through this block alone, which is only possible because no rule below
   carries a literal; the two that must (the parameter hues and the dialog
   backdrop) are marked where they sit.

   THE ONE RULE THAT MATTERS WHEN EDITING THIS BLOCK. Each accent comes in two
   weights, and they are not interchangeable:

     --sea / --amber / --coral / --navy   fills, borders, icons, bars, and
                                            display type above ~19px
     --sea-deep / --navy (text sizes)     the same colour as TEXT

   A vivid green is only 3.2:1 on white, which is fine for a 6px bar and illegible
   for a 13px caption. Every green word in this file therefore uses --sea-deep
   and every green shape uses --sea. The same split is why --amber and --coral
   sit deeper than the swatch you would pick by eye: white sits on top of them
   inside a tight or clashing parameter band, and that reading has to survive.
*/
:root {
  /* THE SEA (Efe, 2026-10-24: "less green, more blue, aquatic colors"). The
     ground, the lines and the ink are the colours of clear water over sand;
     the brand accent is an ocean blue. Green keeps one job only, meaning
     good, under --good below. */
  --ground:      #eef6fb;   /* pale sea */
  --ground-tint: #dcebf5;
  --panel:       #ffffff;
  --panel-lift:  #e7f2f9;
  --line:        #d0e2ee;
  --line-strong: #a7c8dc;

  --ink:         #0c2433;   /* deep ocean navy: body copy */
  --ink-soft:    #3a5a6c;
  --ink-dim:     #3a5a6c;   /* alias: both names were in use, neither defined twice */
  --ink-faint:   #4b6676;   /* 6:1 on white -- the small mono captions live here */

  /* Named --navy for one reason: data/content.py binds the species-account card's
     default accent to the literal string "navy", and tests/test_web.py reads that
     name back out of this file. The value is an aqua blue, deep enough to set a
     heading in. --aqua is its bright twin, for gradients and bars only. */
  --navy:        #16719c;
  --aqua:        #35a8d8;
  --sea:         #0a6ea8;   /* the brand accent, as a shape: buttons, the lit rule */
  --sea-bright:  #0b7cbd;   /* the lit end of a sea gradient; white still reads on it */
  --sea-deep:    #075985;   /* the brand accent, as a word (7.5:1 on white) */
  --sea-wash:    rgba(10, 110, 168, 0.12);
  /* The opaque twin of --sea-wash. A translucent tint takes its contrast from
     whatever sits behind it, so a badge on the page ground measured differently
     from the same badge on a panel -- and the ground version missed 4.5:1. Small
     text on a tint uses this instead, where the number is fixed. */
  --sea-tint:    #e2f0f9;
  --amber:       #d9821a;   /* the warning, as a shape */
  --amber-deep:  #8a5008;   /* the warning, as a word (6.5:1 on white, 5.5:1 on its own wash) */
  --amber-band:  #a5620b;   /* the lit end of a TIGHT band -- white sits on this */
  --amber-wash:  rgba(217, 130, 26, 0.15);
  --coral:       #e8433f;   /* the clash, as a shape */
  --coral-deep:  #bd2d29;   /* the clash, as a word (5.9:1 on white) */
  --coral-band:  #cf3a36;   /* the lit end of a CLASH band -- white sits on this */
  --coral-wash:  rgba(232, 67, 63, 0.12);
  --gap:         #5f6f68;
  --gap-wash:    rgba(95, 111, 104, 0.10);

  --on-accent:   #ffffff;   /* type on a filled COOL accent (the parameter bands) */
  --on-sea:      #ffffff;   /* type on a filled sea accent (buttons, the CTA): 5.5:1 */
  --glow:        rgba(10, 110, 168, 0.28);

  /* GOOD, and only good: a comfortable water window, a full score bar, a
     pairing that works, a beginner's fish. The verdict colours keep their
     meaning, green next to amber and coral; nothing else on the page is green. */
  --good:        #1db35a;
  --good-bright: #2fca6b;
  --good-deep:   #127a3e;   /* as a word: 5.6:1 on white */
  --good-wash:   rgba(29, 179, 90, 0.13);
  --good-tint:   #e4f6ea;
  --good-glow:   rgba(29, 179, 90, 0.28);
  --glow-aqua:   rgba(53, 168, 216, 0.26);
  --shadow:      rgba(15, 43, 32, 0.14);
  --shadow-deep: rgba(15, 43, 32, 0.22);

  /* The two washes that keep a white page from reading as a blank sheet. Laid on
     the canvas, under the ambient plants, under everything else. */
  --wash-sea:    rgba(10, 110, 168, 0.12);
  --wash-aqua:   rgba(53, 168, 216, 0.11);

  /* The species account. Seven accents, each literal to its section -- moss for
     the planted box, bronze for food, petrol for moving water, brick for risk. A
     separate family from the three above on purpose: --sea/--amber/--coral
     already mean comfortable/tight/clash on the water bars and good/care/avoid on
     tank mates, so a Feeding card in warning-amber would read as a warning about
     feeding. All sit near one value so no card shouts over its neighbours, and all
     clear 4.5:1 on white because each one sets its card's heading. */
  --moss:        #2b7d52;
  --bronze:      #96591f;
  --petrol:      #1f6f8b;
  --olive:       #55731a;
  --brick:       #a53b36;
  --plum:        #74428a;

  /* THE WARM FAMILY. Iron red and ember orange, from the leaves of the plants
     that give a planted tank its colour (Alternanthera reineckii, Ludwigia
     repens) -- Efe: "reds and orange, like the leaves of iron rich plants as
     that's where the green came from". Decoration only: never a verdict,
     which is what amber and coral are for. */
  --iron:        #b8452b;
  --iron-deep:   #8f3320;   /* 6.6:1 on white -- for words */
  --iron-wash:   rgba(184, 69, 43, 0.10);
  --ember:       #e07a2b;
  --ember-wash:  rgba(224, 122, 43, 0.13);

  /* The tank. Water reads as water: clearer and bluer than the old print-era
     version, because the ground behind it is no longer beige. */
  --glass:       linear-gradient(180deg, rgba(140, 220, 240, 0.55), rgba(60, 160, 200, 0.66));
  --water:       linear-gradient(180deg, rgba(120, 205, 232, 0.68), rgba(45, 140, 185, 0.78));
  --surface:     rgba(255, 255, 255, 0.8);
  --bubble:      rgba(255, 255, 255, 0.78);
  --silhouette:  brightness(0);   /* dark on light. NOT inverted. */

  /* THE DIVE (journey.css). A freshwater descent, not the open ocean: the
     spring-clear shallows of the first two steps, then under the surface for
     the star, teal into the dark petrol of deep water by the reveal. One pair
     of stops per step, top and bottom of the sea. The shallows take the site's
     own ink; from the star down the ink is pale, and each pairing clears
     4.5:1 (tests/test_journey_look.py reads these values). */
  --dive-0-top:    #eaf6fb;
  --dive-0-bottom: #c6e4f2;
  --dive-1-top:    #c9e5f3;
  --dive-1-bottom: #a6d3ea;
  --dive-2-top:    #1a5f86;
  --dive-2-bottom: #144f73;
  --dive-3-top:    #155274;
  --dive-3-bottom: #0f415f;
  --dive-4-top:    #11445f;
  --dive-4-bottom: #0b344c;
  --dive-5-top:    #0d3650;
  --dive-5-bottom: #08283e;
  --dive-6-top:    #0a2b40;
  --dive-6-bottom: #051a2a;
  --dive-ink-deep:      #eef7fc;
  --dive-ink-deep-soft: #d2e6f1;
  --dive-accent:        #5cc8f0;   /* the dive's accent under the surface: bright aqua */
  --dive-accent-bright: #8ad9f7;
  --dive-accent-ink:    #04202e;   /* type on the aqua: 8.9:1 */
  --dive-glass:         rgba(255, 255, 255, 0.66);   /* a panel in the shallows */
  --dive-glass-deep:    rgba(4, 24, 33, 0.46);       /* a panel in deep water */
  --dive-line-deep:     rgba(214, 239, 233, 0.24);
  --dive-ray:           rgba(255, 255, 255, 0.20);
  --dive-snow:          rgba(225, 246, 240, 0.55);
  --dive-rope:          #d9c48e;   /* the descent line: a sandy, twisted rope */
  --dive-rope-dark:     #9c8650;
  --dive-lamp:          #ffd88a;   /* the tank light, switched on at the reveal */
  --dive-lamp-glow:     rgba(255, 216, 138, 0.38);
  --dive-lamp-deep:     #f5b95a;
  --dive-lamp-bright:   #ffe3a8;
  --dive-lamp-ink:      #2a1a04;   /* type on the lamp-lit button, 11:1 */
  --dive-lamp-edge:     rgba(255, 216, 138, 0.35);
  --dive-instrument:    #0d3346;   /* the dive computer, carried down from the boat */
  --dive-instrument-solid: #0b2f42;
  --premium-gold:       #f2c66d;   /* Premium, unlocked: the badge and the ring */
  --premium-gold-deep:  #d9a441;
  --premium-gold-ink:   #2b1d03;   /* type on the gold: 11:1 */

  /* PREMIUM: only the top bar changes, to deep ocean blue (Efe, 2026-11-11:
     "just the top tab ... aquatic colors, less green and more blue"). The
     pages keep their own light, blue colours. */
  --premium-bar:         #0b3a52;   /* the top bar, deep ocean */
  --premium-bar-ink:     #e2f0f7;   /* type on the bar */
  --dive-caption:       rgba(4, 24, 33, 0.82);   /* under a fish's name on its photograph */
  --dive-veil:          rgba(2, 14, 20, 0.62);   /* behind the pane about one fish */
  --dive-shade:         #01090d;   /* shadows under deep-water panels */
  --dive-highlight:     rgba(255, 255, 255, 0.35);
  --dive-highlight-soft: rgba(255, 255, 255, 0.08);
  --dive-track:         rgba(238, 251, 246, 0.14);
  --dive-frame:         rgba(238, 251, 246, 0.6);
  --dive-good:          #44d27f;   /* the good accent as a word on deep water */
  --dive-mid:           #f2b25a;
  --dive-bad:           #ff7d75;

  /* TYPE (Efe, 2026-11-11): the care guide's two faces, across the site. The
     serif for headings and anything read, the sans for controls, tables and
     figures. The old names stay as aliases, so every rule keeps its role:
     --display (headings), --body (interface), --mono (figures and labels, no
     longer monospace), --passport (the passport's one italic word). */
  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --sans: "Source Sans 3", "Segoe UI", system-ui, sans-serif;
  --display: var(--serif);
  --passport: var(--serif);
  --body: var(--sans);
  --mono: var(--sans);

  --radius: 12px;

  /* THE TANK PLAN (web/tank_plan.py, plan.css), theme "sea". A theme for the
     fish being kept (blackwater, rift lake, planted nano) swaps these, and
     nothing else in plan.css names a colour. */
  --plan-paper:   #ffffff;
  --plan-ink:     var(--ink);
  --plan-soft:    var(--ink-soft);
  --plan-line:    var(--sea);          /* the drawing's strokes */
  --plan-accent:  #1f6b45;             /* headings and figures: the logo's green (favicon.svg) */
  --plan-accent-deep: #14472e;         /* the logo tile's darker end, for the title */
  --plan-tint:    #eef4f0;             /* a pale fill that groups without a rule */
  --plan-serif:   var(--serif);
  --plan-sans:    var(--sans);
  --plan-water:   var(--sea-tint);
  --plan-band:    rgba(10, 110, 168, 0.18);
  --plan-sand:    #ecdcb6;
  --plan-sand-edge: #c9ab72;
  --plan-plant:   var(--moss);
  --plan-fish:    #0b7cbd;
  --plan-wash:    var(--sea-wash);
  --plan-pending: var(--amber-wash);
  --plan-pending-edge: var(--amber);
  --gutter: clamp(1rem, 4vw, 2.5rem);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 17px/1.55 var(--body);   /* Source Sans sets smaller than Plex did */
}

/* The planted foreground. A single flat SVG pinned to the bottom of the viewport
   at 8% opacity -- present when you look for it, invisible when you are reading.
   z-index -1 puts it under every panel: body paints no background of its own
   (it propagates to the canvas), so there is nothing here for it to hide behind. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.08;
  background: url("ambient.svg") no-repeat bottom center / cover;
}

a { color: var(--sea-deep); }

/* --sea-deep, not --sea. A focus ring has to clear 3:1 against the surface it
   sits on and the vivid green is 2.6:1 on this ground -- the one place in the file
   where the accent colour is not good enough on its own. */
:focus-visible {
  outline: 2px solid var(--sea-deep);
  outline-offset: 2px;
  border-radius: 4px;
}

/* `hidden` has to win. The attribute works through the user-agent's
   `[hidden] { display: none }`, which ANY author rule setting `display` beats --
   and this file sets it on `.button`, `.tab`, `.cand__link` and a dozen more. So
   `el.hidden = true` did nothing at all to a button, which is how `picker.js`
   came to hide the "add another species" control and leave it sitting there,
   relabelled by `stock.js` as "That is every row this form holds".

   One rule, rather than remembering to pair every `display` with an `[hidden]`
   variant of its own. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------- furniture */

.page {
  max-width: 74rem;
  margin: 0 auto;
  padding: 2.25rem var(--gutter) 3rem;
  display: grid;
  gap: 1.1rem;
}

.colophon {
  max-width: 74rem;
  margin: 0 auto;
  padding: 0 var(--gutter) 3rem;
  color: var(--ink-faint);
  font-size: 0.8125rem;
  max-inline-size: 44rem;
}
.colophon p { margin: 0; }

.panel {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.1rem, 2.6vw, 1.6rem);
  /* A plain white sheet with one hairline edge (Efe, 2026-11-11: no generated
     look). The tint wash and the lifted shadow every card used to share are
     gone; space and headings separate things. */
}

/* A section's label, in the heading face and sentence case. It was tracked
   monospace capitals, which Efe called the look of an AI coding tool
   (2026-11-09: "very cliché ... it takes away from the originality"). */
.eyebrow {
  margin: 0 0 0.8rem;
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  font: 650 1.1875rem/1.25 var(--serif);
  color: var(--sea-deep);
}

/* Until now this was an unstyled <h2>: browser-default size, body font. It is
   most of the reason the calculator read as a form rather than as a page. */
.panel__title {
  margin: 0 0 0.9rem;
  font: 700 clamp(1.3rem, 3vw, 1.7rem)/1.15 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}

.hint { color: var(--ink-soft); font-size: 0.875rem; }

/* ------------------------------------------------------------- the two columns */

.board {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 1.1rem;
  align-items: start;
}

/* Two columns of unequal length: the score and the water windows are short, the
   tank and its editor and the shortlist are not. The findings fill the left
   column rather than adding to the bottom of the right one -- Efe asked for
   them "below the parameters box".

   Each column is its own grid so the two flow independently. Placing the
   findings by `grid-area: 2 / 1` instead looked right in the markup and left a
   screen of white space above them, because grid rows span the whole board and
   the right-hand column is a great deal taller than the score. */
.board__main,
.side { display: grid; gap: 1.1rem; align-content: start; }

/* ------------------------------------------------------------------- score */

.score__value {
  margin: 0;
  font: 700 clamp(3.5rem, 9vw, 5.5rem)/0.9 var(--display);
  letter-spacing: -0.01em;
  color: var(--sea-deep);
}

.score__outof {
  display: block;
  margin-top: 0.7rem;
  font: 650 0.8125rem/1 var(--sans);
  letter-spacing: 0;  /* it sits inside the number, which is set tight */
  color: var(--ink-faint);
  -webkit-text-fill-color: var(--ink-faint);
}

.score__withheld { margin: 0; font: 700 1.4rem/1.2 var(--display); letter-spacing: -0.01em; }

.score__note {
  margin: 0.7rem 0 0;
  max-inline-size: 24rem;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-faint);
}
.score__note--warn { color: var(--amber-deep); }

/* ------------------------------------------------------------------- tally */

.tally {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.tally__row {
  display: grid;
  grid-template-columns: 8.5rem 1fr 2.5rem;
  gap: 0.75rem;
  align-items: center;
}

.tally__label { font-size: 0.8125rem; color: var(--ink-soft); }

.tally__meter {
  height: 0.4rem;
  border-radius: 99px;
  background: var(--line);
  overflow: hidden;
}

.tally__fill {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--good-deep);
}
.tally__fill.is-mid {
  /* --amber-band, not --amber-deep: no type sits on a tally bar, so it starts at
     the warmer of the two and does not read as brown. */
  background: var(--amber-band);
}
.tally__fill.is-bad {
  background: var(--coral-deep);
}

.tally__value {
  font: 400 0.875rem/1 var(--mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ink);
}
.tally__value.is-none { color: var(--gap); font-size: 0.6875rem; }

/* ------------------------------------------------------- water parameters */

.params { margin-top: 1.9rem; }
.param { margin-bottom: 1.7rem; }
.param:last-child { margin-bottom: 0; }

/* Each parameter gets its own hue so four bars down a page are four things
   rather than one repeated four times. Every hue is COOL, deliberately: amber
   and coral still mean "tight" and "clash", and a warm bar for temperature --
   the obvious choice -- would read as a warning on a stocking that has none.
   The label above the bar says which parameter it is; the colour only has to
   tell them apart. */
.param {
  /* The fallback pair is deep on both ends on purpose. It is unreachable while
     every parameter has a rule below, and if one ever loses its rule the white
     figure sitting on this gradient still has to be readable. */
  --param-a: var(--good-deep);
  --param-b: var(--good-deep);
  --param-wash: var(--good-wash);
}
/* The four literals this file allows. Each pair is more saturated than the set it
   replaces, and each is held below the point where the white figure inside the
   band drops under 4.5:1 -- which is why none of them is as bright as its hue
   could go. */
.param--temp { --param-a: #12557d; --param-b: #1f79a8; --param-wash: rgba(31, 121, 168, 0.14); }
.param--ph   { --param-a: #5a2b8f; --param-b: #8f56c9; --param-wash: rgba(143, 86, 201, 0.14); }
.param--gh   { --param-a: #07584c; --param-b: #0d7d6a; --param-wash: rgba(13, 125, 106, 0.14); }
.param--kh   { --param-a: #2c3f9e; --param-b: #4a63d4; --param-wash: rgba(74, 99, 212, 0.14); }

/* The verdict outranks the parameter. A tight or clashing window has to look
   wrong at a glance, so it takes back the warm colour it earned. Placed after
   the four above: equal specificity, so source order decides. */
.param.is-tight { --param-a: var(--amber-deep); --param-b: var(--amber-band); --param-wash: var(--amber-wash); }
.param.is-clash { --param-a: var(--coral-deep); --param-b: var(--coral-band); --param-wash: var(--coral-wash); }

.param__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.param__name {
  font: 700 1.1875rem/1.2 var(--display);
  letter-spacing: -0.01em;
  color: var(--navy);
}

/* Three words, never a percentage. There is no useful sense in which a water
   parameter is "83% right": either everyone fits, or they fit with no margin,
   or they cannot share a tank. */
.param__verdict { font: 600 0.875rem/1 var(--sans); }
.is-comfortable .param__verdict { color: var(--good-deep); }
.is-tight .param__verdict { color: var(--amber-deep); }
.is-clash .param__verdict { color: var(--coral-deep); }
.is-unknown .param__verdict { color: var(--gap); }

/* A row per species, and the overlap as a column through them. The band is a grid
   item spanning every row of the track column, declared FIRST so the bars paint
   over it -- an intersection you can point at rather than infer. */
.param__chart {
  --names: 7.5rem;
  --gutter-x: 0.7rem;
  --axis-height: 1rem;
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, var(--names)) minmax(0, 1fr);
  gap: 0.3rem var(--gutter-x);
  align-items: center;
  margin: 0.5rem 0 0.7rem;
}

/* Absolutely positioned, NOT a grid item. As a grid item it consumed a cell and
   every row after it auto-placed into the wrong column. It spans the track column
   by matching the template's own measurements, and stops above the axis. */
.param__band-layer {
  position: absolute;
  left: calc(var(--names) + var(--gutter-x));
  right: 0;
  top: 0;
  bottom: calc(var(--axis-height) + 0.3rem);
  pointer-events: none;
}
.param__band {
  position: absolute;
  top: 0; bottom: 0;
  border-radius: 3px;
  background: var(--param-wash);
  border-left: 1px solid var(--param-b);
  border-right: 1px solid var(--param-b);
}

.param__who {
  font-size: 0.75rem;
  color: var(--ink-faint);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.param__track { position: relative; height: 1.1rem; display: flex; align-items: center; }
.param__span {
  position: absolute;
  height: 0.375rem;
  border-radius: 999px;
  background: var(--line-strong);
}
/* The two species that set the edges are the answer to "why is it this narrow". */
.param__span.is-limit { background: var(--navy); }

.param__figure {
  position: absolute;
  right: 0;
  font: 500 0.75rem/1 var(--body);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  padding-left: 0.5rem;
  background: linear-gradient(90deg, transparent, var(--panel) 0.5rem);
}

.param__axis { position: relative; height: var(--axis-height); }
.param__tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font: 400 0.75rem/1 var(--body);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  white-space: nowrap;
}
.param__tick:first-child { transform: none; }
.param__tick:last-child { transform: translateX(-100%); }

.param__read {
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
.is-clash .param__read { color: var(--coral-deep); }

.param__gap {
  margin: 0.3rem 0 0;
  font-size: 0.75rem;
  color: var(--gap);
}

/* -------------------------------------------------------------------- tank */

/* One inline SVG in a cabinet projection, composed by the presenter. It scales
   with its viewBox, so a phone needs no rule of its own; the caps below keep a
   400 L tank from filling the column with empty glass. */
.tank__stage {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: 8rem;
  padding: 0.2rem 0 0;
}
.tank__scene {
  display: block;
  /* The presenter sets --tank-w from the cube root of the volume, so a 20 L
     tank draws smaller than a 200 L one. Floored, because on a phone a small
     tank at 55% of a narrow panel was 168px of glass. */
  width: max(var(--tank-w, 76%), 60%);
  height: auto;
  max-width: 26rem;
  max-height: 15.5rem;
  overflow: visible;
}
@media (max-width: 34rem) {
  .tank__scene { width: 100%; }
}

.tank__top { fill: #e4f4fa; opacity: 0.8; }
/* The glass edges drawn at a fixed pixel width whatever the tank's size, in
   a dark ink rather than the soft grey, so the box reads crisp on the hero
   and in the calculator alike (Efe: "the boundaries of the tank ... a little
   more crisp"). */
.tank__frame {
  fill: none;
  stroke: rgba(15, 43, 32, 0.62);
  stroke-width: 1.25px;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
}
.tank__glare { fill: #ffffff; opacity: 0.35; }
.tank__surface { stroke: var(--surface); stroke-width: 1.25px; vector-effect: non-scaling-stroke; opacity: 0.95; }
.tank__sand-side { fill: #a88a5c; opacity: 0.9; }
.tank__light { fill: #1c2925; }
.tank__shadow { fill: var(--shadow-deep); opacity: 0.4; }

.tank__bubble {
  fill: var(--bubble);
  transform-box: fill-box;
  transform-origin: center;
  animation: rise 6s ease-in infinite;
}
.tank__bubble--b { animation-delay: 2.1s; }
.tank__bubble--c { animation-delay: 4s; }
@keyframes rise {
  0%   { transform: translateY(0) scale(0.6); opacity: 0; }
  15%  { opacity: 0.75; }
  100% { transform: translateY(-40px) scale(1); opacity: 0; }
}

/* An empty tank rather than an empty space: the same box, lightly dashed. */
.tank__scene.is-empty .tank__frame { stroke-dasharray: 2 1.5; }
.tank__scene.is-empty .tank__water,
.tank__scene.is-empty .tank__side,
.tank__scene.is-empty .tank__glow { opacity: 0.45; }

.tank__label {
  margin: 0.2rem 0 0;
  text-align: center;
  font: 400 0.75rem/1 var(--mono);
  color: var(--ink-faint);
}

.stocklist {
  list-style: none;
  margin: 1.25rem 0 0;
  padding: 0.9rem 0 0;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0.5rem;
}

.stocklist__item {
  display: grid;
  grid-template-columns: 2.2rem minmax(0, auto) 1fr;
  gap: 0.55rem;
  align-items: baseline;
  font-size: 0.875rem;
}

.stocklist__count {
  font-family: var(--mono);
  color: var(--sea-deep);
  text-align: right;
}
.stocklist__name { color: var(--ink); white-space: nowrap; }
.stocklist__facts {
  font: 400 0.6875rem/1.5 var(--mono);
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ advice */

.advice__heading {
  margin: 1.1rem 0 0.55rem;
  font: 700 0.8125rem/1 var(--sans);
  letter-spacing: 0.01em;
}
.advice__heading:first-of-type { margin-top: 0; }
.advice__heading.is-stop { color: var(--coral-deep); }
.advice__heading.is-improve { color: var(--amber-deep); }
.advice__heading.is-unknown { color: var(--gap); }

.advice__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }

.advice__item {
  position: relative;
  padding-left: 1rem;
  font-size: 0.875rem;
  line-height: 1.5;
}
.advice__item::before {
  content: "";
  position: absolute;
  left: 0; top: 0.55em;
  width: 5px; height: 5px;
  border-radius: 50%;
}
.advice__item.is-stop::before { background: var(--coral); }
.advice__item.is-improve::before { background: var(--amber); }
.advice__item.is-unknown::before { background: var(--gap); }

.advice__text { color: var(--ink); }
.advice__detail { display: block; color: var(--ink-soft); font-size: 0.8125rem; }
.advice__item.is-unknown .advice__text { color: var(--ink-soft); }

.advice__clear {
  margin: 0;
  font: 600 1rem/1.5 var(--display);
  letter-spacing: -0.01em;
  color: var(--good-deep);
}

/* ---------------------------------------------------------------- the form */

.stack { display: grid; gap: 1.4rem; }

.field-grid {
  display: grid;
  gap: 0.875rem;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}

.field { display: grid; gap: 0.35rem; }
.field__label { font-size: 0.8125rem; color: var(--ink-soft); }
.field__note { color: var(--ink-faint); }

input[type="number"], input[type="search"], select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--panel-lift);
  color: var(--ink);
  font: 400 0.9375rem/1.4 var(--body);
}

/* The reported bug. A native <select> draws its chevron hard against the right
   edge of the control, and on a full-width box that reads as a button that has
   slipped its moorings. There is no property that moves the platform arrow, so
   the only fix is to remove it and draw our own at a stated inset: `appearance:
   none` takes the arrow away, the padding reserves the room, and the background
   image puts a green chevron back 0.85rem in. The chevron is a data URI rather
   than a file so a form control never waits on a second request to look
   finished; its colour is --sea-deep, spelled out because a data URI cannot
   read a custom property. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.5rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6 8 10.5 12.5 6' fill='none' stroke='%23127a3e' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.85rem center;
  background-size: 0.85rem;
  cursor: pointer;
}

input[type="number"] { font-family: var(--mono); }
input::placeholder { color: var(--ink-faint); }
input:hover, select:hover { border-color: var(--sea); }

/* Four options do not need the full width of the panel. Unbounded, the planting
   scale looked like the most important field on the form, which it is not. */
.field--planting { max-width: 24rem; }
.field__hint {
  margin: 0.15rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-faint);
}

.switch {
  display: flex; align-items: center; gap: 0.55rem;
  font-size: 0.9375rem; color: var(--ink-soft);
}

.button {
  appearance: none;
  border: 0;
  border-radius: 10px;
  /* Dark type on a lit green, not white. White on a green this vivid is 2.8:1;
     the ink is 5.9:1 and lets the button keep the accent colour. */
  background: var(--sea);
  color: var(--on-sea);
  padding: 0.8rem 1.5rem;
  font: 700 0.9375rem/1 var(--sans);
  letter-spacing: -0.005em;
  cursor: pointer;
  justify-self: start;
  transition: filter 140ms ease, box-shadow 140ms ease;
  /* inline-flex, because these are a mix of <button> and <a>. A button centres its
     own label; an anchor does not, so in a flex row that stretches both to the same
     height the anchor's text sat at the top of its box. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.button:hover { background: var(--sea-deep); }
/* A link dressed as a button is still a link, and links underline. */
a.button, a.button:hover { text-decoration: none; }

.button--quiet {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--line-strong);
  padding: 0.5rem 0.95rem;
  font-weight: 500;
  font-size: 0.8125rem;
  text-decoration: none;
  box-shadow: none;
}
.button--quiet:hover {
  background: var(--sea-wash);
  border-color: var(--sea);
  color: var(--sea-deep);
  filter: none;
  box-shadow: none;
}

.error {
  margin: 0 0 1rem;
  padding: 0.75rem 0.95rem;
  border-radius: 9px;
  border: 1px solid var(--coral);
  background: var(--coral-wash);
  color: var(--coral-deep);
  font-size: 0.9375rem;
}

/* ------------------------------------------------------------- the picker */

.picker { position: relative; }

.picker__results {
  position: absolute;
  z-index: 20;
  left: 0; right: 0;
  margin: 0.4rem 0 0;
  padding: 0.35rem;
  list-style: none;
  /* Scrolls. Capped at eight it was searchable but not browsable, so a species
     you could not spell was unreachable. */
  max-height: 22rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--panel-lift);
  border: 1px solid var(--line-strong);
  border-radius: 11px;
  box-shadow: 0 24px 48px -20px var(--shadow-deep);
}

.picker__results::-webkit-scrollbar { width: 10px; }
.picker__results::-webkit-scrollbar-thumb {
  background: var(--line-strong); border-radius: 99px;
  border: 3px solid var(--panel-lift);
}

.picker__empty { margin: 0.5rem 0 0; font-size: 0.875rem; color: var(--gap); }

.result {
  display: grid;
  grid-template-columns: 2.4rem 1fr;
  gap: 0.7rem;
  align-items: center;
  padding: 0.45rem 0.55rem;
  border-radius: 8px;
  cursor: pointer;
}
.result.is-active, .result:hover { background: var(--sea-wash); }

.result__text { display: grid; gap: 0.05rem; min-width: 0; }
.result__name { font: 600 0.9375rem/1.35 var(--sans); letter-spacing: -0.01em; color: var(--ink); }
.result__sci, .stocklist__sci {
  font: italic 400 0.8125rem/1.3 var(--body);
  color: var(--ink-soft);
}
.result__facts { font: 400 0.6875rem/1.4 var(--mono); color: var(--ink-faint); }
.result__name, .result__sci, .result__facts {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.thumb {
  width: 2.4rem; height: 2.4rem;
  border-radius: 7px;
  object-fit: contain;
  background: var(--panel);
}
.thumb--placeholder { padding: 0.3rem; color: var(--ink-faint); opacity: 0.8; }

.tanklist { list-style: none; margin: 0.9rem 0 0; padding: 0; display: grid; gap: 0.4rem; }

.tankrow {
  display: grid;
  grid-template-columns: 2.4rem 1fr 5rem auto;
  gap: 0.7rem;
  align-items: center;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel-lift);
}
.tankrow__text { display: grid; gap: 0.05rem; min-width: 0; }
.tankrow__name { font-size: 0.9375rem; }
.tankrow__sci { font: italic 400 0.8125rem/1.3 var(--body); color: var(--ink-soft); }
.tankrow__name, .tankrow__sci { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tankrow__count {
  width: 100%;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--panel);
  color: var(--ink);
  font: 400 0.875rem/1.3 var(--mono);
  text-align: center;
}

.tankrow__drop, .stock__drop {
  appearance: none; border: 0; background: transparent;
  color: var(--ink-faint);
  font: 400 1.15rem/1 var(--body);
  cursor: pointer; padding: 0.3rem 0.45rem; border-radius: 7px;
}
.tankrow__drop:hover, .stock__drop:hover { color: var(--coral-deep); background: var(--panel); }

.stock { display: grid; gap: 0.6rem; }
.stock__row { display: grid; grid-template-columns: 1fr 6.5rem auto; gap: 0.6rem; align-items: center; }

/* ----------------------------------------------------------------- sources */

.cite {
  display: inline-block;
  margin-left: 0.2rem;
  font: 400 0.625rem/1 var(--mono);
  vertical-align: super;
  color: var(--ink-faint);
  text-decoration: none;
}
.cite:hover { color: var(--sea-deep); }

.sources { padding: 0 clamp(1.1rem, 2.6vw, 1.6rem); }

.sources__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  list-style: none;   /* the default disclosure triangle, replaced below */
  font: 650 0.75rem/1 var(--sans);
  color: var(--ink-faint);
  padding: 0.3rem 0;
}
.sources__toggle::-webkit-details-marker { display: none; }
.sources__toggle:hover { color: var(--ink-dim); }
.sources__toggle:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 3px; }

/* A plus that becomes a minus. Two bars; the upright one collapses when open. */
.sources__sign {
  position: relative;
  width: 0.62rem;
  height: 0.62rem;
  flex: none;
}
.sources__sign::before,
.sources__sign::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 1px;
  background: currentColor;
}
.sources__sign::after {
  transform: rotate(90deg);
  transition: transform 140ms ease, opacity 140ms ease;
}
.sources[open] .sources__sign::after { transform: rotate(0deg); opacity: 0; }

.sources__count {
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.sources__count::before { content: "("; }
.sources__count::after { content: ")"; }

.sources__list { margin-top: 0.6rem; }

@media (prefers-reduced-motion: reduce) {
  .sources__sign::after { transition: none; }
}
.sources__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.sources__item {
  display: grid; grid-template-columns: 1.4rem 1fr; gap: 0.4rem;
  align-items: baseline; font-size: 0.8125rem; color: var(--ink-soft);
}
.sources__number { font: 400 0.6875rem/1.5 var(--mono); color: var(--ink-faint); text-align: right; }
.sources__item a { color: var(--sea-deep); text-decoration: none; }
.sources__item a:hover { text-decoration: underline; }
.sources__species { color: var(--ink-faint); }
.sources__species::before { content: " — "; }
.sources__item:target .sources__number { color: var(--sea-deep); font-weight: 500; }

.notes { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.notes li { font-size: 0.875rem; color: var(--ink-soft); }
/* The remedy under a gap, quieter than the gap itself -- the same
   treatment .advice__detail gets, because it is the same kind of second
   sentence. Gaps moved here out of "What to do": they are facts about the
   dataset rather than actions, and they may not be invisible either. */
.notes__detail {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

.empty__body { max-inline-size: 40rem; }
.empty__body code {
  font-family: var(--mono); font-size: 0.875em;
  background: var(--panel-lift); padding: 0.1em 0.35em; border-radius: 5px;
}

/* ---------------------------------------------------------------- the toolbar */

.toolbar {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 1rem;
  padding: 0 clamp(1.1rem, 2.6vw, 1.6rem);
}
.toolbar .is-plain {
  border-color: transparent;
  color: var(--ink-dim);
  margin-left: auto;   /* the destructive one sits apart from the two edits */
}
.toolbar .is-plain:hover { color: var(--ink); }

/* ------------------------------------------------------------- the swimmers */

/* Placed and sized in presenters.py -- one source of truth; CSS spends it.
   The body of every symbol is `currentColor`, so the family tint is a `color`;
   the detail strokes read `--detail`, a custom property, because a stylesheet
   selector cannot reach inside a <use>'s shadow tree but inheritance can. */
.swimmer {
  color: var(--tint, #5f6f7c);
  --detail: color-mix(in srgb, var(--tint, #5f6f7c) 40%, var(--ink));
  pointer-events: none;
}
.swimmer__drift {
  transform-box: fill-box;
  transform-origin: center;
  animation: swim 15s ease-in-out infinite;
  animation-delay: calc(var(--delay) * -1);
}
.swimmer.is-substrate .swimmer__drift {
  animation-name: creep;
  animation-duration: 22s;
}
@keyframes swim {
  0%, 100% { transform: translate(0, 0); }
  30%      { transform: translate(2.2px, -1.4px); }
  60%      { transform: translate(-2px, 1.1px); }
}
@keyframes creep {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(3px, 0); }
}

/* FAMILY TINTS. A stylistic palette -- Efe chose tinted over monochrome -- and
   not a claim about any species: a black neon and a cardinal are both "tetra
   blue" here. Muted, so the water still reads as water. */
.swimmer.is-tetra         { --tint: #4d7ea8; }
.swimmer.is-rasbora       { --tint: #b86b3a; }
.swimmer.is-barb          { --tint: #c2793a; }
.swimmer.is-cory          { --tint: #8a7458; }
.swimmer.is-pleco         { --tint: #4b5a4e; }
.swimmer.is-loach         { --tint: #8c6a3f; }
.swimmer.is-shark         { --tint: #5c6d7a; }
.swimmer.is-gourami       { --tint: #c69a3c; }
.swimmer.is-betta         { --tint: #7a4d9c; }
.swimmer.is-cichlid_dwarf { --tint: #c08a2f; }
.swimmer.is-cichlid       { --tint: #6a7f57; }
.swimmer.is-angelfish     { --tint: #5f6f7c; }
.swimmer.is-discus        { --tint: #b05a3a; }
.swimmer.is-livebearer    { --tint: #d47a2a; }
.swimmer.is-rainbowfish   { --tint: #4f9fa8; }
.swimmer.is-goldfish      { --tint: #e0862b; }
.swimmer.is-shrimp        { --tint: #c9483a; }
.swimmer.is-snail         { --tint: #7d6a52; }

.prop {
  color: var(--tint, #4c9a5f);
  --detail: color-mix(in srgb, var(--tint, #4c9a5f) 55%, var(--ink));
  pointer-events: none;
}
.prop.is-plant-a { --tint: #3f8f52; }
.prop.is-plant-b { --tint: #4c9a5f; }
.prop.is-plant-c { --tint: #6aa86b; }
.prop.is-rock-a  { --tint: #8f8578; }
.prop.is-rock-b  { --tint: #a39a8c; }

@media (prefers-reduced-motion: reduce) {
  .swimmer__drift, .tank__bubble { animation: none; }
}

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

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

  /* ONE COLUMN, AND A DIFFERENT ORDER. Stacked, the findings belong last: read
     before you can see what is in the tank, advice is out of order. `display:
     contents` dissolves both column wrappers so all four panels become items of
     the one-column board and can be sequenced.

     The desktop arrangement cannot simply be reused, and the phone arrangement
     cannot be the DOM order, so one of them has to be stated. This is it. */
  .board__main,
  .side { display: contents; }
  /* The heading, then the Premium card, then the score (Efe, 2026-11-11). */
  .board > .board__main > .hero { order: -2; }
  .board > .side > .promo-slot { order: -1; }
  .board > .board__main > #score { order: 1; }
  .board > .board__main > #tank-setup { order: 2; }
  .board > .side > .tank { order: 3; }
  .board > .side > #suggestions { order: 4; }
  .board > .board__main > #findings { order: 5; }
  /* Premium's advice reads with the findings it explains, just above them
     (same order, earlier in the page). */
  .board > .board__main > #ai-advice { order: 5; }
}

@media (max-width: 34rem) {
  .tally__row { grid-template-columns: 6.5rem 1fr 2.2rem; }
  .stocklist__item { grid-template-columns: 2rem 1fr; }
  .stocklist__facts { grid-column: 2; }
  .tankrow { grid-template-columns: 2rem 1fr 4rem auto; }
  .result { grid-template-columns: 2rem 1fr; }
  .thumb { width: 2rem; height: 2rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ------------------------------------------------------------ species profile */

.profile {
  display: grid;
  gap: 1.1rem;
  width: 100%;
  max-width: 58rem;
  margin-inline: auto;
}

.profile__head {
  display: grid;
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
  gap: clamp(1.2rem, 3vw, 2.4rem);
  align-items: center;
  padding: 0 clamp(1.1rem, 2.6vw, 1.6rem);
}

.profile__art {
  border-radius: 14px;
  overflow: hidden;
  background: var(--glass);
  border: 1px solid var(--line);
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
}
.profile__art img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* A glyph is a symbol, not a photograph: it sits small and centred rather than
   being stretched to fill a frame it was never drawn for. */
.profile__art.is-glyph img {
  width: 46%;
  height: auto;
  object-fit: contain;
  filter: var(--silhouette);
  opacity: 0.4;
}

.profile__intro { display: grid; gap: 0.55rem; justify-items: start; }

.taglist { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0; }
.tag { font: 600 0.875rem/1.2 var(--sans); color: var(--sea-deep); }

.profile__name {
  font: 700 clamp(2rem, 5vw, 3rem)/1.05 var(--display);
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--navy);
}
.profile__sci { margin: 0; font-style: italic; color: var(--navy); }
.profile__summary { margin: 0.3rem 0 0; color: var(--ink-dim); max-width: 44ch; }
.profile__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.5rem; }
.profile__unsourced {
  padding: 0 clamp(1.1rem, 2.6vw, 1.6rem);
  color: var(--ink-faint);
  font-size: 0.875rem;
}

.care__title {
  font: 700 1.25rem/1.2 var(--display);
  letter-spacing: -0.01em;
  margin: 0 0 1rem;
  color: var(--navy);
}
.care__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.4rem 1.6rem;
  align-items: start;
}

.fact { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.7rem; }
.fact__icon {
  width: 2.1rem;
  height: 2.1rem;
  padding: 0.5rem;
  box-sizing: border-box;
  border-radius: 9px;
  background: var(--sea-wash);
  color: var(--sea);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fact__body { display: grid; gap: 0.1rem; min-width: 0; }
.fact__label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  font: 400 0.75rem/1.3 var(--body);
  color: var(--ink-faint);
}
.fact__value { font-weight: 500; color: var(--ink); text-wrap: balance; }
.fact__note { font-size: 0.75rem; color: var(--ink-faint); }

/* An unrecorded value is stated, not hidden and not filled in. It is also the
   to-do list, so it should be legible rather than shouted. */
/* `.fact.is-empty` is gone with the empty rows: a fact with no figure no longer
   gets one. What is missing is named once, under the grid. */
.care__gaps {
  margin: 0.9rem 0 0;
  color: var(--gap);
  font-size: 0.8125rem;
  line-height: 1.55;
}

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

.stocklist__link { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
.stocklist__link:hover { color: var(--sea-deep); border-bottom-color: var(--sea); }

a.tankrow__name { color: inherit; text-decoration: none; }
a.tankrow__name:hover { color: var(--sea-deep); text-decoration: underline; }

.keeping__body { margin: 0; color: var(--ink-dim); line-height: 1.68; }

/* A value that is not from a cited source: keeping knowledge, or one this tool
   calculated. Small and quiet -- it qualifies the number, it does not warn about it. */
.fact__flag {
  display: inline-block;
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  background: var(--amber-wash);
  color: var(--amber-deep);
  font: 650 0.6875rem/1.4 var(--sans);
  vertical-align: 1px;
}

.profile__figure { display: grid; gap: 0.4rem; align-content: start; }
.credit { margin: 0; font-size: 0.6875rem; color: var(--ink-faint); }
.credit a { color: var(--ink-faint); }


/* ----------------------------------------------------------- fish database */

.db { width: 100%; }
.db__head { margin-bottom: 2rem; }
.db__title {
  font: 700 clamp(1.6rem, 4vw, 2.2rem)/1.1 var(--display);
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--navy);
}
.db__lede { margin: 0.6rem 0 0; color: var(--ink-dim); max-width: 60ch; }

.db__search {
  width: 100%;
  margin-top: 1.4rem;
  padding: 0.8rem 1rem;
  font: 400 1rem/1.2 var(--body);
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.db__search:focus { outline: none; border-color: var(--sea-deep); }
.db__count { margin: 0 0 0.9rem; font: 400 0.75rem/1 var(--mono); color: var(--ink-faint); }

.db__kind {
  font: 650 0.75rem/1 var(--sans);
  color: var(--sea-deep);
  /* No top margin by default; the rule below adds it between groups. See the
     note there -- the obvious way to write this does not work. */
  margin: 0 0 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.db__tally { color: var(--ink-faint); }
.db__tally::before { content: "("; }
.db__tally::after { content: ")"; }

.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.75rem;
}
.card__link {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr);
  gap: 0.8rem;
  align-items: center;
  padding: 0.7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  text-decoration: none;
  color: inherit;
  height: 100%;
}
.card__link:hover { border-color: var(--sea); background: var(--panel-lift); }
.card__link:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }

.card__art {
  width: 4rem; height: 4rem;
  border-radius: 9px;
  overflow: hidden;
  background: var(--glass);
  display: grid;
  place-items: center;
}
.card__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card__art.is-glyph img { width: 62%; height: auto; object-fit: contain; filter: var(--silhouette); opacity: 0.4; }

.card__body { display: grid; gap: 0.1rem; min-width: 0; }
.card__name { font: 600 0.9375rem/1.3 var(--sans); letter-spacing: -0.01em; color: var(--ink); }
.card__sci { font-style: italic; font-size: 0.8125rem; color: var(--ink-faint); }
.card__facts { font: 400 0.6875rem/1.4 var(--mono); color: var(--ink-faint); }

.db__empty { margin: 2rem 0; color: var(--ink-faint); }

/* ------------------------------------------------------- plain prose pages */

.prose { width: 100%; max-width: 44rem; margin-inline: auto; }
.prose__title {
  font: 700 clamp(1.6rem, 4vw, 2.2rem)/1.1 var(--display);
  letter-spacing: -0.01em;
  margin: 0 0 1rem;
  color: var(--navy);
}
.prose__empty { color: var(--ink-faint); font-style: italic; }

/* The prose pages -- About and the FAQ. Measured at 44rem above, which is the
   line length the rest of the site already reads at, so these need type scale and
   rhythm rather than any layout of their own. */

.prose__lede {
  margin: 0 0 2rem;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

.prose__heading {
  margin: 2.4rem 0 0.75rem;
  font: 700 1.25rem/1.25 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
  /* The one piece of furniture on these pages: a short lit rule, the same device
     the step eyebrows and the database headings use. */
  display: flex;
  align-items: center;
  gap: 0.6rem;
}


.prose p { margin: 0 0 1rem; line-height: 1.7; }
.prose strong { color: var(--ink); font-weight: 600; }

.prose__list { margin: 0 0 1rem; padding-left: 1.1rem; display: grid; gap: 0.6rem; }
.prose__list li { line-height: 1.65; }
.prose__list li::marker { color: var(--sea); }

/* The closing qualifier on the About page: true, worth saying, and not the
   headline -- so it sits quieter than the body it follows. */
.prose__note {
  margin: 2rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------- the FAQ list */

/* A real <dl>: these are questions and their answers, and the markup should say
   so. Answers are open rather than folded away, because half of them exist to
   warn about a limit the reader does not yet know to click on. */
.qa { margin: 0; display: grid; gap: 1.6rem; }

.qa__q {
  font: 700 1.125rem/1.35 var(--display);
  letter-spacing: -0.015em;
  color: var(--navy);
  scroll-margin-top: 5rem;
}

.qa__a {
  margin: 0.55rem 0 0;
}
.qa__a p { margin: 0 0 0.7rem; }
.qa__a p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------- tank mates */

.mates__heading {
  font: 650 0.75rem/1 var(--sans);
  margin: 1.6rem 0 0.7rem;
  display: flex; align-items: center; gap: 0.5rem;
}
.mates__heading:first-of-type { margin-top: 0; }
.mates__heading.is-bad { color: var(--coral-deep); }
.mates__heading.is-mid { color: var(--amber-deep); }
.mates__heading.is-good { color: var(--good-deep); }
.mates__tally { color: var(--ink-faint); }
.mates__tally::before { content: "("; }
.mates__tally::after { content: ")"; }

.mates__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.mate {
  display: grid; gap: 0.15rem;
  padding: 0.55rem 0.7rem;
  border-radius: 10px;
}
.mate__name { font-weight: 500; color: var(--ink); text-decoration: none; }
.mate__name:hover { text-decoration: underline; }
.mate__why { font-size: 0.8125rem; color: var(--ink-dim); }

.mates__flat { margin: 0; line-height: 1.9; color: var(--ink-dim); font-size: 0.875rem; }
.mates__flat a { color: var(--ink-dim); text-decoration: none; }
.mates__flat a:hover { color: var(--sea-deep); text-decoration: underline; }
.mates__gap { margin: 1.2rem 0 0; font-size: 0.8125rem; color: var(--ink-faint); }

/* The remainder, stated rather than listed. Deliberately quiet and unbordered:
   this is the section saying what it did NOT find, and it must not compete with
   the two lists above it that say what it did. */
.mates__rest {
  margin: 1.4rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ink-faint);
}


.mates__more {
  appearance: none;
  margin-top: 0.6rem;
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: transparent;
  color: var(--ink-soft);
  font: 400 0.8125rem/1 var(--body);
  cursor: pointer;
}
.mates__more:hover { background: var(--panel-lift); color: var(--ink); }
.mates__more:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }

/* The browser owns the backdrop, Escape and focus handling for <dialog>. */
.sheet {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(34rem, calc(100vh - 4rem));
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel);
  color: var(--ink);
  overflow: hidden;
}
.sheet::backdrop { background: rgba(15, 43, 32, 0.42); }

.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.1rem;
  border-bottom: 1px solid var(--line);
  background: var(--panel-lift);
}
.sheet__title { margin: 0; font: 700 0.9375rem/1.3 var(--sans); letter-spacing: -0.01em; }
.sheet__title.is-bad { color: var(--coral-deep); }
.sheet__title.is-mid { color: var(--amber-deep); }
.sheet__title.is-good { color: var(--good-deep); }

.sheet__close {
  appearance: none; border: 0; background: transparent;
  color: var(--ink-faint); font-size: 1.5rem; line-height: 1;
  cursor: pointer; padding: 0 0.2rem;
}
.sheet__close:hover { color: var(--ink); }

.sheet__body {
  padding: 1rem 1.1rem;
  overflow-y: auto;
  max-height: calc(min(34rem, 100vh - 4rem) - 4rem);
}

/* ------------------------------------------------- the species account cards */

.account { display: grid; gap: 0.9rem; }

.account__origin {
  margin: 0 0 0.2rem;
  width: 100%;
  font: 400 0.75rem/1.5 var(--body);
  color: var(--ink-faint);
}

/* The spine is the signature: a field-guide divider tab flattened into the card.
   One device, repeated seven times, doing all the differentiating. */
.account__card {
  --accent: var(--navy);
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.1rem, 2.6vw, 1.5rem);
}
.account__card.is-moss   { --accent: var(--moss); }
.account__card.is-bronze { --accent: var(--bronze); }
.account__card.is-petrol { --accent: var(--petrol); }
.account__card.is-olive  { --accent: var(--olive); }
.account__card.is-brick  { --accent: var(--brick); }
.account__card.is-plum   { --accent: var(--plum); }

/* Side-headed, the way a field guide sets a species account: the heading sits in
   its own rail so all seven align down the page, and the prose gets the width it
   was already measured for. Stacked on narrow screens, where a rail would leave
   the text nothing to live in. */
@media (min-width: 52rem) {
  .account__card {
    display: grid;
    grid-template-columns: 9.5rem minmax(0, 1fr);
    gap: 0 1.8rem;
    align-items: start;
  }
  .account__head { margin-bottom: 0; }
}

.account__head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
  flex-wrap: wrap;
}

.account__chip {
  width: 2rem; height: 2rem;
  flex: none;
  display: grid; place-items: center;
  border-radius: 8px;
  /* Tinted from the accent itself rather than a second hand-picked colour, so the
     chip can never drift out of step with its spine. */
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.account__chip svg {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}

.account__heading {
  margin: 0;
  font: 700 1.0625rem/1.25 var(--display);
  letter-spacing: -0.01em;
  color: var(--accent);
}


.account__p {
  margin: 0 0 0.85rem;
  color: var(--ink-dim);
  line-height: 1.68;
}
.account__body > .account__p:last-child { margin-bottom: 0; }
.account__p strong { color: var(--ink); font-weight: 600; }

/* color-mix is well supported but not universal; a flat tint is the fallback. */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
  .account__chip { background: var(--ground-tint); }
}

/* ------------------------------------------------------------------- top bar */

:target, [id^="source-"], .panel__title { scroll-margin-top: 5rem; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter: blur(8px);
}

/* A lit rule instead of a grey border. The bar is the only furniture on every
   page, so it is where the palette introduces itself. */
.topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--sea);
}
@supports not (backdrop-filter: blur(8px)) { .topbar { background: var(--panel); } }

.topbar__inner {
  max-width: 82rem;
  margin: 0 auto;
  padding: 0.6rem var(--gutter);
  display: flex;
  align-items: center;
  gap: clamp(0.6rem, 2vw, 1.6rem);
}

.topbar__mark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font: 700 1.0625rem/1 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.topbar__mark span { color: var(--sea-deep); }

/* The Finform wordmark (templates/_wordmark.html; Efe, 2026-11-14, option C
   of design/finform-wordmark-finalists.html). Outfit SemiBold, lowercase: a
   green "fin" whose i wears a leaf for a dot, and a blue "form" whose o is a
   glossy bubble (static/brand/bubble-o.svg). Sized by the text around it.
   The selectors name .topbar__mark too, so the bar's own span colours above
   never reach the two halves. */
.wordmark {
  --wm-fin: #267253;
  --wm-form: #075985;
  display: inline-block;
  font: 600 1em/1 "Outfit", var(--sans);
  letter-spacing: -0.03em;
  white-space: nowrap;
}
.wordmark .wordmark__fin, .topbar__mark .wordmark .wordmark__fin,
html.is-premium .topbar__mark .wordmark .wordmark__fin,
.wordmark .wordmark__i, .topbar__mark .wordmark .wordmark__i,
html.is-premium .topbar__mark .wordmark .wordmark__i { color: var(--wm-fin); }
.wordmark .wordmark__form, .topbar__mark .wordmark .wordmark__form,
html.is-premium .topbar__mark .wordmark .wordmark__form { color: var(--wm-form); }
/* The guide previews set every span to a block (".mini span"), which would
   stack the letters; the wordmark's own display wins at two classes deep. */
.mini .wordmark { display: inline-block; }
.wordmark .wordmark__fin, .wordmark .wordmark__form { display: inline; }
.wordmark .wordmark__i { position: relative; display: inline-block; margin-left: 0.065em; }
.wordmark__i::after {
  content: ""; position: absolute; left: calc(50% + 0.015em); top: calc(0.14em - 1px);   /* clear of the stem even when small */
  width: 0.176em; height: 0.12em; transform: translateX(-50%) rotate(-33deg);
  border-radius: 100% 0 100% 0; background: currentColor;
}
/* Two classes deep, so a holder's rule for its own icon (".plansheet__brand
   img", ".mini__brand img") never reaches the bubble. */
.wordmark .wordmark__o {
  display: inline-block; width: 0.528em; height: 0.528em; max-width: none;
  vertical-align: -0.02em; margin: 0 0.019em 0 0.023em;
}
/* On the deep-ocean Premium bar: a mint fin and a pale sky form. */
html.is-premium .topbar__mark .wordmark, .wordmark--dark { --wm-fin: #a1ddab; --wm-form: #86bcd4; }
html.is-premium .topbar__mark .wordmark__o, .wordmark--dark .wordmark__o { filter: saturate(1.15); }
.topbar__mark .wordmark { font-size: 1.22em; }
.topbar__glyph { color: var(--sea); display: grid; place-items: center; }
/* The mark shows the favicon file itself rather than a copy of its definition.
   Sized here now that this file is no longer mid-QA-round; the size was inline
   in base.html, which said to fold it in at the first opportunity. */
.topbar__glyph img { width: 1.45rem; height: 1.45rem; display: block; }
.topbar__glyph svg {
  width: 1.35rem; height: 1.35rem;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

.tabs { display: flex; align-items: center; gap: 0.2rem; flex: 1; flex-wrap: wrap; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.85rem;
  border-radius: 9px;
  font: 600 0.875rem/1 var(--sans);
  letter-spacing: -0.01em;
  color: var(--ink-soft);
  text-decoration: none;
  white-space: nowrap;
}
.tab:hover { background: var(--ground-tint); color: var(--ink); }
.tab:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }
.tab.is-current { background: var(--sea-wash); color: var(--sea-deep); }

.tab__icon {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

/* No longer rendered: it was a second link to the tab you were already on, and a
   full green fill -- part of why Efe read the page as "too much green". The rule
   stays because `scripts/make_favicon.py` reads this colour pair, and a call to
   action on some other page will want the same treatment. */
.topbar__cta {
  margin-left: auto;
  padding: 0.55rem 1.05rem;
  border-radius: 9px;
  background: linear-gradient(120deg, var(--sea), var(--sea-bright));
  color: var(--on-sea);
  font: 700 0.875rem/1 var(--sans);
  letter-spacing: -0.01em;
  box-shadow: 0 6px 16px -8px var(--glow);
  text-decoration: none;
  white-space: nowrap;
}
.topbar__cta:hover { filter: brightness(1.06); }

@media (max-width: 46rem) {
  .topbar__cta { display: none; }   /* the calculator tab already goes there */

  /* STACK, rather than let the tabs wrap beside the wordmark.
     On a phone the one-row bar runs out of width and `flex-wrap` breaks the four
     tabs into a ragged second column next to the mark -- three links in one
     stack, the fourth stranded in another, which reads as a layout bug because
     it is one. So the mark takes its own centred line and the sections become a
     proper four-across tab bar underneath.

     A grid of four equal columns, not a flex row: equal columns mean the break
     points are ours, and "Fish database" wraps inside its own cell instead of
     deciding where the whole row breaks. */
  .topbar__inner {
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.55rem var(--gutter) 0.35rem;
  }
  .topbar__mark { align-self: center; font-size: 1.125rem; }

  .tabs {
    width: 100%;
    display: grid;
    /* Was repeat(4, 1fr) when there were exactly four tabs. Six now, and a
       hardcoded count silently overflows the row the moment one is added --
       auto-fit sizes itself and wraps to a second row instead. */
    /* 3.5rem, not 4 or 4.5: five tabs at 4.5rem need 22.5rem plus gaps,
       which a 390px phone has not got, so "About" fell alone onto a second
       row; at 4rem the same happened at 340px once a classic scrollbar took
       its 15px (Codex, round 012). At 3.5rem all five sit on one line down
       to a 325px row. "Fish database" wraps inside its cell, as intended.
       3rem since the sixth tab, the dive (2026-10-20): six cells, five gaps
       and the gutters make 332px, so one row on every phone from 360px up;
       narrower, auto-fit wraps the last tab rather than overflowing. */
    grid-template-columns: repeat(auto-fit, minmax(3rem, 1fr));
    gap: 0.15rem;
  }

  /* Icon over label. At four across there is no room for them side by side, and
     the icons are what make the tabs scannable at this size -- which is why they
     come BACK here rather than being hidden as they were. */
  .tab {
    flex-direction: column;
    justify-content: flex-start;
    gap: 0.25rem;
    padding: 0.4rem 0.15rem;
    font-size: 0.6875rem;
    line-height: 1.15;
    text-align: center;
    white-space: normal;   /* or "Fish database" overflows its cell */
  }
  .tab__icon { display: block; width: 1.15rem; height: 1.15rem; }
}

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

.hero { margin: 0 0 0.3rem; }
.hero__title {
  margin: 0;
  font: 700 clamp(1.75rem, 4.4vw, 2.75rem)/1.05 var(--display);
  letter-spacing: -0.01em;
  text-wrap: balance;
  max-width: 22ch;
  color: var(--ink);
}
.hero__lede { margin: 0.7rem 0 0; color: var(--ink-faint); max-width: 56ch; }

/* The Premium card opens the right column, above the tank (Efe, 2026-11-09:
   further down nobody saw it). Light frosted glass in the page's own colors,
   not a dark block (Efe, 2026-11-10). Empty when Premium is not offered. */
.promo-slot:empty { display: none; }
.promo {
  position: relative; overflow: hidden;
  border-radius: var(--radius); padding: 1.15rem 1.25rem 1.2rem;
  background: color-mix(in srgb, #ffffff 58%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.15); backdrop-filter: blur(14px) saturate(1.15);
  border: 1px solid color-mix(in srgb, var(--sea) 20%, transparent);
  box-shadow: 0 1px 2px var(--shadow), 0 18px 40px -32px var(--shadow-deep);
  color: var(--ink);
}
.promo__tag { margin: 0; font: 650 0.8125rem/1.2 var(--sans); color: var(--sea-deep); }
.promo__title { margin: 0.25rem 0 0.6rem; font: 700 1.35rem/1.15 var(--display); letter-spacing: -0.01em; color: var(--ink); }
.promo__list { margin: 0 0 0.8rem; padding-left: 1.1rem; display: grid; gap: 0.3rem; font-size: 0.9375rem; line-height: 1.45; color: var(--ink-soft); }
.promo__list li::marker { color: var(--sea); }
.promo__list strong { color: var(--ink); }
.promo__proof { margin: 0 0 0.95rem; font-size: 0.8125rem; line-height: 1.45; color: var(--ink-faint); }

/* PREMIUM, UNLOCKED. The same glass, made to feel earned by small things: a
   faint ring of gold, a gold badge, the two things as tiles, and on the first
   visit one soft sweep of light. Reduced motion gets no sweep. */
.promo--on { border-color: color-mix(in srgb, var(--premium-gold-deep) 45%, transparent); }
.promo__badge {
  display: inline-block; padding: 0.2rem 0.65rem; border-radius: 999px;
  background: linear-gradient(135deg, var(--premium-gold), var(--premium-gold-deep));
  color: var(--premium-gold-ink); font: 700 0.75rem/1.2 var(--sans);
}
.promo--on .promo__title { margin: 0.5rem 0 0.2rem; font-size: 1.45rem; }
.promo__sub { margin: 0 0 0.9rem; font-size: 0.9375rem; color: var(--ink-soft); }
.perks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
.perk {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.1rem 0.7rem; align-items: center;
  padding: 0.75rem 0.85rem; border-radius: 12px; text-align: left; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--sea) 18%, transparent);
  background: color-mix(in srgb, #ffffff 65%, transparent);
  color: var(--ink); font: inherit; text-decoration: none;
  transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.perk:hover { background: #ffffff; border-color: color-mix(in srgb, var(--sea) 45%, transparent); transform: translateY(-1px); }
.perk:focus-visible { outline: 3px solid var(--sea); outline-offset: 2px; }
.perk__icon {
  grid-row: span 2; width: 2.3rem; height: 2.3rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--sea-wash); color: var(--sea-deep);
}
.perk__icon svg { width: 1.25rem; height: 1.25rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.perk__name { font: 700 1rem/1.2 var(--display); }
.perk__line { font-size: 0.8125rem; line-height: 1.3; color: var(--ink-faint); }
.promo--on.is-welcome::after {
  content: ""; position: absolute; inset: -40% auto -40% -60%; width: 40%;
  background: linear-gradient(100deg, transparent, color-mix(in srgb, var(--premium-gold) 28%, transparent), transparent);
  transform: skewX(-18deg); pointer-events: none;
  animation: premium-sheen 1.6s 0.35s ease-out both;
}
@keyframes premium-sheen { from { left: -60%; } to { left: 130%; } }
@media (prefers-reduced-motion: reduce) { .promo--on.is-welcome::after { animation: none; display: none; } }
@media (max-width: 30rem) { .perks { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ database filters */

.db__layout { display: grid; gap: 1.6rem; align-items: start; }
@media (min-width: 60rem) {
  .db__layout { grid-template-columns: 14rem minmax(0, 1fr); gap: 2rem; }
  /* The rail is hidden until database.js starts. Reserve the results column
     now, or auto-placement briefly squeezes every card into the 14rem rail:
     the one-column flicker Efe saw on the database pages (Codex's fix). */
  .db__results { grid-column: 2; }
  .filters { position: sticky; top: 5rem; }
}

/* The panel chrome moved off .filters and onto the <details> inside it, so the
   summary bar can sit flush against the border and read as one control. */
.filters { display: block; }

.filters__box {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
}

/* The summary must not be subtle. It is the only thing standing between a phone
   user and the filters, so it gets an icon, a label, the count of what is
   currently ticked, and a chevron that turns -- four separate signals that this
   row opens. */
.filters__summary {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.8rem 1.05rem;
  cursor: pointer;
  user-select: none;
  list-style: none;          /* Firefox's default triangle */
}
.filters__summary::-webkit-details-marker { display: none; }   /* Safari's */
.filters__summary:hover { background: var(--ground-tint); }
.filters__summary:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: -2px; }

.filters__icon {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: var(--sea); stroke-width: 1.8; stroke-linecap: round;
}
.filters__label {
  font: 650 0.75rem/1 var(--sans);
  color: var(--sea-deep);
}
.filters__active {
  padding: 0.2rem 0.44rem;
  border-radius: 999px;
  background: var(--sea);
  color: var(--on-sea);
  font: 600 0.6875rem/1 var(--body);
}
.filters__chevron {
  margin-left: auto;
  width: 1.1rem; height: 1.1rem;
  fill: none; stroke: var(--ink-faint); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform 0.18s ease;
}
.filters__box[open] .filters__chevron { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .filters__chevron { transition: none; }
}

.filters__body {
  display: grid;
  gap: 1.1rem;
  padding: 0.9rem 1.05rem 1.05rem;
  border-top: 1px solid var(--line);
}

.filters__clear {
  justify-self: start;
  appearance: none; border: 0; background: transparent; padding: 0;
  font: 400 0.75rem/1 var(--body); color: var(--ink-faint);
  cursor: pointer; text-decoration: underline;
}
.filters__clear:hover { color: var(--coral-deep); }

.facet { border: 0; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.facet__legend {
  padding: 0;
  margin-bottom: 0.35rem;
  font: 500 0.75rem/1 var(--body);
  color: var(--ink-soft);
}
.facet__option {
  display: flex; align-items: center; gap: 0.5rem;
  font-size: 0.8125rem; color: var(--ink-dim);
  cursor: pointer;
}
.facet__option:hover { color: var(--ink); }
.facet__option input { accent-color: var(--sea); width: 0.95rem; height: 0.95rem; }
.facet__option input:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }

.db__results { min-width: 0; }
/* Space between one kind and the next, and the reason it is written this way.

   `.db__kind:first-of-type` looks like it exempts the first heading, and it
   exempted ALL of them: every `.db__group` holds exactly one <h2>, so each one
   is the first of its type inside its own section. The three headings sat flush
   against the cards above them and no test noticed, because a wrong margin is
   still a rendered page.

   Keying off the PREVIOUS group fixes it, and `:not([hidden])` makes it survive
   filtering -- database.js hides a whole group by setting `hidden`, so when the
   fish are filtered out the shrimp heading correctly becomes the top one again
   and drops its margin. */
.db__group:not([hidden]) + .db__group .db__kind { margin-top: 2rem; }

/* The collapsed parameter: the overlap alone, on a scale, as a button. */
.param__rail {
  display: block;
  width: 100%;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0.35rem 0 0;
  margin: 0.2rem 0 0.5rem;
  cursor: pointer;
  text-align: left;
}
.param__rail:focus-visible { outline: 2px solid var(--param-b); outline-offset: 3px; border-radius: 6px; }

.param__scale {
  position: relative;
  display: block;
  height: 2.1rem;
  border-radius: 8px;
  background: var(--ground-tint);
  box-shadow: inset 0 1px 2px var(--shadow);
}
.param__overlap {
  position: absolute;
  top: 0; bottom: 0;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--param-a), var(--param-b));
  box-shadow: 0 0 10px var(--param-wash);
  display: flex;
  align-items: center;
  justify-content: center;
}
.param__value {
  font: 700 0.8125rem/1 var(--sans);
  letter-spacing: -0.01em;
  color: var(--on-accent);
  white-space: nowrap;
}
/* Too narrow to hold its own label: the value steps outside rather than clipping. */
.param__overlap.is-narrow { justify-content: flex-start; overflow: visible; }
.param__overlap.is-narrow .param__value {
  position: absolute;
  left: calc(100% + 0.5rem);
  color: var(--param-a);
}
.param__overlap.is-narrow.is-left .param__value {
  left: auto;
  right: calc(100% + 0.5rem);
}
.param__rail:hover .param__overlap { filter: brightness(1.12); }

/* The rail's own axis needs the variable the chart normally declares. */
.param__rail .param__axis { --axis-height: 1rem; display: block; margin-top: 0.3rem; }

.sheet__note {
  margin: 1rem 0 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line);
  font-size: 0.8125rem;
  color: var(--ink-faint);
}
.sheet__note strong { color: var(--navy); }

/* ------------------------------------------------------------- the planner */

/* A candidate is a database card that can be chosen, so it borrows that layout
   and adds the one thing the database has no use for: a verdict. The colour is
   the whole point -- at a hundred rows the reader is scanning the edge, not
   reading the reasons. */

.cands {
  list-style: none;
  margin: 0 0 0.4rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0.6rem;
}

.cand__link {
  display: grid;
  grid-template-columns: 3.4rem minmax(0, 1fr) auto;
  gap: 0.7rem;
  align-items: center;
  padding: 0.6rem;
  height: 100%;
  background: var(--panel);
  border-radius: 12px;
  text-decoration: none;
  color: inherit;
}
a.cand__link:hover { border-color: var(--sea); }
a.cand__link:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }

.cand--no .cand__link { opacity: 0.72; }

.cand__art {
  width: 3.4rem; height: 3.4rem;
  border-radius: 9px;
  overflow: hidden;
  background: var(--glass);
  display: grid; place-items: center;
}
.cand__art img { width: 100%; height: 100%; object-fit: cover; display: block; transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.cand__art.is-glyph img {
  width: 62%; height: auto; object-fit: contain;
  filter: var(--silhouette); opacity: 0.4;
}

.cand__body { display: grid; gap: 0.1rem; min-width: 0; }
.cand__name { font: 600 0.9375rem/1.3 var(--sans); letter-spacing: -0.01em; }
.cand__sci { font-style: italic; font-size: 0.8125rem; color: var(--ink-faint); }
.cand__facts { font: 400 0.6875rem/1.4 var(--mono); color: var(--ink-faint); }
.cand__name, .cand__sci, .cand__facts {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The compromise, when there is one. Wraps rather than truncating: a reason cut
   off mid-sentence is worse than no reason, because the reader cannot tell which. */
.cand__why {
  margin-top: 0.15rem;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--ink-soft);
}
.cand--caution .cand__why { color: var(--amber-deep); }

.cand__add {
  align-self: center;
  font: 400 1.3rem/1 var(--display);
  color: var(--sea-deep);
  padding: 0 0.3rem;
}
.cand--no .cand__add, li.cand > span.cand__link .cand__add { display: none; }

/* ---------------------------------------------------------- what is chosen */

.chosen { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.chosen__item {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr) auto;
  gap: 0.55rem;
  align-items: center;
  padding: 0.45rem 0.55rem;
  background: var(--panel-lift);
  border: 1px solid var(--line);
  border-radius: 9px;
}
.chosen__count { font-family: var(--mono); color: var(--sea-deep); text-align: right; }
.chosen__name { font-weight: 500; }
.chosen__drop {
  color: var(--ink-faint);
  text-decoration: none;
  font-size: 1.15rem;
  line-height: 1;
  padding: 0.15rem 0.4rem;
  border-radius: 7px;
}
.chosen__drop:hover { color: var(--coral-deep); background: var(--panel); }

.chosen__room {
  margin: 0.8rem 0 0;
  font: 400 0.8125rem/1.5 var(--body);
  color: var(--ink-soft);
}

/* ------------------------------------------------ the live tank-fit warning */

/* Coral, and the only element on the calculator that raises its voice. It sits
   directly above the submit button because that is the moment it is arguing
   with: you are about to score a stocking the tank cannot hold. */
.fit {
  border: 1px solid var(--coral);
  border: 1px solid color-mix(in srgb, var(--coral) 35%, transparent);
  background: var(--coral-wash);
  border-radius: 10px;
  padding: 0.9rem 1.05rem;
}

.fit__title {
  margin: 0 0 0.5rem;
  font: 700 0.9375rem/1.35 var(--sans);
  letter-spacing: -0.01em;
  color: var(--coral-deep);
}

.fit__list { margin: 0; padding-left: 1.05rem; display: grid; gap: 0.35rem; }
.fit__list li { font-size: 0.875rem; line-height: 1.55; color: var(--ink); }
.fit__list li::marker { color: var(--coral); }

/* The escape hatch. The warning is worth making loud; refusing to let someone
   past it would be a different and worse product. */
.fit__note {
  margin: 0.7rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* The soft stocking warning. Amber rather than coral on purpose: coral is the
   colour of "this cannot work", and a heavy bioload is a compromise the keeper can
   choose. It is the only box on the page that is neither a refusal nor a pass. */
.fit--warn {
  border-color: var(--amber);
  border-color: color-mix(in srgb, var(--amber) 40%, transparent);
  background: var(--amber-wash);
}
.fit--warn .fit__title { color: var(--amber-deep); }
.fit--warn .fit__list li::marker { color: var(--amber); }

/* The same box, saying the tank is fine. Green, quieter, and a statement rather
   than a warning -- it exists so a satisfied requirement is visible too, instead
   of the page only ever speaking up to object. */
.fit--ok {
  margin: 1rem 0 0;
  border-color: var(--good);
  border-color: color-mix(in srgb, var(--good) 40%, transparent);
  background: var(--good-wash);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink);
}
.fit--ok strong { color: var(--good-deep); }

/* The one thing you do with a filled-in form, plus whatever a path adds. */
.actions { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.actions .button--quiet { padding: 0.8rem 1.4rem; font-size: 0.9375rem; }

.eyebrow__aside { color: var(--ink-faint); letter-spacing: 0.08em; }

/* --------------------------------------------------------------- the two paths */

/* The fork. Two cards, equal weight, because neither is the recommended answer --
   they are different questions, and the page cannot know which one you have. Side
   by side above 46rem, stacked below, and tall enough that the second card is not
   an afterthought under the first. */
.paths {
  display: grid;
  gap: 0.9rem;
  margin: 1.4rem 0 0;
}
@media (min-width: 46rem) {
  .paths { grid-template-columns: 1fr 1fr; }
}

.path {
  display: grid;
  align-content: start;
  gap: 0.5rem;
  padding: clamp(1.2rem, 3vw, 1.7rem);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background-image: linear-gradient(180deg, var(--panel-lift), transparent 6rem);
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep);
  text-decoration: none;
  color: inherit;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.path:hover, .path:focus-visible {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: 0 2px 4px var(--shadow), 0 20px 36px -26px var(--shadow-deep);
}

.path__icon {
  width: 1.9rem; height: 1.9rem;
  fill: none; stroke: var(--sea-deep); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

.path__title {
  margin: 0.2rem 0 0;
  font: 700 clamp(1.15rem, 2.4vw, 1.4rem)/1.15 var(--display);
  letter-spacing: -0.01em;
}
.path__body {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.path__more {
  margin: 0.3rem 0 0;
  font: 600 0.875rem/1.4 var(--sans);
  color: var(--sea-deep);
}

/* Which path you are on, and the way back out of it. Small, because it is a note
   about the page rather than part of the work -- but never absent, so a path can
   never become a room with no door. */
.switcher {
  margin: 1.2rem 0 0.9rem;
  font: 400 0.8125rem/1.5 var(--body);
  color: var(--ink-faint);
}
.switcher a { color: var(--sea-deep); margin-left: 0.35rem; }

/* The tick on a suggestion card, in the slot the "+" used on the planner's cards.
   Sized up from the browser default: it is the only control on the card, the whole
   card is its label, and a 13px box next to a 54px thumbnail reads as decoration. */
.cand__tick {
  width: 1.15rem; height: 1.15rem;
  margin: 0 0.25rem;
  accent-color: var(--sea);
  cursor: pointer;
}
li.cand:has(.cand__tick:checked) .cand__link {
  border-color: var(--sea);
  background: var(--sea-tint);
}
label.cand__link { cursor: pointer; }

/* -------------------------------------------------------------- the one page */

/* THE TANK PANEL, in the verdict column under the score. A <details>: open on
   arrival while nothing is described, folded to its summary -- the "Edit tank"
   button -- once a setup exists. Efe asked for the popover to go: "it should
   be in the empty slot ... and once the tank is setup ... a button of 'Edit
   Tank'". */
.tanksetup { padding: 0; }
.tanksetup__summary {
  list-style: none;
  cursor: pointer;
  padding: 0.9rem 1.1rem;
}
.tanksetup__summary::-webkit-details-marker { display: none; }
.tanksetup__summary:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: -2px; border-radius: 12px; }
.tanksetup__when-open { display: none; align-items: baseline; justify-content: space-between; }
/* FOLDED, THE WHOLE PANEL IS THE BUTTON. The first version kept the panel's
   white card and put a button inside it -- "a box within a box" (Efe). So
   the folded panel sheds its card entirely: no background, no border, no
   shadow, and the summary alone is drawn as one wide button. */
.tanksetup:not([open]) {
  background: transparent;
  background-image: none;
  border: 0;
  box-shadow: none;
}
.tanksetup:not([open]) .tanksetup__summary { padding: 0; }
.tanksetup__when-closed {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  font-size: 1.0625rem;
  font-weight: 600;
  padding: 1rem 1.4rem;
  border-radius: var(--radius);
  color: var(--ink);
  background: var(--panel);
  border: 1.5px solid var(--ink-soft);
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep);
}
.tanksetup:not([open]) .tanksetup__summary:hover .tanksetup__when-closed {
  background: var(--sea-wash);
  border-color: var(--sea-deep);
  color: var(--sea-deep);
}
.tanksetup[open] .tanksetup__when-open { display: flex; }
.tanksetup[open] .tanksetup__when-closed { display: none; }
.tanksetup__collapse { font: 500 0.8125rem/1 var(--mono); color: var(--sea-deep); }
/* The saved size on the folded button: "Edit tank  20 US gal". */
.tanksetup__size { margin-left: 0.6rem; font-weight: 500; color: var(--ink-dim); }
.tanksetup__size::before { content: "·"; margin-right: 0.6rem; color: var(--line-strong); }
.tanksetup__body {
  display: grid;
  gap: 0.9rem;
  padding: 0 1.1rem 1.1rem;
}
.tanksetup__body .field__hint { margin: 0; }
.tanksetup__custom { display: grid; gap: 0.9rem; }
.tanksetup__actions { margin: 0.2rem 0 0; }

/* Live updating has failed and the figures below are the previous ones. Amber
   rather than red: nothing is wrong, it is only out of date. */
.live-status {
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  border-radius: 10px;
  color: var(--amber-deep);
  font-size: 0.875rem;
  line-height: 1.5;
}

/* The global failure notice may be offscreen while the keeper edits. Keep an
   explicit label attached to each old result, including the suggestion list. */
[data-live-stale]::before,
.panel.score[data-live-stale]::before {
  content: attr(data-live-stale);
  display: block;
  position: static;
  inset: auto;
  height: auto;
  box-shadow: none;
  margin-bottom: 0.75rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid color-mix(in srgb, var(--amber) 45%, transparent);
  border-radius: 8px;
  background: var(--amber-wash);
  color: var(--amber-deep);
  font-size: 0.875rem;
  line-height: 1.5;
}

/* A refresh is in flight. Deliberately faint: a whole column flickering on
   every keystroke is worse than a figure arriving a moment late. */
[aria-busy="true"] #score,
[aria-busy="true"] #findings { opacity: 0.72; transition: opacity 0.12s ease; }

/* NO JAVASCRIPT: the controls that only a script can wire are hidden. The
   tank panel needs none of this -- a <details> opens and closes on its own. */
.no-js .stock__drop,
.no-js #add-species { display: none; }

/* An empty tank rather than an empty space: the page is a calculator you land
   in, not a form you fill before it exists. Dashed, so it reads as a placeholder
   and not as a tank holding nothing. */
/* (the empty state is styled with the tank, above: .tank__scene.is-empty) */

/* The suggestions panel. No accent of its own -- the cards inside it carry the
   verdict colours, and a coloured frame around amber and grey cards would fight
   them. */
.suggest .cands { margin-top: 0; }

/* A COUNT NOBODY SOURCED. The card shows no "12x" and says why, so the number
   cannot read as advice; the dotted edge marks it without spending a colour,
   because the verdict spine already owns the left border. */
.cand.is-trial .cand__link { border-left-style: dotted; }
.cand.is-trial .cand__name { color: var(--ink-soft); }

/* ------------------------------------------------------- the stocking level
   The figure that was nowhere on the page for a tank under capacity. Sits
   under the overall score and is deliberately a different shape from it: a
   number with a word, not a number with "out of 100". */

.level {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0.6rem 0 0;
  padding: 0.6rem 0.8rem;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel-lift);
}
.level__figure {
  font: 700 1.5rem/1 var(--display);
  letter-spacing: -0.01em;
  color: var(--good-deep);
}
.level__text { display: grid; gap: 0.1rem; }
.level__label { font: 600 0.875rem/1.2 var(--sans); }
.level__hint { font-size: 0.75rem; color: var(--ink-faint); }
.level.is-mid { border-color: var(--amber); background: var(--amber-wash); }
.level.is-mid .level__figure { color: var(--amber-deep); }
.level.is-bad { border-color: var(--coral); background: var(--coral-wash); }
.level.is-bad .level__figure { color: var(--coral-deep); }

/* ------------------------------------------------------------------ chips
   The water-window verdict's shape, reused. One chip vocabulary rather than
   three. */

.chip {
  display: inline-block;
  font: 600 0.8125rem/1.2 var(--sans);
  color: var(--ink-soft);
}
.chip.is-good, .chip--beginner { color: var(--good-deep); }
.chip.is-mid, .chip--intermediate { color: var(--amber-deep); }
.chip.is-bad, .chip--advanced { color: var(--coral-deep); }

/* ------------------------------------------------------- preset stocking plans */

.presets__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.8rem;
}
/* Five across where five fit. `auto-fill` gave four and an orphan on a desktop,
   which is the one layout a row of five cards must not have. */
@media (min-width: 62rem) {
  .presets__grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
.preset {
  display: grid;
  grid-template-rows: auto auto auto auto 1fr auto;
  gap: 0.45rem;
  padding: 1rem 1rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel);
}
.preset__name { margin: 0; font: 700 1.0625rem/1.25 var(--display); letter-spacing: -0.015em; }
.preset__size { margin: 0; font: 400 0.6875rem/1.4 var(--mono); color: var(--ink-faint); }
.preset__chips { margin: 0.1rem 0 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.preset__stock {
  list-style: disc;   /* a nested <ul> defaults to hollow circles */
  margin: 0.3rem 0 0;
  padding: 0 0 0 1.1rem;
  font-size: 0.875rem;
  line-height: 1.55;
}
.preset__blurb {
  margin: 0.2rem 0 0.4rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  font-style: italic;
  color: var(--ink-soft);
}
.preset__load { justify-self: start; }

/* ---------------------------------------------------- paging the shortlist */

.suggest__page {
  margin: 0.6rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.9rem;
  font-size: 0.8125rem;
}
.suggest__more, .suggest__first { font-weight: 600; }
.suggest__count { color: var(--ink-faint); }

/* --------------------------------------------------------------- photo cards
   The catalogue card the reference site draws, which Efe pointed at: a tall
   photograph with the names set on a dark gradient at its foot, three pills,
   three figures. Used by the plant catalogue and, at his request, the fish
   database too, so the two read as one. Same .card element, so database.js
   filters both.
   The pill tints are the account-card family (navy, moss, bronze), NOT the
   green/amber/coral that mean comfortable/tight/clash everywhere else -- a
   "CO2: Required" pill in warning-amber would read as a warning. */
.cards--photo { grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 1rem; }
.card--photo .card__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  padding: 0;
  overflow: hidden;
  border-left: 1px solid var(--line);
  border-radius: 14px;
}
.card--photo .card__link:hover { border-color: var(--moss); }
.card__photo {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  background: var(--glass);
  overflow: hidden;
}
.card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.card__photo.is-glyph { display: grid; place-items: center; }
.card__photo.is-glyph img { width: 40%; height: auto; object-fit: contain; filter: var(--silhouette); opacity: 0.35; }
.card__caption {
  position: absolute;
  inset: auto 0 0 0;
  display: grid;
  gap: 0.05rem;
  padding: 2.2rem 0.85rem 0.7rem;
  background: linear-gradient(180deg, rgba(15, 43, 32, 0) 0%, rgba(15, 43, 32, 0.78) 100%);
}
.card__caption .card__name { color: #fff; font-size: 1.05rem; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35); }
.card__caption .card__sci { color: rgba(255, 255, 255, 0.86); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35); }
.card__photo.is-glyph .card__caption { background: linear-gradient(180deg, rgba(15, 43, 32, 0) 0%, rgba(15, 43, 32, 0.55) 100%); }

.card__badges { display: flex; flex-wrap: wrap; gap: 0.35rem; padding: 0.75rem 0.85rem 0; }
.badge {
  font: 500 0.6875rem/1 var(--mono);
  letter-spacing: 0.02em;
  padding: 0.34rem 0.55rem;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge--difficulty { color: var(--moss); background: color-mix(in srgb, var(--moss) 12%, white); border-color: color-mix(in srgb, var(--moss) 35%, white); }
.badge--difficulty.is-difficult { color: var(--bronze); background: color-mix(in srgb, var(--bronze) 12%, white); border-color: color-mix(in srgb, var(--bronze) 35%, white); }
.badge--co2 { color: var(--navy); background: color-mix(in srgb, var(--navy) 10%, white); border-color: color-mix(in srgb, var(--navy) 30%, white); }
.badge--co2.is-required { color: var(--plum); background: color-mix(in srgb, var(--plum) 10%, white); border-color: color-mix(in srgb, var(--plum) 30%, white); }
.badge--light { color: var(--ink-faint); background: var(--ground-tint); border-color: var(--line); }

.card__stats {
  /* Each stat takes the width it needs, spread across the row: three equal
     columns clipped "Very fast" and "Very slow" to "Very fas" (Efe). */
  display: grid;
  grid-template-columns: auto auto auto;
  justify-content: space-between;
  gap: 0.4rem;
  padding: 0.6rem 0.85rem 0.8rem;
  font: 400 0.75rem/1.3 var(--mono);
  color: var(--ink-faint);
}
.stat { display: inline-flex; align-items: center; gap: 0.35rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat svg { width: 1em; height: 1em; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.badge--difficulty.is-intermediate { color: var(--bronze); background: color-mix(in srgb, var(--bronze) 12%, white); border-color: color-mix(in srgb, var(--bronze) 35%, white); }
.badge--difficulty.is-advanced { color: var(--brick); background: color-mix(in srgb, var(--brick) 10%, white); border-color: color-mix(in srgb, var(--brick) 30%, white); }
.badge--temperament { color: var(--navy); background: color-mix(in srgb, var(--navy) 10%, white); border-color: color-mix(in srgb, var(--navy) 30%, white); }
.badge--temperament.is-semi-aggressive { color: var(--bronze); background: color-mix(in srgb, var(--bronze) 12%, white); border-color: color-mix(in srgb, var(--bronze) 35%, white); }
.badge--temperament.is-aggressive, .badge--temperament.is-predatory { color: var(--brick); background: color-mix(in srgb, var(--brick) 10%, white); border-color: color-mix(in srgb, var(--brick) 30%, white); }
.tanksetup__prompt { margin: 0; color: var(--ink-dim); line-height: 1.6; }

/* ------------------------------------------------------------ the explainer
   Under the calculator: what it does and what its answers mean, in plain
   words. Wide two-column prose blocks, fading in as they scroll into view. */
.explain { display: grid; gap: clamp(2rem, 5vw, 3.5rem); margin: clamp(2.5rem, 6vw, 4.5rem) 0 1rem; }
.explain__block { max-width: 62rem; }
.explain__title {
  margin: 0.25rem 0 0.9rem;
  font: 700 clamp(1.5rem, 3vw, 2rem)/1.15 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.explain__cols { display: grid; gap: 1.2rem 2.2rem; }
@media (min-width: 52rem) { .explain__cols { grid-template-columns: 1fr 1fr; } }
.explain__cols p { margin: 0; color: var(--ink-dim); line-height: 1.7; }
.explain__cols strong { color: var(--ink); font-weight: 600; }

/* reveal.js adds `is-hidden` before first paint and removes it on entry. With
   scripting off the class is never added and every block is simply there. */
.reveal { transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.is-hidden { opacity: 0; transform: translateY(1.2rem); }
@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none; }
  .reveal.is-hidden { opacity: 1; transform: none; }
}

/* The preset pager. */
.presets__page { display: flex; align-items: center; gap: 0.9rem; margin: 1.1rem 0 0; }
.presets__count { font: 400 0.75rem/1 var(--mono); color: var(--ink-faint); }

/* ---------------------------------------------------------------- the FAQ
   One question per row: a thin, long box with the question on the left and
   the answer beside it. Efe: "thin, long rectangular boxes, so each has its
   own row." On a phone the question sits above its answer. */
.prose--wide { max-width: 62rem; }
.faq {
  display: grid;
  gap: 0.7rem;
  max-width: 62rem;
  margin: 1.4rem auto 0;
}
.faq__card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.35rem 2rem;
  align-items: start;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.9rem 1.2rem;
  scroll-margin-top: 5rem;
}
@media (min-width: 46rem) {
  .faq__card { grid-template-columns: 17rem minmax(0, 1fr); }
}
.faq__q {
  margin: 0;
  font: 700 1rem/1.35 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.faq__a { margin: 0; color: var(--ink-dim); line-height: 1.6; font: 400 1.0625rem/1.6 var(--serif); }
.faq__a strong { color: var(--ink); font-weight: 600; }

/* ----------------------------------------------------------- the front door
   The landing page. One thing is allowed to be loud: the drawn tank with a
   real preset swimming in it. Everything else is a left-aligned column of
   plain sentences, in the site's own palette. */
.door {
  display: grid;
  gap: clamp(1.6rem, 4vw, 3rem);
  align-items: center;
  margin: clamp(1rem, 3vw, 2.5rem) 0 clamp(3rem, 7vw, 5rem);
}
@media (min-width: 58rem) {
  .door { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}
.door__title {
  margin: 0;
  font: 700 clamp(2.4rem, 6vw, 4.25rem)/1.02 var(--display);
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--ink);
  max-width: 12ch;
}
.door__lede {
  margin: 1.2rem 0 0;
  max-width: 44ch;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink-dim);
}
.door__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.4rem; margin: 1.8rem 0 0; }
.door__cta { font-size: 1.0625rem; padding: 0.95rem 1.6rem; }

.door__tank { margin: 0; display: grid; gap: 1rem; }
/* Bigger than the calculator's: this one is the picture. The caps below
   override the tank rules' 26rem / 15.5rem. */
.door__tank .tank__scene { width: 100%; max-width: 34rem; max-height: 21rem; }
.door__tank .tank__label { display: none; }

/* The caption is a legend. Three lines in the display face -- what it is,
   how big, who lives in it -- and each shoal in the colour its fish are drawn
   in, with a dot of the same colour so the pairing reads before the words do.
   Efe: "make it simple and add colors ... and then make it a nicer font". */
.door__caption { display: grid; gap: 0.3rem; justify-items: start; padding: 0 8% 0 3.5%; }
.door__kicker {
  margin: 0;
  font: 600 0.875rem/1.3 var(--sans);
  color: var(--sea-deep);
}
.door__headline {
  margin: 0;
  font: 700 clamp(1.4rem, 2.4vw, 1.85rem)/1.1 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.door__shoals { list-style: none; margin: 0.25rem 0 0.4rem; padding: 0; display: grid; gap: 0.25rem; }
.door__shoal {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font: 600 1.1875rem/1.3 var(--display);
  letter-spacing: -0.01em;
  color: var(--shoal, var(--ink));
}
.door__shoal::before {
  content: "";
  flex: none;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 999px;
  background: var(--shoal, var(--ink));
}
.door__load { justify-self: start; margin-top: 0.2rem; }

.checks { max-width: 60rem; margin: 0 0 clamp(3rem, 7vw, 5rem); }
.checks__title {
  margin: 0 0 1.2rem;
  font: 700 clamp(1.5rem, 3vw, 2rem)/1.15 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.checks__list { margin: 0; display: grid; gap: 1.2rem 2.4rem; }
@media (min-width: 52rem) { .checks__list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.checks__item { border-top: 2px solid var(--sea); padding-top: 0.9rem; }
.checks__item dt { font: 600 1.125rem/1.3 var(--display); color: var(--ink); margin: 0 0 0.4rem; }
.checks__item dd { margin: 0; color: var(--ink-dim); line-height: 1.6; }

.species-strip { margin: 0 0 clamp(3rem, 7vw, 5rem); }
.species-strip__head { max-width: 60rem; }
.species-strip__lede { margin: -0.6rem 0 1.2rem; color: var(--ink-dim); max-width: 56ch; line-height: 1.6; }
.species-strip__row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: 0.9rem;
}
.species-strip__item a { display: grid; gap: 0.5rem; text-decoration: none; color: inherit; }
.species-strip__art {
  /* 4:3 rather than square: a square crop lost the neon tetra's tail, and
     the photographs were cropped for this frame on the database cards. */
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
  background: var(--glass);
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
}
.species-strip__art img { width: 100%; height: 100%; object-fit: cover; display: block; transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.species-strip__art.is-glyph img { width: 50%; height: auto; object-fit: contain; filter: var(--silhouette); opacity: 0.4; }
.species-strip__item a:hover .species-strip__art { border-color: var(--sea); }
.species-strip__name { font-weight: 600; color: var(--ink); font-size: 0.9375rem; }
.species-strip__more { display: flex; gap: 1.4rem; margin: 1.2rem 0 0; }
.species-strip__more a { color: var(--sea-deep); font-weight: 500; }

.why { max-width: 60rem; margin: 0 0 2rem; }
.why__body { margin: 0; max-width: 62ch; font-size: 1.0625rem; line-height: 1.7; color: var(--ink-dim); }
.why__body a { color: var(--sea-deep); font-weight: 500; }

/* ONE MEASURE, JUSTIFIED. Every section below the hero shares the same
   width and its prose runs edge to edge of it, so each block starts and ends
   on the same lines -- Efe: "every row starts and ends in the same place". */
.checks, .species-strip, .species-strip__head, .why { max-width: 60rem; }
.species-strip__lede, .why__body { max-width: none; }
.door__lede, .checks__item dd, .species-strip__lede, .why__body, .steps__body {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}
/* On a phone the measure is forty characters and justifying it opens rivers
   between the words, so the same prose is set ragged-right there. */
@media (max-width: 40rem) {
  .door__lede, .checks__item dd, .species-strip__lede, .why__body, .steps__body { text-align: left; }
}

/* THE PHONE BUG. A grid item's automatic minimum width is its min-content
   size, and the reel's min-content size is thirty tiles side by side (about
   1900px), because a flex item's percentage basis is unresolvable while the
   row's own width is being worked out. The section passed that minimum up to
   the page's single column, which grew to 960px inside a 390px screen and
   took every other section with it -- Efe: "the carousel and the landing
   page do not fit the iPhone at all". The reel is a scroll container; its
   contents must never size its ancestors. */
.species-strip { min-width: 0; }
.reel-wrap { contain: inline-size; }


/* The reel. Five in view on a wide screen, two and a half on a phone; the
   scrollbar is hidden because the row moves by itself and by touch. The
   outer box is the scroll container; the track inside it is the flex row,
   and carousel.js glides the track with a transform between folds of that
   offset into the scroll position, because a scroll position moves a whole
   pixel at a time and at 20px a second that is a visible hop every third
   frame -- Efe: "micro vibrations visible although it's moving". */
.reel {
  --reel-gap: 0.9rem;
  --reel-visible: 5;
  overflow-x: auto;
  overscroll-behavior-x: contain;   /* a swipe past the end stays in the reel; it never becomes the browser's back gesture */
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.reel::-webkit-scrollbar { display: none; }
.reel__track {
  list-style: none;
  margin: 0;
  padding: 0.2rem 0;
  display: flex;
  gap: var(--reel-gap);
  will-change: transform;
}
.reel__item {
  flex: 0 0 calc((100% - (var(--reel-visible) - 1) * var(--reel-gap)) / var(--reel-visible));
  min-width: 0;
}
@media (max-width: 52rem) { .reel { --reel-visible: 2.5; } }
.reel__item a { display: grid; gap: 0.5rem; text-decoration: none; color: inherit; }
.reel__item a:hover .species-strip__art { border-color: var(--sea); }
.reel__item a:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 3px; border-radius: 14px; }

/* The reel's arrows, inserted by carousel.js. Round, on the row's edges,
   quiet until hovered; a click steps one fish and holds the reel still. */
.reel-wrap { position: relative; }
.reel__arrow {
  position: absolute;
  top: 34%;
  z-index: 2;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 1px 2px var(--shadow), 0 10px 24px -18px var(--shadow-deep);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.reel__arrow svg { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.reel__arrow--back { left: -1.2rem; }
.reel__arrow--forward { right: -1.2rem; }
.reel__arrow:hover { background: var(--sea-wash); border-color: var(--sea-deep); color: var(--sea-deep); }
.reel__arrow:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; }
@media (max-width: 52rem) {
  .reel__arrow--back { left: 0.2rem; }
  .reel__arrow--forward { right: 0.2rem; }
}

/* ------------------------------------------------ the landing page, longer
   How it works: four numbered steps, because using the calculator is a
   sequence and the numbers are earned. The five questions: rows in the
   FAQ's own shape, each linking to its full answer. The closing band: a
   tinted panel, the one place the page raises its voice. */
.steps { max-width: 60rem; margin: 0 0 clamp(3rem, 7vw, 5rem); }
.steps__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.4rem 2.4rem;
  counter-reset: step;
}
@media (min-width: 52rem) { .steps__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.steps__item {
  counter-increment: step;
  border-top: 2px solid var(--sea);
  padding-top: 0.9rem;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.25rem 1rem;
  align-items: start;
}
.steps__item::before {
  content: counter(step);
  grid-row: 1 / span 2;
  font: 700 2.4rem/1 var(--display);
  letter-spacing: -0.01em;
  color: var(--sea-deep);
  min-width: 1.6rem;
}
.steps__title { margin: 0; font: 600 1.125rem/1.3 var(--display); color: var(--ink); }
.steps__body { margin: 0; color: var(--ink-dim); line-height: 1.6; }

.asks { max-width: 60rem; margin: 0 0 clamp(3rem, 7vw, 5rem); }
.asks__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.asks__row {
  display: grid;
  gap: 0.35rem 2rem;
  align-items: start;
  border-radius: 12px;
  padding: 0.9rem 1.2rem;
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep);
}
@media (min-width: 46rem) { .asks__row { grid-template-columns: 15rem minmax(0, 1fr); } }
.asks__q { margin: 0; font: 700 1rem/1.35 var(--display); letter-spacing: -0.01em; color: var(--ink); }
.asks__a { margin: 0; color: var(--ink-dim); line-height: 1.55; font-size: 0.9375rem; }
.asks__a a { color: var(--sea-deep); font-weight: 500; white-space: nowrap; }
.asks__more { margin: 1rem 0 0; }
.asks__more a { color: var(--sea-deep); font-weight: 500; }

.band {
  padding: clamp(1.4rem, 3vw, 2.2rem);
  border-radius: var(--radius);
  background: var(--sea-tint);
  border: 1px solid var(--line);
}
.band .checks__title { margin-bottom: 0.6rem; }

/* ------------------------------------------------ the warm palette
   Iron and ember as accents on the landing page; the mint ground stays.
   Efe chose this over a warm hero wash, from screenshots of both, on
   2026-09-12. Iron carries the step numerals and rules, ember the question
   rows and the check rules, and the closing band gets the warm wash. */
.steps__item { border-top-color: var(--iron); }
.steps__item::before { color: var(--iron-deep); }
.asks__a a, .asks__more a { color: var(--iron-deep); }
.door__kicker { color: var(--iron-deep); }
.band {
  background: linear-gradient(135deg, var(--ember-wash), var(--iron-wash));
  border-color: rgba(184, 69, 43, 0.25);
}
.band .why__body a { color: var(--iron-deep); }
.checks__item { border-top-color: var(--ember); }
.species-strip__more a { color: var(--iron-deep); }

/* ------------------------------------------------ tanks saved on this device
   A row to name the tank, and a list under the presets. Plain rows, the
   name in the display face, the summary beneath; Open is a quiet button and
   Remove a plain word that asks once before it acts. */
.saver {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem 0.8rem;
  margin-top: 0.8rem;
}
.saver__field { display: grid; gap: 0.3rem; flex: 1 1 14rem; min-width: 0; }
.saver__field input {
  font: inherit;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--panel);
  color: var(--ink);
}
.saver__note { margin: 0.5rem 0 0; }
.saved__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.saved__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 0.75rem 0.9rem;
  border-radius: 12px;
}
.saved__body { display: grid; gap: 0.1rem; min-width: 0; flex: 1 1 16rem; }
/* A name is whatever the keeper typed, up to sixty characters with no space;
   it wraps rather than widening the page (Codex, round 014). */
.saved__name { font: 700 1rem/1.3 var(--display); color: var(--ink); overflow-wrap: anywhere; }
.saved__summary { overflow-wrap: anywhere; }
.saved__summary { color: var(--ink-dim); font-size: 0.9375rem; }
.saved__when { color: var(--ink-faint); font-size: 0.8125rem; }
.saved__actions { display: flex; align-items: center; gap: 0.8rem; }
.saved__remove {
  appearance: none;
  border: 0;
  background: none;
  font: 500 0.875rem/1 var(--body);
  color: var(--ink-faint);
  cursor: pointer;
  padding: 0.4rem 0.2rem;
}
.saved__remove:hover, .saved__remove[data-armed="yes"] { color: var(--coral-deep); }

/* ------------------------------------------------------------- about
   One letter, not a page of sections: a large opening in the display face
   under an iron rule, the body at a reading measure, the button, and the
   note about the numbers as a quiet tinted aside. Efe: "less like 'where
   the numbers come from' ... you can just say this without posing the
   question." */
.about { max-width: 46rem; margin: 0 auto; display: grid; gap: 1.6rem; }
.about__title {
  margin: 0;
  font: 700 clamp(2rem, 4vw, 2.8rem)/1.05 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.about__opening {
  margin: 0;
  padding-top: 1.1rem;
  border-top: 3px solid var(--iron);
  font: 500 clamp(1.2rem, 2.1vw, 1.45rem)/1.5 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.about__body { display: grid; gap: 1.1rem; }
.about__body p { margin: 0; font-size: 1.0625rem; line-height: 1.72; color: var(--ink-dim); }
.about__cta { margin: 0.4rem 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 0.8rem 1rem; color: var(--ink-dim); }

/* ------------------------------------------------ one page title, everywhere
   The calculator's title style -- the ink-to-green gradient Efe likes -- on
   the About, FAQ, Fish database and Plants headings too. Sized to the text
   (fit-content) so the gradient crosses the words, not an empty block. */
.db__title, .prose__title, .about__title {
  margin: 0;
  font: 700 clamp(1.75rem, 4.4vw, 2.75rem)/1.05 var(--display);
  letter-spacing: -0.01em;
  text-wrap: balance;
  width: fit-content;
  max-width: 100%;
  /* Each page's title in its own colour, solid (Efe: green for plants, red for
     fish, blue for FAQ and About); it used to fade into it. */
  color: var(--title-end, var(--ink));
}
.prose__title { margin-bottom: 1rem; }
/* Each page's title resolves into its own colour -- Efe: "green is fitting
   for plants, but maybe more red for fish? and maybe blue for FAQ and About".
   Iron for the fish (the warm family), navy and petrol for the two blues. */
.title--plants { --title-end: var(--sea-deep); }
.title--fish   { --title-end: var(--iron-deep); }
.title--about  { --title-end: var(--navy); }
.title--faq    { --title-end: var(--petrol); }

/* ------------------------------------------ the pair: photo and passport
   Two cards the same height, side by side like a pair of playing cards: the
   photograph fills one edge to edge with its credit on a strip at the foot,
   the passport is the other -- kicker, name, origin, the world map, the
   stamped essentials, the buttons. On a phone they stack, the photo first. */
.pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1rem, 2.4vw, 1.6rem);
  align-items: start;   /* the photo keeps its own 4:3; the passport its own height */
}
@media (min-width: 58rem) {
  .pair { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
}
.card-photo {
  margin: 0;
  position: relative;
  display: grid;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--glass);
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep);
}
.card-photo__art { display: grid; place-items: center; min-height: 0; }
.card-photo__art img { display: block; width: 100%; height: 100%; object-fit: cover; }
.card-photo__art.is-glyph img { width: 40%; height: auto; object-fit: contain; filter: var(--silhouette); opacity: 0.35; }
.card-photo__credit {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  margin: 0;
  padding: 0.45rem 0.9rem;
  background: rgba(255, 255, 255, 0.86);
  color: var(--ink-dim);
  font-size: 0.75rem;
}
.card-photo__credit a { color: var(--ink-dim); }

.passport {
  display: grid;
  gap: 0.5rem;
  justify-items: start;
  align-content: start;
  padding: clamp(1rem, 2.4vw, 1.5rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep);
}
.passport__kicker {
  margin: 0;
  font: italic 500 1.2rem/1.2 var(--passport);
  color: var(--iron-deep);
}
/* The flags on the "From" line: the countries the origin names. */
.passport__flag {
  display: inline-block; width: 1.5rem; height: 1.125rem;
  border-radius: 3px; box-shadow: 0 0 0 1px rgba(15, 43, 32, 0.14);
}
.passport__label .fact__flag { margin-left: 0.35rem; }
.passport__stamp dt .fact__flag { margin-left: 0.35rem; }
.passport__name {
  margin: 0;
  font: 700 clamp(1.7rem, 3.4vw, 2.3rem)/1.02 var(--display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.passport__sci { margin: -0.2rem 0 0; font-style: italic; color: var(--navy); }
.passport__origin { display: grid; gap: 0.05rem; margin-top: 0.3rem; }
.passport__label { font-size: 0.8125rem; color: var(--ink-faint); }
.passport__where { font: 600 1.0625rem/1.35 var(--display); color: var(--ink); }
.passport__map {
  width: 100%;
  height: auto;
  margin-top: 0.2rem;
  border-radius: 10px;
  background: #eef7fb;
  border: 1px solid var(--line);
}
.passport__area { fill: var(--ember); opacity: 0.22; }
.passport__mark { fill: var(--iron); opacity: 0.85; stroke: #fff; stroke-width: 6; }
.passport__stamps {
  margin: 0.3rem 0 0;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.15rem 1rem;
}
.passport__stamp { display: grid; gap: 0.05rem; padding: 0.4rem 0 0.3rem; border-top: 1px dashed var(--line-strong); }
.passport__stamp dt { font-size: 0.8125rem; color: var(--ink-faint); }
.passport__stamp dd { margin: 0; font: 600 0.9375rem/1.3 var(--sans); color: var(--ink); }
.passport .profile__actions { margin-top: 0.6rem; }

/* --------------------------------- the species header: the cover
   The photo full width with the name over its foot, then the
   facts on the left and the outline map on the right at the facts' own
   height, the buttons beneath both. */
.cover {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel);
  box-shadow: 0 1px 2px var(--shadow), 0 14px 30px -26px var(--shadow-deep); overflow: hidden;
}
/* 16:9 on a wide screen (21:9 cut too much of a 4:3 photograph), the
   photo's own 4:3 on a phone so nothing is cut and the name has room under
   the fish. The zoom scales about the focus point, so the crop Efe set is
   the crop that shows. */
.cover__art { position: relative; aspect-ratio: 16 / 9; background: var(--glass); overflow: hidden; }
.cover__art img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%);
}
@media (max-width: 40rem) {
  .cover__art { aspect-ratio: 4 / 3; }
  .cover__title { padding: 2rem 1rem 0.75rem; }
}
.cover__title {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 2.4rem 1.4rem 1rem;
  background: linear-gradient(180deg, rgba(15,43,32,0), rgba(15,43,32,0.78));
  color: #fff;
}
.cover__name { margin: 0; font: 700 clamp(1.3rem, 5vw, 2.8rem)/1.02 var(--display); letter-spacing: -0.01em; color: #fff; }
.cover__sci { margin: 0.1rem 0 0; font-style: italic; color: rgba(255,255,255,0.85); }
.cover__credit { position: absolute; right: 0.9rem; top: 0.6rem; margin: 0; padding: 0.2rem 0.5rem; border-radius: 6px; background: rgba(255,255,255,0.85); }
.cover__body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.2rem; padding: 1.2rem 1.4rem 1.4rem; }
@media (min-width: 52rem) { .cover__body { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; } }
.cover__facts { display: grid; gap: 0.5rem; justify-items: start; }
.cover__facts .passport__kicker { margin-bottom: -0.1rem; }
.cover__map { margin: 0; height: 100%; background: none; border: 0; border-radius: 0; }   /* the outline alone, no box */
/* The map's column: the flags at the top right, muted (Efe: "a little more
   minimalist"), the map filling the rest. */
.cover__side { display: grid; grid-template-rows: auto auto auto; align-content: start; gap: 0.5rem; min-height: 0; }
.cover__flags { margin: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.4rem; min-height: 1.5rem; }
.cover__flags .passport__flag { margin: 0; width: 1.75rem; height: 1.3125rem; filter: saturate(0.45); opacity: 0.8; transition: filter 0.2s, opacity 0.2s; }
.cover__flags .passport__flag:hover { filter: none; opacity: 1; }
.cover__side .cover__map { align-self: start; height: auto; }   /* the countries sit right under it */
@media (max-width: 52rem) { .cover__flags { justify-content: flex-start; } }
.cover__actions { grid-column: 1 / -1; margin: 0.2rem 0 0; }

/* The care box's info button in its corner, and its panel. A <details>,
   so the same click closes it and no script is involved. */
.care { position: relative; }
.care__info { position: absolute; top: 0.85rem; right: 0.9rem; }
.care__info-button {
  list-style: none; cursor: pointer;
  width: 1.5rem; height: 1.5rem; border-radius: 999px;
  display: grid; place-items: center;
  font: 700 0.8125rem/1 var(--sans);
  color: var(--ink-faint); background: var(--panel); border: 1px solid var(--line-strong);
}
.care__info-button::-webkit-details-marker { display: none; }
.care__info-button:hover, .care__info[open] .care__info-button { color: var(--sea-deep); border-color: var(--sea-deep); }
.care__info-panel {
  position: absolute; right: 0; top: 2rem; z-index: 3;
  width: min(22rem, 80vw);
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--line-strong); border-radius: 12px;
  background: var(--panel); box-shadow: 0 10px 28px -14px var(--shadow-deep);
  font-size: 0.8125rem; line-height: 1.5; color: var(--ink-dim);
}
.care__info-panel p { margin: 0 0 0.45rem; }
.care__info-panel p:last-child { margin-bottom: 0; }

/* The plain cover (the plant page): the same header, no passport beneath,
   so the body is one column -- the tags, then the button. */
.cover__body--plain { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; padding-bottom: 1.2rem; }
.cover__body--plain .taglist { margin: 0; }
.cover__body--plain .cover__actions { margin-top: 0.2rem; }
@media (min-width: 52rem) { .cover__body--plain { grid-template-columns: minmax(0, 1fr); } }
/* No photograph: the glyph small on the wash, never zoomed or cropped. */
.cover__art.is-glyph { display: grid; place-items: center; }
.cover__art.is-glyph img { width: 28%; height: auto; object-fit: contain; filter: var(--silhouette); opacity: 0.35; transform: none; }
.cover__art.is-glyph .cover__title { color: var(--ink); background: none; }
.cover__art.is-glyph .cover__name { color: var(--ink); }
.cover__art.is-glyph .cover__sci { color: var(--ink-dim); }

/* The photo credit as a button in the picture's corner. The photo's box is
   the positioning context (.card-photo, .cover__art, .photo-host). */
.photo-host { position: relative; }
.photo-credit { position: absolute; top: 0.6rem; right: 0.6rem; z-index: 2; }
.photo-credit__button {
  list-style: none; cursor: pointer;
  width: 1.6rem; height: 1.6rem; border-radius: 999px;
  display: grid; place-items: center;
  font: 700 0.8125rem/1 var(--sans);
  color: var(--ink); background: rgba(255, 255, 255, 0.88); border: 1px solid rgba(15, 43, 32, 0.15);
  box-shadow: 0 1px 2px var(--shadow);
}
.photo-credit__button::-webkit-details-marker { display: none; }
.photo-credit__text {
  position: absolute; right: 0; top: 2rem;
  width: max-content; max-width: min(20rem, 70vw);
  padding: 0.45rem 0.7rem;
  border-radius: 8px; background: rgba(255, 255, 255, 0.96);
  border: 1px solid var(--line-strong); box-shadow: 0 10px 24px -14px var(--shadow-deep);
  color: var(--ink-dim); font-size: 0.75rem; line-height: 1.4;
}
.photo-credit__text a { color: var(--ink-dim); }

/* ------------------------------------------------ the pet fish passport
   A dialog holding a card: the photo across the top, the name, where it is
   from, the essentials in two columns beside a small map, and a foot line.
   passport.js draws the same card to a canvas for the download. */
.passdialog {
  border: 0; border-radius: 22px; padding: 1rem;
  max-width: min(30rem, 92vw); background: var(--ground);
  box-shadow: 0 30px 60px -30px rgba(15, 43, 32, 0.5);
}
.passdialog::backdrop { background: rgba(15, 43, 32, 0.45); }
.passcard {
  border-radius: 18px; overflow: hidden; background: var(--panel);
  border: 1px solid var(--line); box-shadow: 0 1px 2px var(--shadow);
}
.passcard__art { aspect-ratio: 4 / 3; background: var(--glass); }
.passcard__art img { display: block; width: 100%; height: 100%; object-fit: cover; }
.passcard__art.is-glyph img { object-fit: contain; width: 40%; margin: 0 auto; filter: var(--silhouette); opacity: 0.35; }
.passcard__body { padding: 0.9rem 1.1rem 1rem; display: grid; gap: 0.25rem; }
.passcard__name { margin: 0; font: 700 1.5rem/1.05 var(--display); letter-spacing: -0.01em; color: var(--ink); }
.passcard__sci { margin: 0; font-style: italic; color: var(--navy); font-size: 0.9375rem; }
.passcard__from { margin: 0.35rem 0 0; font: 600 0.9375rem/1.35 var(--sans); color: var(--ink); }
.passcard__from span { display: block; font: 400 0.75rem/1.2 var(--body); color: var(--ink-faint); }
.passcard__row { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 0.6rem; align-items: start; margin-top: 0.4rem; }
.passcard__stamps { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0.15rem 0.6rem; }
.passcard__stamps div { border-top: 1px dashed var(--line-strong); padding-top: 0.25rem; }
.passcard__stamps dt { font-size: 0.7rem; color: var(--ink-faint); }
.passcard__stamps dd { margin: 0; font: 600 0.8125rem/1.25 var(--sans); color: var(--ink); }
.passcard__map { width: 100%; height: auto; }   /* the page's outline, no box (Efe) */
.passcard__kicker { margin-top: 0.4rem; font-size: 1.05rem; }
.passcard__side { display: grid; gap: 0.35rem; align-content: start; }
.passcard__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 0.6rem; }
.passcard__head > div { min-width: 0; }
.passcard__flags { margin-top: 0.35rem; flex: none; }
/* The countries the map points at: small callouts, one per country. */
.countries { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.35rem 0 0; }
.countries span {
  font: 500 0.625rem/1 var(--mono); letter-spacing: 0.04em;
  padding: 0.22rem 0.5rem; border-radius: 999px;
  color: var(--ink-dim); background: var(--panel); border: 1px solid var(--line-strong);
}
.passcard__countries { justify-content: center; }
.cover__countries { justify-content: flex-end; }
@media (max-width: 52rem) { .cover__countries { justify-content: flex-start; } }
.passcard__flags { min-height: 0; gap: 0.3rem; }
.passcard__flags .passport__flag { width: 1.25rem; height: 0.9375rem; }
.passcard__art img { transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.passcard__art { overflow: hidden; }
.passcard__foot { margin: 0.5rem 0 0; font-size: 0.7rem; color: var(--ink-faint); text-align: right; }
.passdialog__actions { display: flex; gap: 0.6rem; margin: 0.9rem 0 0; }
.passdialog__note { margin: 0.5rem 0 0; }

/* ------------------------------------------------- Efe's photo editor (gated)
   One bar fixed to the foot of the page; every framed photograph on the
   page can be picked, dragged and zoomed; the crop is kept on this device
   and copied out as text to be baked in. */
[data-crop].is-editable { cursor: grab; touch-action: none; user-select: none; }
[data-crop].is-editable img { -webkit-user-drag: none; }
[data-crop].is-editable::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 2px rgba(224, 122, 43, 0.55);
}
[data-crop].is-selected::after { box-shadow: inset 0 0 0 3px var(--ember); }
.editor {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.9rem;
  padding: 0.6rem 1rem; background: rgba(255, 250, 244, 0.97);
  border-top: 2px solid var(--ember); box-shadow: 0 -10px 30px -18px rgba(15, 43, 32, 0.4);
  font-size: 0.8125rem; color: var(--ink-dim);
}
.editor__hint { margin: 0; flex-basis: 100%; }
.editor label { display: inline-flex; align-items: center; gap: 0.4rem; }
.editor input[type="range"] { width: 8rem; }
.editor__pos { font: 500 0.75rem/1 var(--mono); color: var(--ink); }
.editor .button { padding: 0.3rem 0.65rem; font-size: 0.8125rem; }
.editor__out { flex-basis: 100%; width: 100%; min-height: 4rem; font: 0.75rem/1.4 var(--mono); }
body.has-editor { padding-bottom: 7rem; }

/* ===================================================================== PREMIUM
   AI on top of Finform's checks (Efe, 2026-10-28), unlocked with a code for now.
   The advice panel wears the dive computer's deep water: on the pale page it
   is the one dark object, which is how it says "Premium" without a banner.
   The score keeps the page's only lit edge. */

/* A light sheet like every other panel (Efe, 2026-11-11), so it reads the
   page's own colours rather than the dive's dark ones. */
.aiadvice { color: var(--ink); }
.aiadvice[hidden] { display: none; }
.aiadvice .eyebrow { color: var(--sea-deep); }

.aiadvice__tag {
  margin-left: auto; padding: 0.3rem 0.65rem; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--sea-deep) 55%, transparent);
  font: 650 0.75rem/1 var(--sans); color: var(--sea-deep);
}
.aiadvice__headline {
  margin: 0 0 0.6rem;
  font: 700 clamp(1.25rem, 1rem + 1vw, 1.55rem)/1.2 var(--display);
  letter-spacing: -0.01em; text-wrap: balance;
}
.aiadvice__summary { margin: 0 0 1rem; line-height: 1.6; color: var(--ink-soft); max-width: 62ch; }
.aiadvice__subhead { margin: 1.2rem 0 0.7rem; font: 650 0.9375rem/1.2 var(--sans); color: var(--ink); }
.aiadvice__changes { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.aiadvice__change { display: grid; grid-template-columns: 1.9rem minmax(0, 1fr); gap: 0.75rem; align-items: start; }
.aiadvice__num {
  display: grid; place-items: center; width: 1.9rem; height: 1.9rem; border-radius: 50%;
  background: var(--sea-deep); color: #ffffff; font: 700 0.875rem/1 var(--sans);
}
.aiadvice__body { display: grid; gap: 0.2rem; }
.aiadvice__title { font: 700 1rem/1.3 var(--display); }
.aiadvice__why { line-height: 1.55; color: var(--ink-soft); }
.aiadvice__working {
  margin: 1.1rem 0 0; padding-top: 0.9rem; border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  line-height: 1.55; color: var(--ink-soft);
}
.aiadvice__by { margin: 0.9rem 0 0; font-size: 0.75rem; color: var(--ink-soft); opacity: 0.85; }
.aiadvice__note { margin: 0; line-height: 1.55; color: var(--ink-soft); }
.aiadvice__unlock { margin-top: 0.4rem; background: var(--sea-deep); color: #ffffff; box-shadow: none; }
.aiadvice__wait { display: flex; align-items: center; gap: 0.7rem; color: var(--ink-soft); }
.aiadvice__pulse {
  width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--sea-deep);
  animation: ai-pulse 1.2s ease-in-out infinite;
}
@keyframes ai-pulse { 50% { opacity: 0.25; transform: scale(0.7); } }
.aiadvice__stale {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.9rem;
  margin: 1rem 0 0; padding-top: 0.9rem; font-size: 0.875rem;
  border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  color: var(--ink-soft);
}
.aiadvice__stale[hidden] { display: none; }
.aiadvice .button--quiet { color: var(--ink); border-color: color-mix(in srgb, var(--ink) 35%, transparent); }
.aiadvice .button--quiet:hover { background: color-mix(in srgb, var(--sea-deep) 18%, transparent); border-color: var(--sea-deep); color: var(--ink); }
.board__main > .aiadvice { margin-bottom: clamp(1rem, 2vw, 1.4rem); }

/* The Premium page: what it adds, and the box to unlock it. */
.premium__title { margin-bottom: 0.8rem; }
.premium__list { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; gap: 0.9rem; }
.premium__item {
  padding: 1rem 1.1rem; border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--panel); line-height: 1.55; color: var(--ink-soft);
}
.premium__item strong { display: block; margin-bottom: 0.15rem; font: 700 1rem/1.3 var(--display); color: var(--ink); }
.premium__panel { display: grid; gap: 0.75rem; }
.premium__label { font: 650 0.9375rem/1.2 var(--sans); color: var(--ink); }
.premium__row { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.premium__input {
  flex: 1 1 12rem; min-height: 2.9rem; padding: 0.7rem 0.9rem; border-radius: 10px;
  border: 1px solid var(--line-strong); background: var(--ground); color: var(--ink);
  font: 500 1rem/1.2 var(--mono); letter-spacing: 0.04em;
}
.premium__input:focus-visible { outline: 2px solid var(--sea); outline-offset: 2px; }
.premium__error { margin: 0; font-weight: 600; color: var(--coral-deep); }
.premium__on { margin: 0; font: 700 1.05rem/1.3 var(--display); color: var(--ink); }
.premium__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem; }
.premium__actions form { margin: 0; }
.premium__note, .premium__small { color: var(--ink-soft); line-height: 1.55; }
.premium__small { margin-top: 1.4rem; font-size: 0.8125rem; color: var(--ink-faint); }
.advice-page { display: grid; gap: 1rem; }
.advice-page__back { justify-self: start; }

@media (prefers-reduced-motion: reduce) {
  .aiadvice__pulse { animation: none; }
}


/* ---------------------------------------------------------------- reading
   Anything a person reads as prose is set in the guide's serif (Efe,
   2026-11-11): the explainer, care sheets, FAQ answers, About, findings and the
   advice. Controls, tables and figures stay in the sans. */
.prose p, .prose li, .about__body p, .qa__a p, .explain__cols p,
.hero__lede, .profile__summary, .advice__text, .advice__detail,
.aiadvice__summary, .aiadvice__change, .aiadvice__why, .aiadvice__working {
  font-family: var(--serif);
}
.prose p, .prose li, .about__body p, .qa__a p, .explain__cols p { font-size: 1.0625rem; line-height: 1.62; }
.tally__value, .stocklist__count, .chosen__count, .tankrow__count, .param__value,
input[type="number"] { font-variant-numeric: tabular-nums; }

/* Controls are interface, not reading: the sans, whatever size they are. */
.button, .tab, button, input, select, textarea, .chip, .badge, .tag, .param__verdict,
.fbadge, .tlevel, .dive-button, .dive-ghost, .dive-premium__unlock, .perk__name, .promo__tag {
  font-family: var(--sans);
}


/* ---------------------------------------------------------------- premium
   An unlocked browser keeps every page as it is; the top bar turns deep ocean
   blue, with the gold mark by the name and a gold line under it. */
html.is-premium .topbar { background: color-mix(in srgb, var(--premium-bar) 97%, transparent); }
html.is-premium .topbar__mark, html.is-premium .topbar__mark span { color: var(--premium-bar-ink); }
/* The badge keeps its dark type on the gold (11:1); the rule above had
   turned it pale (2026-11-14, found beside the new wordmark). */
html.is-premium .topbar__mark .topbar__premium { color: var(--premium-gold-ink); }
html.is-premium .tab { color: var(--premium-bar-ink); }
html.is-premium .tab:hover { background: rgba(255, 255, 255, 0.1); color: #ffffff; }
html.is-premium .tab.is-current { background: rgba(255, 255, 255, 0.16); color: #ffffff; }
html.is-premium .tab:focus-visible { outline-color: var(--premium-gold); }
html.is-premium .topbar::after { background: var(--premium-gold); }
.topbar__premium {
  margin-left: 0.45rem; padding: 0.12rem 0.5rem; border-radius: 999px;
  background: linear-gradient(135deg, var(--premium-gold), var(--premium-gold-deep));
  color: var(--premium-gold-ink); font: 700 0.6875rem/1.4 var(--sans);
  vertical-align: 0.2em;
}


/* ------------------------------------------------- selling Premium, honestly
   The card is the care guide itself: four of its pages fanned out, built from
   this tank, opening a look inside. Under them the comparison of Free and
   Premium, folded away until asked for (Efe, 2026-11-11). Paid words never
   reach the page: grey bars stand where they would be. */
.promo__corner {
  position: absolute; top: 0.85rem; right: 0.85rem;
  padding: 0.2rem 0.65rem; border-radius: 999px;
  background: linear-gradient(135deg, var(--premium-gold), var(--premium-gold-deep));
  color: var(--premium-gold-ink); font: 700 0.75rem/1.3 var(--sans);
}
.promo .promo__title { padding-right: 6rem; margin-bottom: 0.8rem; }

/* The fanned pages. Each page is sized in em, so one font-size scales it. */
.fan {
  display: grid; justify-items: center; gap: 0.5rem; width: 100%;
  margin: 0 0 0.9rem; padding: 0; border: 0; background: none; font: inherit; color: inherit;
}
button.fan { cursor: pointer; }
.fan:focus-visible { outline: 3px solid var(--sea); outline-offset: 4px; border-radius: 8px; }
.fan__pages { position: relative; display: block; width: 100%; max-width: 26rem; aspect-ratio: 27 / 12.6; }
.fan__page {
  position: absolute; top: 4%; width: 33%; height: 92%;
  font-size: clamp(5.4px, 1.75vw, 7.4px); transform-origin: 50% 100%; transition: transform 220ms ease;
}
.fan__page--1 { left: 0; z-index: 1; transform: rotate(-2deg); }
.fan__page--2 { left: 25%; z-index: 2; transform: rotate(0.5deg); }
.fan__page--3 { left: 46.5%; z-index: 3; transform: rotate(2.5deg); }
.fan__page--4 { left: 67%; z-index: 4; transform: rotate(4.5deg); }
button.fan:hover .fan__page--1 { transform: translateY(-3%) rotate(-2deg); }
button.fan:hover .fan__page--2 { transform: translateY(-3%) rotate(0.5deg); }
button.fan:hover .fan__page--3 { transform: translateY(-3%) rotate(2.5deg); }
.fan__ribbon {
  position: absolute; top: 0; left: 19%; width: 0.9rem; height: 2.8rem; z-index: 1;
  background: var(--premium-gold-deep); transform: rotate(-2deg);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 78%, 0 100%);
}
.fan__look {
  font: 650 0.9375rem/1.3 var(--sans); color: var(--sea-deep);
  text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px;
}
.fan__look--quiet { font-weight: 400; color: var(--ink-faint); text-decoration: none; }
@media (min-width: 62.01rem) { .fan__page { font-size: 7.3px; } }

/* A page of the guide in miniature. */
.mini {
  display: block; box-sizing: border-box; overflow: hidden; padding: 1.1em 1.15em;
  background: #ffffff; border: 1px solid var(--line); border-radius: 4px;
  box-shadow: 0 1px 2px var(--shadow);
  text-align: left; font-family: var(--serif); color: var(--ink); line-height: 1.35;
}
.mini span { display: block; }
.mini .mini__brand { display: flex; align-items: center; gap: 0.4em; margin-bottom: 0.9em; font: 700 1em/1 var(--serif); color: var(--plan-accent-deep); }
.mini__brand img { width: 1.45em; height: 1.45em; display: block; }
.mini__kicker { font-style: italic; font-size: 0.9em; color: var(--ink-soft); }
.mini__title { margin: 0.1em 0 0.2em; font-weight: 600; font-size: 1.7em; line-height: 1.08; color: var(--plan-accent-deep); }
.mini__line { font-size: 0.95em; color: var(--ink-soft); margin-bottom: 0.6em; }
.mini__h { margin: 0 0 0.55em; font-weight: 700; font-size: 1.2em; line-height: 1.15; color: var(--plan-accent); }
.mini .mini__fish { display: grid; grid-template-columns: 2.6em minmax(0, 1fr); gap: 0.6em; align-items: start; margin-bottom: 0.75em; }
.mini .mini__art { width: 2.6em; height: 2.6em; border-radius: 50%; overflow: hidden; background: var(--sea-tint); }
.mini__art img { width: 100%; height: 100%; object-fit: cover; display: block; transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.mini__name { font-weight: 700; font-size: 0.95em; line-height: 1.2; }
/* The honest blur: images cut from a sample tank's guide and blurred before
   they were saved, so nothing behind them can be read or inferred. */
.mini .mini__blur { display: block; width: 100%; height: auto; margin-top: 0.6em; }
.mini__fish .mini__blur--lines { height: 1.9em; object-fit: cover; object-position: left top; margin-top: 0.3em; }
.mini__fish:nth-child(odd) .mini__blur--lines { object-position: left bottom; }

/* Free and Premium: the top rows show and fade out, blurring as they go,
   and the arrow opens the rest. Whole without scripting. */
.compare { margin: 0 0 1.1rem; }
.compare__defs { position: absolute; }
.compare__body { max-height: 46rem; transition: max-height 320ms ease; }
html:not(.no-js) .compare:not(.is-open) .compare__body {
  max-height: 12rem; overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, #000 52%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 52%, transparent 100%);
}
html:not(.no-js) .compare:not(.is-open) .cmp tbody tr:nth-child(n+5) > * { filter: blur(1.2px); }
.compare__toggle {
  position: relative; z-index: 1; display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem; margin: -1.5rem auto 0; padding: 0; cursor: pointer;
  border-radius: 50%; border: 1px solid color-mix(in srgb, var(--sea) 28%, transparent);
  background: #ffffff; color: var(--sea-deep);
  box-shadow: 0 2px 6px -2px var(--shadow);
}
.compare__toggle:hover { border-color: var(--sea); background: var(--sea-wash); }
.compare__toggle:focus-visible { outline: 3px solid var(--sea); outline-offset: 2px; }
.compare.is-open .compare__toggle { margin-top: 0.6rem; }
.no-js .compare__toggle { display: none; }
.compare__chevron {
  width: 1.25rem; height: 1.25rem;
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 200ms ease;
}
.compare.is-open .compare__chevron { transform: rotate(180deg); }

/* The table: no rules between rows, one pale gold column for Premium. */
.cmp { width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0; }
.cmp col.cmp__c { width: 4.6rem; }
.cmp th[scope="row"] { padding: 0.3rem 0.75rem 0.3rem 0; text-align: left; font: 400 0.9375rem/1.3 var(--serif); color: var(--ink); }
.cmp thead th { padding: 0 0 0.35rem; font: 700 0.8125rem/1.2 var(--sans); text-align: center; color: var(--ink-faint); }
.cmp td { text-align: center; vertical-align: middle; }
.cmp .cmp__prem { background: color-mix(in srgb, var(--premium-gold) 22%, #ffffff); }
.cmp thead .cmp__prem { background: none; padding: 0; vertical-align: bottom; }
.cmp thead .cmp__prem > span {
  display: block; padding: 0.38rem 0 0.32rem; border-radius: 10px 10px 0 0;
  background: var(--premium-gold); color: var(--premium-gold-ink);
}
.cmp tbody tr:last-child .cmp__prem { border-radius: 0 0 10px 10px; }
.cmp tbody tr:last-child > * { padding-bottom: 0.45rem; }
.cmp__group th { padding: 0.75rem 0.75rem 0.15rem 0; text-align: left; font: 700 0.8125rem/1.25 var(--sans); color: var(--sea-deep); }
.cmp__yes { width: 1.15rem; height: 1.15rem; vertical-align: middle; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.cmp__free .cmp__yes { color: var(--ink-faint); }
.cmp__prem .cmp__yes { color: var(--premium-gold-ink); }
.cmp__no { font: 400 1rem/1 var(--sans); color: #9db3c0; }

/* The one gold button on the site: Premium's own color, larger than any
   other, in the guide's serif (Efe, 2026-11-11: "a little flashier"). */
.button--gold {
  background: linear-gradient(135deg, var(--premium-gold), var(--premium-gold-deep));
  color: var(--premium-gold-ink);
  font: 700 1.125rem/1.2 var(--serif);
  letter-spacing: 0;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 -1px 0 rgba(120, 80, 10, 0.25);
}
.button--gold:hover { background: linear-gradient(135deg, #f7d58e, var(--premium-gold-deep)); color: var(--premium-gold-ink); }
.button--gold:focus-visible { outline: 3px solid var(--premium-gold-ink); outline-offset: 3px; }
.promo__foot { display: grid; gap: 0.6rem; justify-items: center; }
.promo__unlock { width: 100%; padding: 1rem 1.5rem; font-size: 1.25rem; }
.promo__foot .promo__proof {
  margin: 0; text-align: center;
  font: italic 400 0.9375rem/1.4 var(--serif); color: var(--ink-soft);
}
.promo__proof b { font-style: normal; font-weight: 700; color: var(--ink); font-variant-numeric: lining-nums; }

@media (max-width: 34rem) {
  .cmp col.cmp__c { width: 3.7rem; }
  .cmp th[scope="row"] { padding-right: 0.5rem; }
}
@media (prefers-reduced-motion: reduce) {
  .fan__page, .compare__chevron, .compare__body { transition: none; }
}

/* The look inside: every page of the guide in order, the offer pinned under
   them while they scroll. */
.look { width: min(40rem, calc(100vw - 1.5rem)); max-height: min(44rem, calc(100vh - 2rem)); overflow: hidden; display: none; }
.look[open] { display: flex; flex-direction: column; }
.look__scroll {
  position: relative; overflow-y: auto; flex: 1 1 auto; min-height: 0;
  background: var(--ground); padding: 0.9rem 0.9rem 0;
  display: grid; gap: 0.7rem; align-content: start;
}
.look__note { margin: 0; font: 400 0.875rem/1.4 var(--sans); color: var(--ink-soft); }
.look__page { background: #ffffff; border: 1px solid var(--line); border-radius: 6px; padding: 1.2rem 1.35rem 1.1rem; font-family: var(--serif); }
.look__page p { margin: 0; }
.look__page .look__brand { display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.9rem; font: 700 1rem/1 var(--serif); color: var(--plan-accent-deep); }
.look__kicker { font: italic 400 1rem/1.3 var(--serif); color: var(--ink-soft); }
.look__page .look__title { margin: 0.1rem 0 0.15rem; font: 600 1.6rem/1.1 var(--serif); color: var(--plan-accent-deep); }
.look__page .look__line { margin-bottom: 0.6rem; font: 400 1rem/1.4 var(--serif); color: var(--ink-soft); }
.look__page .look__sub { margin: 0.9rem 0 0.3rem; font: 700 1rem/1.25 var(--serif); color: var(--plan-accent); }
.look__page .look__head { margin-bottom: 0.65rem; font: 700 1.25rem/1.2 var(--serif); color: var(--plan-accent); }
.look__glance { margin: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
.look__glance div { background: var(--plan-tint); border-radius: 8px; padding: 0.5rem 0.65rem; }
.look__glance dt { font: 400 0.8125rem/1.2 var(--sans); color: var(--ink-soft); }
.look__glance dd { margin: 0.15rem 0 0; font: 700 1rem/1.2 var(--sans); color: var(--ink); }
.look__fish { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr); gap: 0.8rem; align-items: start; margin-bottom: 0.8rem; }
.look__art { display: block; width: 3.4rem; height: 3.4rem; border-radius: 50%; overflow: hidden; background: var(--sea-tint); }
.look__art img { width: 100%; height: 100%; object-fit: cover; display: block; transform: scale(var(--zoom, 1)); transform-origin: var(--focus, 50% 50%); }
.look__name { font: 700 1rem/1.25 var(--serif); }
.look__blur { display: block; width: 100%; height: auto; margin: 0.35rem 0 0.15rem; }
.look__fish .look__blur--lines { height: 2.5rem; object-fit: cover; object-position: left top; margin-top: 0.3rem; }
.look__fish:nth-child(odd) .look__blur--lines { object-position: left bottom; }
/* A cropped strip of blurred lines fades at its cut edges instead of ending in
   a hard line through the text. */
.look__blur--lines, .mini__blur--lines {
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0, #000 22%, #000 78%, transparent 100%);
}
.look__veil {
  position: sticky; bottom: 0; margin: 0 -0.9rem; padding: 3.2rem 1.5rem 1.2rem; text-align: center;
  background: linear-gradient(180deg, rgba(238, 246, 251, 0) 0, rgba(238, 246, 251, 0.96) 2.6rem, var(--ground) 3.6rem);
}
.look__veil p { margin: 0 auto 0.75rem; max-width: 27rem; font: 600 1rem/1.45 var(--sans); color: var(--ink); }
@media (max-width: 34rem) {
  .look__page { padding: 1rem; }
  .look__glance { grid-template-columns: 1fr; }
  .look__page .look__title { font-size: 1.35rem; }
}

/* The tour of what is new: started by the keeper, three steps. */
.promo__tour {
  margin-top: 0.8rem; padding: 0; border: 0; background: none; cursor: pointer;
  font: 600 0.9375rem/1.3 var(--sans); color: var(--sea-deep);
  text-decoration: underline; text-underline-offset: 3px;
}
.tour {
  position: fixed; z-index: 40; width: min(20rem, calc(100vw - 24px));
  padding: 0.9rem 1rem 0.85rem; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--line); color: var(--ink);
  box-shadow: 0 12px 32px -18px rgba(12, 36, 51, 0.45);
}
.tour:focus { outline: 2px solid var(--premium-gold); outline-offset: 2px; }
.tour__count { margin: 0 0 0.3rem; font: 600 0.8125rem/1 var(--sans); color: var(--ink-faint); }
.tour__title { margin: 0 0 0.3rem; font: 700 1.0625rem/1.25 var(--serif); }
.tour__text { margin: 0 0 0.8rem; font: 400 0.9375rem/1.5 var(--serif); color: var(--ink-soft); }
.tour__actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
.tour__actions .button { padding: 0.55rem 1rem; }
.is-tour-target { outline: 3px solid var(--premium-gold); outline-offset: 4px; border-radius: 10px; }
/* The logo's green tile would vanish on the dark bar: a thin light edge keeps it. */
html.is-premium .topbar__glyph img { border-radius: 6px; box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.6); }


/* ------------------------------------------- what the tank's settings mean
   A small round "i" beside each setting, never inside an option, and the
   card it opens (Efe, 2026-11-11). */
.infobtn {
  display: inline-grid; place-items: center; width: 1.25rem; height: 1.25rem;
  margin-left: 0.3rem; padding: 0; vertical-align: 0.05em; cursor: pointer;
  border-radius: 50%; border: 1.5px solid currentColor; background: transparent;
  color: var(--sea-deep); opacity: 0.8;
  font: italic 700 0.75rem/1 var(--serif);
}
.infobtn:hover { opacity: 1; background: var(--sea-wash); }
.infobtn:focus-visible { outline: 2px solid var(--sea-deep); outline-offset: 2px; opacity: 1; }
.setupinfo { width: min(30rem, calc(100vw - 1.5rem)); }
.setupinfo__body { padding: 0.4rem 1.2rem 1.2rem; }
.setupinfo__for { margin: 0 0 0.8rem; font: 400 0.9375rem/1.45 var(--sans); color: var(--ink-soft); }
/* The choice Finform takes when a setting is left blank (Efe, 2026-11-12). */
.setupinfo__default { font-weight: 400; font-size: 0.875em; color: var(--ink-soft); white-space: nowrap; }
.setupinfo__list { margin: 0; display: grid; gap: 0.7rem; }
.setupinfo__list dt { font: 700 1rem/1.25 var(--serif); color: var(--ink); }
.setupinfo__list dd { margin: 0.15rem 0 0; font: 400 0.9375rem/1.5 var(--serif); color: var(--ink-soft); }


/* ------------------------------------------------------------ surface light
   The boxes that once had a coloured strip down the left (an AI-design
   giveaway, Efe, 2026-11-11) take the look he chose instead: a waterline
   along the top, catching a thin line of light, tinted by what the box
   says, and the box deepening gently towards the bottom, glossy. The tone
   is set per box below; the words say the same, for anyone who cannot
   tell the colours apart. */
:root {
  --wave-sea: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%230a6ea8%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
  --wave-good: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%231db35a%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
  --wave-amber: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%23d9821a%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
  --wave-gap: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%235f6f68%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
  --wave-coral: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%23e8433f%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
  --wave-ember: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%27160%27%20height%3D%2716%27%20viewBox%3D%270%200%20160%2016%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20d%3D%27M0%209%20C20%202%2040%202%2060%209%20S100%2016%20120%209%20S150%202%20160%207%20L160%200%20L0%200Z%27%20fill%3D%27%23e07a2b%27%20fill-opacity%3D%270.16%27/%3E%3Cpath%20d%3D%27M0%2010%20C20%203%2040%203%2060%2010%20S100%2017%20120%2010%20S150%203%20160%208%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-opacity%3D%270.95%27%20stroke-width%3D%271.6%27/%3E%3C/svg%3E");
}
.cand__link, .mate, .saved__item, .asks__row, .live-status, .band {
  --tone: var(--sea);
  --wave: var(--wave-sea);
  background-color: var(--panel);
  background-image: var(--wave),
    linear-gradient(180deg, color-mix(in srgb, var(--tone) 9%, var(--panel)) 0%, var(--panel) 38%,
                    color-mix(in srgb, var(--sea) 7%, var(--panel)) 100%);
  background-repeat: no-repeat;
  background-size: 100% 13px, 100% 100%;
  border: 1px solid color-mix(in srgb, var(--sea) 16%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), 0 8px 18px -14px rgba(12, 36, 51, 0.45);
}
.band { background-size: 100% 18px, 100% 100%; }
.cand--good .cand__link       { --tone: var(--good);  --wave: var(--wave-good); }
.cand--caution .cand__link    { --tone: var(--amber); --wave: var(--wave-amber); }
.cand--unknown .cand__link,
.cand--no .cand__link         { --tone: var(--gap);   --wave: var(--wave-gap); }
.mate.is-bad                  { --tone: var(--coral); --wave: var(--wave-coral); }
.mate.is-mid, .live-status    { --tone: var(--amber); --wave: var(--wave-amber); }
.asks__row, .band             { --tone: var(--ember); --wave: var(--wave-ember); }
a.cand__link:hover, .saved__item:hover {
  border-color: color-mix(in srgb, var(--sea) 45%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), 0 10px 22px -14px rgba(12, 36, 51, 0.55);
}

/* What a suggestion is, in words as well as its colour. */
.cand__status {
  display: flex; align-items: center; gap: 0.35rem; margin-top: 0.2rem;
  font: 650 0.8rem/1.3 var(--sans); color: var(--ink-faint);
}
.cand__status::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; flex: none; }
.cand--good .cand__status    { color: var(--good-deep); }
.cand--caution .cand__status { color: var(--amber-deep); }


/* WHAT A BUTTON NEEDS FIRST (the audit, 2026-11-12; Efe: highlight the missing
   fields "alongside a pop up at the top"). The message floats under the top
   bar, and each missing field is ringed in coral until it is answered. */
.needs {
  position: fixed; top: 4.4rem; left: 50%; transform: translateX(-50%); z-index: 60;
  width: max-content; max-width: min(34rem, calc(100% - 2rem)); margin: 0;
  padding: 0.8rem 1.15rem; border-radius: 14px;
  background: var(--panel-lift); border: 1.5px solid var(--coral); color: var(--ink);
  box-shadow: 0 18px 40px -22px var(--shadow-deep); font: 500 0.95rem/1.4 var(--body);
}
.field.is-missing select, .field.is-missing input,
.picker.is-missing .picker__input {
  border-color: var(--coral); box-shadow: 0 0 0 3px var(--coral-wash);
}
.field.is-missing .field__label { color: var(--coral-deep); }
