/* ============================================================
   Oscar clinical log. Palette taken from Diff-Quik cytology
   staining (methylene violet + eosin pink), the stain actually
   used on Oscar's samples. Severity ramp is deliberately kept
   outside the accent hue so lesion grade never reads as chrome.
   ============================================================ */
:root{
  --stain:#4A3A8F; --stain-soft:#6A58B0; --stain-wash:#EFEBF9;
  --eosin:#B83C6E; --eosin-wash:#FBEEF3;
  --ink:#1D1926; --ink-2:#4A4358; --ink-3:#70687D;
  --paper:#F5F3F8; --card:#FFFFFF; --line:#DBD5E4; --line-soft:#EAE6F0;
  --line-strong:#8E86A0;
  --sev0:#587F6B; --sev1:#94712A; --sev2:#B75E2B; --sev3:#9B2B36;
  --ok:#4F7A65; --warn:#986D1A; --bad:#9B2B36;
  --shadow:0 1px 2px rgba(29,25,38,.05), 0 6px 20px rgba(29,25,38,.06);
  /* Five steps, and they are the only corners in the app. There used to be
     twenty distinct hand-typed radii, which is what a stylesheet looks like
     when every card was rounded by eye on the day it was written: 7px next to
     9px next to 8px reads as sloppy without anyone being able to say why. */
  --r-xs:2px;    /* ink: a corner on a dot or a 9px chip           */
  --r-sm:8px;    /* small controls: chips, inputs, tags            */
  --r:12px;      /* the default card and button                    */
  --r-lg:16px;   /* sheets, panels, anything that owns the screen  */
  --r-pill:999px;
  --f-display:"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,serif;
  --f-body:"Avenir Next",Avenir,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,sans-serif;
  --f-data:ui-monospace,"SF Mono",Menlo,Consolas,"Cascadia Mono",monospace;
  --nav-h:56px;
  --on-accent:#FFFFFF;
  --edge:#8E86A0;
  --eosin-ink:#B83C6E;
  --sev0-ink:#517864; --sev1-ink:#8D6A23; --sev2-ink:#B05724; --sev3-ink:#9B2B36;
  --ok-ink:#3E6151; --warn-ink:#7A5714; --bad-ink:#9B2B36;
  /* One answer for text on a severity fill, as page 08 draws it. The severity
     ramp is now dark enough to carry white at every grade, which is what let
     the four separate on-sev tokens collapse into one value. */
  --on-sev0:#FFFFFF; --on-sev1:#FFFFFF; --on-sev2:#FFFFFF; --on-sev3:#FFFFFF;
  --on-ok:#FFFFFF; --on-warn:#FFFFFF; --on-bad:#FFFFFF; --on-eosin:#FFFFFF;
  /* The camera backdrop, on the same terms as the plate below: declared
     once and never themed. A viewfinder is a viewfinder, and the live
     frame is the only thing on it that should be carrying colour. */
  --vf-back:#14111C;
  /* Scrims. --vf-back fading to nothing, so the white chrome stays
     legible whatever the camera is pointed at. Bottom is heavier
     because the shutter row sits on it. */
  --vf-scrim-t:linear-gradient(180deg,rgba(20,17,28,.82),rgba(20,17,28,0));
  --vf-scrim-b:linear-gradient(0deg,rgba(20,17,28,.86),rgba(20,17,28,0));
  /* White at low alpha, the one way to draw a control over an image
     whose colours are not knowable in advance. */
  --vf-chip:rgba(255,255,255,.16); --vf-ctl:rgba(255,255,255,.14);
  /* The plate. Declared once and never overridden by a theme: the drawing and
     the severity fills on it have to mean the same thing at 07:00 and at 23:00,
     and inverting the whole thing for dark mode was how a green site came to
     read magenta on a phone at night. */
  --plate:#F7F5FA; --plate-line:#CFC8DC; --plate-ink:#1D1926;
  /* The OSM tile, turned grey as asked. Held as a token because the
     dark theme has to invert it as well or a walk at ten at night is a
     white rectangle held up to somebody's face, and this app declares
     its palette three times over. */
  --tile-f:grayscale(1) contrast(.86) brightness(1.04);
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root{
    --stain:#A594E8; --stain-soft:#8B79D4; --stain-wash:#241E38;
    --eosin:#E5789F; --eosin-wash:#331E28;
    --ink:#EDE9F4; --ink-2:#B4ADC3; --ink-3:#8C859B;
    --paper:#14121B; --card:#1C1926; --line:#332E41; --line-soft:#262230;
    --line-strong:#767090;
    --sev0:#7BAA92; --sev1:#E0B457; --sev2:#DC7C45; --sev3:#D06B75;
    --ok:#7BAA92; --warn:#E0B457; --bad:#D4707A;
    --shadow:0 1px 2px rgba(0,0,0,.3), 0 6px 20px rgba(0,0,0,.28);
    --on-accent:#14121B;
    --edge:#767090;
    --eosin-ink:#E5789F;
    --sev0-ink:#7BAA92; --sev1-ink:#E0B457; --sev2-ink:#DC7C45; --sev3-ink:#D06B75;
    --ok-ink:#7BAA92; --warn-ink:#E0B457; --bad-ink:#D4707A;
    --on-sev0:#14121B; --on-sev1:#14121B; --on-sev2:#14121B; --on-sev3:#14121B;
    --on-ok:#14121B; --on-warn:#14121B; --on-bad:#14121B; --on-eosin:#14121B;
    --tile-f:grayscale(1) invert(1) contrast(.72) brightness(.92);
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --stain:#A594E8; --stain-soft:#8B79D4; --stain-wash:#241E38;
  --eosin:#E5789F; --eosin-wash:#331E28;
  --ink:#EDE9F4; --ink-2:#B4ADC3; --ink-3:#8C859B;
  --paper:#14121B; --card:#1C1926; --line:#332E41; --line-soft:#262230;
  --line-strong:#767090;
  --sev0:#7BAA92; --sev1:#E0B457; --sev2:#DC7C45; --sev3:#D06B75;
  --ok:#7BAA92; --warn:#E0B457; --bad:#D4707A;
  --shadow:0 1px 2px rgba(0,0,0,.3), 0 6px 20px rgba(0,0,0,.28);
  --on-accent:#14121B;
  --edge:#767090;
  --eosin-ink:#E5789F;
  --sev0-ink:#7BAA92; --sev1-ink:#E0B457; --sev2-ink:#DC7C45; --sev3-ink:#D06B75;
  --ok-ink:#7BAA92; --warn-ink:#E0B457; --bad-ink:#D4707A;
  --on-sev0:#14121B; --on-sev1:#14121B; --on-sev2:#14121B; --on-sev3:#14121B;
  --on-ok:#14121B; --on-warn:#14121B; --on-bad:#14121B; --on-eosin:#14121B;
  --tile-f:grayscale(1) invert(1) contrast(.72) brightness(.92);
  color-scheme:dark;
}
:root[data-theme="light"]{
  --stain:#4A3A8F; --stain-soft:#6A58B0; --stain-wash:#EFEBF9;
  --eosin:#B83C6E; --eosin-wash:#FBEEF3;
  --ink:#1D1926; --ink-2:#4A4358; --ink-3:#70687D;
  --paper:#F5F3F8; --card:#FFFFFF; --line:#DBD5E4; --line-soft:#EAE6F0;
  --line-strong:#8E86A0;
  --sev0:#587F6B; --sev1:#94712A; --sev2:#B75E2B; --sev3:#9B2B36;
  --ok:#4F7A65; --warn:#986D1A; --bad:#9B2B36;
  --shadow:0 1px 2px rgba(29,25,38,.05), 0 6px 20px rgba(29,25,38,.06);
  --on-accent:#FFFFFF;
  --edge:#8E86A0;
  --eosin-ink:#B83C6E;
  --sev0-ink:#517864; --sev1-ink:#8D6A23; --sev2-ink:#B05724; --sev3-ink:#9B2B36;
  --ok-ink:#3E6151; --warn-ink:#7A5714; --bad-ink:#9B2B36;
  /* One answer for text on a severity fill, as page 08 draws it. The severity
     ramp is now dark enough to carry white at every grade, which is what let
     the four separate on-sev tokens collapse into one value. */
  --on-sev0:#FFFFFF; --on-sev1:#FFFFFF; --on-sev2:#FFFFFF; --on-sev3:#FFFFFF;
  --on-ok:#FFFFFF; --on-warn:#FFFFFF; --on-bad:#FFFFFF; --on-eosin:#FFFFFF;
  --tile-f:grayscale(1) contrast(.86) brightness(1.04);
  color-scheme:light;
}

*{box-sizing:border-box}
/* Some tabs are long enough to scroll and some are not, and on a desktop that
   means the scrollbar appears and disappears as you move between them. Every card
   on the page then jumps sideways by the width of the bar, which reads as the
   layout being loose. Reserving the gutter whether or not it is used keeps the
   left edge of every tab on the same line. Phones draw the bar over the content,
   so this costs them nothing. */
/* --navdrop is the one custom property in this stylesheet that is written by
   script on every resume, and it is read inside two calc()s: the bar's own
   position and the clearance the body reserves under it. A calc() is
   all-or-nothing at computed-value time - one token it cannot read as a length
   and the whole declaration is discarded - and the var() fallback does NOT
   catch that case: `var(--navdrop, 0px)` only applies its default when the
   property is UNSET, never when it holds something unusable. So a single bad
   write took out the anchoring of the bottom bar AND the padding that keeps the
   last line of every screen above it, and did it silently.

   Registering the property is what makes the fallback real. An invalid value is
   now rejected where it is set and the initial value stands, so neither calc()
   ever sees a token it cannot read. Measured 03.09.2026 against the fault
   Ovidiu photographed on the Nutrienti screen: with --navdrop poisoned, the
   body's reserve read 0px and the bar floated exactly --nav-h + --s1 - 60px -
   off the bottom of the glass, to the pixel. With this rule it reads 60px and
   the bar stays flush. */
@property --navdrop{
  syntax:"<length>";
  inherits:true;
  initial-value:0px;
}

/* Everything stuck to the top of the screen, added up: the header plus the mode
   bar, measured and published by tbFit() in js/18-shell.js. Registered rather
   than left to a var() fallback at each use, so there is one place that says
   what it is when nothing has measured it yet, and so a bad write can never
   take out a sticky heading the way an unregistered --navdrop took out the
   bottom bar. The initial value is the header alone on a screen with no mode
   bar, which is the state the app boots into. */
@property --chrome-h{
  syntax:"<length>";
  inherits:true;
  initial-value:64px;
}

html{scrollbar-gutter:stable}
body{
  margin:0; background:var(--paper); color:var(--ink);
  font-family:var(--f-body); font-size:15px; line-height:1.5;
  -webkit-text-size-adjust:100%; -webkit-tap-highlight-color:transparent;
  /* Same --navdrop the bar itself uses, so the clearance under the content and
     the height of the bar can never disagree. */
  /* --nav-h is 56 and the bar is 57, because it also carries a 1 border-top,
     so the old reserve was one short and the last line of a short page came out
     sliced by the bar rather than ending above it. One --s1 covers the border
     and leaves 3 of air, and stays on the grid. */
  padding-bottom:calc(var(--nav-h) + var(--s1) + env(safe-area-inset-bottom) + var(--navdrop, 0px));
  /* A double tap anywhere would otherwise zoom, and tapping chips fast enough to
     count as a double tap is exactly how the day gets filled in. The body map keeps
     its own touch-action:none so pinch-to-zoom still works where it is wanted. */
  touch-action:manipulation;
}
h1,h2,h3{font-family:var(--f-display); font-weight:400; text-wrap:balance; margin:0}
h1{font-size:1.5rem; letter-spacing:.005em}
h2{font-size:1.24rem}
h3{font-size:15px; line-height:1.3}
p{margin:0}
button,input,select,textarea{font:inherit; color:inherit}
a{color:var(--stain)}
:focus-visible{outline:2px solid var(--stain); outline-offset:2px; border-radius:var(--r-sm)}

/* ---------- shell ---------- */
/* ---------------- spacing scale ----------------
   A 4pt grid, which is the one thing the deck's shared.css was not.
   It carried twelve steps - 2, 3, 5, 6, 8, 9, 10, 11, 13, 14, 16, 18 - six of
   them odd and none of them a common multiple, so no two things laid out
   independently could ever line up and the whole app read as crowded and
   slightly out of true. Every step below is a multiple of four, so anything
   can share an edge with anything.

   --s1..--s9 is the scale. The --sp-N names are the old ones kept as aliases,
   each rounded to its nearest step, so the 86 existing call sites did not all
   have to be rewritten to get onto the grid. Prefer --s1..--s9 in new work. */
:root{
  --s1:4px;  --s2:8px;  --s3:12px; --s4:16px; --s5:20px;
  --s6:24px; --s7:32px; --s8:40px; --s9:48px;

  --sp-1:var(--s1);  --sp-2:var(--s1);  --sp-3:var(--s1);  --sp-4:var(--s2);
  --sp-5:var(--s2);  --sp-6:var(--s2);  --sp-7:var(--s3);  --sp-8:var(--s3);
  --sp-9:var(--s3);  --sp-10:var(--s4); --sp-11:var(--s4); --sp-12:var(--s5);

  --card-gap:var(--s3);  /* one block to the next inside a card */
  --row-y:var(--s3);     /* one list row, top and bottom */
  --field-gap:var(--s3); /* one field to the next inside a sheet */

  /* Touch. Apple asks for 44x44pt, Material for 48x48dp separated by 8dp.
     Nothing you can tap is allowed below --tap, and where the ink has to
     stay small the target is grown around it instead of the other way up. */
  --tap:48px;
  --tap-min:44px;
  --tap-gap:var(--s2);

  /* How wide the app is allowed to be, on whatever it was opened on.
     This is going onto other people's phones now, and one of them folds.
     1180px was a desktop number on an app that has no desktop screen: it let
     a card stretch to eleven hundred pixels of one-line rows, which is not a
     wide layout, it is a phone layout that has been pulled apart. 680 is a
     column that still reads as a page. Anything that genuinely wants the room
     asks for it through a container query further down, so the decision is
     made by the thing being laid out rather than by the window. */
  /* The slider thumb, named because two other rules are geometry derived from
     it rather than spacing of their own: the tick rail is inset by half a thumb
     so the first and last tick sit under the thumb's centre at either end, and
     the webkit thumb is pulled up by half its own overhang. Typing 14 in one
     place and 28 in another is how a tick row drifts off its own slider the
     first time somebody makes the thumb bigger. */
  --sld-thumb:28px;
  --maxw:680px;

  /* The side margin, fluid rather than a step. At 320px a fixed 16px gutter is
     ten percent of the screen spent on nothing; at 820px it is a hairline. */
  --gut:clamp(12px, 4vw, 20px);
}

/* A book-style foldable reports its panes as viewport segments. Without this
   the column centres itself across the whole opened width, which puts the
   middle of every card directly on the hinge. Pinning the measure to the first
   segment keeps the app on one pane, which is also where the thumb is. The
   fallback in env() is what every phone shipping today uses. */
@media (horizontal-viewport-segments:2){
  :root{ --maxw:env(viewport-segment-width 0 0, 680px); }
}
.wrap{max-width:var(--maxw); margin:0 auto; padding:0 var(--gut)}
.topbar{
  position:sticky; top:0; z-index:40; background:var(--paper);
  border-bottom:1px solid var(--line-soft);
  padding-top:env(safe-area-inset-top);
}
.topbar__in{display:flex; align-items:center; gap:12px; min-height:56px}
.brand{display:flex; align-items:center; gap:8px; margin-right:auto; min-width:0}
/* His own head from the body map, not a generic paw glyph: the same drawing the
   Head view uses, so the mark on the bar and the mark on the map are one animal.
   It was drawn as black line art for the light --plate card it sits on
   everywhere else, so on --paper in a dark palette it was a black dog on a near
   black page and the sign in screen had a hole above the name. Deck 17.4 F4
   settles it: the mark is the stain colour over the paper colour and there is
   no second piece of artwork for dark. Line art on transparency is an alpha
   channel, so the plate masks a --stain fill and comes out themed, same file
   and same shape. The @supports arm exists because a mask that does not apply
   paints a solid lilac square where the dog should be, and nothing is better
   than that. */
.brand__mark{
  width:32px; height:32px; flex:none; object-fit:contain; display:block;
}
.brand__mark--mk{
  width:44px; height:44px; margin-bottom:8px; flex:none;
  color:var(--stain); line-height:0;
}

/* The mark itself, deck 17.3 A1. It was a mask over /img/head.png until
   06.08.2026, which themed correctly and could never draw itself, because a
   mask has no lines in it. It is the same drawing, traced to strokes in
   33-mark.js, and it now takes its colour from currentColor rather than from
   a background the mask cuts holes in. Nothing here needs an @supports arm:
   an svg that does not render leaves nothing behind, where a mask that does
   not apply paints a solid lilac square where the dog should be. */
/* hmk and not the deck's own mk. Page 12's rater monogram has owned .mk since
   the people build: 34px, rounded, filled --stain. The drawing inherited all
   of it and came out as a small lilac tile with two dots in it, which is the
   restyle-never-rename trap the register already carries, walked into a
   second time by copying a class name straight out of the deck. */
.hmk{width:100%; height:100%; display:block}

/* Deck 17.3 A1, at the 900ms Ovidiu asked for rather than the deck's 3400ms
   demonstration loop. Head to 520, muzzle from 200 to 680, folds from 420 to
   840, philtrum from 560 to 900, and the eyes and nostrils fade in over the
   last 280. Everything is finished at 900, which is exactly the floor in
   splEnd, so the drawing is never cut mid-stroke: that is the whole cost of
   this choice and it is paid on every cold open.

   pathLength on the paths is what makes one dash length mean the same amount
   of drawing on a 4-unit fold and an 86-unit ear. */
.hmk--draw .hmk__o path{
  stroke-dasharray:100; stroke-dashoffset:100;
  animation:hmkDraw 520ms cubic-bezier(.62,.02,.34,1) forwards;
}
.hmk--draw .hmk__m path{animation-duration:480ms; animation-delay:200ms}
.hmk--draw .hmk__w path{animation-duration:420ms; animation-delay:420ms}
.hmk--draw .hmk__p path{animation-duration:340ms; animation-delay:560ms}
.hmk--draw .hmk__d{opacity:0; animation:hmkDots 280ms linear 620ms forwards}
@keyframes hmkDraw{to{stroke-dashoffset:0}}
@keyframes hmkDots{to{opacity:1}}
.brand__txt{display:flex; flex-direction:column; gap:1px; min-width:0}
.brand__name{font-family:var(--f-display); font-size:19px; line-height:1.15; white-space:nowrap}
.brand__sub{
  font-size:11px; letter-spacing:normal; text-transform:none;
  color:var(--ink-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

.icobtn{
  background:transparent; border:1px solid var(--edge); color:var(--ink-2);
  height:var(--tap-min); min-width:var(--tap-min); padding:0 var(--s3);
  border-radius:var(--r-pill);
  font-size:.72rem; letter-spacing:.06em; cursor:pointer; display:inline-flex;
  align-items:center; justify-content:center; gap:4px;
}
.icobtn:hover{border-color:var(--stain); color:var(--stain)}
.icobtn--on{background:var(--stain); border-color:var(--stain); color:var(--on-accent)}
/* A stepper at the end of the record is still drawn, because a control that
   vanishes at the edge moves the one beside it and you tap the wrong day. */
.icobtn:disabled{opacity:.3; cursor:default}
.icobtn:disabled:hover{border-color:var(--edge); color:var(--ink-2)}

/* ---------- nav ---------- */
.nav{
  /* --navdrop is written by navFit() in js/18-shell.js and is 0 on every
     browser that sizes its layout viewport to match what it paints. It is only
     ever positive on the iOS case where the two disagree, and it is the exact
     measured difference, so this is a correction and not a constant. */
  /* bottom is a literal 0 and never a calc(). A calc() is all-or-nothing at
     computed-value time: one bad token anywhere inside it and the whole
     declaration is thrown away, `bottom` falls back to `auto`, and a fixed
     element with `bottom:auto` drops to its static position - which here is
     the end of the body content, exactly `--nav-h` + `--s1` above the glass.
     That is the 60px float Ovidiu photographed on the Nutrienti screen on
     03.09.2026, measured off the screenshot to the pixel. The iOS correction
     is a transform now, so if --navdrop is ever unreadable the transform is
     dropped on its own and the bar stays anchored where it belongs. */
  position:fixed; left:0; right:0; bottom:0; z-index:50;
  transform:translateY(var(--navdrop, 0px));
  background:var(--card); border-top:1px solid var(--line);
  display:grid; grid-template-columns:repeat(3,1fr);
  /* The bar is full bleed, its three tabs are not. Left to stretch, the labels
     end up a hand apart on anything wider than a phone and the tab you want is
     never where your thumb already is. The padding centres the tabs on the
     app's own measure while the surface still runs edge to edge, so it reads
     as a bar rather than as a floating strip. */
  padding-inline:max(0px, calc((100% - var(--maxw)) / 2));
  padding-bottom:env(safe-area-inset-bottom);
}
.nav__b{
  min-width:0; appearance:none; background:none; border:0; cursor:pointer;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  /* --ink-3 is the dimmest ink in the scale and it was being used for the only
     permanent control in the app. An unselected tab is secondary, not disabled,
     so it takes --ink-2: still clearly quieter than the selected one, which
     carries both the stain and the weight, but no longer reads as switched off. */
  gap:3px; height:var(--nav-h); color:var(--ink-2);
  font-size:11px; font-weight:500; letter-spacing:.01em;
}
/* three labels now, but "Recapitulare" is still long enough to wrap on a 320px
   phone and push the bar taller than the icons it holds */
.nav__b span{max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* The pack is drawn on a 24 board with a 1.75 stroke. Painted at 16 the stroke
   came out at 1.17, which is why these read as faint rather than small: the
   glyph was being shrunk below the weight it was designed at. 24 is also what
   every tab bar checked on Mobbin uses (Spotify, Satispay, Qonto, Apple Games),
   none of them below 22. */
.nav__b svg{width:24px; height:24px}
.nav__b[aria-selected="true"]{color:var(--stain); font-weight:600}

/* ---------- modebar ---------- */
/* The surfaces inside a mode. Sticky rather than fixed: it belongs to the
   content, and on a long Review page it should stay reachable without the
   thumb travelling back to the bottom bar and out again. */
/* The bleed is --gut, never a hard 16. The gutter is clamp(12px, 4vw, 20px), so
   a fixed 16 overshoots the screen edge by 3.2px on a 320 phone - six pixels of
   sideways page scroll on five tabs - and stops 4px short of it on a tablet. */
.modebar{
  /* Below the header, not on top of it. --tb-h is measured and published by
     tbFit() in js/18-shell.js because the header's own height carries
     env(safe-area-inset-top) and is therefore not a constant. z-index one under
     the header so that if the measurement is ever stale the header wins, which
     shows a seam rather than a torn title. */
  position:sticky; top:var(--tb-h, 64px); z-index:39; margin:0 calc(var(--gut) * -1);
  display:flex; gap:0; overflow-x:auto; scrollbar-width:none;
  background:var(--paper); border-bottom:1px solid var(--line);
  padding:0 var(--gut);
}
.modebar::-webkit-scrollbar{display:none}
/* 42px, which is two short of the minimum and reads as compliant until it is
   measured. The padding is asymmetric on purpose - the underline needs room
   below the word - so the floor is set rather than the padding raised. */
.modebar__b{
  flex:none; appearance:none; background:none; border:0; cursor:pointer;
  padding:16px var(--s2) 12px; color:var(--ink-3); white-space:nowrap;
  font:600 .74rem/1 var(--f-body); letter-spacing:.02em;
  border-bottom:2px solid transparent; margin-bottom:-1px;
  min-height:var(--tap-min);
}
/* Six tabs, not five, since Nutrienti stopped being a page you could only
   arrive at from a row inside a card. Six do not fit a narrow phone at this
   type size, and the sixth is precisely the one that was hard to find, so the
   bar gives up type and padding rather than pushing anything behind a scroll
   nobody can see. Measured: at 360 the Romanian bar needs 376px of the 360 it
   has, and English needs 401. At .70rem with 4px flanks both fit from 360 up.
   A 320 phone still scrolls in English by about 15px; that is the one case
   left, and it is left knowingly rather than solved by an abbreviation.
   A media query and not @container, which is the house rule everywhere else:
   the container is .view, the modebar sits above every .view in the shell, and
   @container view simply never matched here. The rule that screens measure
   themselves and not the window is about screens. This is chrome, it is always
   exactly as wide as the phone, and the window is the honest thing to ask. */
@media (max-width:420px){
  .modebar__b{font-size:.70rem; padding-inline:var(--s1)}
}
.modebar__b:hover{color:var(--ink-2)}
.modebar__b[aria-selected="true"]{color:var(--stain); border-bottom-color:var(--stain)}
/* Every screen is a query container. What decides whether a scale takes five
   columns or six, or whether the map splits in two, is how much room that
   screen actually has - not how wide the window is. The two stop being the
   same number the moment anything is put in a pane, a sheet or the printed
   preview, and a viewport media query gets all three wrong. */
.view{display:none; padding:var(--sp-11) 0 var(--sp-9); container:view / inline-size}
.view[data-active="1"]{display:block}
/* Depth. Entering a pushed screen slides it in from the right; returning from
   one settles the parent back in from the left. The distances are small on
   purpose: there is no second view rendered underneath to slide over, so a
   full-width sweep would drag a blank page across the screen. What this buys
   is the one thing a hard cut cannot say: you went a level down and the way
   back exists. No fill mode, so once the animation ends the view carries no
   transform at all and position:fixed children (the full map, the route
   overlay) keep the viewport as their containing block. */
.view--push{animation:viewpush .28s cubic-bezier(.32,.72,.36,1)}
.view--pop{animation:viewpop .22s cubic-bezier(.32,.72,.36,1)}
@keyframes viewpush{from{transform:translateX(56px); opacity:.2}to{transform:none; opacity:1}}
@keyframes viewpop{from{transform:translateX(-32px); opacity:.4}to{transform:none; opacity:1}}
@media (prefers-reduced-motion:reduce){
  .view--push,.view--pop{animation:none}
}

/* ---------- cards ---------- */
.card{
  background:var(--card); border:1px solid var(--line-soft); border-radius:var(--r-lg);
  padding:var(--sp-9) var(--sp-10); box-shadow:var(--shadow);
  display:flex; flex-direction:column; gap:var(--card-gap);
}
.stack{display:flex; flex-direction:column; gap:var(--sp-8)}
#apptHost > * + *,#provHost > * + *,#rbHost > * + *,
#vsHost > * + *,#mhHost > * + *,#resBody > * + *{margin-top:var(--sp-8)}
.row{display:flex; align-items:center; gap:12px; flex-wrap:wrap}
.row--tight{gap:8px}
.grow{flex:1 1 auto; min-width:0}

/* A row that has something to absorb the slack must never wrap.

   This is the bug behind the recipe editor going to pieces: seven ingredients
   drew as nineteen ragged lines, with one row's delete button sitting directly
   above the next row's name. It looked like a styling accident and it is
   actually the flexbox line-breaking rule, which is worth writing down because
   nothing about it is visible from the markup.

   Flex line breaking happens BEFORE flex shrinking. Items are placed onto a
   line at their hypothetical main size - for a text div that is its max-content
   width - and anything that does not fit starts a new line. Only then does the
   shrink factor run, on each line separately. So `.grow` carrying min-width:0
   and flex-shrink:1 never got the chance to shrink: with flex-wrap:wrap the
   long name was already on a line of its own before shrinking was considered.
   A name 307 wide next to a 160 control cluster on a 430 screen wraps, and a
   name of 200 does not, which is why the same row rendered three different
   ways down one list. Written without the unit because the spacing gate reads
   a number and a unit inside a comment as a declaration on the rule below it.

   `.grow` says "this child takes whatever is left", which is a promise a
   wrapping row cannot keep. Turning wrap off wherever one is present makes the
   promise true and costs nothing anywhere else: the row shrinks the growing
   child instead of breaking the line, which is what every one of these rows
   wanted in the first place.

   :has() is Chrome 105 / Safari 15.4, so an old browser keeps today's
   behaviour rather than getting a worse one. */
.row:has(> .grow){flex-wrap:nowrap}
.eyebrow{
  font-size:10.5px; letter-spacing:.05em; text-transform:uppercase;
  font-weight:600; color:var(--ink-3); font-family:var(--f-body);
}
.hint{font-size:.8rem; color:var(--ink-3); line-height:1.45; min-width:0}
/* An aside about the reading, set off from it. Anything that qualifies a
   number rather than stating one belongs here and not in .hint. */
.note{border-left:2px solid var(--line-strong); padding-left:var(--sp-7);
  font:italic 12px/1.5 var(--f-body); color:var(--ink-2); margin:0}
.hint strong{color:var(--ink-2)}

/* The save line, and the one sentence in the app that is not allowed to fade.
   `data-hold` is set only when something somebody wrote reached neither the
   server nor the store, so it stops being a hint and becomes the loudest thing
   on the card until a later write succeeds. --bad-ink, not --bad: this is text
   on a surface and --bad is a fill. */
#saveState[data-kind="bad"]{
  color:var(--bad-ink); font-weight:600; font-size:.86rem;
  padding:8px 12px; border-radius:var(--r-sm); border:1px solid var(--edge);
  background:color-mix(in srgb, var(--bad) 12%, var(--card));
}
.rule{height:1px; background:var(--line-soft); border:0; margin:0}
.mono{font-family:var(--f-data); font-variant-numeric:tabular-nums}

/* ---------- controls ---------- */
/* A class that sets display beats the browser's own [hidden] rule, which is how
   the confirm-password field stayed on screen for a returning user. */
[hidden]{display:none !important}
.field{display:flex; flex-direction:column; gap:8px}
.label{font-size:.78rem; color:var(--ink-2); font-weight:600}
/* 16px is not a taste decision. Any text control under 16px makes iOS Safari zoom
   the whole page in when it gets focus, and it does not zoom back out, which is why
   the log looked like it had jumped scale after typing a note or a password. */
input,textarea,select{font-size:16px}
/* tel and time were missing from this list and had been since it was written,
   so on seven sites, the appointment sheet and the provider sheet among them,
   those two fields dropped straight through to the browser's own rendering: 30
   tall against everybody else's 44, a grey box in a dark theme, square corners,
   no padding. `.remtime` had already patched it in one place, which is the
   usual sign that the base rule is short rather than that the exception is
   special. Audited 08.08. */
input[type="text"],input[type="password"],input[type="email"],input[type="number"],input[type="date"],input[type="time"],input[type="tel"],input[type="search"],textarea,select{
  width:100%; background:var(--paper); border:1px solid var(--edge);
  border-radius:var(--r); padding:8px 12px; font-size:16px;
  /* 8px of padding around a 16px line came to 42, two pixels under the target
     everything else in the app is held to. A field is something you aim a thumb
     at as much as a button is. */
  min-height:var(--tap-min);
}
textarea{min-height:72px; resize:vertical; line-height:1.45}
input[type="number"]{font-family:var(--f-data)}
/* iOS Safari gives a date input its own intrinsic width and then centres the
   value inside whatever box it is given, so the date floated in the middle of a
   full-width field with a hand's width of empty space to its left. Killing the
   native appearance and left-aligning the inner value box puts it where every
   other field on the page puts its text. min-height keeps it the same height as
   a text input once the native chrome is gone. */
/* time gets the same treatment as date, and for the same reason: both are
   composite fields the browser draws itself, and both come out short and
   left-of-centre without this. */
input[type="date"],input[type="time"]{
  -webkit-appearance:none; appearance:none; text-align:left;
  min-height:44px; font-family:var(--f-data);
}
input[type="date"]::-webkit-date-and-time-value,
input[type="time"]::-webkit-date-and-time-value{ text-align:left; margin:0 }
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator{ margin-left:auto; opacity:.55 }
.chips{display:flex; flex-wrap:wrap; gap:8px}

/* A row of actions that has to wrap. Left to flex-wrap, three destinations at
   320px come out as one wide button and two runts on a second line, which is
   what "buttons should be aligned and not on multiple rows if different sizes"
   was about: the raggedness reads as three different kinds of thing when they
   are three of the same thing. Equal cells instead, and an odd one left over at
   the end takes the whole last row rather than sitting beside a hole.

   It is a modifier and not a change to .chips or .row, because both of those
   also carry tag clouds and label rows where variable width is correct: a
   lesion term is as wide as its word. Only a row of ACTIONS gets this. */
.evens{display:grid; grid-template-columns:1fr 1fr; gap:var(--s2); align-items:stretch}
.evens > *{min-width:0; width:auto}
.evens > :last-child:nth-child(odd){grid-column:1 / -1}
/* Except in a cloud of peers, which never stretches its odd one out.

   The span above is written for a pair of fields with a closing action under
   them, and across the whole app it fires in exactly one place: the three
   "show the vet" destinations in js/24-clinic.js. There it does the opposite of
   what the comment beside that code asks for. That comment says the grid was
   chosen so three equal choices would stop coming out at three different
   widths - and then the span hands the third one a row of its own, 185 wider
   than the two above it, so "Harta leziunilor" reads as the important one and
   Raport and Comparație foto read as its footnotes. They rank the same.

   Same finding as the answer clouds a screen earlier: uniform width was the
   whole point, and a single empty cell aligned to the column above it is a
   calendar's last week, not a hole. .chips is the marker for a set of peers,
   so it is what the exception is keyed to. Numbers unitless in this comment,
   for the spacing gate. */
.chips.evens > :last-child:nth-child(odd){grid-column:auto}

/* An answer cloud, not a vocabulary.

   Nine stool tags came out as nine different widths over four rows with a 99px
   hole at the end of the last one. That is what "sooo cluttered" was pointing
   at, and it measured exactly as bad as it looked: rows=4, widths=9,
   spread=119px. Equal columns fix all three numbers at once.

   Deliberately NOT applied to .chips itself. The 23 lesion terms in the flare
   sheet are a vocabulary you scan, and there a term really is as wide as its
   word; a glossary forced into a grid reads as a form somebody has to fill in.
   The test is what the cloud IS, not how it looks: a set of answers to one
   question gets the grid, a legend does not.

   auto-fit rather than a fixed two, because these clouds do not all get the
   same width to live in. The stool cloud has a whole sheet; the two lymph-node
   clouds on Today sit in half a .row each, about 175 wide, and two columns
   there would be 82 a cell, which does not hold "Boaba de strugure" on one
   line at any weight. auto-fit reads the container it is actually in and
   answers 1, 2 or 3 without anybody hard-coding a breakpoint - which is also
   the answer to the folding phone: the cloud follows the box, not the
   viewport. Written without units in this comment because the spacing gate
   reads a number-plus-unit inside a comment as a declaration on the rule
   below it.

   An odd one left over stays at column width and leaves the last cell empty.
   The first version stretched it across the row instead, on the theory that a
   hole reads as ragged. Re-measuring said otherwise: it put two widths back
   into a cloud built to have one, 186px apart on a 430px screen, and the
   stretched chip read as though that answer mattered more than the other eight.
   A single empty cell, aligned to the column above it, is a calendar's last
   week. Uniform width was the whole point. */
.chips--set,
/* grid-auto-rows makes every row the height of the tallest chip in the
   grid rather than the height of the tallest chip in ITS OWN row. One
   label that wraps, "Uleiuri și grăsimi" or "Iepure și mamifer mic", used
   to make only its own row taller, so scanning down the grid the height
   stepped 44, 49, 44 or 65, 44. Ovidiu, 08.08: "the spacing should be
   from the pill or whatever." It is: the pill sets the row, every row is
   the same, and no Romanian gets shortened or cut to achieve it. */
.chips--set3,
.chips[data-single="1"]{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(136px, 1fr));
  grid-auto-rows:1fr;
  gap:var(--s2); align-items:stretch;
}
.chips--set > *,
.chips--set3 > *,
.chips[data-single="1"] > *{min-width:0; width:auto}

/* Narrower cells for one-word answers. Twelve coat colours at 136 is two
   columns and six rows of half-empty chips in a sheet already at the height
   cap; 96 gives three columns in the same box and hands back two rows, and
   still falls to two on a 320 phone on its own. Leftovers sit left-aligned in
   their own cells, same reasoning as above. */
.chips--set3{grid-template-columns:repeat(auto-fit, minmax(96px, 1fr))}

@container view (max-width:340px){
  /* One column on the smallest phone. Two 140px cells there put two words on
     three lines each. */
  .evens{grid-template-columns:1fr}
  .evens > :last-child:nth-child(odd){grid-column:auto}
}
.chip{
  appearance:none; cursor:pointer; background:var(--card);
  border:1px solid var(--edge); border-radius:var(--r-pill);
  min-height:var(--tap-min); min-width:var(--tap-min);
  padding:var(--s2) var(--s3);
  font-size:12.5px; color:var(--ink-2); line-height:1.25;
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s1);
}
.chip[aria-pressed="true"]{
  background:var(--stain); border-color:var(--stain); color:var(--on-accent); font-weight:600;
}
.chip__dot{
  display:inline-block; width:8px; height:8px; border-radius:var(--r-xs); margin-right:8px;
  vertical-align:baseline; border:1px solid color-mix(in srgb,var(--ink) 30%,transparent);
}
/* the way out of a question, so it never reads as one more thing to pick */
.chip--ghost{border-style:dashed; color:var(--ink-3)}
.chip--sev[aria-pressed="true"]{background:transparent; color:var(--on-accent); font-weight:600}
.chip--sev[data-sev="0"][aria-pressed="true"]{background:var(--sev0); border-color:var(--sev0); color:var(--on-sev0)}
.chip--sev[data-sev="1"][aria-pressed="true"]{background:var(--sev1); border-color:var(--sev1); color:var(--on-sev1)}
.chip--sev[data-sev="2"][aria-pressed="true"]{background:var(--sev2); border-color:var(--sev2); color:var(--on-sev2)}
.chip--sev[data-sev="3"][aria-pressed="true"]{background:var(--sev3); border-color:var(--sev3); color:var(--on-sev3)}
.btn{
  appearance:none; cursor:pointer; border-radius:var(--r-pill);
  border:1px solid var(--stain); background:var(--stain); color:var(--on-accent);
  padding:12px 20px; font-size:.94rem; font-weight:600; line-height:1.2;
}
.btn--ghost{background:transparent; border-color:var(--edge); color:var(--ink-2); font-weight:500}
/* an area with nothing in it yet: present, but not competing for attention */
.test--idle{opacity:.55}
.btn--quiet{background:transparent; color:var(--ink-2); border-color:var(--edge)}
.btn--wide{width:100%}
/* Two full-width buttons appended one after the other into a plain host share
   an edge, because a block host has no gap to give them. The expired-session
   sheet put Sign in directly on top of Keep working offline that way, two
   different decisions welded into what looks like one control with a line
   through it. Fixed here rather than at each of the twenty-odd call sites,
   which is also the only version that covers the next one somebody writes.
   Zeroed again wherever the host does have a gap of its own, so a stack does
   not end up spacing them twice: in flex, a margin adds to the gap. */
.btn--wide + .btn--wide{margin-top:var(--tap-gap)}
.stack > .btn--wide + .btn--wide,
.row > .btn--wide + .btn--wide,
.evens > .btn--wide + .btn--wide,
.chips > .btn--wide + .btn--wide{margin-top:0}
.btn:disabled{opacity:.45; cursor:default}

/* toggle list */
.tlist{display:flex; flex-direction:column; gap:4px}
.tog{
  /* Top, not centre. Half of these rows carry a .tog__n under the label, and
     the note is two or three lines on a 320px screen, so centring put the box
     level with the middle of the explanation instead of level with the thing it
     ticks. On the longest of them the box sat below the label entirely and read
     as belonging to the row underneath. The first line of the label is given the
     box's own height just below, so the two line up by construction rather than
     by a nudge that would need re-measuring every time the type changed. */
  display:flex; align-items:flex-start; gap:var(--s3);
  min-height:var(--tap-min); padding:var(--s2) var(--s1); cursor:pointer;
  border-bottom:1px solid var(--line-soft);
}
.tlist .tog:last-child{border-bottom:0}
/* Every checkbox in the app looks like this one. Two of them did not: the
   reminder slot and the print sheet were a native 20 OS box and a styled
   24 box, one tap apart from the same hub, because only the ones wrapped
   in .tog or carrying .test__box were ever styled. This is the same rule
   as .tog input, applied to the type rather than to the wrapper, so a
   checkbox written tomorrow is right without anybody remembering to say
   so. Both specific rules below still win where they apply. Audited 08.08. */
input[type="checkbox"]{
  appearance:none; -webkit-appearance:none;
  width:24px; height:24px; flex:0 0 auto; cursor:pointer;
  border:1.5px solid var(--edge); border-radius:var(--r-sm); background:var(--paper);
  position:relative; margin:0;
}
input[type="checkbox"]:checked{background:var(--stain); border-color:var(--stain)}
input[type="checkbox"]:checked::after{
  content:""; position:absolute; left:7.5px; top:3.5px; width:6px; height:11px;
  border:solid var(--on-accent); border-width:0 2px 2px 0; transform:rotate(42deg);
}
.tog input{
  appearance:none; width:24px; height:24px; flex:0 0 auto; cursor:pointer;
  border:1.5px solid var(--edge); border-radius:var(--r-sm); background:var(--paper);
  position:relative;
}
.tog input:checked{background:var(--stain); border-color:var(--stain)}
.tog input:checked::after{
  content:""; position:absolute; left:7.5px; top:3.5px; width:6px; height:11px;
  border:solid var(--on-accent); border-width:0 2px 2px 0; transform:rotate(42deg);
}
.tog__t{flex:1 1 auto; min-width:0; font-size:.9rem; display:flex; flex-direction:column; gap:4px}
/* The label's first line is made as tall as the checkbox and its text centred
   inside that, which is what puts the two on the same optical line now that the
   row aligns to the top. Deliberately not extended to .med__t, which has the
   same shape: that row still centres on purpose, because what sits beside it is
   a 32px button for the whole row rather than a box for the first line, and the
   medication row is rebuilt in full later anyway. */
.tog__t > :first-child{display:flex; align-items:center; min-height:24px}
.tog__n{font-size:.72rem; line-height:1.35; color:var(--ink-3)}
.tog--flag input:checked{background:var(--bad); border-color:var(--bad)}

/* ---------- the sign list ----------
   Fifty-odd lines is more than anyone will scroll past every evening, so the
   list is shut. A group opens on a tap, and any group holding something today
   is already open when the page draws: what is wrong with him is never hidden
   behind a tap, only what is not. */
.sgrp{display:flex; flex-direction:column; gap:4px}
.sgrp__h{
  display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  padding:12px 4px; background:none; border:0; border-bottom:1px solid var(--line-soft);
  color:var(--ink); font:inherit; font-size:.88rem; cursor:pointer;
}
.sgrp__c{
  flex:none; color:var(--ink-3);
  transition:transform .15s ease; transform:rotate(0deg);
}
.sgrp__h[aria-expanded="true"] .sgrp__c{transform:rotate(90deg)}
.sgrp__n{margin-left:auto; font-family:var(--f-data); font-size:.74rem;
  padding:1px 8px; border-radius:var(--r-pill); background:var(--stain); color:var(--on-accent)}
.sgrp__n[data-n="0"]{background:none; color:var(--ink-3); padding-right:0}
.sgrp__b{display:none; padding:4px 0 8px}
.sgrp__b.is-open{display:block}

.sgn{display:flex; align-items:flex-start; gap:var(--sp-7); padding:var(--row-y) 4px var(--row-y) 12px;
  border-bottom:1px solid var(--line-soft)}
.sgn:last-child{border-bottom:0}
/* One control, four states, cycling. A checkbox plus a separate severity picker
   is two taps and two decisions for a thing most days is neither; this is one
   tap for "yes" and two more if it is worse than that. */
.sgn__sev{
  flex:0 0 auto; width:32px; height:32px; margin-top:1px; cursor:pointer;
  border:1.5px solid var(--edge); border-radius:var(--r-sm); background:var(--paper);
  color:var(--ink-3); font-family:var(--f-data); font-size:.76rem; line-height:1;
}
.sgn[data-sev="1"] .sgn__sev{background:var(--sev1); border-color:var(--sev1); color:var(--on-sev1)}
.sgn[data-sev="2"] .sgn__sev{background:var(--sev2); border-color:var(--sev2); color:var(--on-sev2)}
.sgn[data-sev="3"] .sgn__sev{background:var(--sev3); border-color:var(--sev3); color:var(--on-sev3)}
.sgn__t{flex:1 1 auto; min-width:0; font-size:.88rem; display:flex; flex-direction:column; gap:4px}
.sgn__n{font-size:.72rem; line-height:1.35; color:var(--ink-3)}
/* how long it has been going on, which is the question the vet asks first and
   the one nobody can answer from memory */
.sgn__since{font-family:var(--f-data); font-size:.72rem; color:var(--stain)}
/* which leg, which eye, which ear. Wraps rather than scrolls: a chip that has to
   be swiped to is a chip nobody knows is there, and ten of them is the worst
   case. */
.sgn__q{display:flex; flex-wrap:wrap; gap:var(--sp-4)}
.sgn__qb{
  padding:4px 8px; font:inherit; font-size:.74rem; line-height:1.3; cursor:pointer;
  border:1px solid var(--edge); border-radius:var(--r-pill); background:var(--paper); color:var(--ink-2);
}
.sgn__qb[aria-pressed="true"]{
  background:var(--stain); border-color:var(--stain); color:var(--on-accent); font-weight:600;
}
.sgn__note{
  width:100%; padding:var(--sp-6) var(--sp-7); font:inherit; font-size:.82rem;
  border:1px solid var(--edge); border-radius:var(--r-sm); background:var(--paper); color:var(--ink);
}
/* ---- the food library ------------------------------------------------------
   A food is a thing, not a sentence, so the control is a list of things and a
   box to find them with. The box searches; it never records. Everything that
   ends up in the record got there by being tapped, which is what makes it
   countable a month later. */
.fd{display:flex; flex-direction:column; gap:var(--field-gap)}
.fd__pick{display:flex; flex-direction:column; gap:var(--field-gap)}
.fd__sel{display:flex; flex-direction:column; gap:4px}
.fd__sel:empty{display:none}
.fd__row{
  display:flex; align-items:center; gap:8px; padding:8px 8px;
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--paper);
}
.fd__nm{flex:1 1 auto; min-width:0; font-size:.86rem; line-height:1.3}
/* the kind and the first-time flag, said quietly beside the name */
.fd__k{display:block; font-size:.68rem; color:var(--ink-3); letter-spacing:.02em}
.fd__1st{color:var(--stain); font-weight:600}
.fd__amt{
  flex:0 0 76px; padding:var(--sp-6) var(--sp-5); font:inherit; font-size:.82rem; text-align:right;
  font-family:var(--f-data); font-variant-numeric:tabular-nums;
  border:1px solid var(--edge); border-radius:var(--r-sm); background:var(--card); color:var(--ink);
}
.fd__u{flex:0 0 auto; font-size:.72rem; color:var(--ink-3); min-width:24px}
.fd__x{flex:0 0 auto; color:var(--ink-3)}
.fd__x:hover{color:var(--stain); border-color:var(--stain)}
.fd__q{
  width:100%; padding:var(--sp-6) var(--sp-7); font:inherit; font-size:.86rem;
  border:1px solid var(--edge); border-radius:var(--r-sm); background:var(--paper); color:var(--ink);
}
/* Wraps rather than scrolls, for the same reason the sign chips do: a
   suggestion you have to swipe to is a suggestion nobody knows is there. */
.fd__sug{display:flex; flex-wrap:wrap; gap:var(--sp-4)}
.fd__sug:empty{display:none}
/* What is tapped to log a food, and the row wraps, so a short target is a row
   of near-misses stacked on top of each other. .chip two hundred lines up is
   the same pill and is already 44; this one is the same control by another
   name. Taller pills make the picker taller, which is the correct trade when
   the alternative is logging the wrong food. */
.fd__c{
  min-height:var(--tap-min); padding:var(--s1) var(--s3);
  font:inherit; font-size:.76rem; line-height:1.3; cursor:pointer;
  display:inline-flex; align-items:center;
  border:1px solid var(--edge); border-radius:var(--r-pill); background:var(--paper); color:var(--ink-2);
}
.fd__c:hover{border-color:var(--stain); color:var(--stain)}
.fd__c[aria-pressed="true"]{
  background:var(--stain); border-color:var(--stain); color:var(--on-accent); font-weight:600;
}
.fd__c b{font-weight:600; color:var(--ink)}
.fd__c i{font-style:normal; font-size:.9em; color:var(--ink-3); margin-left:4px}
/* Every food picker is a list, not a cloud.
   A wrapping cloud is right for the kind and unit pickers, where the labels are
   one or two words and all near the same length. A food name is not: a FoodEx
   term is a short sentence with a code underneath it, and the library's own
   rows run from "pork + rice" to "Carne de vita tocata 90% slaba, gatita".
   Once the widths differ the row packs two short ones side by side and gives a
   long one the line to itself, so the left edge walks down the list. That is
   the thing Ovidiu named: different sizes ending up on different rows and
   nothing lining up. Equal columns are no answer here either, because the only
   way to make these names equal is to cut off the part being read. One per
   row, at every width, and the second line under the first rather than beside
   it. Written for the EU picker, now also the day-log picker and the
   trial-break picker. */
.fd__sug--list{
  flex-direction:column; flex-wrap:nowrap; gap:var(--sp-5);
  /* Capped, and the cap is deliberate against the rule two comments up.

     That rule - "wraps rather than scrolls, because a suggestion you have to
     swipe to is a suggestion nobody knows is there" - was written about a
     sideways swipe in a cloud, where the cut-off is invisible because the row
     simply ends. Down the page it is the opposite: ten entries as a list
     measured 530 tall inside a card of 813, which on a phone is the whole
     screen given to a picker for the occasional extra treat. The cap lands
     between rows so the next one is visibly cut in half, and a half-drawn row
     is the plainest "there is more" a list can say. All ten stay reachable and
     the search box above still reaches the other twenty. */
  max-height:280px; overflow-y:auto; overscroll-behavior:contain;
}
.fd__sug--list .fd__c{
  width:100%; display:flex; flex-direction:column; align-items:flex-start;
  justify-content:center; gap:2px; text-align:left;
  border-radius:var(--r-sm); padding:var(--s2) var(--s3);
}
.fd__sug--list .fd__c s{
  text-decoration:none; font-size:.72rem; line-height:1.25; color:var(--ink-3);
  font-family:var(--f-data);
}
.fd__sug--list .fd__c[aria-pressed="true"] s,
.fd__sug--list .fd__c:hover s{color:inherit}
/* Naming a food is the one moment free typing is unavoidable, so it is also the
   one moment worth asking the two questions that make the name useful later. */
.fd__new{
  display:flex; flex-direction:column; gap:8px; padding:8px;
  border:1px dashed var(--edge); border-radius:var(--r-sm); background:var(--paper);
}
.fd__new[hidden]{display:none}
.fd__lbl{font-size:.7rem; color:var(--ink-3); letter-spacing:.04em; text-transform:uppercase}
.fd__hint{font-size:.72rem; color:var(--ink-3); line-height:1.4}

.sgn--urg .sgn__t > span:first-child::after{
  content:"!"; display:inline-block; margin-left:8px; width:16px; height:16px;
  line-height:14px; text-align:center; border-radius:50%; font-size:.66rem;
  font-family:var(--f-data); background:var(--bad); color:var(--on-bad); vertical-align:1px;
}
/* what is wrong with him right now, above the list rather than inside it */
.sgnow{display:flex; flex-wrap:wrap; gap:8px; margin:0 0 12px}
.sgnow__i{
  display:inline-flex; align-items:baseline; gap:8px; padding:4px 8px;
  border-radius:var(--r-pill); border:1px solid var(--line); font-size:.78rem;
}
.sgnow__i b{font-weight:600}
.sgnow__i span{font-family:var(--f-data); font-size:.72rem; color:var(--ink-3)}
.sgnow__i[data-sev="2"]{border-color:var(--sev2)}
.sgnow__i[data-sev="3"]{border-color:var(--sev3)}

/* ---------- pVAS scale ---------- */
.vas{display:flex; flex-direction:column; gap:8px}
.vas__val{
  font-family:var(--f-data); font-size:30px; line-height:1;
  font-variant-numeric:tabular-nums; color:var(--stain);
}
.vas__anchor{
  font-size:.82rem; color:var(--ink-2); min-height:2.6em;
  border-left:2px solid var(--stain); padding-left:8px;
}

/* ---------- the graded slider ----------
   The numeral is the readout and it is large on purpose: it is the one thing
   read back at a vet's desk two weeks later, and it is what tells you the drag
   landed where you meant. The definition of that number goes underneath in the
   .anc block, which every scale in the app already shares, so the slider does
   not grow a second vocabulary for the same five sentences.

   The track carries the whole severity ramp at once rather than filling up to
   the thumb. A fill would say "more is more", which is true of itch and false
   of stool, and the same control has to be honest about both. Where you are is
   the thumb's job, and the ticks under it are the job of saying which values
   can actually be picked - the halves on the stool scale exist only because
   that row shows them.

   Until it is touched the whole thing is drained: dash instead of a numeral,
   hollow thumb, grey track. A slider with a thumb sitting at the left is
   otherwise indistinguishable from a deliberate zero, and on this record a
   deliberate zero means somebody looked at the dog. */
.sld{display:flex; flex-direction:column; gap:var(--s2)}
.sld__top{display:flex; align-items:baseline; gap:var(--s2); min-height:36px}
.sld__v{
  font-family:var(--f-data); font-size:32px; line-height:1; font-weight:600;
  font-variant-numeric:tabular-nums; color:var(--stain);
}
.sld__u{font-size:.78rem; color:var(--ink-3)}
.sld__v .sld__sep{
  font-style:normal; display:inline-block; text-align:center;
  width:.34em;   /* a decimal point, not a digit: see sldPaint() */
}
.sld[data-set="0"] .sld__v{color:var(--ink-3); font-weight:400}
.sld[data-sev="1"] .sld__v{color:var(--sev1-ink)}
.sld[data-sev="2"] .sld__v{color:var(--sev2-ink)}
.sld[data-sev="3"] .sld__v{color:var(--sev3-ink)}

.sld__tr{display:flex; flex-direction:column}
.sld__i{
  -webkit-appearance:none; appearance:none; width:100%; height:var(--tap-min);
  background:transparent; cursor:pointer; margin:0; padding:0;
}
.sld__i:focus-visible{outline:2px solid var(--stain); outline-offset:4px; border-radius:var(--r-pill)}
.sld__i::-webkit-slider-runnable-track{
  height:8px; border-radius:var(--r-pill);
  background:linear-gradient(90deg,var(--sev0),var(--sev1) 45%,var(--sev2) 72%,var(--sev3));
}
.sld__i::-moz-range-track{
  height:8px; border-radius:var(--r-pill);
  background:linear-gradient(90deg,var(--sev0),var(--sev1) 45%,var(--sev2) 72%,var(--sev3));
}
/* Reversed for any scale where the high end is the good end. The stool scale
   is the only one today and it is the reason the modifier exists at all. */
.sld--inv .sld__i::-webkit-slider-runnable-track{
  background:linear-gradient(90deg,var(--sev3),var(--sev2) 28%,var(--sev1) 55%,var(--sev0));
}
.sld--inv .sld__i::-moz-range-track{
  background:linear-gradient(90deg,var(--sev3),var(--sev2) 28%,var(--sev1) 55%,var(--sev0));
}
.sld[data-set="0"] .sld__i::-webkit-slider-runnable-track{background:var(--line)}
.sld[data-set="0"] .sld__i::-moz-range-track{background:var(--line)}

/* 28px of ink inside a 44px target: the thumb is drawn small enough to see the
   track under it and grabbed across the full row height, which is the whole
   reason the input is --tap-min tall and the track is only 8. */
.sld__i::-webkit-slider-thumb{
  -webkit-appearance:none; width:var(--sld-thumb); height:var(--sld-thumb); margin-top:-12px;
  border-radius:var(--r-pill); background:var(--card);
  border:3px solid var(--stain); box-shadow:var(--shadow);
}
.sld__i::-moz-range-thumb{
  width:var(--sld-thumb); height:var(--sld-thumb); border-radius:var(--r-pill); background:var(--card);
  border:3px solid var(--stain); box-shadow:var(--shadow);
}
.sld[data-set="0"] .sld__i::-webkit-slider-thumb{border-color:var(--line-strong)}
.sld[data-set="0"] .sld__i::-moz-range-thumb{border-color:var(--line-strong)}

.sld__ticks{
  display:flex; justify-content:space-between; align-items:flex-start;
  padding:0 calc(var(--sld-thumb) / 2); margin-top:-4px; pointer-events:none;
}
.sld__ticks i{
  display:block; width:2px; height:8px; border-radius:var(--r-pill);
  background:var(--line);
}
.sld__ticks i[data-on="2"]{background:var(--line-strong)}
.sld__ticks i[data-on="1"]{background:var(--stain); height:12px; width:4px}
/* Where the last score landed, on the track it landed on. Neutral ink and a
   hollow cap so it reads as a memory rather than as a second answer competing
   with the one being given; the live tick is the accent colour and wider. */
.sld__ticks i[data-ghost="1"]{
  background:var(--ink-3); height:16px; width:2px; opacity:.7;
}

/* ---------- the itch scale ----------
   Eleven targets across the card. They are equal width because the grades are
   equal: the scale is ordinal and nothing about 7 deserves more room than 3.
   The severity band sits under the number rather than behind it, so a target
   still reads as a number first and a grade second, and the chosen one drops
   the band because it has the accent instead. */
/* Eleven targets in one row across a phone is 27px each, which is not a
   target, it is a coin toss. Six and five over two rows is 49px each, which
   is a target, and 0-5 over 6-10 is a break the scale already has. */
.scale{display:grid; grid-template-columns:repeat(6,1fr); gap:var(--s2); margin:var(--s1) 0}
.scale__b{
  appearance:none; cursor:pointer; padding:0;
  min-height:var(--tap); min-width:var(--tap-min);
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink-2);
  font:500 13px/1 var(--f-data); font-variant-numeric:tabular-nums;
}
.scale__b[data-sev="0"]{box-shadow:inset 0 -3px 0 var(--sev0)}
.scale__b[data-sev="1"]{box-shadow:inset 0 -3px 0 var(--sev1)}
.scale__b[data-sev="2"]{box-shadow:inset 0 -3px 0 var(--sev2)}
.scale__b[data-sev="3"]{box-shadow:inset 0 -3px 0 var(--sev3)}
.scale__b:hover{border-color:var(--line-strong); color:var(--ink)}
.scale__b:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.scale__b[aria-checked="true"]{
  background:var(--stain); border-color:var(--stain); color:var(--on-accent);
  font-weight:600; box-shadow:none;
}
/* 320px phones. Six columns would be 37px, so drop to five and take the
   third row. The number is the only thing in the target, so it does not
   mind being narrower; the finger does. */
@container view (max-width:340px){
  .scale{grid-template-columns:repeat(5,1fr); gap:var(--s1)}
  .scale__b{font-size:12px; min-height:var(--tap-min); min-width:0}
}

/* ---------- single-variable rail ---------- */
.sv{border-radius:var(--r-lg); border:1px solid var(--line-soft); overflow:hidden}
.sv__hd{
  display:flex; align-items:center; gap:8px; padding:12px 12px;
  background:var(--stain-wash); border-bottom:1px solid var(--line-soft);
}
.sv__dot{width:8px; height:8px; border-radius:50%; background:var(--ok); flex:0 0 auto}
.sv__t{font-size:.84rem; font-weight:600}
.sv__body{padding:12px; display:flex; flex-direction:column; gap:8px; background:var(--card)}
.sv[data-state="dirty"] .sv__hd{background:color-mix(in srgb,var(--bad) 12%,transparent)}
.sv[data-state="dirty"] .sv__dot{background:var(--bad)}
.sv[data-state="one"] .sv__hd{background:color-mix(in srgb,var(--warn) 14%,transparent)}
.sv[data-state="one"] .sv__dot{background:var(--warn)}
.chg{display:flex; align-items:flex-start; gap:8px}
.chg input{flex:1 1 auto}
.chg__x{
  appearance:none; background:none; border:1px solid var(--edge); color:var(--ink-3);
  width:32px; height:32px; flex:0 0 auto; border-radius:var(--r); cursor:pointer; font-size:1rem;
}

/* ---------- metric strip ---------- */
/* Flex and not grid. Four tiles in a three-track grid left the fourth one alone
   with two dead tracks beside it, and a laid-out cell with nothing in it reads
   as a tile that failed to draw rather than as the end of a list. Wrapping flex
   items grow into whatever is left of their row, so a partial last row is filled
   by the tiles that are in it and no empty cell exists to be seen. The basis is
   the old track minimum, so a full row is laid out exactly as before. */
.strip{
  display:flex; flex-wrap:wrap;
  gap:var(--sp-8); background:none; border:0;
}
.met{
  /* The floor is the longest word in the tile, not a typed 96. At 96 the
     photographed tile was handed 74px of content at exactly the widths where
     three tiles fit a row, 390 and 520, and the label came out as PHOTOGRAPH
     over ED: a word broken at no syllable at all, which reads as a rendering
     fault rather than as a wrapped label. min-content is that longest word
     measured in the font actually rendering it, in whichever language is on,
     so the tile now takes the width it needs and the row wraps one tile
     earlier instead. The basis stays 96 so a row of short tiles lays out
     exactly as before. */
  flex:1 1 96px; min-width:min-content;
  background:var(--card); border:1px solid var(--line-soft); border-radius:var(--r);
  box-shadow:var(--shadow); padding:var(--sp-7) var(--sp-8);
  display:flex; flex-direction:column; gap:4px;
}
/* Labels wrap and every tile keeps the same baseline for its number. break-word
   and not anywhere: anywhere reports a min-content of one character, which is
   what let the tile shrink under its own label in the first place. */
.met__k{font-size:10.5px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-3); overflow-wrap:break-word; min-height:2.1em}
.met{justify-content:flex-start}
.met__v{font-family:var(--f-data); font-size:1.28rem; font-variant-numeric:tabular-nums; line-height:1.15}
.met__n{font-size:.66rem; color:var(--ink-3)}
.met--sev0 .met__v{color:var(--sev0-ink)} .met--sev1 .met__v{color:var(--sev1-ink)}
.met--sev2 .met__v{color:var(--sev2-ink)} .met--sev3 .met__v{color:var(--sev3-ink)}

/* ---------- the record so far ---------- */
.rtab{width:100%; border-collapse:collapse; font-size:.8rem}
.rtab th,.rtab td{padding:8px 8px; text-align:left; vertical-align:top; border-bottom:1px solid var(--line)}
.rtab th{font-size:.7rem; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3); font-weight:600}
.rtab td.num{font-variant-numeric:tabular-nums; white-space:nowrap}
.rwrap{overflow-x:auto; -webkit-overflow-scrolling:touch}
.rev{display:flex; gap:var(--sp-7); padding:var(--row-y) 0; border-bottom:1px solid var(--line)}
.rev:last-child{border-bottom:0}
.rev__d{flex:0 0 78px; font-variant-numeric:tabular-nums; font-size:.76rem; color:var(--ink-3); padding-top:1px}
.rev__t{font-size:.86rem; line-height:1.45}
.rev[data-w="1"] .rev__t{font-weight:600}
.rpill{display:inline-block; padding:1px 8px; border-radius:var(--r-pill); font-size:.7rem;
  border:1px solid var(--line); color:var(--ink-2); white-space:nowrap}
.rpill[data-v="good"]{border-color:var(--sev0); color:var(--sev0-ink)}
.rpill[data-v="bad"]{border-color:var(--sev3); color:var(--sev3-ink)}
.rpill[data-v="mixed"]{border-color:var(--sev2); color:var(--sev2-ink)}
.rpill[data-v="open"]{border-style:dashed}
.rdir{font-variant-numeric:tabular-nums; white-space:nowrap}
.rdir[data-d="hi"]{color:var(--sev3-ink)}
.rdir[data-d="lo"]{color:var(--sev2-ink)}

/* ---------- photo save state ---------- */
.psay{
  position:fixed; left:50%; transform:translateX(-50%);
  bottom:calc(var(--nav-h, 64px) + env(safe-area-inset-bottom, 0px) + 12px);
  z-index:150; max-width:min(100% - var(--s6), 460px);
  padding:8px 12px; border-radius:var(--r-lg);
  font-size:.82rem; line-height:1.35; text-align:center;
  border:1px solid var(--line); background:var(--card); color:var(--ink);
  box-shadow:0 6px 22px rgba(0,0,0,.18);
  opacity:0; pointer-events:none; transition:opacity .18s ease;
}
.psay:not([data-kind=""]){opacity:1}
/* While a sheet is up the band's bottom anchor lands exactly on the sheet's
   own photo controls - the 08.08 screenshot has it sitting on "Poză exact
   acestei zone". The sheet panel rises from the bottom and rarely reaches the
   top 14% of the screen, so that is where the band goes while one is open.
   Written by photoSay(), which is the only writer of this attribute. */
.psay[data-at="top"]{
  bottom:auto; top:calc(var(--tb-h, 64px) + env(safe-area-inset-top, 0px) + 8px);
}
.psay[data-kind="work"]{color:var(--ink-2)}
.psay[data-kind="ok"]{border-color:var(--sev1); color:var(--ink)}
.psay[data-kind="queued"]{border-color:var(--sev2)}
.psay[data-kind="bad"]{border-color:var(--sev3); color:var(--sev3-ink)}
@media (prefers-reduced-motion: reduce){ .psay{transition:none} }

/* The same band, carrying an upload instead of a sentence. Deck page 07, S2.
   A photograph of a paw is between one and four megabytes on a phone camera
   and on a bad connection it takes long enough that a spinner is not an
   answer: the thumbnail says which photo, the numbers say how far it has got,
   and the track says whether it is moving at all. */
.psay--up{
  /* Sized rather than shrink-wrapped. Left to itself the flex box takes the
     width of its longest unbreakable word, which turned a two-line card into a
     narrow column of wrapped monospace. */
  width:min(100% - var(--s6), 460px);
  text-align:left; display:flex; gap:12px; align-items:flex-start; padding:12px 12px;
}
/* Deck 10 F2. One line of the bowl: its place in the order, its name, and the
   weight if anybody put one on it. */
.bwl{display:flex; align-items:center; gap:var(--sp-6)}
.bwl__n{flex:1 1 auto; min-width:0; font-size:.86rem; line-height:1.3}
.bwl__n s{display:block; text-decoration:none; font-size:.68rem; color:var(--ink-3)}
.bwl__a{
  flex:0 0 58px; width:56px; text-align:right; padding:4px 8px;
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--paper);
  font:400 .82rem var(--f-data); color:var(--ink);
}
.bwl__u{flex:0 0 auto; font-size:.72rem; color:var(--ink-3); min-width:24px}

/* Deck 07 S2's card. The thumbnail is 44 rather than the 62 the log uses:
   this is a receipt for a photograph, not the photograph. */
.uplr{display:flex; align-items:center; gap:var(--sp-6)}
.uplr__th{width:44px; height:44px; border-radius:var(--r-sm); object-fit:cover;
  flex:none; background:var(--line-soft)}
.uplr__b{min-width:0; flex:1 1 auto; display:flex; flex-direction:column; gap:4px}
.uplr__b b{font-weight:600}
.uplr__b s{text-decoration:none; color:var(--ink-2); font-family:var(--f-data);
  font-size:.78rem}

.psay__th{
  width:40px; height:40px; flex:none; border-radius:var(--r);
  object-fit:cover; background:var(--line-soft);
}
.psay__b{min-width:0; flex:1 1 auto}
.psay__t{
  font-weight:600; font-size:.82rem; color:var(--ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.psay__n{font:400 .69rem/1.45 var(--f-data); color:var(--ink-3)}
/* Numbers get the data face. The sentence underneath is a sentence. */
.psay__h{font:400 .69rem/1.45 var(--f-body); color:var(--ink-3); margin-top:4px}

/* ---------- sign in ---------- */
.gate{
  position:fixed; inset:0; z-index:200; display:none;
  flex-direction:column; align-items:center; justify-content:center; padding:20px;
  background:var(--paper); overflow-y:auto;
}
/* Deck 12, I1. Centred under the card and quiet: it is the last thing on the
   screen because it is the thing somebody reaches for only when the rest of
   the screen is in the wrong language. */
.gate__foot{display:flex; align-items:center; gap:8px; margin-top:16px; font-size:12.5px}
.gate__foot button{
  background:none; border:0; padding:4px 4px; font:inherit; cursor:pointer;
  color:var(--ink-3); border-radius:var(--r);
}
.gate__foot button[aria-pressed="true"]{color:var(--ink); font-weight:500}
.gate__foot button:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.gate__foot span{color:var(--line-strong)}
.gate[data-open="1"]{display:flex}
.gate__card{
  width:100%; max-width:390px; display:flex; flex-direction:column; gap:16px;
  background:var(--card); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:24px 20px 20px;
}
.gate__brand{display:flex; flex-direction:column; gap:4px}
.gate__lead{color:var(--ink-2); font-size:.86rem; margin:0}
.gate__err{color:var(--sev3-ink); font-size:.82rem; margin:0; min-height:1.2em}
.btn--go{background:var(--stain); color:var(--on-accent); border-color:var(--stain); justify-content:center}
.btn--go:hover{filter:brightness(1.08)}
.btn--go[disabled]{opacity:.55; cursor:default}

/* ---------- body map ---------- */
.mapbar{display:flex; gap:var(--sp-7); flex-wrap:wrap; align-items:center}
/* Six body views will not fit as six loose buttons on a phone, so they are one
   scrollable segmented control that always shows where you are. */
/* The clinic hub.
   Seven cards in one column meant scrolling past six to reach the one you
   wanted. A row is a destination now, and it carries its own state on the
   right, so the tab answers "what needs me" before it is opened. Rows are
   buttons, not links, because there is no page to fall back to. */
.hub{display:flex; flex-direction:column; gap:8px}
.hub__b{
  display:flex; align-items:center; gap:12px; width:100%; text-align:left;
  padding:12px 16px; border-radius:var(--r); border:1px solid var(--edge);
  background:var(--card); color:var(--ink); font:inherit; cursor:pointer;
  box-shadow:var(--shadow);
}
.hub__b:hover{border-color:var(--stain-soft)}
.hub__b:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.hub__ic{
  flex:0 0 34px; height:32px; display:grid; place-items:center; border-radius:var(--r-sm);
  background:var(--stain-wash); color:var(--stain);
}
.hub__ic svg{width:20px; height:20px}
.hub__tx{min-width:0; display:flex; flex-direction:column; gap:4px}
.hub__t{font-weight:600; line-height:1.25}
.hub__s{font-size:12.5px; color:var(--ink-3); line-height:1.3}
.hub__r{margin-left:auto; display:flex; align-items:center; gap:8px; flex:0 0 auto}
/* the state, in a word. Colour is the same vocabulary as everywhere else:
   green settled, amber wants attention soon, red overdue. */
.hub__p{
  font-size:11.5px; font-weight:600; padding:4px 8px; border-radius:var(--r-pill);
  border:1px solid var(--line); color:var(--ink-2); white-space:nowrap;
}
.hub__p[data-s="ok"]{color:var(--ok-ink); border-color:color-mix(in srgb,var(--ok) 45%,transparent)}
.hub__p[data-s="warn"]{color:var(--warn-ink); border-color:color-mix(in srgb,var(--warn) 50%,transparent)}
.hub__p[data-s="bad"]{color:var(--bad-ink); border-color:color-mix(in srgb,var(--bad) 50%,transparent)}
.hub__c{width:16px; height:16px; color:var(--ink-3);
  stroke-linecap:round; stroke-linejoin:round}

/* the booklet: one row per stamp, per document, per trip */
.bk{border:1px solid var(--line); border-radius:var(--r); background:var(--card); padding:12px 12px;
  display:flex; flex-direction:column; gap:4px}
.bk__hd{display:flex; align-items:baseline; gap:8px; flex-wrap:wrap}
.bk__t{font-weight:600}
.bk__d{font-family:var(--f-data); font-size:12.5px; color:var(--ink-2); margin-left:auto}
.bk__m{font-size:12.5px; color:var(--ink-3); line-height:1.35}
.bk__m b{font-weight:600; color:var(--ink-2)}
.bk--warn{border-color:color-mix(in srgb,var(--warn) 55%,transparent)}
.bk--bad{border-color:color-mix(in srgb,var(--bad) 55%,transparent)}
/* the one warning this page has to carry: a live vaccine while he is on
   Zenrelia is a question for the vet, and the label says so */
.bk__flag{
  font-size:12px; line-height:1.35; padding:8px 12px; border-radius:var(--r-sm);
  background:color-mix(in srgb,var(--warn) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 40%,transparent); color:var(--ink-2);
}
.segs{
  display:flex; gap:4px; padding:var(--sp-2); border:0; border-radius:var(--r-pill);
  background:var(--line-soft); overflow-x:auto; scrollbar-width:none; max-width:100%;
}
.segs::-webkit-scrollbar{display:none}
/* Seven record sections in Romanian run past the edge of a phone, and a hidden
   scrollbar gives no hint that the last one exists. Wrapping was the first
   answer and it was worse: .segs carries a 999px radius and a filled background,
   so a second row stretched the container into a two-storey lozenge with seven
   words floating loose inside it and nothing lining up with anything.
   A rail that has to wrap is not a rail. It is a grid, so it is drawn as one:
   the container gives up its chrome, each section becomes its own chip, and the
   chips sit in equal columns - three on a phone, four from 430 up - so the two
   rows line up by construction instead of by luck. */
.segs--wrap{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(96px, 1fr));
  gap:var(--s2); padding:0; background:none; border-radius:0; overflow-x:visible;
}
/* The 1fr above was decorative from the day it was written, and this is the
   line that makes it mean something.

   Every one of these rails is a flex item, and a flex item defaults to
   flex-grow 0, so the grid was resolving against its own fit-content width.
   Fit-content of repeat(auto-fit, minmax(96, 1fr)) is every track sitting on
   its 96 floor - there is never any free space for the fr to distribute, so
   the rule was really repeat(auto-fit, 96px). Measured: the span bar came out
   304 inside a 396 row at a 430 phone and 200 inside a 294 row at a 320, which
   is ninety-odd of empty air held open to the right of a control that was
   simultaneously wrapping onto a second row for want of width. Six body views
   did it worse - three rows of chips at 320 with a quarter of the bar unused.

   min-width so the grid may shrink below its min-content instead of pushing
   its neighbours; the tracks are already free to reflow. */
.segs--wrap{flex:1 1 auto; min-width:0}
/* Except where a spacer is already the thing absorbing the slack.

   .row:has(> .grow) is nowrap, so growing the rail there takes the width
   straight off the button beside it - the trend card's rail sits next to
   "Fit all" and that button is not negotiable. .mapbar is deliberately not
   covered by this: it wraps, its own buttons have already dropped to their own
   line, and its spacer was holding open ninety-two of nothing. */
.row:has(> .grow) > .segs--wrap{flex:0 1 auto}
/* And the map bar takes the whole first line rather than splitting it.

   Growing alone was not enough here: .mapbar also carries a .grow, and a
   spacer that grows competes for the same slack, so the six views moved from
   304 to 314 inside a 396 bar and the eighty-odd that was doing nothing before
   went on doing nothing. The spacer is there to push pen, undo and clear to
   the right, and those three have been wrapping onto their own line at every
   phone width measured, where there is no spacer to push them anywhere. So
   the split is made explicit: views own line one, tools own line two. */
.mapbar > .segs--wrap{flex:1 1 100%}
.segs--wrap .segs__b{
  min-width:0; border:1px solid var(--line); background:var(--paper);
}
.segs--wrap .segs__b[aria-selected="true"]{
  background:var(--card); border-color:var(--stain); color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--stain);
}
/* The grid answer does not survive seven items.

   --wrap is right where the whole set fits in one or two rows and you are
   picking between them: three spans, six map views, five builds. It is wrong
   for the record index, where seven sections measured three rows on a 430,
   four on a 360 and four on a 320, so a read-only contents list was taking
   four rows of the screen above the thing it is a contents list for. Nor can
   more columns rescue it - the longest label wants about 113 with its padding
   and a quarter of a 320 phone is 59, so narrowing the track only clips the
   word, silently, because .segs__b is nowrap.

   One row and a scroll, then, with the discoverability objection answered
   instead of waved at: the trailing edge fades while there is more to reach,
   the leading edge fades once you have moved off the start, and js/01-core.js
   drops both flags at the ends so a fade never claims a section that is not
   there. Same device as the food list cap - the chip under the fade is cut in
   half, and a half-drawn thing is the plainest "there is more" a row can say.
   The numbers in this comment carry no unit, because the spacing gate reads a
   number-plus-unit inside a comment as a declaration on the rule below it. */
.segs--rail{
  flex-wrap:nowrap; gap:var(--s2); padding:0; border-radius:0;
  overscroll-behavior-x:contain; scroll-behavior:smooth;
  /* Seven sub-pages on the record screen and only four and a bit fit, and the
     only way to find out was to scroll, which nobody does to a thing that looks
     complete. Same four-layer scroll shadow as .seg: the two `local` covers
     travel with the content and hide the shadow at whichever end the rail is
     against, the two `scroll` shadows stay put, and on a rail that fits both
     covers sit over both shadows so nothing shows. The covers are --card
     because these rails sit on cards. Audited 08.08. */
  background:
    linear-gradient(to right, var(--card), transparent) 0 0 / 20px 100% no-repeat local,
    linear-gradient(to left,  var(--card), transparent) 100% 0 / 20px 100% no-repeat local,
    linear-gradient(to right, var(--line), transparent) 0 0 / 12px 100% no-repeat scroll,
    linear-gradient(to left,  var(--line), transparent) 100% 0 / 12px 100% no-repeat scroll;
}
/* Natural width here, and deliberately, against the equal-columns rule the
   answer clouds got a screen earlier. That rule exists because ragged widths in
   a WRAPPED cloud stop the rows lining up with each other. A single row has no
   row under it to line up with, and padding seven chips out to the widest would
   only push more of them off the edge for nothing. */
.segs--rail .segs__b{
  flex:0 0 auto; border:1px solid var(--line); background:var(--paper);
}
.segs--rail .segs__b[aria-selected="true"]{
  background:var(--card); border-color:var(--stain); color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--stain);
}
/* Both edges are described in one gradient so the two states cannot fight over
   the same property. The stops are a fixed 32 inset via calc, so the fade is
   the same width whatever the rail is.

   currentColor rather than the black these are normally written with, because
   a mask built from a CSS gradient is read on its alpha channel and the colour
   of an opaque stop is therefore irrelevant - while a literal hex in the
   stylesheet is not irrelevant to the contrast gate, which counted eight of
   them and printed eight FAIL lines under a passing run. A gate whose output
   contains failures that are not failures stops being read. */
.segs--rail[data-more="1"]{
  -webkit-mask-image:linear-gradient(to right, currentColor calc(100% - 32px), transparent 100%);
  mask-image:linear-gradient(to right, currentColor calc(100% - 32px), transparent 100%);
}
.segs--rail[data-back="1"]{
  -webkit-mask-image:linear-gradient(to right, transparent 0, currentColor 32px);
  mask-image:linear-gradient(to right, transparent 0, currentColor 32px);
}
.segs--rail[data-back="1"][data-more="1"]{
  -webkit-mask-image:linear-gradient(to right, transparent 0, currentColor 32px, currentColor calc(100% - 32px), transparent 100%);
  mask-image:linear-gradient(to right, transparent 0, currentColor 32px, currentColor calc(100% - 32px), transparent 100%);
}
/* No breakpoint switches the fade off. The first draft had one at 900, on the
   assumption that seven chips fit a tablet outright - they do not, because the
   column is capped at 680 however wide the glass is, and seven of these plus
   their gaps is about 850. The flags are computed from scrollWidth against
   clientWidth, which is the real question, and a media query guessing at the
   same thing can only ever disagree with it. */
/* Six of these across the body map choose which side of the dog you are
   marking, and they sit shoulder to shoulder. Height was 32. */
.segs__b{
  flex:0 0 auto; border:0; background:transparent; color:var(--ink-2); cursor:pointer;
  font:500 .78rem/1 var(--f-body); padding:var(--sp-4) var(--sp-9); border-radius:var(--r-pill);
  white-space:nowrap; min-height:var(--tap-min); min-width:var(--tap-min);
  display:inline-flex; align-items:center; justify-content:center; gap:4px;
}
.segs__b:hover{color:var(--ink)}
.segs__b[aria-selected="true"]{
  background:var(--card); color:var(--ink); font-weight:600;
  box-shadow:0 1px 2px rgba(0,0,0,.1);
}
.segs__b .segs__n{
  display:inline-block; min-width:16px; margin-left:4px; padding:1px 4px; border-radius:var(--r-lg);
  font:700 .62rem/1.35 var(--f-data); background:var(--eosin); color:var(--on-accent);
}
.segs__b[aria-selected="true"] .segs__n{background:var(--stain); color:var(--on-accent)}
.mapstage{
  position:relative; background:var(--card); border:1px solid var(--line-soft);
  border-radius:var(--r-lg); overflow:hidden; touch-action:none;
}
.mapstage svg{display:block; width:100%; height:auto}
.mapstage canvas{
  position:absolute; inset:0; width:100%; height:100%; touch-action:none;
}
.mapstage[data-pen="0"] canvas{pointer-events:none}
.silh{fill:var(--stain-wash); stroke:var(--ink-3); stroke-width:.9; stroke-linejoin:round}
.silh--soft{fill:none; stroke:var(--ink-3); stroke-width:.7; opacity:.55}
/* far-side limbs: filled, but sitting behind the near ones */
.silh--far{fill:var(--paper); stroke:var(--ink-3); stroke-width:.7; opacity:.7; stroke-linejoin:round}
/* eye, nose, folds: drawn so the outline reads as this breed and not a generic dog */
.silh--det{fill:none; stroke:var(--ink-3); stroke-width:.8; opacity:.75; stroke-linecap:round}
.silh--dot{fill:var(--ink-3); opacity:.75; stroke:none}
/* Oscar's own reference drawing, black line art on transparency.
   It used to be inverted in the dark themes, which turned his own drawing into a
   photographic negative and, worse, inverted the severity fills with it: a green
   site read magenta. The plate is now a light card inside whatever theme is
   running, --plate and --plate-line are identical in every theme, and everything
   around it still themes normally. */
.art{opacity:.9}
.mapstage{background:var(--plate)}
.plate{background:var(--plate); border:1px solid var(--plate-line);
  border-radius:var(--r); padding:8px}
.plate svg{display:block; width:100%; height:auto}

/* Which of HIS sides the viewer is looking at. Printed on the plate because a
   lesion recorded on the wrong leg is worse than one not recorded at all. */
.sidemark{
  font-family:var(--f-data); font-size:calc(8px * var(--k,1)); font-weight:700;
  fill:var(--eosin); letter-spacing:.08em; pointer-events:none;
}

.zn{
  fill:transparent; stroke:var(--ink-3); stroke-width:calc(.9px * var(--k,1)); stroke-dasharray:calc(3px * var(--k,1)) calc(3px * var(--k,1));
  cursor:pointer; opacity:.75;
}
/* The invisible half of the tap target. One SVG shape has one stroke, so the
   widening cannot live on .zn itself without replacing the dashed outline that
   makes the zone visible. It is a second, copied shape underneath, carrying
   nothing but a fat transparent stroke, inserted and sized by znFit() in
   js/06-map.js. Its width is the widest THIS zone can grow before it meets its
   nearest neighbour, so no two zones ever fight over one tap. */
.zn__hit{
  fill:none; stroke:transparent; pointer-events:all; cursor:pointer;
}
.zn:hover{stroke:var(--stain); stroke-width:calc(1.6px * var(--k,1)); opacity:1}
.zn:focus-visible{stroke:var(--stain); stroke-width:calc(2px * var(--k,1)); opacity:1; outline:none}
.zn[data-sev="0"]{fill:color-mix(in srgb,var(--sev0) 42%,transparent); stroke:var(--sev0); stroke-dasharray:none; opacity:1}
.zn[data-sev="1"]{fill:color-mix(in srgb,var(--sev1) 48%,transparent); stroke:var(--sev1); stroke-dasharray:none; opacity:1}
.zn[data-sev="2"]{fill:color-mix(in srgb,var(--sev2) 55%,transparent); stroke:var(--sev2); stroke-dasharray:none; opacity:1}
.zn[data-sev="3"]{fill:color-mix(in srgb,var(--sev3) 62%,transparent); stroke:var(--sev3); stroke-width:calc(1.8px * var(--k,1)); stroke-dasharray:none; opacity:1}
.znlab{
  font-family:var(--f-data); font-size:calc(6px * var(--k,1)); fill:var(--ink-2);
  text-anchor:middle; pointer-events:none; letter-spacing:.03em;
}
.zncode{
  font-family:var(--f-data); font-size:calc(7.5px * var(--k,1)); font-weight:700; fill:var(--ink);
  text-anchor:middle; pointer-events:none;
}
.viewlab{
  font-family:var(--f-body); font-size:calc(7px * var(--k,1)); fill:var(--ink-3);
  text-transform:uppercase; letter-spacing:.14em;
}
.legend{display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:4px 12px}
.legend__i{display:flex; align-items:center; gap:8px; font-size:.74rem; color:var(--ink-2)}
.legend__sw{width:12px; height:12px; border-radius:var(--r-xs); flex:0 0 auto; border:1px solid var(--edge)}

/* lesion dictionary: the clinic's legend, their words and their colours */
/* the legend header carries its own scope switch, so "in use today" is a
   statement about this date rather than a permanent setting */
.glosshd{display:flex; align-items:center; justify-content:space-between; gap:12px; padding-bottom:4px}
.glosshd .lbl{margin-top:0}
/* the site code sits at the end of the name line, so the marked list reads
   against the vet's own map key without a second lookup */
.test__t--c{display:flex; align-items:baseline; gap:8px}
.test__c{
  margin-left:auto; flex:none; font-family:var(--f-data); font-size:.66rem;
  font-weight:700; letter-spacing:.06em; color:var(--ink-3);
}
.gloss{display:flex; gap:var(--sp-7); padding:var(--row-y) 0; border-bottom:1px solid var(--line-soft)}
.gloss:last-child{border-bottom:0}
.gloss__sw{
  width:16px; height:16px; border-radius:var(--r-sm); flex:0 0 auto; margin-top:4px;
  border:1px solid color-mix(in srgb,var(--ink) 30%,transparent);
}
.gloss__t{
  font-size:.9rem; font-weight:600; line-height:1.3;
  display:flex; flex-wrap:wrap; align-items:baseline; gap:8px;
}
/* the other language, because the report he is decoding is written in Romanian */
.gloss__alt{font-size:.76rem; font-weight:400; color:var(--ink-3); font-style:italic}
.gloss__k{
  font-family:var(--f-data); font-size:.66rem; font-weight:700; letter-spacing:.06em;
  color:var(--ink-2); background:var(--line-soft); border-radius:var(--r-sm); padding:1px 4px;
}
.gloss__d{font-size:.81rem; color:var(--ink-2); margin-top:4px; line-height:1.45}
.gloss__w{font-size:.78rem; color:var(--ink-3); margin-top:4px; line-height:1.45; border-left:2px solid var(--line); padding-left:8px}

/* the same dictionary entry, inline in the zone sheet, for the term just picked.
   Tinted rather than bordered so it reads as an answer to the tap, not as another
   thing to fill in. */
.tgloss{
  margin-top:8px; padding:8px 12px; border-radius:var(--r-sm);
  background:var(--line-soft); border:1px solid var(--line-soft);
}
.tgloss[hidden]{display:none}
.tgloss__t{
  font-size:.85rem; font-weight:600; line-height:1.3;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px;
}
.tgloss__alt{font-size:.74rem; font-weight:400; color:var(--ink-3); font-style:italic}
.tgloss__d{font-size:.81rem; color:var(--ink-2); margin-top:4px; line-height:1.45}
.tgloss__w{font-size:.78rem; color:var(--ink-3); margin-top:4px; line-height:1.45; border-left:2px solid var(--line); padding-left:8px}

/* which site did that circle mean */
.zpick{
  margin-top:8px; padding:8px 12px; border-radius:var(--r-sm);
  background:var(--stain-wash); border:1px solid var(--stain);
}
.zpick[hidden]{display:none}
.zpick__t{display:block; font-size:.8rem; color:var(--ink-2); margin-bottom:8px; line-height:1.4}
.zpick__r{display:flex; flex-wrap:wrap; gap:8px}

/* reminder slots. A switched-off slot stays visible but recedes, so the second
   one reads as available rather than as clutter. */
/* the medication log. Three states, so a switch would be a lie: the button
   carries the state and the row tints to match. */
/* the month grid */
.cal{display:grid; grid-template-columns:repeat(7,1fr); gap:4px}
.cal__h{
  text-align:center; font-size:11px; color:var(--ink-3);
  font-family:var(--f-body); padding-bottom:4px;
}
.cal__d{
  position:relative; aspect-ratio:1; border-radius:var(--r-sm);
  border:1px solid var(--line-soft); background:var(--paper);
  color:var(--ink-2); font-size:12px; font-family:var(--f-body);
  display:flex; align-items:center; justify-content:center; cursor:pointer;
}
.cal__d--pad{visibility:hidden}
.cal__d--fut{opacity:.35; cursor:default}
.cal__d--today{outline:2px solid var(--stain); outline-offset:-2px}
.cal__d[data-i="0"]{background:color-mix(in srgb, var(--ok) 16%, var(--card))}
.cal__d[data-i="1"]{background:color-mix(in srgb, var(--warn) 22%, var(--card))}
.cal__d[data-i="2"]{background:color-mix(in srgb, var(--warn) 48%, var(--card)); color:var(--ink)}
.cal__d[data-i="3"]{background:color-mix(in srgb, var(--bad) 55%, var(--card)); color:var(--ink)}
.cal__ph{
  position:absolute; left:50%; bottom:4px; transform:translateX(-50%);
  width:4px; height:4px; border-radius:50%; background:var(--ink-2);
}
.cal__md{position:absolute; left:4px; right:4px; bottom:1px; height:2px; border-radius:var(--r-xs); background:var(--ok)}
/* the calendar key draws its swatches from the same shapes the cells use */
.legmk{
  flex:none; position:relative; width:20px; height:20px; margin-top:1px;
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--card);
}
.legmk--ph::after{
  content:""; position:absolute; left:50%; bottom:3px; transform:translateX(-50%);
  width:4px; height:4px; border-radius:50%; background:var(--ink-2);
}
.legmk--chg::after{
  content:""; position:absolute; top:0; right:0;
  border-width:0 7px 7px 0; border-style:solid;
  border-color:transparent var(--eosin) transparent transparent;
}
.callg{display:flex; flex-wrap:wrap; gap:12px; font-size:11px; color:var(--ink-3); font-family:var(--f-body)}
.callg__i{display:flex; align-items:center; gap:4px}
.calsw{width:12px; height:12px; border-radius:var(--r-sm); border:1px solid var(--edge)}
.calsw--0{background:color-mix(in srgb, var(--ok) 16%, var(--card))}
.calsw--1{background:color-mix(in srgb, var(--warn) 22%, var(--card))}
.calsw--2{background:color-mix(in srgb, var(--warn) 48%, var(--card))}
.calsw--3{background:color-mix(in srgb, var(--bad) 55%, var(--card))}
.calsw--n{background:var(--paper)}

/* ---------- the walks month ----------
   The same grid as the observations calendar, asked a different question. That
   one shades a day by how bad it was, which is a scale with a bad end. This one
   counts outings, where more is not worse, so the bands climb in the app's own
   stain rather than through amber into red. Nothing here restyles .cal itself:
   the shapes are shared and only what sits inside a cell is new. */
.wcal__d[data-w="0"]{background:color-mix(in srgb, var(--stain) 10%, var(--card))}
.wcal__d[data-w="1"]{background:color-mix(in srgb, var(--stain) 22%, var(--card))}
.wcal__d[data-w="2"]{background:color-mix(in srgb, var(--stain) 36%, var(--card)); color:var(--ink)}
.wcal__d[data-w="3"]{background:color-mix(in srgb, var(--stain) 52%, var(--card)); color:var(--ink)}
/* A day with nothing in it is not a failure and is not dimmed to look like one.
   It is simply flat, and it does not offer a tap, because there is nothing to
   filter to. :disabled would otherwise inherit the platform's grey. */
.wcal__d:disabled:not(.cal__d--fut){opacity:1; cursor:default}
/* How many, in the corner. The day number stays the main glyph because the day
   number is how a month is navigated; the count is the answer the grid was
   asked for and it sits where a superscript sits. */
.wcal__n{
  position:absolute; top:2px; right:3px;
  font:600 9.5px/1 var(--f-body); color:var(--ink-2);
  font-variant-numeric:tabular-nums;
}
.wcal__d[data-w="2"] .wcal__n, .wcal__d[data-w="3"] .wcal__n{color:var(--ink)}
/* A day holding a name the app proposed rather than a name somebody gave. The
   dashed edge is this app's word for something not settled yet, the same word
   "Undeva nou" wears on the Log screen, so the mark needs no legend to be read
   even though it has one. */
.wcal__d[data-sug="1"]{border:1px dashed var(--stain); border-radius:var(--r-sm)}
/* The day the list is filtered to. A ring rather than another fill, because the
   fill is already carrying the count and two meanings in one channel is how a
   legend stops working. */
.wcal__d--on{box-shadow:inset 0 0 0 2px var(--stain)}
.wcal__d--on, .wcal__d[data-sug="1"]{font-weight:600}
/* The legend swatches read from the same attributes the cells do, so a band
   cannot be restyled in one place and explained in the other. */
.wcalsw[data-w="0"]{background:color-mix(in srgb, var(--stain) 10%, var(--card))}
.wcalsw[data-w="1"]{background:color-mix(in srgb, var(--stain) 22%, var(--card))}
.wcalsw[data-w="2"]{background:color-mix(in srgb, var(--stain) 36%, var(--card))}
.wcalsw[data-w="3"]{background:color-mix(in srgb, var(--stain) 52%, var(--card))}
.wcalsw[data-sug="1"]{background:var(--card); border:1px dashed var(--stain)}

/* Which day the entries under it belong to. Sticky, because the answer to
   "which day am I reading" has to survive the scroll that made the question
   worth asking, and a journal of sixty entries is a long scroll. It stops under
   whatever chrome is already stuck up there: the header, plus the mode bar,
   whose height is a constant here because six segments never wrap. */
.wday{
  position:sticky; top:var(--chrome-h); z-index:20;
  display:flex; align-items:baseline; gap:var(--s2);
  margin:var(--s5) 0 var(--s1); padding:var(--s1) 0;
  background:var(--paper);
}
/* The first heading carries no top margin: it follows the calendar card, which
   has its own, and two margins meeting reads as a gap somebody forgot to close. */
.wday:first-of-type{margin-top:var(--s2)}
.wday b{font:400 1.02rem/1.2 var(--f-display); color:var(--ink)}
.wday__n{font:400 11.5px/1 var(--f-body); color:var(--ink-3); white-space:nowrap}
/* The day the calendar is filtered to, said above the list as well as marked in
   the grid. Two statements of one fact, because the grid scrolls off. */
.wkfilt{margin-top:var(--s3)}

/* ORPHANED 07.08. renderMeds() in js/08-day.js built these rows and now
   delegates to renderDue(), which draws .mrow instead, so nothing in the app
   emits .med any more. Left in place rather than removed: this is the only
   record of what the first medication row looked like, and flagging it costs
   nothing while deleting it costs the ability to tell whether a screenshot
   from July is the old list or the new one. Flagged for Ovidiu to action. */
.med{
  display:flex; align-items:center; gap:12px;
  border:1px solid var(--line-soft); border-radius:var(--r);
  padding:8px 12px 8px 12px; background:var(--paper);
}
.med__t{display:flex; flex-direction:column; gap:4px; flex:1; min-width:0}
.med__b{
  flex:none; width:32px; height:32px; border-radius:var(--r-sm);
  border:1px solid var(--line-strong); background:var(--card);
  color:var(--ink-3); font-size:17px; line-height:1; cursor:pointer;
}
.med[data-st="g"]{border-color:var(--ok)}
.med[data-st="g"] .med__b{border-color:var(--ok); color:var(--ok-ink); font-weight:700}
.med[data-st="x"]{border-color:var(--bad)}
.med[data-st="x"] .med__b{border-color:var(--bad); color:var(--bad-ink); font-weight:700}
.remslot{
  border:1px solid var(--line); border-radius:var(--r); padding:12px 12px;
  background:var(--paper); display:flex; flex-direction:column; gap:12px;
}
.remslot[data-on="0"]{opacity:.6}
.remslot[data-on="0"] .remslot__body{display:none}
/* A label wrapping a checkbox: the label IS the target, so the label is what
   has to be 44 and not the box inside it. */
.remslot__hd{min-height:var(--tap-min); display:flex; align-items:center; gap:8px; cursor:pointer}
.remslot__hd input{width:20px; height:20px; accent-color:var(--stain); flex:none}
.remslot__t{font-weight:600; font-size:.9rem}
.remslot__opt{font-size:.72rem; color:var(--ink-3); letter-spacing:.04em; text-transform:uppercase}
.remslot__body{display:flex; flex-wrap:wrap; gap:var(--field-gap)}
.remtime{
  width:auto; min-width:7.5rem; font-family:var(--f-data);
  background:var(--paper); border:1px solid var(--edge);
  border-radius:var(--r); padding:8px 12px; font-size:16px;
}

/* zone sheet */

/* THE TOP INSET IS A GLOBAL RULE AND THIS IS THE THIRTEENTH SURFACE IT BINDS.

   Every full-screen surface in this stylesheet reserves the notch: .topbar,
   .tb, .srch and .vfbar all carry env(safe-area-inset-top), and .gate and .spl
   centre their content so they never reach it. .sheet was the one that did
   not, and .sheet--full is the tall variant, so on a phone with a notch the
   sheet title sat under the status bar on all thirteen sheets at once. Reported
   from the walk sheet 08.08.2026; the walk sheet was never the bug.

   Bound in two places on purpose, because one of them alone does not hold.
   The padding here shrinks the flex container so the panel has nowhere to go,
   and the clamp on max-height stops a panel measured against the viewport from
   growing back through that padding. Remove either and the notch returns.

   Anything added later that is position:fixed and reaches the top of the
   screen belongs in this list, not in a rule of its own. */
.sheet{
  position:fixed; inset:0; z-index:80; display:none;
  align-items:flex-end; justify-content:center;
  padding-top:env(safe-area-inset-top);
  background:color-mix(in srgb,var(--ink) 46%,transparent);
}
.sheet[data-open="1"]{display:flex}
/* Only the front sheet dims. Two open sheets each drew 46% of --ink over the
   app and the result was 71%, near black, which read as a fault rather than as
   depth. Ordered and marked by sheetsOrder() in js/07-sheets.js. */
.sheet[data-under="1"]{background:transparent}
.sheet__panel{
  background:var(--card); width:100%; max-width:560px;
  border-radius:var(--r-lg) var(--r-lg) 0 0; padding:var(--sp-6) var(--sp-11) var(--sp-12);
  padding-bottom:calc(var(--sp-12) + env(safe-area-inset-bottom));
  display:flex; flex-direction:column; gap:var(--field-gap);
  /* dvh is measured against the viewport, which does not know about the
     padding on .sheet. Subtracting the inset here is what keeps the two
     agreeing; see the note above .sheet. */
  max-height:calc(88dvh - env(safe-area-inset-top));
  overflow-y:auto; overscroll-behavior:contain;
  box-shadow:0 -8px 40px rgba(0,0,0,.24);
}
@media (min-width:640px){
  .sheet{align-items:center}
  /* This re-declares max-height, so it re-declares the clamp with it. A
     media query that drops the inset is how the notch comes back on a wide
     phone in landscape. */
  .sheet__panel{border-radius:var(--r-lg); max-height:calc(82dvh - env(safe-area-inset-top))}
}

/* The panel is a stack of fields, but the BODY inside it was not.

   Every sheet is built the same way: a sticky header, one body div the JS fills,
   and sometimes a footer row. The panel spaces its own three children by
   --field-gap, so the body as a whole sits correctly, and then everything the
   JS appends inside that body touches the thing above it with zero gap. The
   stool sheet stacked a slider, an anchor line, a label, a chip cloud, a time
   field, a note and a camera button with nothing between any of them. That is
   half of what "cluttered" was pointing at, and no amount of fixing the chip
   cloud alone would have reached it.

   Listed by id rather than matched by a selector trick, because these are the
   sheet bodies and a rule that quietly caught one more div would be a bug
   nobody would look for.

   #pickBody joined them on 02.09.2026, when the step that grades the site a
   photograph was just filed to was built into it: a hint, a chip cloud, a
   scale, an anchor line, a note and two buttons, all flush against each other,
   which is the same complaint this rule exists to answer. The site and day
   pickers that already lived there were spaced by it too. */
#hsBody, #flBody, #sessBody, #outBody, #ssBody, #nutBody, #anSheetBody,
#foodBody, #breakBody, #apBody, #prvBody, #roomBody, #cmpBody, #animBody,
#invBody, #whoOut, #pickBody{
  display:flex; flex-direction:column; gap:var(--field-gap);
}
/* A label belongs to the control under it, not halfway between two of them. */
#hsBody > .lbl, #flBody > .lbl, #sessBody > .lbl, #outBody > .lbl,
#ssBody > .lbl, #nutBody > .lbl, #anSheetBody > .lbl, #foodBody > .lbl,
#breakBody > .lbl, #apBody > .lbl, #prvBody > .lbl, #roomBody > .lbl,
#cmpBody > .lbl, #animBody > .lbl, #invBody > .lbl, #whoOut > .lbl,
#pickBody > .lbl{
  margin-bottom:calc(var(--field-gap) * -1 + var(--s1));
}
/* Sticky, because the panel is what scrolls. A long sheet, and the scoring
   legend at 320 is one, carried its title and its close button off the top as
   soon as you read past the third row, so the way out of the sheet left the
   screen at exactly the point somebody had gone deep enough to want it. The
   negative inset undoes the panel's own top padding so the header still sits
   flush when it is stuck, and the background is the panel's so rows pass under
   it rather than through it. */
/* 11.08, Ovidiu, on the nutrient sheet: "Titlul: Iod este mult prea sus."

   The header's top padding was --sp-6, which is 8px, so a title sat eight
   pixels under a 20px rounded corner and read as having been pushed into the
   edge of the panel rather than placed inside it. The negative margin above
   still cancels the panel's own top padding, so the sticky behaviour and the
   flush stuck position are unchanged; only the room above the words moved.

   A full-height sheet gets more of it than a short one. A short sheet reads as
   a card that rose from the bottom and its top edge is close to its title by
   design; a sheet that occupies 96% of the screen reads as a page, and a page
   whose title touches its own top edge looks unfinished. */
.sheet__hd{
  display:flex; align-items:flex-start; gap:12px;
  position:sticky; top:calc(var(--sp-6) * -1); z-index:1;
  margin:calc(var(--sp-6) * -1) 0 0; padding:var(--sp-11) 0 var(--s2);
  background:var(--card);
}
.sheet--full .sheet__hd{padding-top:var(--s5)}

/* ---------- standing facts and scoring anchors ---------- */

/* The question mark sits next to a label it explains, so it has to read as an
   affordance without competing with the label itself. It is the ONLY spelling
   of this control: `.q` was a second one and was merged into it 07.08.2026.

   The dot stays 20px because beside a heading it is punctuation, not a button
   competing with the heading for weight. What a thumb has to hit is 44, so the
   target is grown underneath it with a pseudo-element and nothing moves on
   screen. This block used to grow it twice, once with a ::before at inset -12px
   and once with a ::after at --tap-min, which is the same 44px drawn twice.

   Any audit that measures the visible box reports this as a failed target. It
   is not. Measure the pseudo-element, or skip it. */
.icobtn--q{
  /* height was declared twice here, 20px and then 19px, so the round button
     was actually a one-pixel-squashed oval on every screen that has a help
     affordance. The second declaration is gone rather than rounded. */
  width:20px; height:20px; min-width:20px; padding:0;
  border-color:var(--line-strong); color:var(--ink-3); font-size:11px;
  position:relative;
}
.icobtn--q::after{
  content:""; position:absolute; top:50%; left:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
  border-radius:var(--r-pill);
}
.icobtn--q svg{width:12px; height:12px}
.icobtn--q:hover{border-color:var(--stain); color:var(--stain)}

.stf{
  display:flex; align-items:flex-start; gap:12px;
  padding:8px 0; border-bottom:1px solid var(--line-soft);
}
.stf:last-child{border-bottom:0}
.stf__t{display:flex; flex-direction:column; gap:4px; flex:1; min-width:0}
.stf__k{font-size:12px; letter-spacing:.03em; text-transform:uppercase; color:var(--ink-3)}
.stf__v{font-size:14px; line-height:1.35; word-break:break-word}
.stf[data-empty="1"] .stf__v{color:var(--ink-3); font-style:italic}
.stf__m{font-size:11px; color:var(--ink-3); font-variant-numeric:tabular-nums}
.stf__b{
  flex:0 0 auto; align-self:center; background:none; cursor:pointer;
  border:1px solid var(--edge); border-radius:var(--r-pill);
  min-height:var(--tap-min); padding:var(--s1) var(--s3);
  display:inline-flex; align-items:center;
  font:inherit; font-size:12px; color:var(--ink-2);
}
.stf__b:hover{border-color:var(--stain); color:var(--stain)}
.stask{font-size:13px; margin:0 0 8px}

/* One line, one thing. The amount box is deliberately narrow and the name box
   takes the rest: on a phone the eye reads down the left column to check the
   quantities, which is the column that changes when a diet is adjusted. */
.items{display:flex; flex-direction:column; gap:8px}
.item{display:flex; gap:8px; align-items:center}
.item__q{flex:0 0 84px; min-width:0; font-family:var(--f-data); font-variant-numeric:tabular-nums}
.item__n{flex:1 1 auto; min-width:0}
.item__x{flex:0 0 auto; color:var(--ink-3)}
.item__x:hover{color:var(--stain); border-color:var(--stain)}
/* Smaller in type and in width, not in reach. --sm says secondary, and a
   secondary action is still tapped with the same thumb. */
.btn--sm{
  min-height:var(--tap-min); padding:var(--s2) var(--s4);
  font-size:13px; align-self:flex-start;
}

/* The admin tab. Settings, not diary: it is allowed to look like a form. */
.adm{display:flex; flex-direction:column; gap:var(--sp-5); padding:var(--row-y) 0; border-bottom:1px solid var(--line-soft)}
.adm:last-child{border-bottom:0}
/* The row is the control, so it carries the target and the pressed state. See
   the comment in js/14-settings.js for why the thumbnail beside it stayed. */
.adm__hd{display:flex; align-items:center; gap:8px; cursor:pointer; min-height:var(--tap-min)}
.adm__hd:focus-visible{outline:2px solid var(--stain); outline-offset:2px; border-radius:var(--r-sm)}
.adm__x{
  flex:none; color:var(--ink-3); font-size:20px; line-height:1;
  width:24px; text-align:center;
  transition:transform .16s ease;
}
.adm__x--o{transform:rotate(90deg); color:var(--stain)}
@media (prefers-reduced-motion:reduce){ .adm__x{transition:none} }
.adm__n{flex:1; min-width:0; font-size:14px; line-height:1.3; word-break:break-word}
.adm__s{font-size:11px; color:var(--ink-3)}
.adm[data-off="1"] .adm__n{color:var(--ink-3); text-decoration:line-through}
.adm__g{display:grid; grid-template-columns:1fr 1fr; gap:var(--field-gap)}
.adm__g .field{margin:0}
@container view (max-width:400px){ .adm__g{grid-template-columns:1fr} }

/* Yesterday's number stays quiet until it is allowed to be seen, then it is
   allowed to look like a fact rather than an instruction. */
#itchPrev{font-size:12px}
#itchPrev[data-on="1"]{color:var(--ink-2); font-variant-numeric:tabular-nums}

.anch{display:flex; flex-direction:column; border:1px solid var(--line-soft); border-radius:var(--r-lg); overflow:hidden}
.anch__r{display:flex; gap:12px; padding:8px 12px; border-bottom:1px solid var(--line-soft)}
.anch__r:last-child{border-bottom:0}
.anch__r--now{background:var(--stain-wash); box-shadow:inset 2px 0 0 var(--stain)}
.anch__g{
  /* Sized in characters, not in pixels. The widest band label any scale has is
     "9 - 10", six characters of a monospace face, and 46px was that string
     measured once in one font: whichever ui-monospace the phone actually picks
     is a shade wider, six characters stop fitting in 46, and "10" drops onto a
     second line under "9 -". 7ch is the same six characters expressed in the
     font that is rendering them, with one character of slack, so the label can
     never be the thing that decides how tall a row is. nowrap because a band
     label that wraps is always a defect and never a layout gone tight. */
  flex:0 0 7ch; white-space:nowrap;
  font-variant-numeric:tabular-nums; font-weight:600;
  font-family:ui-monospace,monospace; font-size:13px; color:var(--stain);
}
/* Named grades, not numbered ones. A 7ch column would shred "Scratch damage
   and hair loss" into a ladder, so the name gets its own line. */
.anch--stack .anch__r{flex-direction:column; gap:4px}
.anch--stack .anch__g{flex:none; font-family:inherit; font-size:12px; letter-spacing:.03em; text-transform:uppercase}
.anch__d{font-size:13px; line-height:1.4}
.anch__t{margin:0; padding-left:20px; font-size:12px; color:var(--ink-2); line-height:1.5}
.big{font-variant-numeric:tabular-nums; font-size:20px; font-weight:600; margin-left:12px}

/* ---------- stools, one row each ---------- */
.stool{
  border:1px solid var(--line-soft); border-radius:var(--r-lg);
  padding:12px; margin-bottom:12px; display:flex; flex-direction:column; gap:8px;
}
.stool__n{font-size:12px; letter-spacing:.03em; text-transform:uppercase; color:var(--ink-3)}
.stool__v{font-size:13px; font-variant-numeric:tabular-nums; color:var(--ink-2)}
.stool__r{width:100%}
.stool__sc{
  display:flex; justify-content:space-between; margin-top:-4px;
  font-size:10px; color:var(--ink-3); font-variant-numeric:tabular-nums;
}
.stool__tg{display:flex; flex-wrap:wrap; gap:8px; margin:4px 0 8px}
.stool__note{width:100%}

/* ---------- photo protocol ---------- */
.shots{display:grid; grid-template-columns:1fr; gap:12px}
@container view (min-width:560px){.shots{grid-template-columns:1fr 1fr}}
.shot{
  background:var(--card); border:1px solid var(--line-soft); border-radius:var(--r-lg);
  overflow:hidden; display:flex; flex-direction:column;
}
.shot__hd{display:flex; gap:12px; padding:12px 12px; align-items:flex-start}
.shot__n{
  font-family:var(--f-data); font-size:.7rem; font-weight:700; color:var(--on-accent);
  background:var(--stain); border-radius:var(--r-sm); padding:4px 8px; flex:0 0 auto; margin-top:1px;
}
.shot__t{font-size:.92rem; font-weight:600; line-height:1.3}
.shot__w{font-size:.76rem; color:var(--ink-3); margin-top:4px}
/* the reason the frame exists, kept visually apart from the how-to line */
.shot__why{font-size:.76rem; color:var(--ink-2); margin-top:4px; padding-left:8px;
  border-left:2px solid var(--stain)}
.shot__diag{background:var(--stain-wash); border-top:1px solid var(--line-soft); padding:8px;
  overflow:hidden; touch-action:none; position:relative}
/* the layer a gesture actually moves. Only the drawing inside a diagram scales:
   the page stays pinned, which is what was asked for. */
.zoomer{transform-origin:50% 50%; will-change:transform}
[data-zoomed="1"]{cursor:grab}
.zbadge{
  position:absolute; right:8px; top:8px; z-index:3;
  background:var(--card); border:1px solid var(--line); color:var(--ink-2);
  border-radius:var(--r-pill); padding:4px 8px; font-size:.7rem; font-variant-numeric:tabular-nums;
  cursor:pointer;
}
.shot__diag svg{display:block; width:100%; height:auto; max-height:150px}
/* ---------- search ---------- */
.srch{
  position:fixed; inset:0; z-index:180; display:none; flex-direction:column;
  background:var(--paper); padding-top:env(safe-area-inset-top);
}
.srch[data-open="1"]{display:flex}
.srch__bar{display:flex; gap:8px; align-items:center; padding:12px 16px; flex:none; width:100%}
.srch__in{
  flex:1 1 auto; min-width:0; background:var(--card); border:1px solid var(--edge);
  border-radius:var(--r); padding:12px 12px; font-size:16px;
}
.srch__body{flex:1 1 auto; overflow-y:auto; padding:0 16px 24px; width:100%}
.sres{
  display:block; width:100%; text-align:left; cursor:pointer; appearance:none;
  background:var(--card); border:1px solid var(--line-soft); border-radius:var(--r);
  padding:12px 12px; margin-bottom:8px;
}
.sres:hover{border-color:var(--stain)}
.sres__hd{display:flex; gap:8px; align-items:baseline; flex-wrap:wrap}
.sres__g{
  font-family:var(--f-data); font-size:.62rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--stain); flex:none;
}
.sres__d{font-family:var(--f-data); font-size:.68rem; color:var(--ink-3); margin-left:auto}
.sres__t{font-size:.92rem; font-weight:600; margin-top:4px}
.sres__s{font-size:.78rem; color:var(--ink-3); margin-top:4px}
.sres mark{background:color-mix(in srgb,var(--eosin) 34%,transparent); color:inherit; border-radius:var(--r-xs)}
.srch__note{font-size:.8rem; color:var(--ink-3); padding:12px 0}
/* a hit has to be findable once you land on the page it lives on */
@keyframes flashin{0%,100%{background:transparent}25%,60%{background:color-mix(in srgb,var(--eosin) 22%,transparent)}}
/* Deck 00 section F names three animations and gives this one 300ms. It used to
   run 2.2s, which is long enough that the highlight was still moving while you
   were reading the row it had brought you to. The class outlives the animation:
   flashRow() strips it after 2.4s, because the mark of which row was found has
   to survive the blink that pointed at it. */
.flash{animation:flashin .3s ease-in-out; border-radius:var(--r-sm)}
@media (prefers-reduced-motion:reduce){ .flash{animation:none; outline:2px solid var(--eosin)} }

#nodeDiag svg{display:block; width:100%; max-width:280px; height:auto; margin:8px auto 0}
#nodeDiag .shot__tips{padding:8px 0 0; justify-content:center}
/* the lettering that used to sit inside the drawings, as text that can translate */
.shot__tips{display:flex; flex-wrap:wrap; gap:4px; padding:8px 12px 0}
.shot__tip{
  font-size:.72rem; color:var(--ink-3); background:var(--stain-wash);
  border:1px solid var(--line-soft); border-radius:var(--r-pill); padding:4px 8px;
}
.diag-line{stroke:var(--ink-2); stroke-width:1.1; fill:none}
.diag-fill{fill:var(--card); stroke:var(--ink-2); stroke-width:1.1}
.diag-hi{stroke:var(--eosin); stroke-width:1.8; fill:none}
.diag-hif{fill:color-mix(in srgb,var(--eosin) 30%,transparent); stroke:var(--eosin); stroke-width:1.3}
.diag-t{font-family:var(--f-data); font-size:7px; fill:var(--ink-2)}
.diag-frame{stroke:var(--stain); stroke-width:1.2; stroke-dasharray:4 3; fill:none}
.shot__foot{padding:12px 12px; display:flex; flex-direction:column; gap:8px; border-top:1px solid var(--line-soft)}
.shot__thumbs{display:flex; gap:8px; flex-wrap:wrap}
.thumb{
  position:relative; width:64px; height:64px; border-radius:var(--r-sm);
  overflow:hidden; border:1px solid var(--line); background:var(--line-soft);
}
.thumb img{width:100%; height:100%; object-fit:cover; display:block; position:relative; z-index:1}

/* A photograph that will not load renders its alt text as inline content of
   the image box, and object-fit does not clip inline content, so a caption
   longer than 62px used to run out past the tile and across whatever sat
   beside it. Every thumbnail in the app carries a real alt: the zone name on
   the map strip, the place name on a walk. Register row C-23 (b).

   The text is scaled away rather than the alt being emptied, because the alt
   is what a screen reader says and that is the half of it that carries the
   meaning. What is left is the frame drawn underneath, which a loaded image
   covers and a broken one does not, so a missing picture reads as a missing
   picture instead of as a paragraph. */
/* font-size and colour catch the instant between a cached failure and the
   handler that marks it; data-broke is what actually clears the tile. */
.thumb img, .wph img, .pgrid__c .thumb img{font-size:0; color:transparent}
.thumb img[data-broke="1"], .wph img[data-broke="1"]{visibility:hidden}
.thumb::after, .wph::after{
  content:""; position:absolute; top:50%; left:50%; z-index:0;
  width:20px; height:16px; margin:-8px 0 0 -12px;
  border:1.5px solid var(--ink-3); border-radius:var(--r-xs); opacity:.42;
  pointer-events:none;
}
/* Two half-width columns whose content is words cannot share 320: at that
   width each is 111px and "Boabă de strugure" is not (09.08 audit, the
   popliteal node card). The pair stacks below 380 of the view's own width. */
@container view (max-width:379px){
  .row--stack{flex-direction:column; align-items:stretch}
}

/* 32px drawn on a surface whose every other pixel opens the viewer: a missed
   delete is a mis-navigation. The slop takes the hit area to 44 without
   growing the disc - same pattern as .icobtn--q (09.08 audit). */
.thumb__x::after{content:""; position:absolute; inset:-8px}
.thumb__x{
  position:absolute; top:var(--s1); right:var(--s1); width:32px; height:32px;
  border-radius:50%;
  background:color-mix(in srgb,var(--ink) 74%,transparent); color:#fff; border:0;
  font-size:.78rem; line-height:1; cursor:pointer;
}
/* Deleting somebody's photograph on a mis-tap is the worst thing this control
   can do, so the ring is only grown to 32px and the rest of the target is
   invisible - big enough to hit on purpose, small enough not to swallow the
   corner of the picture. */
.thumb__x::after{
  content:""; position:absolute; top:50%; left:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
}
.filebtn{position:relative; overflow:hidden; display:inline-block}
.filebtn input[type="file"]{
  position:absolute; inset:0; opacity:0; cursor:pointer; font-size:200px;
}

/* ---------- marks drawn on a photograph ----------
   Sits over the image, never in it. Everything below is geometry in the
   photograph's own coordinates, so the same numbers redraw on a phone, on an
   A4 sheet, and on whatever reads this record in five years. */
.thumb{cursor:pointer}
/* When the tile is one half of a before and after, the date is the only thing
   that says which half it is. */
.thumb__d{
  position:absolute; left:2px; top:2px; padding:1px 4px; border-radius:var(--r-pill);
  background:var(--ink); color:var(--paper); font-size:.6rem; line-height:1.5;
  font-weight:600; z-index:2; pointer-events:none;
}
.thumb__ann{
  position:absolute; left:2px; bottom:2px; padding:1px 4px; border-radius:var(--r-pill);
  background:var(--eosin); color:var(--on-eosin); font-size:.6rem; line-height:1.5; font-weight:600;
  pointer-events:none;
}
.annstage{
  position:relative; display:inline-block; max-width:100%;
  background:var(--ink); border-radius:var(--r); overflow:hidden; line-height:0;
  /* With no tool chosen a finger on the photograph still scrolls the sheet.
     Only the pen takes the gesture away, and only while it is on. */
  touch-action:pan-y;
}
.annstage[data-tool="draw"]{touch-action:none}
.annstage img{display:block; max-width:100%; max-height:54dvh; width:auto; height:auto}
.annstage svg{position:absolute; inset:0; width:100%; height:100%}
.annstage[data-tool="draw"]{cursor:crosshair}
.annstage[data-tool="pin"]{cursor:copy}
.ann-ink{fill:none; stroke:var(--eosin); stroke-width:6; stroke-linecap:round; stroke-linejoin:round}
.ann-inkh{fill:none; stroke:#fff; stroke-opacity:.55; stroke-width:11; stroke-linecap:round; stroke-linejoin:round}
.ann-pin{fill:var(--eosin); stroke:#fff; stroke-width:3}
.ann-pint{fill:#fff; font-family:var(--f-data); font-weight:700; text-anchor:middle}
.annwrap{display:flex; flex-direction:column; align-items:center; gap:8px}
.annwrap .field,.annwrap .row,.annwrap .hint{width:100%}
/* which tool is live has to be readable at arm's length, in a kitchen, at night */
.btn--on{background:var(--stain); border-color:var(--stain); color:var(--on-accent)}
.annpins{display:flex; flex-direction:column; gap:8px; width:100%}
.annpin{display:flex; align-items:center; gap:8px}
.annpin__n{
  flex:0 0 auto; width:24px; height:24px; border-radius:50%; background:var(--eosin);
  color:#fff; font-size:.72rem; font-weight:700; display:flex; align-items:center; justify-content:center;
}
.annpin input{flex:1 1 auto; min-width:0}

/* ---------- trend + log ---------- */
/* This used to carry the card's own background and border, which is an outline
   drawn on identical paper inside the box it already sits in. It showed almost
   nothing and cost 9px of inset on each side, so the chart's axis numbers began
   one line to the right of the legend and the hint underneath: three left edges
   in a single card. The chart now sits in the card's text column, the way the
   printed figures sit in the report's. */
.chartbox{padding:12px 0 8px; overflow-x:auto}
.chartbox canvas{display:block; width:100%; height:212px}
/* the gestures are swallowed here so a pinch zooms the chart and not the page */
.chartbox--live{overflow:hidden; touch-action:none; position:relative; cursor:crosshair}
.readout{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  /* no horizontal padding: this line reads out the chart directly above it and
     has to start on the same left edge as the chart's axis numbers */
  min-height:32px; padding:8px 0; font-size:.8rem; color:var(--ink-2);
}
.readout__d{font-weight:600; color:var(--ink)}
.readout__v{font-variant-numeric:tabular-nums}
.readout__go{
  background:transparent; border:1px solid var(--edge); color:var(--stain);
  border-radius:var(--r-pill); padding:4px 12px; font:inherit; font-size:.76rem; cursor:pointer;
}
.readout__hint{color:var(--ink-3)}
.loglist{display:flex; flex-direction:column; gap:1px; background:var(--line-soft);
  border:1px solid var(--line-soft); border-radius:var(--r-lg); overflow:hidden}
.logrow{
  background:var(--card); border:0; width:100%; text-align:left; cursor:pointer;
  padding:12px 12px; display:flex; align-items:center; gap:12px;
}
.logrow:hover{background:var(--stain-wash)}
.logrow__d{font-family:var(--f-data); font-size:.8rem; width:5.6em; flex:0 0 auto; color:var(--ink-2)}
.logrow__bars{display:flex; gap:4px; flex:1 1 auto; align-items:flex-end; height:24px}
.logrow__b{flex:1 1 auto; border-radius:var(--r-xs) var(--r-xs) 0 0; min-height:2px; background:var(--stain-soft)}
/* Four columns with the rules taken out. Every one of them has to be given a
   width, because the readings on the right are as long as whatever they happen
   to say and the bar strip is what absorbs the difference: a row reading "i9"
   left the bars 13px longer than the row above it reading "i8·O", and a bar
   drawn to a different scale on every row is not a chart, it is decoration.
   Fixed columns make the strip the same length on every row, which is the only
   thing that makes two rows comparable at a glance. */
.logrow__m{font-family:var(--f-data); font-size:.78rem; display:flex; gap:8px;
  flex:0 0 auto; color:var(--ink-2)}
.logrow__m span{display:inline-block; text-align:right}
.logrow__m span:nth-child(1){width:3.4em}
.logrow__m span:nth-child(2){width:2.4em}
.logrow__m span:nth-child(3){width:2.4em}
/* the verdict reads as a column, so it is set to the widest word it can hold
   in either language rather than shrinking to fit "3Δ" */
.logrow .pill{flex:0 0 auto; width:5.2em; text-align:center; box-sizing:border-box}
/* A word in a border. Not to be confused with .pdraw, which is the medication
   drawing and was called .pill until 07.08.2026, when the two of them were
   found both matching, both carrying --modifiers, and the later one silently
   winning: this badge was being given display:grid and a 32px box, and only
   looked right because place-items:center happened to centre its text. */
.pill{
  font-size:.64rem; letter-spacing:.05em; text-transform:uppercase; font-weight:700;
  padding:4px 8px; border-radius:var(--r-pill); border:1px solid currentColor;
}
.pill--ok{color:var(--ok-ink)} .pill--warn{color:var(--warn-ink)} .pill--bad{color:var(--bad-ink)}
.empty{
  text-align:center; padding:24px 16px; color:var(--ink-3); font-size:.86rem;
  border:1px dashed var(--line); border-radius:var(--r-lg);
}

/* ---------- clinic ---------- */
.tier{border:1px solid var(--line-soft); border-radius:var(--r-lg); overflow:hidden; background:var(--card)}
.tier__hd{padding:12px 12px; border-bottom:1px solid var(--line-soft); display:flex; gap:12px; align-items:flex-start}
.tier__n{
  font-family:var(--f-display); font-size:1.5rem; line-height:.9; color:var(--stain);
  flex:0 0 auto; width:1.1em;
}
.tier__t{font-size:.96rem; font-weight:600}
.tier__w{font-size:.78rem; color:var(--ink-3); margin-top:4px}
.tier__body{padding:4px 12px 12px}
.test{
  padding:var(--row-y) 0; border-bottom:1px solid var(--line-soft);
  display:flex; gap:var(--sp-7); align-items:flex-start; min-height:var(--tap-min);
  /* The row is a label now. The box was a bare 24px checkbox with the sentence
     that explains it sitting inertly beside it, so the only way to tick a test
     was to hit the box. */
  cursor:pointer;
}
.test:active{background:var(--stain-wash)}
.test:focus-within{outline:2px solid var(--stain); outline-offset:2px; border-radius:var(--r-sm)}
.tier__body .test:last-child{border-bottom:0}
/* The box stays 24 because it is read as a tick against a line of text, not
   as a button. ::after already draws the tick, so the target goes on ::before,
   which is otherwise unused here. The rows are 44 apart, so a 44 target on a
   24 box does not reach its neighbour. */
.test__box{
  appearance:none; width:24px; height:24px; flex:0 0 auto; margin-top:1px; cursor:pointer;
  border:1.5px solid var(--edge); border-radius:var(--r-sm); background:var(--paper); position:relative;
}
.test__box::before{
  content:""; position:absolute; left:50%; top:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
}
.test__box:checked{background:var(--ok); border-color:var(--ok)}
.test__box:checked::after{
  content:""; position:absolute; left:7.5px; top:3.5px; width:6px; height:11px;
  border:solid #fff; border-width:0 2px 2px 0; transform:rotate(42deg);
}
.test__t{font-size:.9rem; font-weight:600; line-height:1.35}
.test__why{font-size:.79rem; color:var(--ink-2); margin-top:4px; line-height:1.45}
/* What somebody wrote about a site, quoted rather than restated: the rule marks
   it as their words and not the app's summary of them. */
.test__note{font-size:.82rem; color:var(--ink); margin-top:4px; line-height:1.5;
  border-left:2px solid var(--line); padding-left:8px; white-space:pre-wrap}
.test__say{
  font-size:.79rem; margin-top:8px; padding:8px 8px; border-radius:var(--r-sm);
  background:var(--eosin-wash); border-left:2px solid var(--eosin); color:var(--ink-2);
}
/* Edit mode on the request list.

   A put-away request is shown, not removed, and it is shown faded with its
   tick disabled so it reads as filed rather than as an option. It only ever
   appears while editing; out of edit mode the row is not drawn at all.

   The action column is fixed rather than flexible, so the three or four
   buttons down a tier share one right edge instead of each finding its own,
   which is the complaint that started this pass: "Check every icon, every
   text, every box against each other." */
.test--off{opacity:.45}
.test--off .test__box{cursor:default}
.test__act{flex:0 0 auto; display:flex; align-items:flex-start; margin-left:auto}
.test__act .btn{white-space:nowrap}
/* Writing one. The three inputs stack full width and share the row's own left
   edge with the text above them, so the form is part of the tier rather than a
   panel that landed in it. */
.test--new{flex-direction:column; align-items:stretch; gap:var(--sp-7); cursor:default}
.test--new .grow{display:flex; flex-direction:column; gap:var(--sp-7)}
.test--new .btn{align-self:flex-start}
/* Both the new-request form and a written request being edited in place lay
   their fields out the same way, so the two never read as different features.
   Full width, stacked, sharing the row's left edge. */
.test--new input,
.test .grow > input{width:100%; display:block}
.test .grow > input + input{margin-top:var(--sp-7)}

.qa{padding:var(--row-y) 0; border-bottom:1px solid var(--line-soft)}
.qa:last-child{border-bottom:0}
.qa__q{font-size:.9rem; font-weight:600; display:flex; gap:8px; line-height:1.35}
.qa__n{font-family:var(--f-data); color:var(--stain); flex:0 0 auto}
.qa__hint{font-size:.78rem; color:var(--ink-3); margin:4px 0 8px 1.6em; line-height:1.45}
.qa__a{margin-left:1.6em}
.qa--key .qa__q{color:var(--stain)}
.qa--key::before{
  content:attr(data-key); display:inline-block; font-size:.58rem; letter-spacing:.13em;
  text-transform:uppercase; color:var(--eosin-ink); font-weight:700; margin-bottom:4px;
}

/* ---------- 2-col when the SCREEN is wide, not when the window is ----------
   960px of viewport was unreachable on every device this app has ever run on,
   so the two-column map was dead code wearing a breakpoint. Asked of the
   container instead, it fires on a foldable that has been opened, which is the
   case it was written for. */
/* Below the query the split is a plain block, and the hint under the drawing
   touched the first card (verify pass 1, 02.09.2026). */
.split{display:flex; flex-direction:column; gap:var(--s3)}
@container view (min-width:600px){
  .split{display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,1fr); gap:20px; align-items:start}
  .split__l{position:sticky; top:72px}
  .shots{grid-template-columns:1fr 1fr}
}

/* ---------- print: vet handout ---------- */
#printdoc{display:none}

/* ==== PAPER  the vet handout, on paper and on screen ======================
   Every rule below is written once and worn by two elements. #printdoc is the
   document the print dialog is handed. .rbpv is the same document drawn inside
   the Report screen so somebody can read six pages before agreeing to them.

   "PDF preview must be identical to the PDF." It was not. The preview was a
   hand-kept copy in pixels: it had 24 rules that disagreed with the printed
   ones and it was missing 35 more outright, so the body maps, the charts, the
   zone chips, the score legend and every section margin were on-screen
   defaults on one surface and print rules on the other. A copy maintained by
   hand drifts the first time somebody edits one side, and it had.

   So there is no copy. The paired selector is the mechanism: a rule cannot
   reach paper without also reaching the preview. Millimetres and points are
   real CSS units on screen too, which is what makes this possible at all - a
   44mm photograph is 166px on any display, so the preview is not an
   approximation of the page, it is the page at 1:1, scaled to fit the phone
   by .rbpv alone.

   What stays in @media print above: the page box, the paper reset, and the
   rule that hides the application. Those are about printing, not about the
   document. What is here is the document.

   Both gates are told about this block by name. tools/spacing_gate.py blanks
   it for the same reason it blanks @media print - it is a millimetre system on
   A4 and has nothing to do with the 4pt screen grid. tools/contrast_gate.py
   already exempts anything whose selector mentions .rbpv, and every selector
   here does; the print inks are checked as literal pairs in its FIXED table. */
/* Every ink here clears WCAG 2.2 AA against white: 4.5:1 for text, 3:1 for
   borders and rules. The previous values did not. #767676 is the lightest
   grey that still clears 4.5:1, so it is safe for both. */
#printdoc h1,
.rbpv h1{font-size:15pt; margin:0 0 1.5mm; break-after:avoid}
#printdoc h2,
.rbpv h2{font-size:11.5pt; margin:6mm 0 2mm; padding-bottom:1mm;
  border-bottom:.6pt solid #172A53;
  /* the rule that stops a section title stranded at the foot of a page */
  break-after:avoid; break-inside:avoid}
#printdoc h3,
.rbpv h3{font-size:9.5pt; margin:3mm 0 1.5mm; break-after:avoid}
#printdoc p,
.rbpv p{orphans:3; widows:3; margin:1.5mm 0}
#printdoc .psec,
.rbpv .psec{break-inside:auto}

#printdoc table,
.rbpv table{width:100%; border-collapse:collapse; font-size:8.5pt; margin:0 0 3mm}
/* Repeats the column headings on every page a table continues onto. Without
   a real thead element this does nothing, which is why the markup builds
   one: a continuation page of bare numbers is unreadable. */
#printdoc thead,
.rbpv thead{display:table-header-group;
  /* a column heading alone at the foot of a page with its rows overleaf is
     the same defect as a stranded section title */
  break-after:avoid}
#printdoc tfoot,
.rbpv tfoot{display:table-footer-group}
#printdoc tr,
.rbpv tr{break-inside:avoid}
#printdoc th,
.rbpv th,
#printdoc td,
.rbpv td{border:.5pt solid #767676; padding:1.3mm 1.7mm;
  text-align:left; vertical-align:top}
/* Browsers drop background colours in print unless told not to. Without this
   the header row loses the only thing distinguishing it from the data. */
#printdoc th,
.rbpv th{background:#e4e4e4; font-weight:700;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
#printdoc tbody tr:nth-child(even) td,
.rbpv tbody tr:nth-child(even) td{background:#f6f6f6;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
#printdoc td.pnum,
.rbpv td.pnum,
#printdoc th.pnum,
.rbpv th.pnum{text-align:right; white-space:nowrap}
#printdoc .pdim,
.rbpv .pdim{color:#595959}

#printdoc .pmeta,
.rbpv .pmeta{font-size:8pt; color:#333; margin-bottom:3mm}
#printdoc .pnote,
.rbpv .pnote{font-size:8pt; border-left:2.5pt solid #172A53;
  padding-left:2.2mm; margin:2mm 0; color:#2b2b2b}
/* who scored it. Baseline rather than superscript: as a superscript it
   forced the itch cell onto two lines. */
#printdoc .pby,
.rbpv .pby{font-size:7.5pt; color:#444}

#printdoc figure,
.rbpv figure{margin:0 0 3mm; break-inside:avoid}
#printdoc figcaption,
.rbpv figcaption{font-size:7.5pt; color:#333; margin-top:1mm}
/* Sits inside the 44mm frame the photograph would have filled, so the page
   lays out the same whether or not the network was up. */
/* Keeps the frame the photograph would have had. Without the border it is a
   grey sentence floating between two captions and reads as a stray note
   about the record rather than as the place a photograph belongs. */
#printdoc .pgone,
.rbpv .pgone{
  position:absolute; left:0; top:0; width:44mm; height:44mm;
  display:flex; align-items:center; justify-content:center; text-align:center;
  padding:2mm; box-sizing:border-box;
  border:.5pt dashed #767676;
  font-size:7pt; color:#595959; line-height:1.35;
}
/* Chart titles sit above their plate, so the space has to go the other way:
   tight to the drawing it names, open above it. Without this the title of one
   chart and the base of the one before it touch. */
#printdoc .pchart figcaption,
.rbpv .pchart figcaption{margin:0 0 2mm; color:#000; font-weight:700;
  line-height:1.3}
/* The explanatory line is not part of the title. Own line, lighter, smaller,
   so the eye reads name then drawing, and only reads the instructions if it
   needs them. */
#printdoc .pchart figcaption .psub,
.rbpv .pchart figcaption .psub{display:block; font-weight:400;
  font-size:7pt; color:#444; margin-top:.7mm}
/* Not flex and not grid. Chrome fragments neither across printed pages, so a
   wall of photographs taller than the sheet spills off the bottom of it
   instead of continuing overleaf. Inline-block figures sit in an ordinary
   block, which breaks between rows the way a paragraph breaks between lines.
   font-size:0 on the container is what removes the whitespace between them. */
#printdoc .pshots,
.rbpv .pshots{font-size:0}
#printdoc .pshots figure,
.rbpv .pshots figure{display:inline-block; vertical-align:top;
  width:44mm; margin:0 3mm 3mm 0; font-size:8pt}
/* A fixed box, and contain rather than cover. The box makes the page height
   predictable before the images have loaded; contain means no part of a
   lesion is cropped away to make the grid tidy. The frame is placed inside
   that square at its own aspect, with explicit dimensions, because anything
   drawn over it has to land on the photograph and not on the letterboxing. */
#printdoc .pimg,
.rbpv .pimg{position:relative; display:block; width:44mm; height:44mm}
#printdoc .pimg img,
.rbpv .pimg img,
#printdoc .pimg svg,
.rbpv .pimg svg{position:absolute; display:block}
#printdoc .pimg img,
.rbpv .pimg img{border:.5pt solid #767676}
#printdoc .pimg svg,
.rbpv .pimg svg{overflow:visible;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
/* A fixed ink for paper. The screen colour follows the theme, and the dark
   theme's pink is too pale to read on white; this one measures 6.5:1 against
   the sheet, comfortably past the 3:1 that non-text has to hold. */
/* 13 units of a 1000-unit box printed at 44mm is 0.57mm of ink, near enough a
   1.6pt line: thin enough to leave the lesion visible underneath, thick enough
   to survive a clinic printer and a photocopy. */
#printdoc .pimg .ann-ink,
.rbpv .pimg .ann-ink{stroke:#A82E58; stroke-width:13}
#printdoc .pimg .ann-inkh,
.rbpv .pimg .ann-inkh{stroke:#fff; stroke-opacity:.7; stroke-width:23}
#printdoc .pimg .ann-pin,
.rbpv .pimg .ann-pin{fill:#A82E58; stroke:#fff; stroke-width:5}
#printdoc .pimg .ann-pint,
.rbpv .pimg .ann-pint{fill:#fff}
/* What each number on a photograph means, printed under it. Without this the
   marks are decoration. */
#printdoc .pmarks,
.rbpv .pmarks{display:block; color:#000; margin-top:.6mm}
/* Seven figures down one column need air between them or the reader cannot
   tell where one ends and the next begins. 7mm is roughly two blank lines,
   which is what separates the sections of this report elsewhere. */
#printdoc .pchart,
.rbpv .pchart{break-inside:avoid; margin:0 0 7mm}
#printdoc .pchart:last-child,
.rbpv .pchart:last-child{margin-bottom:2mm}
#printdoc svg,
.rbpv svg{max-width:100%; height:auto; display:block}

/* ---- the headline row -------------------------------------------------
   The first page of this report is read standing up, in a consulting room,
   by somebody who has thirteen minutes. Six numbers set large answer the
   questions that get asked out loud before anybody turns a page. The tables
   underneath are the evidence for them, not the summary.
   Inline-block, not grid: Chrome will not fragment a grid across a printed
   page, and this row sits at the top of page one where a bad break costs
   the whole first impression. */
#printdoc .pdash,
.rbpv .pdash{font-size:0; margin:0 0 3mm}
/* A fixed height, not a min-height. These are read as a row, and a row whose
   boxes end at different depths because one of them happens to carry a
   second line reads as a broken layout rather than as a set of readings.
   The height fits the tallest card in either language; the value is pinned
   to the bottom so the numbers sit on one line across the whole row. */
#printdoc .pkpi,
.rbpv .pkpi{display:inline-block; vertical-align:top; width:35.4mm;
  height:15.6mm; box-sizing:border-box; position:relative;
  margin:0 2mm 2mm 0; padding:1.8mm 2mm; border:.5pt solid #767676;
  border-top:1.6pt solid #172A53; break-inside:avoid;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
/* last in each row of five, so the row ends flush with the text column */
#printdoc .pkpi:nth-child(5n),
.rbpv .pkpi:nth-child(5n){margin-right:0}
#printdoc .pkpi__v,
.rbpv .pkpi__v{display:block; font-size:15pt; line-height:1.05;
  font-weight:700; color:#172A53}
#printdoc .pkpi__u,
.rbpv .pkpi__u{font-size:8.5pt; font-weight:400; color:#333}
#printdoc .pkpi__k,
.rbpv .pkpi__k{display:block; font-size:7.5pt; line-height:1.2;
  margin-top:.8mm; color:#000}
#printdoc .pkpi__s,
.rbpv .pkpi__s{display:block; font-size:7pt; line-height:1.15; color:#595959}

/* ---- the body map on paper -------------------------------------------
   Same geometry as the screen, different ink. The screen fills follow the
   theme and the dark theme's severity colours wash out to nothing on white,
   so paper gets its own fixed set. Every filled site also carries its
   severity as a numeral, because this sheet gets photocopied in black and
   white and a colour-only code does not survive that. */
#printdoc .pmaps,
.rbpv .pmaps{font-size:0}
/* Three across, filling the text column exactly, and every plate the same
   box. The six views have three different aspect ratios, so left to
   themselves the frames end at three different depths and the captions
   stagger. A fixed frame with the drawing letterboxed inside it keeps the
   six plates reading as one set. */
#printdoc .pmapfig,
.rbpv .pmapfig{display:inline-block; vertical-align:top; width:61.3mm;
  margin:0 3mm 3mm 0; font-size:8pt; break-inside:avoid}
#printdoc .pmapfig:nth-child(3n),
.rbpv .pmapfig:nth-child(3n){margin-right:0}
#printdoc .pmapfig svg,
.rbpv .pmapfig svg{width:100%; height:62mm; border:.5pt solid #767676;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
#printdoc .pmapfig image,
.rbpv .pmapfig image{opacity:.5}
/* the paw plate is generated, not photographed, so it needs its own ink.
   The screen classes it carries are themed and would print as nothing. */
#printdoc .psilh .silh,
.rbpv .psilh .silh{fill:none; stroke:#767676; stroke-width:2}
#printdoc .psilh .silh--soft,
.rbpv .psilh .silh--soft{fill:none; stroke:#b0b0b0; stroke-width:1.5}
#printdoc .psilh .viewlab,
.rbpv .psilh .viewlab{fill:#333; font-size:13px; font-weight:700}
/* the unmarked sites, as the app's own dashed grid: context, never a mark.
   Pale enough that a photocopy drops them before it drops a mark. */
#printdoc .pzc,
.rbpv .pzc{fill:none; stroke:#b8b8b8; stroke-width:.9; stroke-dasharray:3 3}
#printdoc .pz,
.rbpv .pz{stroke-width:1.4; fill-opacity:.55}
#printdoc .pz[data-sev="1"],
.rbpv .pz[data-sev="1"]{fill:#D2A03C; stroke:#7A5A12}
#printdoc .pz[data-sev="2"],
.rbpv .pz[data-sev="2"]{fill:#C4652E; stroke:#6E3313}
#printdoc .pz[data-sev="3"],
.rbpv .pz[data-sev="3"]{fill:#9B2B36; stroke:#5C121A; fill-opacity:.68}
#printdoc .pzn,
.rbpv .pzn{font-size:11px; font-weight:700; fill:#000; text-anchor:middle;
  paint-order:stroke; stroke:#fff; stroke-width:2.6px}
#printdoc .pmapfig figcaption,
.rbpv .pmapfig figcaption{text-align:center; font-weight:700; color:#000}
/* the legend is what turns the plate from a picture into a record */
#printdoc .pkey,
.rbpv .pkey{font-size:7.5pt; margin:0 0 3mm}
#printdoc .pkey b,
.rbpv .pkey b{display:inline-block; width:3.2mm; height:3.2mm;
  vertical-align:-.4mm; margin-right:1mm; border:.5pt solid #333;
  -webkit-print-color-adjust:exact; print-color-adjust:exact}
#printdoc .pkey span,
.rbpv .pkey span{display:inline-block; margin-right:4mm; white-space:nowrap}
/* A key set directly against the bar it explains reads as part of the drawing
   and the two run together. Under a chart it gets its own line of air above
   and room to breathe between entries when it wraps onto a second line. */
#printdoc .pchart .pkey,
.rbpv .pchart .pkey{margin:2.4mm 0 0; line-height:1.75}
#printdoc .pchart .pkey span,
.rbpv .pchart .pkey span{margin-right:5.5mm}
#printdoc .pfoot,
.rbpv .pfoot{font-size:7.5pt; color:#333; margin-top:5mm;
  border-top:.5pt solid #767676; padding-top:1.5mm;
  /* never a page holding nothing but the footer */
  break-inside:avoid; break-before:avoid}
#printdoc .pbreak,
.rbpv .pbreak{break-before:page}
/* ==== END PAPER ======================================================== */

@media print{
  /* All three selectors, deliberately.
     A media query adds nothing to specificity, so a bare `:root` here (0,1,0)
     loses to `:root[data-theme="dark"]` (0,2,0) from the theme picker. When a
     theme had been chosen explicitly the print reset never applied: --ink stayed
     near-white against the hardcoded white paper below, and the handout printed
     as an empty skeleton. On theme "auto" the attribute is absent, which is why
     this only broke for people who had touched the setting.
     color-scheme is pinned as well, or the user agent still renders form
     controls and scrollbars dark inside the printed page. */
  :root,
  :root[data-theme="dark"],
  :root[data-theme="light"]{
        --paper:#fff; --card:#fff; --ink:#000; --ink-2:#333; --ink-3:#555;
        --line:#bbb; --line-soft:#ddd; --stain:#172A53; --stain-wash:#fff;
        color-scheme:light}
  body{padding:0; font-size:9pt; background:#fff; color:#000}
/* Named the wrong element and had done since it was written: nothing in this
     app carries .topbar, the header is .tb, and so the app's own toolbar -
     the date, the calendar button, the account disc - printed as the first
     thing on page one of every report ever handed to a vet. The walkthrough
     panel is worse: it is position:fixed, and a fixed element in paged output
     is painted onto EVERY sheet, so a first-run report came out with a nine
     step tour stamped across all five pages.

     Chasing that with a longer list of class names is how the first list got
     it wrong. There is exactly one thing on this page that is the document,
     and it is #printdoc. Everything else at body level is the application.
     Stated that way, an overlay added next year is excluded by default rather
     than by somebody remembering to come back here. */
  body > *:not(#printdoc){display:none !important}
  .noprint{display:none !important}
  @page{size:A4; margin:14mm 13mm}
  #printdoc{display:block; padding:0; color:#000}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms !important; transition-duration:.01ms !important}
}
/* sync lamp and the signed-in initial. Both are read-outs, not controls.
   The lamp keeps its words in the DOM, for the screen reader and for the
   aria-live announcement, and shows none of them. */
.netpill{ font:600 0px/1 var(--f-body); letter-spacing:.04em; text-transform:uppercase;
  padding:0; width:12px; height:12px; border-radius:var(--r-pill);
  border:2px solid var(--line); white-space:nowrap; flex:none; overflow:hidden; }
.netpill[data-s="ok"]{ border-color:color-mix(in srgb, var(--sev0) 45%, transparent);
  background:var(--sev0-ink); }
.netpill[data-s="queued"]{ border-color:color-mix(in srgb, var(--sev1) 55%, transparent);
  background:var(--sev1-ink); }
.netpill[data-s="off"]{ border-color:color-mix(in srgb, var(--sev3) 55%, transparent);
  background:var(--sev3-ink); }
.whopill{ display:inline-grid; place-items:center; width:32px; height:32px; border-radius:var(--r-pill);
  background:var(--stain); color:var(--on-accent); font:600 12px/1 var(--f-body); flex:none; }
/* ============================================================
   PHASE 4. The adaptive day, the flare flow and guided capture.
   Everything below drives markup added in js/19-adapt.js,
   js/20-flare.js and js/21-capture.js. Nothing here restyles an
   existing component: the cards, chips and sheets are the ones
   that were already there, and these are the containers that
   decide when they are on screen.
   ============================================================ */

/* ---------- collapsible blocks ----------
   A fold is not a disappearance. Every folded block still states in one line
   what is inside it, which is the difference between "nothing to see" and
   "nobody looked". */
.blk{
  background:var(--card); border:1px solid var(--line-soft); border-radius:var(--r-lg);
  box-shadow:var(--shadow); overflow:hidden;
}
.blk__hd{
  appearance:none; width:100%; background:none; border:0; cursor:pointer;
  display:flex; align-items:center; gap:12px; text-align:left;
  padding:12px 16px; min-height:48px; color:var(--ink);
}
.blk__t{font-weight:600; font-size:.94rem; flex:none}
.blk__s{
  flex:1 1 auto; min-width:0; color:var(--ink-3); font-size:11px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right;
}
.blk__c{flex:none; color:var(--ink-3); transition:transform .16s ease}
#shotSkip{display:inline-flex; align-items:center; gap:4px}
.blk[data-open="1"] .blk__c{transform:rotate(90deg)}
.blk[data-open="0"] .blk__b{display:none}
.blk__b{
  display:flex; flex-direction:column; gap:16px;
  padding:0 16px 16px;
}
/* Inside a block the cards already sit on a card, so they lose their own shell
   and keep only their spacing. Two nested boxes read as a bug. */
.blk__b > .card{box-shadow:none; border-color:var(--line-soft); padding:12px}

/* ---------- the urgent interrupt ----------
   Four signs out of sixty-three do not wait, and the answer to any of them must
   never be a saved day and a closed app. Save is held until it is read. */
.urg{
  border:2px solid var(--sev3); border-radius:var(--r-lg); background:var(--card);
  padding:12px 16px; display:flex; flex-direction:column; gap:8px;
}
.urg[data-ack="1"]{border-width:1px; opacity:.8}
.urg__t{font-weight:700; color:var(--sev3-ink); font-size:1rem}
.urg__l{margin:0; padding-left:20px; color:var(--ink); font-size:.9rem; line-height:1.5}
.urg__n{font-size:.8rem; color:var(--ink-2); line-height:1.45}

/* ---------- somebody else is writing ----------
   It announces and never applies. A banner that reloads the field under a thumb
   is how one person's afternoon gets overwritten by the other's evening. */
.live{
  border:1px solid var(--stain-soft); border-left:3px solid var(--stain);
  border-radius:var(--r); background:var(--stain-wash);
  padding:12px 12px; display:flex; align-items:center; gap:12px; flex-wrap:wrap;
}
.live__t{flex:1 1 auto; min-width:0; font-size:.84rem; color:var(--ink-2)}

/* Who wrote today, and the initial that appears on a value only when the other
   person wrote it. Marking everything is noise; marking nothing is a mystery. */
.daywho{font-size:.78rem; color:var(--ink-3)}
.byi{
  display:inline-grid; place-items:center; width:16px; height:16px; margin-left:4px;
  border-radius:var(--r-pill); background:var(--stain); color:var(--on-accent);
  font:600 10px/1 var(--f-body); vertical-align:middle; flex:none;
}
/* The same mark, leading a row instead of trailing a value. Deck 10 F1 puts
   the initial in front of the bowl, and a second component that also means
   "this person" is how one screen ends up disagreeing with the next. */
.byi--lead{margin-left:0; width:20px; height:20px; font-size:11px}
.hint--blind{color:var(--stain)}

.calibq{
  border:1px dashed var(--edge); border-radius:var(--r); padding:12px 12px;
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
}
.calibq__t{flex:1 1 auto; min-width:0; font-size:.84rem; color:var(--ink-2)}

/* The nutrient sheet's title and its state on one line, deck 15 N3. The tag
   wraps under the name rather than squeezing it, because "Calcium to
   phosphorus" is already two words longer than anything else in the list. */
.nut__h{display:flex; align-items:center; flex-wrap:wrap; gap:var(--sp-6) var(--sp-8)}
.nut__h h3{margin:0}

/* Deck 02 S1. The four that mean tonight, in a card with the grade-three edge
   on it, above the ten groups that mean mention it at the appointment. */
.card.sgurg{border:1px solid var(--sev3)}
.sgurg .sgrp__h > span:first-of-type{color:var(--sev3)}

/* ---------- something is wrong ----------
   It was fixed, then sticky, and both of those are ways of saying permanently
   visible. Sticky is the politer of the two - it has its own line to come to
   rest on - but while the day is longer than the screen it still rides the
   bottom, and what is under the bottom of the day is the note field. A pill
   sitting on the words being typed is the complaint, whatever the position
   property is called.

   So it rests where it belongs: at the end of the day, after Save, in flow.
   Something is wrong is not an emergency stop, it is a shortcut into four
   steps that are also reachable the ordinary way, and it does not earn a
   permanent place on the screen. */
.flarebtn{
  position:static; appearance:none; cursor:pointer;
  display:flex; align-items:center; justify-content:center; gap:8px;
  width:100%; min-height:52px; padding:0 20px;
  border:1px solid var(--eosin); border-radius:var(--r-pill);
  background:var(--eosin); color:var(--on-eosin);
  font:600 .95rem/1 var(--f-body); letter-spacing:.01em;
  box-shadow:0 6px 22px color-mix(in srgb,var(--eosin) 34%,transparent);
}
.flarebtn:hover{filter:brightness(1.06)}

/* ---------- stools ----------
   One line each in the day, and the whole editor in a sheet that states the
   direction of the scale before anything else. */
.srow{
  appearance:none; width:100%; cursor:pointer; text-align:left;
  display:flex; align-items:center; gap:12px;
  border:1px solid var(--line-soft); border-left:3px solid var(--line);
  border-radius:var(--r); background:var(--paper); padding:12px 12px; min-height:44px;
  color:var(--ink); font-size:.88rem;
}
.srow[data-sev="0"]{border-left-color:var(--sev0)}
.srow[data-sev="1"]{border-left-color:var(--sev1)}
.srow[data-sev="2"]{border-left-color:var(--sev2)}
.srow[data-sev="3"]{border-left-color:var(--sev3)}
.srow__t{font-family:var(--f-data); font-variant-numeric:tabular-nums; color:var(--ink-3); flex:none}
.srow__v{font-weight:600; flex:none}
.srow__x{color:var(--ink-3); font-size:.78rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right}

/* The band picker. Five printed grades, best first, and the sentence above them
   says which end is good. Everything about this scale is counter-intuitive and
   none of it can be left to the reader. */
.bands{display:flex; flex-direction:column; gap:8px}
.band{
  appearance:none; cursor:pointer; text-align:left;
  display:flex; align-items:center; gap:12px; min-height:48px; padding:0 12px;
  border:1px solid var(--line); border-radius:var(--r); background:var(--card); color:var(--ink);
}
.band[data-on="1"]{border-color:var(--stain); box-shadow:inset 0 0 0 1px var(--stain)}
.band__v{
  font-family:var(--f-data); font-variant-numeric:tabular-nums; font-weight:700;
  font-size:1rem; width:32px; flex:none; color:var(--ink-2);
}
.band__t{font-size:.88rem}

/* ---------- shared small parts ---------- */
/* A micro-heading, and headings are blocks. It read correctly everywhere it
   was the first child of a .card, because a flex column blockifies its
   children anyway; inside a plain div the next element ran into it and the
   invitation sheet printed EXPIRES17.08.2026. */
.lbl{
  display:block;
  font-size:10.5px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-3);
  font-weight:600; margin:0 0 var(--sp-4);
}
.mut{font-size:12px; color:var(--ink-3); line-height:1.45}
/* A role or a state, never a sentence. Wrapping one turns a pill into a
   paragraph with a rounded border, which is what the Romanian for sitter did. */
.tag{
  display:inline-block; white-space:nowrap;
  font:600 9.5px/1.4 var(--f-body); letter-spacing:.06em;
  text-transform:uppercase; padding:4px 8px; border-radius:var(--r-sm);
  background:var(--stain-wash); color:var(--stain); align-self:flex-start;
}
/* A colour swatch on a term chip, so the word and the plate agree at a glance.
   Twenty-three terms is more than anyone holds in their head. */
.sw{
  display:inline-block; width:8px; height:8px; border-radius:var(--r-xs); margin-right:8px;
  vertical-align:baseline; background:currentColor; flex:none;
}
.chip--g{border-style:dashed; color:var(--ink-3)}
/* The card that says exactly what is about to be written. Nothing above it has
   touched the record. */
.c{
  border:1px solid var(--line); border-radius:var(--r); background:var(--paper);
  padding:12px 12px; display:flex; flex-direction:column; gap:8px;
}
.row2{display:flex; align-items:baseline; gap:12px; font-size:.86rem}
.row2 > :first-child{font-weight:600; flex:none}
.row__x{margin-left:auto; font-family:var(--f-data); color:var(--ink-3); font-size:.78rem; flex:none}
.tgloss{font-size:.82rem; color:var(--ink-2); line-height:1.5}
.tgloss strong{color:var(--ink)}

/* Segmented control. The view menu on the plate, and every two-or-three-way
   switch added from here on. */
.seg{
  display:flex; gap:4px; padding:4px; border-radius:var(--r-pill);
  border:1px solid var(--line-soft); overflow-x:auto;
  /* The six-way view rail on the flare sheet shows four of its six on a 390
     phone and hid the other two in silence: the rail ends flush at the edge
     with nothing to say there is more, so half the body regions were simply
     not findable. Audited 08.08.
     These four layers are the standard scroll shadow and need no script. The
     two drawn `local` scroll with the content and sit over the shadow at
     whichever end the rail is currently against; the two drawn `scroll` stay
     put. On a rail that fits, both covers sit over both shadows and nothing is
     visible, which is why this can go on every .seg and not just the long one. */
  background:
    linear-gradient(to right, var(--paper), transparent) 0 0 / 24px 100% no-repeat local,
    linear-gradient(to left,  var(--paper), transparent) 100% 0 / 24px 100% no-repeat local,
    linear-gradient(to right, var(--line), transparent) 0 0 / 16px 100% no-repeat scroll,
    linear-gradient(to left,  var(--line), transparent) 100% 0 / 16px 100% no-repeat scroll;
  background-color:var(--paper);
}
/* Nine of these choose the report's language, its window, and how busy his
   day was. 34px each, shoulder to shoulder inside a 4px-padded rail. */
.seg button{
  appearance:none; border:0; background:none; cursor:pointer; white-space:nowrap;
  padding:var(--s2) var(--s3); border-radius:var(--r-pill); color:var(--ink-3);
  font:600 .8rem/1 var(--f-body); min-height:var(--tap-min);
  display:inline-flex; align-items:center; justify-content:center;
}
.seg button.on{background:var(--card); color:var(--ink); box-shadow:var(--shadow)}
/* Equal shares of the rail rather than each button as wide as its own word.
   Three answers to one question are three choices of the same weight, and a
   rail where one is twice the width of its neighbour reads as a recommendation.
   Only where the choices ARE equal: the report's window rail is not, because
   "Custom" is genuinely the odd one out there. */
/* The wrap matters as much as the flex. .seg button is nowrap because every
   other rail in the app holds one short word, and "A vrut acasă" on a 320px
   phone is 90px inside a 90px share: nowrap would push the rail into its own
   horizontal scroll, which is a scrollbar under a three-choice question. */
.seg--even button{flex:1 1 0; min-width:0; white-space:normal; line-height:1.15; padding-inline:var(--s2)}

/* The one slider outside .vas, and the only one that was left at its native
   16px track. Scoring the same dog is a two-person exercise done on a phone
   held by somebody who is not the one who wrote the app. */
#calibBox input[type="range"]{
  -webkit-appearance:none; appearance:none; width:100%; height:var(--tap-min);
  background:transparent; cursor:pointer;
}

/* The plate inside a sheet. Same drawing, same hit targets, same fractional
   coordinates: sending somebody to the Map tab to answer one question is how
   this became three tabs in the first place. */
.plate--sm{max-width:340px; margin:0 auto}
.plate .zn[data-pick="1"]{stroke:var(--stain); stroke-width:calc(2.2px * var(--k,1)); opacity:1}
.flpick{font-size:.84rem; color:var(--ink-2); text-align:center}
.flframe{font-weight:600}
.flcmp{display:flex; align-items:flex-start; gap:12px; flex-wrap:wrap}
/* Deck 02 F3. The empty half of the pair, which is a target and has to look
   like one: dashed, so it never reads as a photograph that failed to load. */
.flcmp__new{
  width:64px; height:64px; border-radius:var(--r-sm); flex:none;
  border:1px dashed var(--eosin); background:transparent; color:var(--eosin);
  display:grid; place-items:center; cursor:pointer; padding:0;
  font:400 22px/1 var(--f-body);
}
.flcmp__new:active{background:var(--stain-wash)}

/* ---------- guided capture ----------
   The instruction on the viewfinder and the last photograph of the same frame
   ghosted inside it. Comparability is the entire clinical value of these
   photographs, and it was being left to memory. */
/* The tall variant, and the one the notch actually bit: 96dvh leaves 4dvh of
   clearance, which is under 35 on a phone whose status bar is 59. Same clamp
   as the base panel, for the same reason. See the note above .sheet. */
.sheet--full .sheet__panel{max-height:calc(96dvh - env(safe-area-inset-top))}
.vf{
  position:relative; width:100%; aspect-ratio:3/4; max-height:52dvh;
  border-radius:var(--r-lg); overflow:hidden; background:var(--vf-back);
  display:flex; align-items:center; justify-content:center;
}
.vf video{width:100%; height:100%; object-fit:cover; display:block}
#shotSheet[data-live="0"] .vf video{display:none}
#shotSheet[data-live="1"] .vf__fb{display:none}
.vf__fb{padding:20px; display:flex; flex-direction:column; align-items:center; gap:12px}
.ghost{
  position:absolute; inset:0; pointer-events:none; opacity:.42;
  display:flex; align-items:center; justify-content:center;
}
.ghost img{width:100%; height:100%; object-fit:cover; display:block}
.ghost[data-off="1"]{display:none}
/* The filmstrip. Five frames, the ones already taken marked, so the count is a
   fact on the screen rather than something to hold in your head. */
.film{display:flex; gap:8px; overflow-x:auto; padding-bottom:4px}
.film__i{
  appearance:none; cursor:pointer; flex:none; white-space:nowrap;
  min-width:48px; min-height:48px; padding:0 12px;
  border:1px solid var(--line); border-radius:var(--r); background:var(--card);
  color:var(--ink-3); font:600 .72rem/1.3 var(--f-body);
}
.film__i[data-st="on"]{border-color:var(--stain); color:var(--stain); box-shadow:inset 0 0 0 1px var(--stain)}
.film__i[data-st="dn"]{border-color:var(--sev0); color:var(--sev0-ink)}
.tips{margin:0; padding-left:20px; font-size:.82rem; color:var(--ink-2); line-height:1.55}
.row--shut{justify-content:center}
.shut{
  appearance:none; cursor:pointer; width:64px; height:64px; border-radius:var(--r-pill);
  border:3px solid var(--eosin); background:var(--card);
  box-shadow:inset 0 0 0 4px var(--card), inset 0 0 0 30px var(--eosin);
}
.shut:active{transform:scale(.95)}

/* ---------- then and now ----------
   Two dated photographs and everything the projection already knows about the
   days between them. No new field, no new table: this screen is the whole
   reason the projection exists. */
.cmp{display:grid; grid-template-columns:1fr 1fr; gap:12px}
.cmp__c{display:flex; flex-direction:column; gap:8px; min-width:0}
.cmp__c img{width:100%; border-radius:var(--r); display:block}
/* The pair IS the comparison, and a comparison of two 64px squares is not one.
   thumbEl draws a fixed 64px tile because that is the right size in a strip of
   forty of them; here the tile is the whole content of its column, so it takes
   the column's width and keeps its square. The rule underneath this one has
   been setting width:100% on the img since the screen was written, but the img
   sits inside that fixed tile and never saw a pixel of it. */
.cmp__c .thumb{width:100%; height:auto; aspect-ratio:1; border-radius:var(--r)}
/* The same two rings the strip and the radar legend use, so all three name the
   same two days in the same way. Without them the pair is two undifferentiated
   photographs and only their order says which is which, which is one
   convention more than anybody should have to hold. */
.cmp__c[data-now="1"] .thumb{outline:3px solid var(--eosin); outline-offset:2px}
.cmp__c[data-now="0"] .thumb{outline:3px solid var(--ink-3); outline-offset:2px}
.cmp__d{font-family:var(--f-data); font-weight:700; font-size:.85rem}
.cmp__all{display:flex; gap:8px; overflow-x:auto; padding-bottom:4px}
.cmp__all .thumb{flex:none}
/* Which two of them are on the screen above. The strip is the index and the
   index has to say where you are, or moving a handle changes two pictures with
   no visible cause. Same eosin the "now" column already carries, and the muted
   ink for the earlier one, so the strip, the pair and the radar all name the
   two ends the same way. Outline rather than border: a border would resize the
   thumb and shuffle the whole row sideways on every tap. */
.cmp__all .thumb[data-sel="b"]{outline:3px solid var(--eosin); outline-offset:2px}
.cmp__all .thumb[data-sel="a"]{outline:3px solid var(--ink-3); outline-offset:2px}

/* The two handles. A label and a rail per row rather than one range control
   with two thumbs, because a dual-thumb range does not exist natively and the
   app already owns exactly one slider engine. */
.cmp__scrub{display:flex; flex-direction:column; gap:var(--s4)}
.cmp__rail{display:flex; flex-direction:column; gap:var(--s1)}
/* A date is not a grade. Every other slider here runs green to red because it
   is answering "how bad", and under a photograph that gradient reads as a
   verdict on the picture: a shot taken in June is not worse than one taken in
   May. Flat track, and a date printed at reading size instead of at the 32px
   the numerals get. */
.sld--plain .sld__i::-webkit-slider-runnable-track{background:var(--line)}
.sld--plain .sld__i::-moz-range-track{background:var(--line)}
/* Body font and not the monospace the numerals get: tabular figures exist so a
   score does not jump sideways while a thumb is dragged, and applied to
   "6 Aug" they open a gap between the day and the month wide enough to read as
   two separate things. */
.sld--plain .sld__v{
  font-family:var(--f-body); font-size:1.05rem; font-weight:700;
  font-variant-numeric:normal;
}
.sld--plain .sld__top{min-height:24px}

/* ---------- the radar ----------
   Eleven body areas on eleven spokes, two days drawn over each other. The svg
   scales to whatever column it lands in, so it is the one thing on this sheet
   that needs no breakpoint. */
/* Capped, because the labels are drawn INSIDE the viewBox and therefore scale
   with it. At 820 an uncapped svg is ~560px across, the 11px label renders at
   20px, and eleven body parts end up shouting louder than the heading above
   them. The cap is the same 340 the plate uses, so the radar and the body map
   are the same object at the same size on a wide screen. */
.rad{width:100%; max-width:340px; height:auto; display:block; margin:var(--s2) auto 0}
.rad__t{font-family:var(--f-body); font-size:11px; font-weight:600}
.rad__lg{
  display:flex; flex-wrap:wrap; gap:var(--s3);
  margin-top:var(--s2); font-size:.78rem; color:var(--ink-2);
}
.rad__k{display:inline-flex; align-items:center; gap:var(--s1)}
.rad__k i{width:12px; height:12px; border-radius:var(--r-pill); display:block}

/* The itch re-ask on the confirm step. Same control, smaller, because the flare
   is the reason to reconsider a number set this morning. */
.vas--sm .vas__val{font-size:1.6rem}

/* ============================================================
   PHASE 5. Anchors and Review.
   ============================================================ */

/* The description of the score you just chose, under the scale. It reserves its
   height whether or not a value has been picked, so the page does not jump the
   first time the slider is touched, and it is sized on the Romanian text, which
   runs about a quarter longer than the English. */
.anc{
  display:flex; gap:12px; align-items:flex-start;
  min-height:76px; padding:8px 12px; margin-top:4px;
  border-radius:var(--r); border:1px solid var(--line-soft);
  background:var(--paper);
  cursor:pointer; -webkit-tap-highlight-color:transparent;
  transition:background .16s ease, border-color .16s ease, opacity .16s ease,
             min-height .16s ease, padding .16s ease;
}
/* Unscored, this kept its full box and merely went transparent, which left a
   blank strip the height of a card under the itch slider and under the stool
   slider on every screen nobody had answered yet. An empty box that large reads
   as something that failed to load. It collapses instead, and grows back with
   the easing the fade already used, so an answer still does not jolt the page
   under a thumb that is mid-drag. */
.anc[data-on="0"]{
  opacity:0; pointer-events:none; border-color:transparent; background:transparent;
  min-height:0; padding-block:0; margin-top:0; overflow:hidden;
}
.anc:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.anc__n{
  flex:none; width:24px; height:24px; padding:0;
  display:flex; align-items:center; justify-content:center;
  font:600 12px/1 var(--f-data); font-variant-numeric:tabular-nums;
  border-radius:var(--r-sm); border:1px solid transparent;
  background:var(--sev0); color:var(--on-sev0);
}
.anc[data-sev="1"] .anc__n{background:var(--sev1); color:var(--on-sev1)}
.anc[data-sev="2"] .anc__n{background:var(--sev2); color:var(--on-sev2)}
.anc[data-sev="3"] .anc__n{background:var(--sev3); color:var(--on-sev3)}
.anc__t{display:flex; flex-direction:column; gap:4px; min-width:0}
.anc__t b{font-size:.86rem; color:var(--ink); font-weight:600; line-height:1.35}
.anc__t span{font-size:.78rem; color:var(--ink-3); line-height:1.4}
.anc__x{color:var(--ink-2) !important; font-style:italic}
/* Same severity colour as the scale that produced the number, so the anchor and
   the control read as one thing rather than two. */
.anc[data-sev="0"]{border-color:color-mix(in srgb, var(--sev0) 40%, var(--line-soft)); background:color-mix(in srgb, var(--sev0) 8%, var(--paper))}
.anc[data-sev="1"]{border-color:color-mix(in srgb, var(--sev1) 40%, var(--line-soft)); background:color-mix(in srgb, var(--sev1) 8%, var(--paper))}
.anc[data-sev="2"]{border-color:color-mix(in srgb, var(--sev2) 40%, var(--line-soft)); background:color-mix(in srgb, var(--sev2) 8%, var(--paper))}
.anc[data-sev="3"]{border-color:color-mix(in srgb, var(--sev3) 45%, var(--line-soft)); background:color-mix(in srgb, var(--sev3) 9%, var(--paper))}
/* The number used to be severity-coloured type on the card; page 11 fills the
   square with the severity instead, so the ink is now the on-severity token
   and lives with the fill three rules above. Setting the ink here as well is
   what painted #B75E2B on #B75E2B and left the square blank. */

/* ---------------- coverage ---------------- */
.card--cov{gap:12px}
.cov__ring{width:56px; height:56px; flex:none}
.cov__t{font-size:1rem; font-weight:600; color:var(--ink); line-height:1.3}

/* ---------------- the small charts ----------------
   One SVG per measure, all sharing the window and the day width, so two of them
   stacked read against the same calendar. */
.rchart{width:100%; height:auto; display:block; overflow:visible}
.rchart__gap{opacity:.3}
.rchart__x{font:500 8px/1 var(--f-data)}

/* ---------------- observation rows ----------------
   Used by Worth a second look, the standing changes, the record summary and the
   photo gaps. One row shape rather than four, because they are all the same
   thing: a label, a fact, and what it is about. */
.orow{
  display:flex; gap:12px; align-items:flex-start;
  padding:var(--row-y) 0; border-top:1px solid var(--line-soft);
}
.orow:first-of-type{border-top:0}
/* a row that opens something says so before it is pressed, and gives a finger
   the whole row rather than the chevron */
.orow--tap{cursor:pointer; margin:0 -8px; padding-left:8px; padding-right:8px; border-radius:var(--r-sm)}
.orow--tap:hover{background:var(--line-soft)}
.orow--tap:focus-visible{outline:2px solid var(--stain); outline-offset:1px}
.orow .grow{min-width:0}
.orow b{display:block; font-size:.86rem; font-weight:600; color:var(--ink); line-height:1.35}
/* The second line of a row, wherever a row shape is used: inside .orow, and
   inside the plain .row the day record's header uses. */
.sub{display:block; font-size:.78rem; color:var(--ink-3); line-height:1.4; margin-top:1px}
/* The chip here carries a date or a span of days rather than a label, so it
   keeps the chip shape and drops the shouting. */
.orow .tag{margin-top:1px}
.row__x{
  flex:none; font:400 11.5px/1.4 var(--f-data); color:var(--ink-3);
  font-variant-numeric:tabular-nums; margin-top:4px;
}
/* Not every .row__x is tappable; the ones that are carry --act, and those are
   the ones that need the reach. Small on purpose: it is the quiet action at
   the end of a row whose subject is the appointment, not the verb. */
.row__x--act{position:relative}
.row__x--act::after{
  content:""; position:absolute; left:50%; top:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
}
.row__x > svg{display:block; margin-top:1px}
.tag--e{background:var(--eosin-wash); color:var(--eosin-ink)}
.tag--n{background:var(--line-soft); color:var(--ink-3)}
.zsw{flex:none; width:12px; height:12px; border-radius:var(--r-xs); margin-top:4px; border:1px solid var(--edge)}

/* A card that carries an accent because something in it is the point of the
   screen, and a dashed one for something absent rather than present. */
.card--acc{border:1.5px solid var(--stain)}
/* Deck 04 R2 draws the card listing deliberate changes in eosin, matching the
   vertical marks those same changes put on the charts above it. It is a
   different claim from .card--acc: not "this is the point of the screen" but
   "this is the same event as that line". */
.card--eos{border:1.5px solid var(--eosin); background:var(--eosin-wash)}
.card--dash{border-style:dashed; background:transparent; box-shadow:none}
/* An action is not another paragraph. Body copy that offers a button gets
   more air above it than two paragraphs get between them, or the button reads
   as the end of the sentence rather than a thing you can press. */
.card > p + .btn, .card > p + .duo, .card > p + .row,
.card > .mut + .btn, .card > .empty + .btn{ margin-top:var(--sp-2) }
.card--tight{gap:0}

/* before and after a change */
.cell--worse{color:var(--bad); font-weight:600}
.cell--better{color:var(--ok-ink); font-weight:600}

/* ---------------- the photo archive ---------------- */
.pgrid{display:grid; grid-template-columns:repeat(3,1fr); gap:8px}
.pgrid__c{display:flex; flex-direction:column; gap:4px}
.pgrid__c .thumb{width:100%; aspect-ratio:1; margin:0}
.pgrid__c .thumb img{width:100%; height:100%; object-fit:cover}
.pgrid__c[data-now="1"] .thumb{outline:2px solid var(--eosin); outline-offset:1px}
.pgrid__d{font:500 .7rem/1.2 var(--f-data); color:var(--ink-3); text-align:center; margin:0}

/* ---------------- the day record ---------------- */
.drbig{
  flex:none; display:flex; flex-direction:column; align-items:center; justify-content:center;
  width:64px; height:64px; border-radius:var(--r);
  background:var(--paper); border:1px solid var(--line);
}
.drbig b{font:700 1.7rem/1 var(--f-data); font-variant-numeric:tabular-nums}
.drbig span{font-size:.62rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3); margin-top:4px}
.drbig[data-sev="0"]{border-color:var(--sev0); color:var(--sev0-ink)}
.drbig[data-sev="1"]{border-color:var(--sev1); color:var(--sev1-ink)}
.drbig[data-sev="2"]{border-color:var(--sev2); color:var(--sev2-ink)}
.drbig[data-sev="3"]{border-color:var(--sev3); color:var(--sev3-ink)}

/* the wedge for a day a standing fact changed, drawn on the cell itself so it
   survives the severity background underneath it */
.cal__d[data-chg="1"]::before{
  content:""; position:absolute; top:0; right:0;
  border-width:0 7px 7px 0; border-style:solid;
  border-color:transparent var(--eosin) transparent transparent;
}

/* ============================================================
   PHASE 6. Providers and appointments.

   Nothing here is a new idea. Every component is the existing card, row, chip
   and tag vocabulary with the two shapes this mode actually needed added to it:
   a row that leads with a date, and a switch you can hit with a thumb while
   holding a dog.
   ============================================================ */

/* A row that leads with a date or a short tag, then two lines, then one thing
   on the right. Used by every list on the clinic side. */
/* flex-start put the 21 high date tag and the 22 high "programează" button at
   the top of a row whose middle column is two lines and 57 high, so both of
   them floated with about 35 of dead space under them. Three heights on one
   line, none of them agreeing, on the three vaccination rows a vet reads
   first. Centred they read as one row. Measured 08.08. */
.aprow{
  display:flex; gap:12px; align-items:center; width:100%;
  padding:8px 0; border-top:1px solid var(--line-soft);
  background:none; border-left:0; border-right:0; border-bottom:0;
  text-align:left; font:inherit; color:inherit;
}
/* align-items on the row is not enough on its own: .tag carries its own
   align-self:flex-start, which is right everywhere else it is used and is what
   held the date at the top here regardless of the row. Both the tag and the
   action button also carry a top margin meant for a stacked layout. Undone for
   this row only. */
.aprow > *{align-self:center; margin-top:0}
.aprow:first-of-type{border-top:0}
button.aprow{cursor:pointer}
button.aprow:active{background:var(--stain-wash)}
.aprow > .tag{flex:none; margin-top:4px; min-width:40px; text-align:center}
/* The one this sheet is currently editing. A list where the reader has to
   remember which row they tapped is a list that gets the wrong one saved. */
.aprow[data-on="1"]{background:var(--stain-wash); box-shadow:inset 2px 0 0 var(--stain)}
.aprow[data-on="1"] b{color:var(--stain)}
.aprow b{display:block; font-size:.86rem; font-weight:600}
.aprow .hub__c{flex:none; width:16px; height:16px; margin-top:4px; color:var(--ink-3)}

/* A right-hand cell that is a control rather than a number. It is the only
   place in a list where something is tappable on its own. */
/* "programează" was 12.48px mono in --stain with no border, no fill and no
   underline, sitting beside "Nenotat niciodată" at 12.48px in --ink-3. The
   three actions on that page were the same size and shape as the metadata next
   to them, so the only things you can do on the screen read as things you can
   only look at. The 44 hit area was already there and correct; nothing said so.
   A quiet ghost pill: still secondary, now visibly a control. Audited 08.08. */
.row__x--act{
  background:none; border:1px solid var(--line); border-radius:var(--r-pill);
  padding:4px 12px; cursor:pointer;
  color:var(--stain); font:600 .78rem var(--f-data); letter-spacing:.02em;
}

/* The header line inside a card: a label, a title, and a count on the right. */
.aphd{display:flex; gap:12px; align-items:baseline}
.aphd__t{font:400 1.05rem/1.25 var(--f-display); margin:1px 0 0}
.aphd__r{font:400 .8rem/1 var(--f-body); font-style:italic; color:var(--ink-3)}
/* The same right-hand slot when it carries a count rather than a role. A pill
   beside a heading reads as a status, and these are only ever a number of
   things, so they are set as quiet upright type instead. */
.aphd__n{flex:none; font:400 .69rem/1 var(--f-body); font-style:normal; color:var(--ink-3)}
.aphd > .tag{flex:none}

/* Two card variants the clinic side needed. Warn is what is due and unbooked,
   and the emergency provider. Flat is a grouping that is not a card in its own
   right and should not draw a second border inside one. */
.card--warn{border:1.5px solid var(--sev2); background:var(--card)}
.card--flat{box-shadow:none; border-color:var(--line-soft)}
/* Deck 06, W4. A flat card that still has something in it takes the solid line
   back, so the one-line entry beside it reads as lighter on purpose. */
.card--line{border-color:var(--line)}

/* A labelled switch. Big enough to hit without looking, because half of these
   are set in a waiting room. */
/* Deck 09, H5. Three roles and exactly one answer. A switch on each row says
   they are independent and can all be left on, which is the wrong promise for
   a question that has one answer; the ring says so without a word of copy. */
.rdo{
  width:20px; height:20px; flex:0 0 19px; border-radius:50%;
  border:1.5px solid var(--line-strong); background:var(--card);
  display:inline-block; position:relative;
}
.rdo--on{border-color:var(--stain)}
.rdo--on::after{
  content:""; position:absolute; inset:3.5px; border-radius:50%; background:var(--stain);
}
.swrow{
  display:flex; gap:12px; align-items:center; width:100%;
  padding:var(--row-y) 0; border-top:1px solid var(--line-soft);
  background:none; border-left:0; border-right:0; border-bottom:0;
  text-align:left; font:inherit; color:inherit; cursor:pointer;
}
.swrow:first-of-type{border-top:0}
.swrow b{display:block; font-size:.86rem; font-weight:600}
.knob{
  flex:none; width:44px; height:24px; border-radius:var(--r-pill);
  background:var(--line); border:1px solid var(--line); position:relative;
  transition:background .12s linear, border-color .12s linear;
}
.knob::after{
  content:""; position:absolute; top:2px; left:2px; width:20px; height:20px;
  border-radius:50%; background:var(--card);
  box-shadow:0 1px 2px rgba(0,0,0,.28);
  transition:transform .12s ease-out;
}
.knob--on{background:var(--stain); border-color:var(--stain)}
.knob--on::after{transform:translateX(17px)}

/* A labelled group inside a sheet. */
.fgrp{display:flex; flex-direction:column; gap:var(--field-gap); margin-top:4px}

/* The questions list. One line each, removable, and never more than twenty
   because past that it is a document and not a list of things to ask. */
.qlist{display:flex; flex-direction:column; gap:8px}
.qrow{display:flex; gap:var(--field-gap); align-items:center}
.qrow input{flex:1 1 auto; min-width:0}

/* Two numbers side by side, read at arm's length across a consulting table. */
.sgrid{display:grid; grid-template-columns:1fr 1fr; gap:var(--field-gap); margin-top:4px}
.sgrid__c{
  border:1px solid var(--line); border-radius:var(--r); background:var(--paper);
  padding:12px 12px; display:flex; flex-direction:column; gap:4px;
}
.sgrid__c b{font:700 1.6rem/1 var(--f-data); font-variant-numeric:tabular-nums}
.sgrid__c span{font-size:.62rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3)}

/* The one sentence you say first. Large, because it is read once and out loud. */
.rbig{font:400 1.15rem/1.4 var(--f-display); margin:4px 0 0}

/* Adherence. Three states, three colours, never merged: given, recorded as
   missed, and a day nobody answered. The third is the most common and the one
   a percentage silently swallows. */
/* Fifty-six days in about 325 of card. At a 4 gap that left 220 of gap and
   1.9 of bar, so the strip was mostly the space between it, and the one
   day with a dose recorded stood 32 tall against 12 for the rest, which
   reads as a text cursor rather than as a reading. A 2 gap is the app's
   own hairline pair and gives the bars about 3.8 each, and every bar is
   now the same height so the colour is the only thing carrying the state,
   which is what the legend beside it promises. Audited 08.08. */
.adh{display:flex; gap:2px; align-items:stretch; height:24px}
.adh__b{flex:1 1 0; border-radius:var(--r-xs); background:var(--line); height:100%}
.adh__b[data-v="1"]{background:var(--sev0)}
.adh__b[data-v="0"]{background:var(--sev3)}
.adh__b[data-v="-1"]{background:var(--line)}

/* Call now. The only button in the app that assumes you are panicking. */
.btn--e{background:var(--sev3); color:var(--on-accent); border-color:var(--sev3)}
.btn--e:active{filter:brightness(.92)}

/* The photo grid in the room marks the newest frame rather than relying on
   position, because four thumbnails in a row have no obvious reading order. */
.pgrid__c[data-now="1"] .thumb{outline:2px solid var(--eosin); outline-offset:1px}

/* A call or a directions link is a button that happens to be an anchor. Without
   this it draws with the browser's underline and its own baseline. */
a.btn{text-decoration:none; display:inline-flex; align-items:center; justify-content:center}


/* ============================================================
   Phase 7, what went in the bowl and what it was made of
   ============================================================ */

/* An empty state is a card that says why it is empty, so it keeps the dashed
   edge and loses the padding a full card needs. */
/* An action under a centred sentence is centred. It was inheriting the card's
   flex alignment and sitting hard left under text that was not, which reads as
   two components rather than one empty state. */
.c--empty .btn{margin:var(--sp-8) auto 0}
.c--empty{text-align:center; padding:20px 16px}

/* ---- the walkthrough -----------------------------------------------------
   Above the navigation bar rather than over it: every step but the first talks
   about somewhere you can go, and covering the three buttons that go there
   while explaining them would be a peculiar way to teach anybody anything.
   No backdrop for the same reason. The panel is what is raised, not the app. */
/* The layer order, written down once because it was five numbers in five
   places and the tour had the wrong one: topbar and modebar 40, nav 50, sheets
   80, splash 90, the tour 100, a toast 150, search 180, the sign-in gate 200.
   At 70 the tour sat under both the sheets and the splash, so a first sign-in
   that took a moment opened the walkthrough behind the splash screen, and the
   very first thing the app ever did was mark its own tour as seen while nobody
   could see it. It is guarded in tourMaybe() as well; this is the half that
   holds when something opens a sheet after the tour has already started. */
.tour{
  position:fixed; left:0; right:0; z-index:100;
  bottom:calc(var(--nav-h) + env(safe-area-inset-bottom));
  display:none; justify-content:center; padding:0 var(--s2) var(--s2);
  pointer-events:none;
}
.tour[data-open="1"]{display:flex}
/* The tour sits at 100 and a sheet at 80, so an open sheet used to arrive with
   the tour panel still on top of it. On the flare and the break sheets that put
   two full-width primary buttons on the screen at once, 61 apart, both reading
   "Mai departe", one belonging to the tour and one to the sheet. Two audit
   passes found it independently on 08.08. It only happens on a profile that has
   not finished the tour, which is why it survived this long. */
body:has(.sheet[data-open="1"]) .tour{display:none}
.tour__p{
  pointer-events:auto; width:100%; max-width:560px;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--r-lg); padding:var(--s3) var(--s4) var(--s4);
  display:flex; flex-direction:column; gap:var(--s2);
  box-shadow:0 -6px 28px rgba(0,0,0,.20);
}
.tour__hd{display:flex; align-items:center; gap:var(--s2)}
.tour__p h3{
  margin:0; font-family:var(--f-display); font-weight:400;
  font-size:17px; line-height:1.25;
}
.tour__p p{margin:0; font-size:.86rem; line-height:1.5; color:var(--ink-2)}
.tour__dots{flex:1 1 auto; display:flex; align-items:center; gap:4px}
.tour__dots i{
  width:4px; height:4px; border-radius:50%; background:var(--line-strong);
}
.tour__dots i[data-on="1"]{background:var(--stain); width:16px; border-radius:var(--r-xs)}
.tour__b{display:flex; gap:var(--s2); margin-top:var(--s1)}
.tour__b .btn{flex:1 1 0}
@media (prefers-reduced-motion:no-preference){
  .tour[data-open="1"] .tour__p{animation:tourIn 180ms ease-out}
}
@keyframes tourIn{from{transform:translateY(14px); opacity:0} to{transform:none; opacity:1}}

/* ---- the coat picker ---- */
.coats{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--sp-5); margin:0}
.coats__b{
  appearance:none; cursor:pointer; padding:0; height:44px; width:100%;
  border-radius:var(--r); border:1px solid var(--line); background:var(--line-soft);
  display:block; position:relative;
}
.coats__b:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
/* The chosen tile is ringed rather than ticked. A tick mark has to be dark
   enough to read on cream and light enough to read on black, and there is no
   such colour; a ring lives outside the swatch and never has to choose. */
.coats__b[aria-pressed="true"]{
  border-color:var(--ink); box-shadow:0 0 0 2px var(--card) inset, 0 0 0 1px var(--ink);
}
.coats__n{display:block; font-size:12px; color:var(--ink-2); margin-top:var(--sp-6)}
.c--empty .hint{margin:0 0 12px}

/* A bowl carries a time only if somebody knew it. The field is small and
   unlabelled because the slot beside it already says which part of the day. */
.meal__t{font:400 .82rem/1 var(--f-data); color:var(--ink-2); background:transparent;
  border:1px solid var(--line); border-radius:var(--r); padding:4px 8px; min-width:88px;
  /* measured 42px tall on the outing sheet (09.08 audit), two short of the
     floor every neighbouring control meets */
  min-height:var(--tap-min)}
.meal__t:focus{outline:2px solid var(--stain); outline-offset:1px}

/* ---- the band ----
   One nutrient as a position between a floor and a ceiling, never as a
   percentage of the floor: a percentage draws adequate and toxic as the same
   full bar, which is the opposite of what this panel is for. The floor always
   sits at 30% of the track and the ceiling always at 70%, for every nutrient
   and every unit, so short compresses into the left third, over into the right
   third, and any two rows are comparable without reading their units.

     u   the track
     i   the band. .o opens it to the right where no ceiling is published,
         .x dashes it out where there is no target at all
     em  a tick, at the floor and at the ceiling
     s   the marker. .w near an edge, .b outside the band, .u hollow for a
         nutrient nobody has measured                                        */
.bd{display:flex; align-items:center; gap:var(--sp-6); padding:var(--row-y) 0;
  border-bottom:1px solid var(--line-soft)}
.bd:last-child{border-bottom:0}
.bd > b{flex:0 0 92px; font:500 11.5px/1.25 var(--f-body); color:var(--ink)}
.bd__t{flex:1 1 auto; position:relative; height:20px; display:block; min-width:60px}
.bd__t u{position:absolute; left:0; right:0; top:8px; height:2px;
  background:var(--line-strong); border-radius:var(--r-xs); text-decoration:none}
.bd__t i{position:absolute; left:30%; right:30%; top:4px; height:12px;
  border-radius:var(--r-sm); box-sizing:border-box;
  background:var(--stain-wash); border:1px solid var(--stain-soft)}
.bd__t i.o{right:0; border-right-style:dashed; border-radius:var(--r-sm) 0 0 var(--r-sm)}
.bd__t i.x{background:none; border-style:dashed; border-color:var(--line-strong)}
.bd__t em{position:absolute; top:1px; width:1px; height:16px; background:var(--ink-3)}
.bd__t s{position:absolute; top:0; width:4px; height:20px; border-radius:var(--r-xs);
  margin-left:-4px; text-decoration:none; background:var(--ok)}
.bd__t s.w{background:var(--warn)}
.bd__t s.b{background:var(--bad)}
/* Not measured and no published target are both absences, and an absence is
   never drawn as a value. The marker hollows out at the middle rather than
   collapsing to a zero-width bar on the left, because a bar at nought reads as
   none present, and none present is a finding. Not knowing is not a finding. */
.bd__t s.u{background:none; border:1.5px dashed var(--ink-3); border-radius:var(--r-xs);
  width:12px; height:12px; top:3px; margin-left:-8px}
.bd > var{flex:0 0 62px; text-align:right; font:500 11px var(--f-data);
  font-style:normal; font-variant-numeric:tabular-nums; color:var(--ink-2)}
.bd > var.u{color:var(--ink-3); font-style:italic}
.bd[role="button"]{cursor:pointer}
.bd[role="button"]:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* The affordance. Forty rows opened a sheet and nothing on screen said so, so
   the interactivity was found by accident on 11.08 and could as easily never
   have been found at all. The value column was 62px for figures that rarely
   reach five characters, so the chevron is paid for out of that rather than
   out of the track, and the track is what the row is for. */
.bd__go{flex:0 0 14px; display:flex; align-items:center; justify-content:flex-end;
  color:var(--ink-3); margin-left:calc(var(--sp-6) * -1)}
.bd[role="button"]:hover .bd__go{color:var(--ink-2)}
.bd > var{flex:0 0 56px}

/* ---------- what to do about a row ----------
   The verdict, set apart from the reading above it. The tone stripe is the same
   four states the marker uses, so the colour on the left of this block is the
   colour of the marker the reader just looked at. */
.adv{
  border-left:3px solid var(--line-strong); padding:var(--sp-6) 0 var(--sp-6) var(--sp-8);
  display:flex; flex-direction:column; gap:var(--s2);
}
.adv[data-tone="b"]{border-left-color:var(--bad)}
.adv[data-tone="w"]{border-left-color:var(--warn)}
.adv[data-tone="k"]{border-left-color:var(--ok)}
.adv[data-tone="u"]{border-left-color:var(--ink-3)}
.adv__h b{font:600 12px/1.2 var(--f-body); letter-spacing:.04em; text-transform:uppercase; color:var(--ink)}
.adv[data-tone="b"] .adv__h b{color:var(--bad)}
.adv[data-tone="w"] .adv__h b{color:var(--warn)}
.adv > p{font:400 13px/1.55 var(--f-body); color:var(--ink-2); margin:0}
.adv__m{margin:0; padding-left:var(--sp-9); display:flex; flex-direction:column; gap:var(--s1)}
.adv__m li{font:500 13px/1.5 var(--f-body); color:var(--ink)}
/* The caveat. Smaller than the moves it qualifies, because it must not compete
   with them, and never hidden, because it is the difference between a number
   that can be acted on and one that cannot. */
.adv__c{font:400 11px/1.5 var(--f-body) !important; color:var(--ink-3) !important}

/* ---------- the citation, folded ----------
   Everything is still here. It is one tap away instead of nine lines of FEDIAF
   dry-matter reasoning sitting on top of the sentence the sheet was opened for. */
.cite--fold{padding-top:0}
.cite--fold > summary{
  cursor:pointer; list-style:none; padding:var(--sp-6) 0;
  font:500 11.5px/1.3 var(--f-body); letter-spacing:.03em; color:var(--ink-3);
  display:flex; align-items:center; gap:var(--s2); min-height:44px;
}
.cite--fold > summary::-webkit-details-marker{display:none}
.cite--fold > summary::after{
  content:""; width:8px; height:8px; margin-top:-4px;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
  transform:rotate(45deg); transition:transform .15s ease;
}
.cite--fold[open] > summary::after{transform:rotate(-135deg); margin-top:2px}
.cite--fold[open] > summary{color:var(--ink-2)}

/* ---- the count line ----
   Four numbers, and the fourth is not a failure. "Unknown" is what the record
   does not contain, and it is kept the same size as the rest so that a bowl
   half made of foods nobody has typed up cannot read as a bowl that passed. */
.cnt{display:flex; gap:var(--sp-5); flex-wrap:wrap; margin:0}
.cnt b{flex:1 1 0; min-width:104px; display:flex; flex-direction:column; gap:4px;
  padding:var(--sp-6) var(--sp-8); border-radius:var(--r);
  background:var(--card); border:1px solid var(--line-soft);
  font:400 11px var(--f-body); color:var(--ink-2)}
.cnt b i{font:400 22px/1 var(--f-display); font-style:normal;
  font-variant-numeric:tabular-nums; color:var(--ink)}
.cnt b span{font-size:11px; color:var(--ink-2)}
.cnt b.s i{color:var(--bad)}
.cnt b.o i{color:var(--warn)}
.cnt b.u i{color:var(--ink-3)}

/* A whole row that opens something. It is a button, so it gets the affordance
   of one without the frame of one. */
.row--tap{width:100%; text-align:left; background:transparent; border:0;
  border-top:1px solid var(--line-soft); padding:12px 0; cursor:pointer;
  display:flex; align-items:center; gap:12px; color:inherit; font:inherit}
.row--tap:first-child{border-top:0}
.row--tap:active{background:var(--stain-wash)}
.row--tap:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* ---- the trial strip ----
   Eight weeks as eight weeks. A break is a mark on the day it happened and
   never a reset, because the trial ends when it was always going to end. */
.cd{display:flex; gap:var(--sp-2); flex-wrap:wrap; margin:12px 0 4px}
.cd__d{flex:1 1 6px; min-width:4px; height:8px; border-radius:var(--r-xs);
  background:var(--line); box-sizing:border-box}
.cd__d[data-st="k"]{background:var(--stain)}
.cd__d[data-st="x"]{background:var(--bad)}
.cd__d[data-st="f"]{background:none; border:1px dashed var(--line-strong)}


/* ============================================================
   PHASE 8 - walks and drives
   ============================================================ */

/* ---- the big chip ----
   The prompt on the day screen is answered with a thumb, in the dark, holding a
   dog. .chip is 6px 11px and correct inside a form; this is the same chip at a
   size you can hit without looking, and it is the only place it is used. */
.chip--lg{padding:12px 16px; font-size:13.5px}
/* ---- the small chip ----
   The journal shows what was different that day as a footnote to a photograph,
   not as a control. It is never pressed. */
.chip--sm{padding:4px 8px; font-size:.72rem}
/* ---- a chip that is an answer ----
   .chip[aria-pressed="true"] is the filled state of a control, and both the
   journal and a place draw chips that were never controls: they are what
   somebody already wrote down. Same fill, without telling a screen reader that a
   span can be pressed. Deck 06 draws them filled on W4 and W5. */
.chip--on{background:var(--stain); border-color:var(--stain); color:var(--on-accent); font-weight:600}

/* ---- a comparison, when there is one worth drawing ----
   Two bars and two numbers. The fill is a background colour set from script per
   bar, because which colour a bar takes is a statement about the reading and
   belongs with the reading. The value keeps a tabular font so 3.8 and 3.7 line
   up and the eye can see they are the same. Deck 06, W5 puts the label over the
   full width and the value at the end of its own bar, which is a two column
   grid: the number belongs to the bar next to it and to nothing else. */
.bar{display:grid; grid-template-columns:1fr auto; align-items:center;
  column-gap:var(--sp-5); margin:var(--sp-7) 0}
.bar__l{grid-column:1 / -1; font-size:10.5px; color:var(--ink-3);
  font-family:var(--f-data); letter-spacing:.01em; margin:0 0 var(--sp-2)}
.bar__t{height:8px; border-radius:var(--r-pill);
  background:var(--line-soft); overflow:hidden}
.bar__f{height:100%; border-radius:var(--r-pill); min-width:3px}
.bar__v{min-width:32px; text-align:right; font-family:var(--f-data);
  font-size:.78rem; color:var(--ink-2); font-variant-numeric:tabular-nums}

/* ---- the state bar ----
   Deck page 07. Says what did not happen and what is still safe, in that order,
   and never occupies more than one line. The -wash fills are 12 percent, so the
   text takes the solid ink and clears contrast on both themes. */
.sbar{display:flex; align-items:center; gap:var(--sp-4); padding:8px 12px;
  border-radius:var(--r); font:600 11.5px/1.35 var(--f-body); margin:0 0 12px;
  background:var(--stain-wash); color:var(--stain)}
.sbar--w{background:color-mix(in srgb, var(--warn) 12%, var(--paper)); color:var(--warn-ink)}
.sbar--ok{background:color-mix(in srgb, var(--ok) 12%, var(--paper)); color:var(--ok-ink)}
.sbar--bad{background:color-mix(in srgb, var(--bad) 12%, var(--paper)); color:var(--bad-ink)}
.sbar__i{flex:none; font-size:.8rem; line-height:1; opacity:.85}
.sbar__x{flex:none; margin-left:auto; color:var(--ink-3); font-size:.9rem}
/* Only the conflict bar is a control. The others are statements, and a
   statement that looks pressable is a statement people press and then distrust
   when nothing happens. */
/* And a control has to be reachable. The statement bars are one line of 11.5px
   inside 8px of padding, which lands at 35px: fine for something you only read,
   eleven short of the minimum for something you press. The conflict bar has been
   under the threshold since it was written and the update bar would have
   inherited the same miss. */
button.sbar{width:100%; border:0; font-family:inherit; text-align:left;
  cursor:pointer; min-height:var(--tap-min)}

/* ---- who logged it ----
   Deck page 12. The record's answer to "who said this", printed under the value
   rather than beside a control, because the old header toggle was mistaken for
   a way to change it. The second observer's initial takes the eosin so two
   entries on the same day are told apart at a glance and not by reading. */
.stamp{display:inline-flex; align-items:center; gap:8px;
  font:500 .69rem/1.4 var(--f-body); color:var(--ink-3)}
.stamp i{
  display:grid; place-items:center; flex:none; font-style:normal;
  width:16px; height:16px; border-radius:50%;
  background:var(--stain); color:var(--on-accent);
  font:600 .55rem/1 var(--f-data);
}
.stamp i.stamp--alt{background:var(--eosin)}

/* ---- looked, and found nothing ----
   Two answers of equal weight on one line. Neither is the primary: recording
   that nothing happened is exactly as much of an observation as recording that
   something did, and making one of them a full-width button would say
   otherwise. */
.duo{display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-5); margin-top:var(--sp-5)}
.duo .btn{flex:1 1 0; min-width:0}
/* .btn--sm carries align-self:flex-start, which is right inside a flex row -
   a secondary action should not grow to the height of whatever tall thing it
   sits beside. Inside a .duo it is wrong, because a duo is 1fr 1fr and its two
   children are peers by definition. Measured on the day screen at a 390:
   "Checked, nothing new" wrapped to two lines and came out 49 tall, "Open the
   map" stayed on one and came out 44, both 145 wide, side by side in the same
   grid row. Same width, different height, which is the same defect as
   different widths seen down the other axis. Stretch is the grid default and
   flex-start was overriding it; this puts it back for this case only. */
.duo > .btn--sm{align-self:stretch}

/* ---- something broke ----
   Says what did not happen and what is still safe, in that order, and offers
   the one thing worth trying. Centred and quiet, because a fault in the picture
   store is not a fault in the log and must not be dressed as one. */
.fail{text-align:center; padding:24px 20px; border-color:var(--line)}
.fail .stt{margin:0 0 8px}
.fail .btn{margin-top:12px}
/* A machine-readable line, small enough not to alarm and precise enough to find
   the fault later without turning the screen into a stack trace. */
.fcode{display:block; margin-top:12px; text-align:center;
  font:400 .63rem/1.4 var(--f-data); color:var(--ink-3)}

/* A state's own heading. Display face, because these are sentences the app says
   about itself and not labels on a form. */
.stt{font:400 1.06rem/1.3 var(--f-display); color:var(--ink); margin:0}
.stt--lg{font-size:1.19rem}

/* ---- an upload in flight ----
   4px, because it is a fact about a photograph and not a measurement of the
   dog. The track is the soft line so it reads as absent rather than as empty. */
.upl{height:4px; border-radius:var(--r-pill); background:var(--line-soft);
  overflow:hidden; margin:8px 0 8px}
.upl__f{height:100%; background:var(--stain); border-radius:var(--r-pill);
  min-width:3px; transition:width .25s linear}

/* ---- the journal photograph ----
   118px for a walk and 88px for a drive in the deck. One band here, because the
   difference between the two was a drawing decision and not a rule, and a
   photograph of him in a car is not worth less than one of him in a park. */
.wph{position:relative; background:var(--line-soft);
  margin:-4px -4px 8px; border-radius:var(--r) var(--r) 0 0; overflow:hidden;
  background:var(--line-soft); line-height:0}
.wph img{width:100%; height:120px; object-fit:cover; display:block; position:relative; z-index:1}

/* The time on an outing sits inline the way the meal time does, and inherits
   nothing from the form fields around it. */
#walkBody2 .meal__t{max-width:9em}
#walkBody2 textarea{width:100%}
/* The two fields on the Where card. Full width and stacked, because the name is
   a sentence and the place is a list, and putting them side by side makes the
   list too narrow to read a place name in. */
.wwh__in{width:100%; margin-top:var(--sp-5)}

/* The place a journal entry is about. .meal__t was the wrong borrow: it carries
   a border and a focus ring because it dresses the time input, and a boxed
   static line reads as a field somebody forgot to fill in. */
.wnm{font:400 1.06rem/1.25 var(--f-display); color:var(--ink)}

/* Six segments in the Review bar do not fit a phone in English and fit less of
   one in Romanian, and a hidden scrollbar gives no hint that Record is still
   there. The right edge fades only while there is more to the right: the cover
   gradient is attached to the content, so at the end of the scroll it lands on
   top of the shadow and cancels it. */
.modebar{
  background-image:
    linear-gradient(to right, transparent, var(--paper)),
    linear-gradient(to right, transparent, var(--line));
  background-position:right center, right center;
  background-size:26px 100%, 26px 100%;
  background-repeat:no-repeat;
  background-attachment:local, scroll;
}

/* A whole card that is itself a tap target. Not .row--tap: that one is declared
   after .card and strips the background, the border and the padding back off
   again, which is how a card ends up looking like text somebody forgot to wrap. */
/* .row wraps, and wrapping happens before shrinking, so a line whose text is
   wider than the card puts the trailing chevron on a line of its own. Where a
   row is a label and a mark at the end, it must not wrap. */
.row--nw{flex-wrap:nowrap}
.card--tap{
  cursor:pointer; text-align:left; width:100%; font:inherit; color:inherit;
  min-height:var(--tap-min);
}
.card--tap:active{background:var(--stain-wash)}
.card--tap:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* ---------------- the household ----------------
   Deck page 09, plus the two marks page 12 asks for. */

/* Which animal a line is about, at the width of a full stop. Colour is the
   fastest unmistakable signal that you are writing tonight into the wrong
   file, and it has to survive being read at arm's length in a dark kitchen. */
.dot{
  display:inline-block; width:8px; height:8px; border-radius:50%;
  vertical-align:1px; margin-right:8px; flex:none;
}

/* A species the mechanism supports and nobody has written the words for. Half
   strength, and said out loud next to it, because a chip that looks live and
   behaves generic is how an app teaches somebody it lies. */
/* Half strength only while it is NOT the chosen one. Unscoped, this made the
   species you had just selected the faintest chip on the sheet, because the
   selection is carried by a fill and the fill was being dimmed along with
   everything else. Audited 08.08. */
.chip--soon:not([aria-pressed="true"]){opacity:.5}

/* Who wrote it. Page 12: the rater is derived from the session and never
   chosen from a list, so this is a statement of fact rather than a control. */
.mk{
  display:inline-flex; align-items:center; justify-content:center;
  width:32px; height:32px; border-radius:var(--r-sm); flex:none;
  background:var(--stain); color:var(--on-accent);
  font:400 18px/1 var(--f-display); letter-spacing:0;
  margin:0;
}
.mk--lg{width:72px; height:72px; border-radius:var(--r-lg); font-size:38px; margin:0}

/* Coat themes. Each one moves the three stain tokens and nothing else, so a
   switched record reads as the same app in a different coat rather than as a
   different app. Applied to the root only when there is more than one animal:
   repainting a single-animal record would announce a state that does not
   exist. Severity, warning and eosin stay put, because a red that means "this
   is bad" must not become a red that means "this is the ginger one". */
.coat-fawn   {--stain:#8A6A3E; --stain-soft:#A98A5C; --stain-wash:#FBF7F0}
.coat-blue   {--stain:#55606E; --stain-soft:#77828F; --stain-wash:#ECEEF1}
.coat-brindle{--stain:#4A3B2E; --stain-soft:#6B5747; --stain-wash:#F0EAE4}
.coat-cream  {--stain:#7A6A52; --stain-soft:#9A8A70; --stain-wash:#F8F5EE}
.coat-pied   {--stain:#4A4A52; --stain-soft:#6E6E78; --stain-wash:#EDEDF1}
.coat-black  {--stain:#2A2A30; --stain-soft:#4E4E58; --stain-wash:#EAEAEF}
.coat-ginger {--stain:#9A5A2E; --stain-soft:#B77B50; --stain-wash:#FAF3ED}
.coat-choc   {--stain:#5A3A2E; --stain-soft:#7B564A; --stain-wash:#F2E9E5}
.coat-lilac  {--stain:#6A5A72; --stain-soft:#8B7B94; --stain-wash:#F1EDF3}
.coat-tortie {--stain:#6A4230; --stain-soft:#8B6250; --stain-wash:#F3EAE6}
.coat-silver {--stain:#676E73; --stain-soft:#8B9196; --stain-wash:#F1F2F3}
/* Not a coat. The palette an animal with no colour recorded, or a species
   with no coat to record, resolves to: still an accent, still distinct from
   the app's own, and never one of the twelve read as a claim about the dog. */
.coat-neutral{--stain:#4A3A8F; --stain-soft:#6A58B0; --stain-wash:#EFEBF9}

/* A coat is chosen for the animal, not for the screen. Every one of the
   twelve above is picked for contrast on paper, so on a dark screen the
   darkest four fall under the card and the accent disappears. The dark set
   is the same twelve hues lifted to carry against #1C1926; --stain-soft and
   --stain-wash follow from the base and are left to the cascade. */
[data-theme="dark"] .coat-fawn   {--stain:#C9B08A}
[data-theme="dark"] .coat-blue   {--stain:#A2AEBB}
[data-theme="dark"] .coat-brindle{--stain:#9A8574}
[data-theme="dark"] .coat-cream  {--stain:#C4B69C}
[data-theme="dark"] .coat-pied   {--stain:#A0A0AA}
[data-theme="dark"] .coat-black  {--stain:#87878F}
[data-theme="dark"] .coat-ginger {--stain:#D49A6E}
[data-theme="dark"] .coat-choc   {--stain:#AD8A7C}
[data-theme="dark"] .coat-lilac  {--stain:#B3A6BA}
[data-theme="dark"] .coat-tortie {--stain:#B48A74}
[data-theme="dark"] .coat-silver {--stain:#AFB5B9}
[data-theme="dark"] .coat-neutral{--stain:#A79BE0}

/* ---------------------------------------------------------------------------
   Icons (js/30-icons.js writes the markup; this sizes it)

   Sized in em rather than pixels so a mark scales with the text it labels,
   which is the whole reason the app can raise its type size for a reading
   glasses moment and not end up with hand-set marks stranded at their old
   size. 1.15em is a hair larger than the cap height, which is what makes an
   outline drawing sit level with letterforms instead of looking undersized.

   flex:none because most of these live inside a .row and a shrinking icon is
   a squashed icon. The negative vertical-align is the optical baseline nudge
   for the inline case; it does nothing in a flex parent, where the parent
   aligns instead.

   Colour is never set here. Every mark is stroked with currentColor, so it
   inherits from whatever it sits in and follows all twelve themes and both
   modes for free. The moment one of these gets its own colour that stops
   being true. --------------------------------------------------------------- */
.ic{width:1.15em;height:1.15em;flex:none;vertical-align:-.18em}
.ic--sm{width:.95em;height:.95em}
.ic--lg{width:1.5em;height:1.5em}

/* Empty-state art. Fixed pixels, not em: this is an illustration sitting on
   its own above a sentence, not a mark inside one, and it should not grow
   with the type. Muted because an empty screen is not an error. */
.ic--art{width:48px;height:48px;color:var(--ink-3);opacity:.75}
/* The two children of an empty state that are not text. A column flex parent
   aligns to the start, which left-aligned a 48px illustration and a pill
   button above and below a centred sentence. */
.card--dash > .ic--art{align-self:center}
.card--dash > .btn{align-self:center}

/* ============================================================
   Analysis results. Deck page 13.
   ============================================================ */

/* Where a number came from, stated on the number rather than in a footnote.
   A value somebody typed off a phone photograph at 23:00 is not the same
   evidence as a laboratory feed, and the only place that distinction survives
   long enough to matter is next to the value itself. */
.prov{
  font-family:var(--f-body); font-size:9.5px; font-weight:600;
  display:inline-flex; align-items:center; gap:4px;
  letter-spacing:.05em; text-transform:uppercase;
  /* A pill border at the pill radius, in the border colour a .chip uses, is
     the app's own word for "tappable", and none of these six are. Flattened to
     a label: small radius, no border, quieter ink. It still reads as a badge
     and it stops promising a tap that does not exist. Audited 08.08. */
  color:var(--ink-2); border:0;
  background:var(--paper);
  border-radius:var(--r-xs); padding:4px 8px; white-space:nowrap; align-self:center;
}
.prov--h{ border-color:var(--stain); color:var(--stain) }

/* One value. Label, answer, band. The band is last and quietest because it is
   the only one of the three that the app supplied rather than a laboratory. */
.res{
  display:flex; align-items:baseline; gap:var(--sp-5);
  padding:8px 0; border-bottom:1px solid var(--line-soft);
}
.res:last-of-type{ border-bottom:0 }
.res b{ flex:1; font-family:var(--f-body); font-size:12.5px; font-weight:500; color:var(--ink) }
.res i, .res .res__v{ font-family:var(--f-data); font-size:13px; font-weight:600; font-style:normal; color:var(--ink) }
/* The out-of-range arrow used to sit inside the right-aligned value, so a
   flagged number ended 16 to the left of an unflagged one and the numerals
   staggered down the single column a vet actually scans. Worse, it was the
   abnormal values that were knocked out of line. The glyph now has its own
   fixed cell to the right, empty when there is nothing to flag, so every
   numeral in the column ends on the same edge. Audited 08.08. */
.res .res__v{ display:inline-grid; grid-template-columns:1fr 16px; align-items:center; gap:0 }
.res .res__v svg{ width:12px; height:12px; margin-left:4px; vertical-align:-1px }
.res__v--ph{ color:var(--ink-3); font-weight:400 }
.res s, .res .res__b{
  font-family:var(--f-body); font-size:10.5px; font-weight:400; text-decoration:none;
  color:var(--ink-3); text-align:right; flex:0 0 74px;
}
/* Out of band is drawn, never enforced. If the laboratory printed 8.9 and the
   band says 4.1 to 6.0, this colours 8.9 and stores 8.9. A form that refuses
   the number is a form that loses the finding. */
.res--hi .res__v, .res--hi i{ color:var(--bad-ink) }
.res--lo .res__v, .res--lo i{ color:var(--warn-ink) }

/* The same row, editable. */
.res input.res__v, .res input.res__lab, .res select.res__sel{
  min-width:0; padding:4px 8px; border-radius:var(--r-sm);
  border:1px solid var(--line); background:var(--card); color:var(--ink);
}
.res input.res__v{ flex:0 0 84px; width:84px; text-align:right; font-family:var(--f-data); font-weight:600 }
.res input.res__lab{ flex:1; font-family:var(--f-body); font-size:12.5px }
.res select.res__sel{ flex:0 0 118px; font-size:12.5px }
.res--hi input.res__v{ border-color:var(--bad-ink); color:var(--bad-ink) }
.res--lo input.res__v{ border-color:var(--warn-ink); color:var(--warn-ink) }
/* A description, a comment, a conclusion. It gets the width of the row rather
   than a value field, because histopathology does not answer in numbers. */
.res--txt{ flex-direction:column; align-items:stretch; gap:4px }
.res textarea.res__txt{ width:100%; min-height:56px; font-size:12.5px }
/* The band, opened for correction. Small and inline: this is a fix to one line
   of one report, not a change to the catalogue, and it should not look like one. */
.res__band{ flex:0 0 100%; display:flex; gap:8px; padding:8px 0 4px }
/* Dashed is how this app says "add a new one", and this button edits an
   existing reference band. It also inherited a 74 basis and free height,
   so down thirteen rows it measured 37, 24, 37, 24 and so on against a
   constant 44 field beside it, and the centre of the row moved 6.5 every
   time. Solid hairline, one width, one height. Audited 08.08. */
.res button.res__b{
  border:1px solid var(--line-soft); border-radius:var(--r-sm); background:transparent;
  padding:4px 8px; cursor:pointer; line-height:1.3;
  flex:0 0 88px; min-height:32px;
  display:inline-flex; align-items:center; justify-content:flex-end;
}
.res button.res__b:active{ background:var(--line-soft) }
.inp--xs{ flex:1; min-width:0; padding:4px 8px; font-size:12.5px }

/* One value across every report it has ever appeared in. Keyed on the label,
   which is why a type the app has never heard of charts from the second entry. */
.spark{
  display:flex; align-items:flex-end; gap:4px; height:52px;
  margin:var(--sp-7) 0; padding:0;
}
.spark i{
  flex:1; min-width:3px; border-radius:var(--r-xs) var(--r-xs) 0 0;
  background:var(--stain-soft); opacity:.82; font-style:normal;
}
.spark i:last-child{ background:var(--stain); opacity:1 }

/* The two ways a report actually arrives: photographed off a desk, or a PDF
   the laboratory emailed. */
.androp{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:8px; min-height:92px; padding:12px 8px; cursor:pointer;
  border:1px dashed var(--line); border-radius:var(--r);
  background:transparent; color:var(--ink-2);
  font-family:var(--f-body); font-size:12px; font-weight:500; text-align:center;
}
.androp svg{ color:var(--stain) }
.androp:active{ background:var(--stain-wash) }

/* ---- report builder preview: the frame around the page ------------------
   Everything the document is made of now lives in the PAPER block above, worn
   by #printdoc and .rbpv together. What is left here is the part that is about
   LOOKING at a page rather than about the page itself: the sheet, its edge,
   and the scale that fits A4 onto a phone.

   184mm is A4 less the @page margins, which is the width the printer will lay
   the document out in. The page is built at exactly that and then scaled with
   zoom, which reflows instead of transforming, so the frame collapses to the
   page's real height and nothing has to be measured in JavaScript. rbFit() in
   js/24-clinic.js sets --rbpv-k. The 1 below is what a wide screen gets: a
   preview must never be magnified past the paper it is previewing. */
.rbpv{
  margin-top:var(--sp-5); padding:var(--sp-7);
  background:var(--line-soft); border:1px solid var(--line);
  border-radius:var(--r-lg); overflow:auto;
}
.rbpv__pg{
  width:184mm; zoom:var(--rbpv-k, 1);
  background:#fff; color:#000; line-height:1.4;
  padding:var(--sp-9); box-shadow:0 1px 4px rgba(0,0,0,.28);
}
/* The page break is the one thing the paper decides and the screen can only
   report, so it is drawn as the edge between two sheets rather than inherited
   from the shared block, where it is a break-before nobody can see. */
.rbpv .pbreak{border-top:2px dashed #767676; margin:var(--sp-11) 0}

/* ======================================================================
   DECK SHELL AND THE TWO CONTROLS IT IS BUILT AROUND

   The app had already taken the deck's cards, chips, bands and segmented
   controls. What it never took was the deck's screen SHELL, and that is why
   the screens read as a different application wearing the right colours.

   The shell is three parts and the middle one is the whole idea: a toolbar
   that carries the subject of the screen rather than the name of the app, a
   scroll region that is the only thing that scrolls, and a three-tab bar.
   Everything a screen wants to say has to fit in that middle part, which is
   what forces the detail down behind disclosure rows instead of letting it
   pile up in one column a metre long.

   Values are the deck's own, unchanged. Two deliberate departures, both
   because a mock is not an app:
     .scr scrolls. In the deck it is overflow:hidden because a static frame
       has nothing to scroll and a scrollbar would have drawn in the picture.
     .dis and .md are buttons here, so they carry cursor and a focus ring.
   ====================================================================== */

/* ---- toolbar ---------------------------------------------------------- */
/* The title is the screen's subject: the date on the log, the month on the
   calendar, the animal's name on its record. The strikethrough tag under it
   is the state of that subject, which is the line that answers "is this
   saved" without spending a card on it. */
.tb{flex:none; display:flex; align-items:center; gap:12px; padding:8px 16px 12px}
.tb__t{
  font-family:var(--f-display); font-size:19px; flex:1; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.15;
}
.tb__t s{
  display:block; font-family:var(--f-body); font-size:11px; color:var(--ink-3);
  text-decoration:none; letter-spacing:.01em; margin-top:4px;
}

/* Who is writing. A filled disc, never a control that changes attribution:
   the record decides who you are, not this. */
.who{
  flex:none; width:32px; height:32px; border-radius:50%;
  background:var(--stain); color:var(--on-accent);
  display:grid; place-items:center; font:600 13px var(--f-body); border:0;
  cursor:pointer; padding:0; position:relative;
}
/* Its neighbour .ico grew a 44px target around a 31px disc; this one is in the
   same bar, is the same size, and opens the sheet everything else is behind. */
.who::after{
  content:""; position:absolute; left:50%; top:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
}
.who--c{background:var(--eosin)}

/* Its outlined sibling, for a toolbar action rather than a person. */
.ico{
  flex:none; width:32px; height:32px; border-radius:50%;
  border:1px solid var(--line-strong); background:var(--card);
  display:grid; place-items:center; color:var(--ink-2);
  cursor:pointer; padding:0; position:relative;
}
/* The circle stays 31px and the target becomes 44. Growing the disc itself
   would put a 44px ring in a 54px bar, which reads as a button that has eaten
   the toolbar; this is the same control with the thumb-sized part of it made
   honest. Apple asks 44x44pt, Material 48dp, and the bar's circles sit 12px
   apart, so 44 is as far as this can go without two of them overlapping.
   Not applied to .ico--cal: that one is a span with an invisible date input
   stretched across it, and a pseudo-element on the span would sit on top of
   the input and swallow the tap that opens the picker. It grows the input. */
button.ico::after{
  content:""; position:absolute; left:50%; top:50%;
  width:var(--tap-min); height:var(--tap-min);
  transform:translate(-50%,-50%);
}
.ico svg{width:16px; height:16px}
.ico:hover{border-color:var(--stain); color:var(--stain)}

/* The only thing on a screen that scrolls. */
.scr{
  flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;
  padding:0 16px 12px; display:flex; flex-direction:column; gap:12px;
}

/* ---- the itch scale --------------------------------------------------- */
/* Eleven targets, equal width, always all eleven on screen. Not a slider: a
   slider invites a drag to somewhere near the middle, and this number is the
   one number in the record that has to be chosen rather than settled on. */
.sc{display:grid; grid-template-columns:repeat(11,1fr); gap:4px}
.sc b{
  aspect-ratio:1; border-radius:var(--r-sm); border:1px solid var(--line-strong);
  display:grid; place-items:center; font:600 12px var(--f-data);
  color:var(--ink-2); background:var(--card); cursor:pointer;
}
.sc b.on{background:var(--sev2); border-color:var(--sev2); color:var(--on-sev2)}
/* Yesterday, shown as an outline rather than a number, and only after today
   has been scored. */
.sc b.g{border-color:color-mix(in srgb,var(--sev0) 55%,var(--line-strong)); border-width:1.5px}
.sc__f{display:flex; justify-content:space-between; font-size:10.5px; color:var(--ink-3); margin-top:8px}

/* ---- the medication pair ---------------------------------------------- */
/* Two buttons, not one cycling button. A dose is missed or taken, and the
   third state is simply neither pressed, which is what an unanswered dose
   looks like. A cycling button makes "not answered" a thing you tap past. */
/* Given and not given, side by side, and a mis-tap here writes the opposite of
   what happened to a dog on daily medication. No pseudo-element trick: two
   invisible 44px squares 4px apart would overlap, and an overlap between YES
   and NO is worse than a small button. Real size, real separation, 8px, which
   is Material's minimum for adjacent targets. */
.md{display:flex; gap:var(--s2); flex:none}
.md b{
  width:var(--tap-min); height:var(--tap-min); border-radius:var(--r);
  border:1px solid var(--line-strong);
  display:grid; place-items:center; font-size:13px; color:var(--ink-3);
  background:var(--card); cursor:pointer;
}
.md b svg{width:16px; height:16px}
.md b.y,.md b.on{background:var(--ok); border-color:var(--ok); color:var(--on-sev2)}
.md b.n{background:var(--bad); border-color:var(--bad); color:var(--on-sev2)}

/* ---- disclosure ------------------------------------------------------- */
/* The row that holds a screenful of fields behind one line. The badge on the
   right is the only thing that survives being closed, so it says how much is
   inside rather than what is inside. */
.dis{
  display:flex; align-items:center; gap:8px; padding:12px 0;
  border-top:1px solid var(--line-soft); font-size:13.5px;
  width:100%; background:none; border-left:0; border-right:0; border-bottom:0;
  font-family:inherit; color:var(--ink); text-align:left; cursor:pointer;
}
.dis:first-child{border-top:0}
/* A disclosure is a button here, so it has to say when the keyboard is on it.
   The block comment at the top of this section claimed it already did, which
   was true of .md and never of this one. */
.dis:focus-visible{outline:2px solid var(--stain); outline-offset:-2px}
.dis__c{
  flex:none; width:8px; height:8px;
  border-right:1.5px solid var(--ink-3); border-bottom:1.5px solid var(--ink-3);
  transform:rotate(-45deg); margin:0 4px;
}
.dis__c--o{transform:rotate(45deg); margin-top:-4px}
.dis b{flex:1; font-weight:500}
.dis em{
  font-style:normal; font-size:11px; color:var(--on-accent);
  background:var(--eosin); border-radius:var(--r-pill); padding:4px 8px; font-weight:600;
}
.dis em.qt{background:var(--stain-wash); color:var(--stain)}
/* A .dis with nothing in it is not a disclosure, it is the note field. */
.dis:not(:has(*)){
  display:block; border:1px solid var(--line-strong); border-radius:var(--r);
  padding:12px 12px; background:var(--paper); color:var(--ink-2);
  font-size:13.5px; line-height:1.45; cursor:text;
}

/* ---- native picker worn as a row -------------------------------------- */
.sel{
  display:flex; align-items:center; gap:8px; border:1px solid var(--line-strong);
  border-radius:var(--r); padding:12px 12px; background:var(--card);
  font-size:13.5px; color:var(--ink); text-align:left;
}
.sel b{flex:1; font-weight:500}
.sel s{text-decoration:none; color:var(--ink-3); font-size:11.5px}
.sel::after{
  content:""; flex:none; width:8px; height:8px;
  border-right:1.5px solid var(--ink-3); border-bottom:1.5px solid var(--ink-3);
  transform:rotate(45deg); margin:-4px 4px 0;
}
.sel--ph b{color:var(--ink-3); font-weight:400}

/* ---- value and unit on one line --------------------------------------- */
.fld{display:flex; flex-direction:column; gap:4px; flex:1; min-width:0}
.fld .lbl{margin:0}
.fld i{
  display:block; border:1px solid var(--line-strong); border-radius:var(--r);
  padding:8px 12px; background:var(--card); font:500 13.5px var(--f-data);
  color:var(--ink); font-style:normal;
}
.fld i.ph{color:var(--ink-3); font-weight:400}

/* ======================================================================
   THE LOG SCREEN, AS DECK 02 DRAWS IT

   Restyled rather than renamed. The deck's look is in its VALUES, and the app
   already owns the classes those values belong on: .card is the deck's .c to
   the pixel, .card--acc and .card--flat already exist, and .c and .row are
   both already taken here by other components. Renaming toward the mock would
   have silently restyled five screens that have nothing to do with this one.
   ====================================================================== */

/* ---- the toolbar, in a document that scrolls -------------------------- */
/* The deck's shell is a fixed-height flex column and its toolbar simply does
   not move. This app scrolls its body, so the same bar has to be sticky to
   behave the way the mock behaves, and it has to carry the page background or
   the cards would run underneath it. */
.tb{
  position:sticky; top:0; z-index:40; background:var(--paper);
  max-width:var(--maxw); margin:0 auto;
  padding:calc(8px + env(safe-area-inset-top)) var(--gut) var(--sp-9);
  /* Content scrolls UNDER this bar, and with no edge the first clipped line
     read as a torn layout (Ovidiu's 08.08 map-screen screenshot: card titles
     guillotined against bare paper). One hairline makes the same pixels read
     as a pass-under. On screens where the modebar sits flush below, its own
     hairline is the seam and this one steps aside - data-mbar is written by
     drawModebar(). */
  border-bottom:1px solid var(--line);
}
html[data-mbar="1"] .tb{border-bottom-color:transparent}
.tb__t{margin:0; font-weight:400}
.tb__t s[data-back="1"]{cursor:pointer; text-decoration:none; color:var(--stain)}
.tb .netpill{flex:none}
.tb .daywho{flex:none; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:96px}

/* The calendar circle IS the date field. The input is stretched across it and
   made invisible rather than hidden, because a display:none input does not
   open a picker and a visually-hidden one does. */
/* overflow:visible so the input below can be bigger than the circle it sits
   in. The circle is still 31px; what you hit is 44. */
.ico--cal{position:relative; overflow:visible}
.ico--cal input[type="date"]{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:var(--tap-min); height:var(--tap-min);
  opacity:0; border:0; margin:0; padding:0; background:none; cursor:pointer;
  color:transparent;
}
.ico--cal input[type="date"]::-webkit-calendar-picker-indicator{
  position:absolute; inset:0; width:100%; height:100%; margin:0; padding:0;
  opacity:0; cursor:pointer;
}

/* Search, language and theme, in the account sheet where deck 12 I4 puts
   everything about this device. */
.tbstash{display:flex; flex-direction:column; margin-top:var(--sp-9)}
.tbstash__r{
  appearance:none; width:100%; background:none; border:0;
  border-top:1px solid var(--line-soft); cursor:pointer; text-align:left;
  display:flex; align-items:center; gap:12px; padding:var(--sp-7) 0;
  font:500 .88rem/1.4 var(--f-body); color:var(--ink); min-height:48px;
}
/* display:block because the colour row's .grow is a <span>: a <button> may
   only contain phrasing content, and a <div> inside one is invalid. */
.tbstash__r > .grow{flex:1 1 auto; min-width:0; display:block}
.tbstash__r svg{flex:none; color:var(--ink-3)}
/* What a settings row opens under itself. Twelve colour chips today. */
.tbstash__x{padding:0 0 var(--sp-7)}
/* The same row on the Settings page, where the control sits inside the row and
   the row itself is a label. Without this it inherits a pointer cursor and
   offers a tap that does nothing. */
.tbstash__r--flat{cursor:default}
.tbstash__r > .dot{width:16px; height:16px; margin:0; vertical-align:0}

/* ---- card heading ------------------------------------------------------ */
/* One heading primitive for every card in the build: the question on the left,
   the one fact that qualifies it on the right. It replaces an eyebrow stacked
   over an h3, which spent two lines and a type size saying what four words say
   in one. */
.c__h{display:flex; align-items:center; gap:var(--sp-5); margin:0}
.c__h h4{
  margin:0; flex:1 1 auto; min-width:0;
  font-family:var(--f-display); font-weight:400; font-size:15px; line-height:1.25;
}
.c__h em{flex:none; font-style:normal; font-size:11px; color:var(--ink-3)}

/* The method, the citation and the worked examples, behind one character.
   This used to be `.q`, a second spelling of `.icobtn--q` written years apart
   from it: same mark, same job, same 20px ink, and a 44px target each had been
   grown separately. Merged 07.08.2026 onto `.icobtn--q`, which is the spelling
   that draws its mark through the icon system like everything else since
   Phase 11 rather than typing a literal question mark into the shell. The old
   rules are in _dev\PARKED.md. */

.hr{height:1px; background:var(--line-soft); border:0; margin:4px 0}

/* ---- the eleven targets ------------------------------------------------ */
/* Square, edge to edge, and the chosen one filled. The severity bar that used
   to sit under each target was a second scale drawn on top of the first: it
   told you which band a number fell in before you had decided the number. */
.scale{gap:var(--s2); margin:0}
.scale__b{
  border-radius:var(--r-sm); border:1px solid var(--line-strong);
  font:600 13px/1 var(--f-data); box-shadow:none;
}
.scale__b[data-sev="0"],.scale__b[data-sev="1"],
.scale__b[data-sev="2"],.scale__b[data-sev="3"]{box-shadow:none}
.scale__b[aria-checked="true"]{
  background:var(--sev2); border-color:var(--sev2);
  color:var(--on-sev2); font-weight:600; box-shadow:none;
}
/* Both ends of the scale, named, with the blind notice between them. */
.sc__f{align-items:baseline; gap:var(--sp-5)}
.sc__f > span{flex:none}
.sc__f #itchBlind{flex:1 1 auto; min-width:0; text-align:center; font-size:10.5px}

/* ---- four rows in one card --------------------------------------------- */
/* The blocks stop being five separate cards and become one card of rows. Five
   cards each with a shadow, a border and a fourteen-pixel gap is a screen that
   reads as five decisions; this is one place where the rest of the day lives. */
.blkcard{gap:0; padding:4px var(--sp-10)}
.blkcard .blk{
  background:none; border:0; border-radius:0; box-shadow:none; overflow:visible;
}
.blkcard .blk__hd{
  padding:var(--sp-8) 0; min-height:0; gap:var(--sp-6);
  border-top:1px solid var(--line-soft);
}
.blkcard .blk:first-child .blk__hd{border-top:0}
/* The chevron leads the label rather than trailing the badge, because it is
   the thing that says this row opens, and that is the first thing to know. */
.blkcard .blk__c{order:-1; color:var(--ink-3)}
.blkcard .blk__c svg{width:16px; height:16px}
.blkcard .blk__t{flex:1 1 auto; min-width:0; font-weight:500; font-size:13.5px}
.blkcard .blk__s{
  flex:none; text-align:right; font:600 11px/1.35 var(--f-body);
  color:var(--stain); background:var(--stain-wash);
  border-radius:var(--r-pill); padding:4px 8px; max-width:52%;
}
.blkcard .blk__s:empty{display:none}
.blkcard .blk__b{padding:0 0 var(--sp-9); gap:var(--sp-8)}
/* The photograph card has no row of its own: deck 02 L1 names four rows and it
   is not one of them. It opens on the same rule it always did, inside the card
   rather than beside it. */
.blk--bare .blk__b{padding-top:var(--sp-5)}

/* ---- something is wrong ------------------------------------------------ */
/* A pill at the end of the day, not a bar across the screen. Full width made
   it read as the second half of Save, which is the one thing it must never
   read as: Save ends an ordinary evening and this one opens four steps.

   It was position:fixed, which took it out of the flow and left it hovering
   over whatever happened to be under it - over a note being typed, over the
   last row of the stool list, over the Save button itself. Sticky keeps the
   reach and gives the pill its own line, so there is nothing for it to cover.
   It rides the bottom of the screen while the day is long and comes to rest at
   the end of it. */
.flarebtn{
  align-self:flex-end; width:auto;
  min-height:var(--tap); padding:0 var(--sp-12);
  font-size:.9rem;
}

/* Synced is not a sentence worth a permanent pill. Deck 07 gives the connection
   its own line and gives it only when there is something to say, so the pill in
   the toolbar keeps that rule: it appears offline, it appears with work still
   held on the phone, and it is not there the rest of the time. */
.tb .netpill[data-s="ok"]{display:none}

/* ======================================================================
   DECK 02, THE PARTS THAT ARE COMPONENTS RATHER THAN LAYOUT

   Every value below is taken from the deck's own stylesheet and put on the
   class this app already uses for that thing. Nothing is renamed.
   ====================================================================== */

/* ---- the day strip ----------------------------------------------------- */
/* Deck .strip b: a label, and one small line under it. The tile used to lead
   with a number set at 1.28rem, which made three facts nobody asked for the
   loudest thing on a screen that exists to ask one question. Scoped to
   #todayStrip because .strip is also the photograph rail. */
#todayStrip .met{
  border-radius:var(--r); padding:var(--sp-7) var(--sp-8); gap:4px;
  font:600 11px/1.3 var(--f-body); color:var(--ink-2);
}
#todayStrip .met__k{
  font:inherit; letter-spacing:0; text-transform:none;
  color:inherit; min-height:0;
}
#todayStrip .met__v{
  display:inline; font:400 10px/1.45 var(--f-data); color:var(--ink-3);
}
#todayStrip .met__n{display:inline; font:400 10px/1.45 var(--f-data); color:var(--ink-3)}
#todayStrip .met__n:not(:empty)::before{content:" \00b7 "}
#todayStrip .met__v svg{width:12px; height:12px; vertical-align:-2px}
/* The severity colours came off the number, and a tile whose whole content is
   one muted line has nowhere left to put them. The one thing the strip still
   has to signal is what is owed, and the deck signals that on the border. */
#todayStrip .met--sev0 .met__v,#todayStrip .met--sev1 .met__v,
#todayStrip .met--sev2 .met__v,#todayStrip .met--sev3 .met__v{color:var(--ink-3)}
#todayStrip .met--due{border-color:var(--eosin); color:var(--eosin)}
#todayStrip .met--due .met__v,#todayStrip .met--due .met__n{color:var(--eosin)}

/* ---- yesterday, on the scale and under it ------------------------------ */
.scale__b--g{
  border-color:color-mix(in srgb,var(--sev0) 55%,var(--line-strong));
  border-width:1.5px;
}
.sc__y{flex:1 1 auto; min-width:0; text-align:center; color:var(--sev0)}
/* The outline that says which target the mark refers to, drawn rather than
   typed, and drawn with the same 1.5px outline the target itself carries. */
.sc__y::before{
  content:""; display:inline-block; width:8px; height:8px; margin-right:4px;
  border:1.5px solid currentColor; border-radius:var(--r-xs); vertical-align:baseline;
}
.sc__y[hidden]{display:none}

/* ---- what is due ------------------------------------------------------- */
/* Not .row: that name is taken here by a plain wrapping flex row, and taking
   it back would restyle five screens with nothing to do with medication. */
/* The row is now two layers: the actions, laid out underneath, and the face
   that slides off them. The face carries the background so that what is behind
   it is genuinely hidden rather than showing through, and the wrapper clips, so
   a row dragged aside cannot spill over the card's rounded corner. */
.mrow{
  position:relative; overflow:hidden;
  border-top:1px solid var(--line-soft);
  /* the pixel the action layer is inset by shows this, so it has to be the
     same colour the face is, in both the places a row is drawn */
  background:var(--card);
}
.mrow:first-child{border-top:0}
.mrow__f{
  position:relative; z-index:1;
  display:flex; align-items:center; gap:var(--sp-9);
  padding:var(--sp-8) 0;
  background:var(--card);
  touch-action:pan-y;          /* the page keeps the vertical, we take the rest */
  transition:transform .18s ease;
  will-change:transform;
}
/* Only while a finger is on it. A transition during a drag makes the row lag
   the thumb by a fifth of a second, which reads as the app being slow rather
   than as easing. */
.mrow[data-sw="drag"] .mrow__f{transition:none}
.mrow .row__m{flex:1; min-width:0; display:flex; flex-direction:column; gap:4px}
/* Two lines, then the ellipsis. "Allerderm Ceramide pipette" is 170px of text
   in a 137px column and was cut to "Allerderm Ceramide pi...", which is not a
   medication anybody recognises. A second line costs 17px on the four rows
   that need it and nothing on the rest. */
.mrow .row__m b{
  font-weight:600; font-size:13.5px; line-height:1.25;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
  overflow:hidden;
}
.mrow .row__m s{text-decoration:none; font-size:11.5px; color:var(--ink-3)}

/* ---- the actions under a medication row -------------------------------- */
/* Right-aligned and full height, so the row uncovers them from its own right
   edge and they read as having been there all along rather than as having
   flown in. 76px each: two of them is 152px, which is the distance the face
   travels, so an open row rests exactly on top of them and never half over. */
/* Inset by a pixel top and bottom, and it is not a decorative inset.

   The face and this layer used to end on exactly the same fractional line: a
   two-line name gives the row a height like 79.375px, and both boxes closed at
   79.375. The face is painted over this one, so at that shared boundary the
   device pixel is 37.5% card colour and the rest of it is whatever is
   underneath, which is amber on the left and red on the right. The result was
   a hairline of Skip-and-Remove showing under the last row of the card, all
   the time, on a row nobody had touched. Sub-pixel, and visible.

   Pulling this layer one pixel clear of both edges means the blend at the
   face's edge falls on the row's own background instead. When the row is
   actually dragged open the same pixel reads as a hairline between the
   buttons and the rows above and below, which is what it should have been. */
.mrow__a{
  position:absolute; inset:1px 0 1px auto; z-index:0;
  display:flex; align-items:stretch;
}
.mrow__a > button{
  width:76px; border:0; cursor:pointer; padding:0;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:2px; font:600 11px/1.1 var(--f-body);
}
.mrow__a > button svg{width:20px; height:20px}
.mrow__s{background:var(--warn); color:var(--on-warn)}
.mrow__x{background:var(--bad); color:var(--on-bad)}
.mrow__a > button:focus-visible{outline:2px solid var(--stain); outline-offset:-3px}
/* Skipped on purpose is not missed. It gets the same quiet the warning tokens
   carry everywhere else, and never the red: a red row is a failure, and this
   one is a decision. */
.mrow[data-st="s"] .mrow__f .row__m b{color:var(--warn-ink)}
/* The skipped caption. A word, not a control: see the note in medRow(). */
.mrow__u{font-size:11.5px; font-weight:600; color:var(--warn-ink)}


/* ---- what is late, folded ---------------------------------------------- */
/* One row instead of three red ones. It is a button, not a heading with an
   arrow beside it, because the whole strip has to be the target: a chevron is
   an 18px goal on a moving bus. */
.lateb{
  border:1px solid var(--warn); border-radius:var(--r);
  background:color-mix(in srgb, var(--warn) 8%, var(--card));
  margin-top:var(--s2);
}
.lateb__h{
  width:100%; min-height:var(--tap); padding:var(--s2) var(--s3);
  display:flex; align-items:center; gap:var(--s3);
  background:none; border:0; cursor:pointer; text-align:left; color:inherit;
}
.lateb__i{flex:none; display:grid; place-items:center; color:var(--warn-ink)}
.lateb__i svg{width:20px; height:20px}
.lateb__t{flex:1; min-width:0; display:flex; flex-direction:column; gap:2px}
.lateb__t b{font-weight:600; font-size:13.5px; color:var(--warn-ink)}
.lateb__t s{
  text-decoration:none; font-size:11.5px; color:var(--ink-3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.lateb__c{flex:none; display:grid; place-items:center; color:var(--ink-3); transition:transform .18s ease}
.lateb__c svg{width:20px; height:20px}
.lateb[data-open="1"] .lateb__c{transform:rotate(90deg)}
.lateb__h:focus-visible{outline:2px solid var(--stain); outline-offset:-2px}
/* Inside the fold the rows sit on the card's own paper, so the amber wash
   stays on the header and the rows underneath read as ordinary rows. */
.lateb__b{
  padding:0 var(--s3) var(--s2);
  border-top:1px solid color-mix(in srgb, var(--warn) 30%, transparent);
}
.lateb__b .mrow, .lateb__b .mrow__f{background:color-mix(in srgb, var(--warn) 8%, var(--card))}
/* The same fold, with nothing wrong. The Log card stacks the earlier outings
   of a day behind one row, and an earlier walk is not a failure: it is simply
   not the one being looked for. Everything the amber version carries as a
   warning - the wash, the border, the ink - steps back to the ordinary card
   language, and the mechanism (the header, the chevron, the body) is untouched
   so the two cannot drift apart. */
.lateb--calm{
  border-color:var(--line-soft);
  background:var(--paper);
}
.lateb--calm .lateb__t b{color:var(--ink); font-weight:600}
.lateb--calm .lateb__b{border-top-color:var(--line-soft)}
.lateb--calm .lateb__b .mrow, .lateb--calm .lateb__b .mrow__f{background:var(--paper)}
/* The rows inside sit on the fold's own paper and keep the card's row rhythm.
   Without this they inherit .lateb__b's tight padding and the first row lands
   flush against the header hairline. */
.lateb--calm .lateb__b{padding-top:var(--s1)}
.lateb--calm .lateb__b > .row + .row{border-top:1px solid var(--line-soft)}

/* The offer at the foot of the Due now card: put something new in the register
   without leaving the screen. Dashed, because a dashed edge is this app's word
   for something that is not there yet - the same word "Undeva nou" and "A stat
   in casa azi" already use on the Log screen - and it is the last thing on the
   card, because it is an offer and not a dose. */
.medadd{
  appearance:none; width:100%; text-align:left; cursor:pointer;
  display:flex; align-items:center; gap:var(--s3);
  min-height:var(--tap); padding:var(--s2) var(--s3); margin-top:var(--s2);
  background:none; color:inherit; font:inherit;
  border:1px dashed var(--line); border-radius:var(--r);
}
.medadd__i{flex:none; display:grid; place-items:center; color:var(--ink-3)}
.medadd__i svg{width:20px; height:20px}
.medadd__t{flex:1; min-width:0; display:flex; flex-direction:column; gap:2px}
.medadd__t b{font-weight:600; font-size:13.5px; color:var(--ink-2)}
/* <s> is the app's carrier for a quiet second line and never a strike-through;
   the same pair .lateb__t uses, so the two rows read as one family. */
.medadd__t s{text-decoration:none; font-size:11.5px; color:var(--ink-3)}
.medadd:active{background:var(--stain-wash)}
.medadd:hover .medadd__t b{color:var(--ink)}
.medadd:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
/* The two doors behind the row, and the rows of the list behind the first of
   them. Same shape as the row that opened them, minus the dashed edge: a dashed
   edge is this app's word for something that is not there yet, and a treatment
   already written down in the register very much is. */
.medadd--pick, .medadd--row{border-style:solid; border-color:var(--line-soft)}
.medadd--row .medadd__i{width:32px; height:32px}
.medadd--row .medadd__i .pill, .medadd--row .medadd__i img{width:100%; height:100%}
.medadd__c{flex:none; display:grid; place-items:center; color:var(--ink-3)}
.medadd--pick .medadd__t b, .medadd--row .medadd__t b{color:var(--ink)}

/* What is recorded at this photograph's site on this photograph's day, and the
   way into changing it. It sits under the two filing buttons because it answers
   the third question of the same kind - where, when, and what is there - and it
   is a full-width row rather than a third cell beside them because it carries a
   sentence, not a word. */
.phs__grade{
  display:flex; align-items:center; gap:var(--s3);
  width:100%; text-align:left; min-height:var(--tap);
  padding:var(--s2) var(--s3); margin-top:var(--s2);
}
.phs__grade__t{flex:1; min-width:0; display:flex; flex-direction:column; gap:2px}
.phs__grade__t b{font-weight:600; font-size:13.5px; color:var(--ink); text-wrap:pretty}
/* <s> is the app's carrier for a quiet second line, never a strike-through. It
   says which site and which day the record belongs to, because the grade is
   shared by every photograph of that site that day and a row inside a photo
   sheet would otherwise read as belonging to the picture. */
.phs__grade__t s{text-decoration:none; font-size:11.5px; color:var(--ink-3); text-wrap:pretty}
.phs__grade__c{flex:none; display:grid; place-items:center; color:var(--ink-3)}
@media (prefers-reduced-motion:reduce){
  .mrow__f, .lateb__c{transition:none}
}
/* The live one: 19-adapt.js draws buttons, the block above styles the <b> the
   older list uses. Both are the same control and have to measure the same. */
.md{display:flex; gap:var(--s2); flex:none}
.md > button{
  width:var(--tap-min); height:var(--tap-min); border-radius:var(--r);
  border:1px solid var(--line-strong);
  display:grid; place-items:center; color:var(--ink-3);
  background:var(--card); cursor:pointer; padding:0;
}
.md > button svg{width:16px; height:16px}
.md > button.y{background:var(--ok); border-color:var(--ok); color:var(--on-accent)}
.md > button.n{background:var(--bad); border-color:var(--bad); color:var(--on-accent)}
.md > button:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* ---- what the medication looks like ------------------------------------ */
/* Twelve shapes and ten colours, drawn in CSS from one element and an inner
   <i>, rather than twelve SVGs or, worse, twelve photographs. Three reasons,
   and the third is the one that decided it: a drawing recolours with the theme
   the way everything else here does, it costs nothing to send to a phone on
   mobile data, and it is honest. A photograph of a tablet says "this is the
   tablet you have"; a drawing says "white, oval", which is all anybody
   actually checks and all the register actually knows.

   The frame is a soft disc, the way Apple Health draws them, so twelve very
   different silhouettes still line up as one row of equals. The <i> is the
   object; the outer element is the disc it sits on. */
/* .pdraw, not .pill: see the note on .pill above. The photograph sibling of
   this is .pthumb, and a caller asks medThumb() for a size without having to
   know which of the two it is going to get back. */
.pdraw{
  flex:none; display:grid; place-items:center;
  width:32px; height:32px; border-radius:var(--r-pill);
  background:var(--paper); border:1px solid var(--line-soft);
}
.pdraw > i{
  display:block; background:var(--p-fill); border:1px solid var(--p-edge);
  /* the default, which every shape below overrides */
  width:18px; height:11px; border-radius:var(--r-pill);
}
/* Not described. Deliberately the card's own quiet ink and not a colour, so a
   register nobody has described reads as unfinished rather than as ten
   identical grey pills pretending to be information. */
.pdraw[data-colour="none"]{--p-fill:var(--paper); --p-edge:var(--ink-3)}
.pdraw[data-colour="white"] {--p-fill:var(--card);  --p-edge:var(--line-strong)}
.pdraw[data-colour="sand"]  {--p-fill:#e8d9b8; --p-edge:#b79f6f}
.pdraw[data-colour="amber"] {--p-fill:#e8a33d; --p-edge:#a86f18}
.pdraw[data-colour="rose"]  {--p-fill:#eab0c2; --p-edge:#b8697f}
.pdraw[data-colour="red"]   {--p-fill:#d9584f; --p-edge:#96322b}
.pdraw[data-colour="violet"]{--p-fill:#b39ad8; --p-edge:#7a5da8}
.pdraw[data-colour="blue"]  {--p-fill:#7ea8dd; --p-edge:#4a71a8}
.pdraw[data-colour="teal"]  {--p-fill:#79c3bd; --p-edge:#3f8a84}
.pdraw[data-colour="green"] {--p-fill:#8cc48a; --p-edge:#4f8a4c}
.pdraw[data-colour="slate"] {--p-fill:#a8aeb8; --p-edge:#6b7280}

/* The silhouettes. Each is width, height, corner and, where it earns one, a
   single detail: the capsule's division line, the bottle's neck, the tube's
   crimp. No more than one, because at 18px a second detail is noise. */
.pdraw[data-shape="capsule"] > i{
  width:20px; height:10px; border-radius:var(--r-pill);
  background:linear-gradient(90deg, var(--p-fill) 0 50%, color-mix(in srgb, var(--p-fill) 55%, var(--card)) 50% 100%);
}
.pdraw[data-shape="round"]  > i{width:15px; height:15px; border-radius:var(--r-pill)}
.pdraw[data-shape="oval"]   > i{width:20px; height:13px; border-radius:var(--r-pill)}
.pdraw[data-shape="tablet"] > i{
  width:16px; height:16px; border-radius:var(--r-sm);
  background:linear-gradient(180deg, var(--p-fill) 0 45%, var(--p-edge) 45% 55%, var(--p-fill) 55% 100%);
}
.pdraw[data-shape="chew"]   > i{width:18px; height:14px; border-radius:6px 10px 6px 10px}
.pdraw[data-shape="liquid"] > i{
  width:13px; height:17px; border-radius:2px 2px 5px 5px;
  background:linear-gradient(180deg, color-mix(in srgb, var(--p-fill) 25%, var(--card)) 0 40%, var(--p-fill) 40% 100%);
}
.pdraw[data-shape="bottle"] > i{
  width:12px; height:17px; border-radius:1px 1px 4px 4px;
  box-shadow:0 -4px 0 -2.5px var(--p-edge);   /* the neck, in one line */
  margin-top:4px;
}
.pdraw[data-shape="tube"]   > i{
  width:19px; height:9px; border-radius:2px 5px 5px 2px;
  box-shadow:inset 3px 0 0 -1.5px var(--p-edge);   /* the crimp */
}
.pdraw[data-shape="pipette"]> i{
  width:8px; height:19px; border-radius:4px 4px 1px 1px;
  box-shadow:inset 0 6px 0 -3px var(--p-edge);
}
.pdraw[data-shape="sachet"] > i{
  width:16px; height:18px; border-radius:1px;
  box-shadow:inset 0 4px 0 -2.5px var(--p-edge);
}
.pdraw[data-shape="drops"]  > i{
  width:12px; height:16px; border-radius:50% 50% 50% 50% / 62% 62% 38% 38%;
  transform:rotate(180deg);
}
.pdraw[data-shape="spray"]  > i{
  width:11px; height:16px; border-radius:2px 2px 4px 4px;
  box-shadow:0 -5px 0 -3.5px var(--p-edge), -4px -6px 0 -4px var(--p-edge);
  margin-top:5px;
}
/* Three sizes and no more: in a row, in the register, and being chosen. The
   row uses the base 32px and has no modifier of its own, because a fourth
   size that differs from the base by two pixels is not a size, it is drift. */
.pdraw--adm{width:36px; height:36px}
.pdraw--pick{width:40px; height:40px; border:0; background:none}
.pdraw--big{width:56px; height:56px}
.pdraw--big > i{transform:scale(1.35)}
.pdraw--big[data-shape="drops"] > i{transform:rotate(180deg) scale(1.35)}

/* ---- the photograph of the box ----------------------------------------- */

/* Same three sizes as the drawing so the two are interchangeable in a row, and
   deliberately not the drawing's disc: a box is recognised by its front, and a
   circular crop cuts off the corners the brand name sits in. Soft square, and
   the smallest of the three gets the smallest radius so a 32px thumbnail does
   not read as a lozenge. */
.pthumb{
  flex:none; display:block; overflow:hidden;
  width:32px; height:32px; border-radius:var(--r-sm);
  background:var(--paper); border:1px solid var(--line-soft);
}
.pthumb img{display:block; width:100%; height:100%; object-fit:cover}
.pthumb--adm{width:36px; height:36px}
.pthumb--big{width:56px; height:56px; border-radius:var(--r)}

/* The control that puts one there.

   The frame IS the button. It was a frame with "Add a photo" and "Replace
   photo" beside it for one revision, and at 320 in Romanian those two words
   became "Înlocuiește poza" and "Elimină", which are different lengths, which
   wrapped onto two rows, which is the exact ragged pair of buttons this app
   was told to stop drawing. Tapping the picture to change the picture needs no
   label at all, is a 56px target where 44 is the floor, and is what every
   phone already teaches. What is left on the right is a line of text and, only
   when there is something to remove, one short button.

   Same two-column anatomy as the drawn preview directly below it, and the same
   gap above each, so "what it looks like" reads as one question with two ways
   of answering rather than two piles. */
.mphoto{display:flex; align-items:center; gap:var(--s3); padding:var(--s3) 0 0}
.mphoto__f{flex:none; display:block; padding:0; border:0; background:none; cursor:pointer}
.mphoto__f:focus-visible{outline:2px solid var(--stain); outline-offset:3px; border-radius:var(--r)}
.mphoto__b{display:flex; flex-direction:column; align-items:flex-start; gap:var(--s2); min-width:0}
.mphoto__t{font:400 12.5px/1.35 var(--f-body); color:var(--ink-2)}
/* An empty frame is a dashed one, so a product with no photograph says so
   instead of looking like a photograph that failed to load. */
.pthumb--empty{
  border:1px dashed var(--line-strong); background:var(--paper);
  display:grid; place-items:center; color:var(--ink-3);
}
.pthumb--empty svg{width:20px; height:20px}

/* ---- choosing one ------------------------------------------------------ */
.pillprev{display:flex; align-items:center; gap:var(--s3); padding:var(--s3) 0 0}
.pillprev__t{font:400 12.5px/1.3 var(--f-body); color:var(--ink-2)}
/* .shgrid, not .pgrid. Two unrelated grids were both called that: this one,
   the shape picker, and the photo archive at 2482. This block is the later of
   the two, so it was winning for both, and the archive - which is written for
   three big square thumbnails - was being packed into five 59px columns with a
   date caption 3px too wide to fit. Renamed here rather than there because it
   is two call sites against five. */
.shgrid{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(56px, 1fr));
  gap:var(--tap-gap);
}
.shgrid__b{
  min-height:var(--tap); display:grid; place-items:center; padding:var(--s1);
  border:1px solid var(--line-soft); border-radius:var(--r);
  background:var(--paper); cursor:pointer;
}
.shgrid__b[aria-pressed="true"]{border-color:var(--stain); background:var(--card); box-shadow:inset 0 0 0 1px var(--stain)}
.shgrid__b:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.cgrid{display:flex; flex-wrap:wrap; gap:var(--tap-gap); margin-top:var(--s3)}
.cgrid__b{
  width:var(--tap-min); height:var(--tap-min); border-radius:var(--r-pill);
  border:1px solid var(--line-soft); background:var(--paper); cursor:pointer;
  display:grid; place-items:center; padding:0;
}
/* The swatch is drawn inside the target rather than being it, so the colour
   circle can stay 24px and legible while the thing a thumb has to hit stays
   44px. A 24px tap target is the commonest reason a colour picker feels
   broken on a phone. */
.cgrid__b::before{
  content:""; width:24px; height:24px; border-radius:var(--r-pill);
  background:var(--p-fill); border:1px solid var(--p-edge);
}
.cgrid__b[data-colour="white"] {--p-fill:var(--card);  --p-edge:var(--line-strong)}
.cgrid__b[data-colour="sand"]  {--p-fill:#e8d9b8; --p-edge:#b79f6f}
.cgrid__b[data-colour="amber"] {--p-fill:#e8a33d; --p-edge:#a86f18}
.cgrid__b[data-colour="rose"]  {--p-fill:#eab0c2; --p-edge:#b8697f}
.cgrid__b[data-colour="red"]   {--p-fill:#d9584f; --p-edge:#96322b}
.cgrid__b[data-colour="violet"]{--p-fill:#b39ad8; --p-edge:#7a5da8}
.cgrid__b[data-colour="blue"]  {--p-fill:#7ea8dd; --p-edge:#4a71a8}
.cgrid__b[data-colour="teal"]  {--p-fill:#79c3bd; --p-edge:#3f8a84}
.cgrid__b[data-colour="green"] {--p-fill:#8cc48a; --p-edge:#4f8a4c}
.cgrid__b[data-colour="slate"] {--p-fill:#a8aeb8; --p-edge:#6b7280}
.cgrid__b[aria-pressed="true"]{border-color:var(--stain); box-shadow:inset 0 0 0 2px var(--stain)}
.cgrid__b:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* ---- a word that follows you ------------------------------------------- */
/* Layer 150, between the tour and the search, which is the order stated above
   .tour. Above the nav because it outlives the screen it was raised on; below
   the sign-in gate because nothing the app has to say matters to somebody it
   has not let in yet. */
.toast{
  position:fixed; z-index:150;
  left:50%; transform:translateX(-50%) translateY(8px);
  bottom:calc(var(--nav-h) + var(--s3) + env(safe-area-inset-bottom, 0px));
  /* A percentage, not 100vw. This is position:fixed, so 100% already resolves
     against the initial containing block, which is the same measurement
     without the two ways vw gets it wrong: it counts the scrollbar, and on a
     folding phone it is read before the fold has finished changing the width. */
  width:min(var(--maxw), calc(100% - var(--s5)));
  display:flex; align-items:center; gap:var(--s3);
  padding:var(--s3) var(--s3) var(--s3) var(--s4);
  border-radius:var(--r-lg); border:1px solid var(--line-strong);
  background:var(--ink); color:var(--paper);
  box-shadow:0 8px 28px rgba(0,0,0,.22);
  opacity:0; pointer-events:none;
  transition:opacity .18s ease, transform .18s ease;
}
.toast[data-on="1"]{opacity:1; transform:translateX(-50%) translateY(0); pointer-events:auto}
.toast__t{flex:1; min-width:0; font:400 13px/1.35 var(--f-body)}
.toast__a{
  flex:none; min-height:36px; padding:0 var(--s3);
  border:1px solid currentColor; border-radius:var(--r-pill);
  background:none; color:inherit; cursor:pointer;
  font:600 12.5px/1 var(--f-body);
}
.toast__a:focus-visible{outline:2px solid var(--paper); outline-offset:2px}
@media (prefers-reduced-motion:reduce){ .toast{transition:none} }

/* ---- the badge on a folded row ----------------------------------------- */
/* Deck 02 writes it as .mut, which is plain muted text. A filled pill made
   four ordinary summaries look like four alerts. */
.blkcard .blk__s{
  background:none; border-radius:0; padding:0;
  font:400 12px/1.35 var(--f-body); color:var(--ink-3);
}

/* ---- the flare pill ---------------------------------------------------- */
/* The deck put this pill 74px off the bottom of the bar, which was the right
   measurement for a pill that hovers. It does not hover any more, so the rule
   is gone rather than left here doing nothing: an offset on a static element
   is a comment pretending to be code. Air under it now comes from the stack's
   own gap, like everything else in the day. */

/* ======================================================================
   DECK 03, THE FLARE AND THE VIEWFINDER, AND DECK 17, OPENING THE APP

   Same rule as the block above it. Every value here is the deck's own, put
   on the class this app already uses for that thing, and a new name appears
   only where the app had no component for it at all.
   ====================================================================== */

/* ---- how far through the four steps ------------------------------------ */
/* Deck 03 .step. The eyebrow already says 1/4, which is a fact you have to
   stop and read. Four bars is the same fact without stopping. */
.step{display:flex; gap:4px; align-items:center; justify-content:center; padding:4px 0 4px}
.step i{width:24px; height:3px; border-radius:var(--r-xs); background:var(--line)}
.step i.on{background:var(--eosin); width:32px}

/* ---- the site the second step is describing ---------------------------- */
/* Deck 03 3.1 P2 opens with the site name at display 19 and its code on the
   right. By step two the plate is gone, and without this line the only thing
   naming the paw is a chip that was tapped one screen ago. */
.flsite{display:flex; align-items:baseline; gap:var(--sp-5)}
.flsite b{font:400 19px/1.2 var(--f-display)}

/* ---- the grades, before one of them is chosen -------------------------- */
/* Deck 03 3.1 P2 draws all four unpressed grades in their own colour and
   fills the chosen one. They were uniformly grey until pressed, which made a
   clinical scale look like four ordinary buttons. */
.chip--sev[data-sev="0"]{border-color:var(--sev0); color:var(--sev0-ink)}
.chip--sev[data-sev="1"]{border-color:var(--sev1); color:var(--sev1-ink)}
.chip--sev[data-sev="2"]{border-color:var(--sev2); color:var(--sev2-ink)}
.chip--sev[data-sev="3"]{border-color:var(--sev3); color:var(--sev3-ink)}

/* ---- the pair at the foot of a sheet ----------------------------------- */
/* Deck 03 sizes the pair 1fr 1.4fr with the action on the wide side, and it
   gives a lone action the whole width. A single Next sitting in half a row
   reads as one half of a disabled pair. */
.duo--w{grid-template-columns:1fr 1.4fr}
.duo--1{grid-template-columns:1fr}
/* Three across, on the one step that has a Back, a Skip and a camera. The two
   ghosts are sized to their own words rather than to a share of the row, so
   the camera keeps the remainder and stays the widest target in Romanian as
   well as in English, where the words are longer. */
.duo--trio{grid-template-columns:auto auto minmax(0,1fr)}

/* ---- the viewfinder, with everything on it ----------------------------- */
/* Deck 03 3.2 C1. The instruction, the counter and the shutter are bars on
   the picture rather than a column beside it. A viewfinder that occupies a
   third of the screen is not a viewfinder, and the thing being framed is a
   lesion the size of a coin. */
#shotSheet .sheet__panel{
  position:relative; overflow:hidden;
  height:92dvh; max-height:92dvh; padding:0; gap:0;
}
#shotSheet .vf{
  flex:1 1 auto; aspect-ratio:auto; max-height:none; height:100%;
  border-radius:var(--r-lg) var(--r-lg) 0 0;
}
@media (min-width:640px){ #shotSheet .vf{border-radius:var(--r-lg)} }
.vfx{
  position:absolute; inset:0; z-index:2; color:#fff;
  display:flex; flex-direction:column; justify-content:space-between;
  pointer-events:none;
}
.vfbar{
  pointer-events:auto; display:flex; flex-direction:column; gap:8px;
  padding:calc(12px + env(safe-area-inset-top)) var(--sp-9) 12px;
  background:var(--vf-scrim-t);
}
.vfbar--b{
  padding:16px var(--sp-9) calc(12px + env(safe-area-inset-bottom));
  background:var(--vf-scrim-b);
}
.vfrow{display:flex; align-items:center; gap:8px}
.vfbar h3{margin:0; font:600 14.5px/1.3 var(--f-body); color:#fff}
.vfbar p{margin:0; font-size:12px; line-height:1.45; opacity:.8}
.vfbar #shotGhostNote{font-size:11.5px; opacity:.7}
.vfcount{
  flex:1 1 auto; min-width:0; align-self:center;
  font:600 10.5px/1 var(--f-data); letter-spacing:.06em; text-transform:uppercase;
  background:var(--vf-chip); border-radius:var(--r-pill); padding:4px 8px;
}
.vfx .icobtn{color:#fff; flex:none}
.vfx .btn{
  flex:none; background:var(--vf-ctl); border-color:transparent; color:#fff;
}
.vfx .film{flex:1 1 auto; min-width:0}
.vfx .film__i{background:var(--vf-ctl); border-color:transparent; color:#fff}
.vfx .film__i[data-st="on"]{background:var(--eosin); border-color:var(--eosin); color:var(--on-eosin); box-shadow:none}
.vfx .film__i[data-st="dn"]{opacity:.5; color:#fff}
.vfx .row--shut{padding:4px 0}
/* Deck C1 puts the ghost control under the shutter as one quiet line, because
   it is a thing you do once and then forget about. */
.vfghost{
  appearance:none; cursor:pointer; background:none; border:0; padding:4px 0 0;
  align-self:center; color:#fff; opacity:.72; font:400 11px/1.3 var(--f-body);
}
#shotGhost[data-off="1"] ~ .vfx .vfghost{opacity:.45}

/* ---- what ruins this frame --------------------------------------------- */
/* Deck 03 3.2 C2. A sheet over the viewfinder, not a list pushed under it:
   the camera stays exactly where it was and the reading comes back off. */
.vfsheet{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  display:flex; flex-direction:column; gap:8px;
  background:var(--card); border-top:1px solid var(--line);
  border-radius:var(--r-lg) var(--r-lg) 0 0; box-shadow:0 -8px 34px rgba(0,0,0,.24);
  padding:8px var(--sp-11) var(--sp-12);
  padding-bottom:calc(var(--sp-12) + env(safe-area-inset-bottom));
  max-height:86%; overflow-y:auto;
}
.vfsheet[hidden]{display:none}
.vfsheet__g{
  flex:none; width:36px; height:4px; border-radius:var(--r-xs);
  background:var(--line-strong); margin:0 auto 4px;
}
.vfsheet h4{margin:0; font:400 19px/1.25 var(--f-display)}
.vfsheet .tips{margin:0}

/* ---- the flare sheet is a column, not a stack --------------------------- */
/* Every sheet in deck 03 is a flex column with 10px between its parts, which
   is what .sheet__panel already does for the panel itself. #flBody was the one
   sheet body that never got the same treatment, so inside it a label sat
   directly on the chips of the group above it and the plate touched the
   paragraph under it: four steps built entirely out of touching elements.
   This is the single largest cause of the too close together complaint. */
#flBody{display:flex; flex-direction:column; gap:var(--field-gap)}

/* ---- one card of rows -------------------------------------------------- */
/* Deck 03 2.3 F4 and 3.2 C2 both put their facts in a card whose own padding
   is almost nothing, because .mrow already carries the row padding and the
   divider. Left on the default card padding every row got its spacing twice
   and the card grew a margin it did not ask for. Same shape as .blkcard. */
.c--rows{gap:0; padding:4px 12px}
/* Deck 03 2.3 F3 tints the card holding the frame and its reason, so the
   answer to Take a photo? reads as one object rather than three remarks. */
.c--wash{background:var(--stain-wash); border-color:transparent}

/* ---- a value is not a deletion ----------------------------------------- */
/* .row2 puts its value in an <s>, the same element .mrow uses, and .mrow had
   already been stripped of the line-through. Left alone here the confirm
   screen struck out every value it was asking you to confirm, and Then and
   now struck out the numbers it exists to show. */
.row2 s{text-decoration:none}

/* ---- the score already standing for today ------------------------------ */
/* Deck 03 2.3 F4 marks two cells on the eleven: the one you are choosing now,
   filled, and the one already recorded this morning, outlined. Without the
   second mark, changing the score loses the only evidence of what it was. */
.scale__b[data-was="1"]{border-color:var(--stain); color:var(--stain)}

/* ---- the filmstrip ----------------------------------------------------- */
/* Deck 03 3.2 C1 draws five equal 46px squares and wraps the frame name onto
   two lines inside them. Sized to their own text instead, the five tiles ran
   from 46px to 127px, the strip overflowed its row, and the fifth frame was
   hidden under Skip: the one thing a filmstrip exists to say, how many are
   left, was the thing it cut off. */
.film{gap:8px}
.film__i{
  width:48px; height:48px; min-width:0; min-height:0; padding:4px;
  display:grid; place-items:center; text-align:center; white-space:normal;
  font:600 9.5px/1.15 var(--f-body);
}

/* ---- the frame counter ------------------------------------------------- */
/* Deck 03 3.2 C1 writes it as one short pill. Given the full frame name it
   wrapped onto a second line, and a pill with two lines in it has stopped
   being a pill. The short name is now in SHOTS, and this is the guard. */
.vfcount{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}

/* ---- opening the app ---------------------------------------------------- */
/* Deck 17. Four numbers hold this whole screen up and none of them is
   decoration: it is not drawn at all before 400ms, so a cached boot never
   sees it; once drawn it stays 600ms, because a frame that appears and
   vanishes reads as a fault; it leaves over 220ms; and at 6000ms it hands
   over whatever the network is doing. The bar is the 6000 drawn to scale,
   which is the only honest thing a progress bar on a boot can be.

   Deck 17 A2 offered the same bar coloured on the severity ramp and it is
   rejected there and here: sev0 to sev3 is clinical vocabulary and it never
   gets spent on decoration. */
.spl{
  position:fixed; inset:0; z-index:90; background:var(--paper);
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:16px;
  transition:opacity 220ms cubic-bezier(.22,.61,.36,1),
             transform 220ms cubic-bezier(.22,.61,.36,1);
}
.spl[data-go="1"]{opacity:0; transform:translateY(-168px) scale(.72); pointer-events:none}
.spl__mk{
  width:128px; height:128px; flex:none; color:var(--stain); line-height:0;
}
.spl__n{margin:0; font:400 22px/1.1 var(--f-display); color:var(--ink)}
.spl__cap{
  position:relative; width:156px; height:3px; border-radius:var(--r-xs);
  background:var(--line); overflow:hidden;
}
.spl__cap i{
  position:absolute; left:0; top:0; bottom:0; width:0; border-radius:var(--r-xs);
  background:var(--stain); animation:splFill 6000ms linear forwards;
}
.spl__cap[data-still="1"] i{animation:none}
.spl__cap[data-full="1"] i{animation:none; width:100%}
.spl__f{
  width:156px; margin-top:-8px; display:flex; justify-content:space-between;
  font:400 9.5px/1 var(--f-data); color:var(--ink-3);
}
.spl__f--one{justify-content:center}
.spl__t{
  margin:0; min-height:16px; max-width:230px; text-align:center;
  font:400 11.5px/1.4 var(--f-body); color:var(--ink-3);
}
@keyframes splFill{from{width:0} to{width:100%}}
/* Deck 17: with motion turned down the mark does not breathe, the bar does not
   travel and the exit is an instant swap. The countdown that replaces the bar
   is written by the script, because a numeral is not a thing CSS can count. */
@media (prefers-reduced-motion:reduce){
  .spl{transition:none}
  .hmk--draw .hmk__o path{stroke-dasharray:none; stroke-dashoffset:0; animation:none}
  .hmk--draw .hmk__d{opacity:1; animation:none}
  .spl__cap i{animation:none}
  .spl[data-go="1"]{transform:none}
}

/* ---------------------------------------------------------------------------
   Deck 08, added after the coat block above rather than inside it.

   That block loses every argument it is in. .coat-blue is one class, and every
   token it moves is also declared on :root[data-theme="light"] and
   :root[data-theme="dark"], which are a class and an attribute. An attribute
   selector outweighs a class no matter which of the two comes last in the file,
   so the moment somebody chose light or dark by hand the coat stopped applying
   and the record went back to the app's own violet. Under auto it did apply,
   because the media block below :root is only one class and loses on source
   order, which is why this looked like it worked.

   The dark half of that block never applied at all, in any state. applyCoat()
   puts the class on documentElement, which is the same element data-theme sits
   on, and `[data-theme="dark"] .coat-blue` is a descendant selector: it asks
   for a coat inside a dark thing, and there is no element inside the root.

   So each coat is written three times, at a weight that wins each of the three
   states rather than at a weight that reads well:

     :root.coat-x                             light, and auto on a light phone
     @media dark  :root:not([light]).coat-x   auto on a dark phone
     :root[data-theme="dark"].coat-x          dark chosen by hand

   The :not() in the media rule is what keeps a dark phone from overruling a
   person who has explicitly asked for light.

   The dark set moves all three tokens and not only --stain. --stain-wash is a
   background and the light washes are near white, so leaving them behind
   painted a white band across the marked row of every sheet on a dark screen.
   The twelve dark values are the ones printed on deck page 08, which are the
   same twelve hues lifted to carry against #14121B.
   --------------------------------------------------------------------------- */
:root.coat-fawn        {--stain:#8A6A3E; --stain-soft:#A98A5C; --stain-wash:#FBF7F0}
:root.coat-blue        {--stain:#55606E; --stain-soft:#77828F; --stain-wash:#ECEEF1}
:root.coat-brindle     {--stain:#4A3B2E; --stain-soft:#6B5747; --stain-wash:#F0EAE4}
:root.coat-cream       {--stain:#7A6A52; --stain-soft:#9A8A70; --stain-wash:#F8F5EE}
:root.coat-pied        {--stain:#4A4A52; --stain-soft:#6E6E78; --stain-wash:#EDEDF1}
:root.coat-black       {--stain:#2A2A30; --stain-soft:#4E4E58; --stain-wash:#EAEAEF}
:root.coat-ginger      {--stain:#9A5A2E; --stain-soft:#B77B50; --stain-wash:#FAF3ED}
:root.coat-choc        {--stain:#5A3A2E; --stain-soft:#7B564A; --stain-wash:#F2E9E5}
:root.coat-lilac       {--stain:#6A5A72; --stain-soft:#8B7B94; --stain-wash:#F1EDF3}
:root.coat-tortie      {--stain:#6A4230; --stain-soft:#8B6250; --stain-wash:#F3EAE6}
:root.coat-silver      {--stain:#676E73; --stain-soft:#8B9196; --stain-wash:#F1F2F3}
:root.coat-neutral     {--stain:#4A3A8F; --stain-soft:#6A58B0; --stain-wash:#EFEBF9}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]).coat-fawn        {--stain:#C9B08A; --stain-soft:#DCC9AC; --stain-wash:#2A2419}
  :root:not([data-theme="light"]).coat-blue        {--stain:#A2AEBB; --stain-soft:#BEC7D0; --stain-wash:#1F252D}
  :root:not([data-theme="light"]).coat-brindle     {--stain:#9A8574; --stain-soft:#B5A493; --stain-wash:#241D18}
  :root:not([data-theme="light"]).coat-cream       {--stain:#C4B69C; --stain-soft:#D8CDB8; --stain-wash:#262218}
  :root:not([data-theme="light"]).coat-pied        {--stain:#A0A0AA; --stain-soft:#BCBCC4; --stain-wash:#1F1F24}
  :root:not([data-theme="light"]).coat-black       {--stain:#87878F; --stain-soft:#A5A5AC; --stain-wash:#1C1C20}
  :root:not([data-theme="light"]).coat-ginger      {--stain:#D49A6E; --stain-soft:#E2B593; --stain-wash:#2B1D14}
  :root:not([data-theme="light"]).coat-choc        {--stain:#AD8A7C; --stain-soft:#C6A99D; --stain-wash:#2A1D18}
  :root:not([data-theme="light"]).coat-lilac       {--stain:#B3A6BA; --stain-soft:#C9BFCE; --stain-wash:#241E28}
  :root:not([data-theme="light"]).coat-tortie      {--stain:#B48A74; --stain-soft:#C9A794; --stain-wash:#251A15}
  :root:not([data-theme="light"]).coat-silver      {--stain:#AFB5B9; --stain-soft:#C6CACD; --stain-wash:#202325}
  :root:not([data-theme="light"]).coat-neutral     {--stain:#A79BE0; --stain-soft:#C0B7EA; --stain-wash:#241F38}
}

:root[data-theme="dark"].coat-fawn        {--stain:#C9B08A; --stain-soft:#DCC9AC; --stain-wash:#2A2419}
:root[data-theme="dark"].coat-blue        {--stain:#A2AEBB; --stain-soft:#BEC7D0; --stain-wash:#1F252D}
:root[data-theme="dark"].coat-brindle     {--stain:#9A8574; --stain-soft:#B5A493; --stain-wash:#241D18}
:root[data-theme="dark"].coat-cream       {--stain:#C4B69C; --stain-soft:#D8CDB8; --stain-wash:#262218}
:root[data-theme="dark"].coat-pied        {--stain:#A0A0AA; --stain-soft:#BCBCC4; --stain-wash:#1F1F24}
:root[data-theme="dark"].coat-black       {--stain:#87878F; --stain-soft:#A5A5AC; --stain-wash:#1C1C20}
:root[data-theme="dark"].coat-ginger      {--stain:#D49A6E; --stain-soft:#E2B593; --stain-wash:#2B1D14}
:root[data-theme="dark"].coat-choc        {--stain:#AD8A7C; --stain-soft:#C6A99D; --stain-wash:#2A1D18}
:root[data-theme="dark"].coat-lilac       {--stain:#B3A6BA; --stain-soft:#C9BFCE; --stain-wash:#241E28}
:root[data-theme="dark"].coat-tortie      {--stain:#B48A74; --stain-soft:#C9A794; --stain-wash:#251A15}
:root[data-theme="dark"].coat-silver      {--stain:#AFB5B9; --stain-soft:#C6CACD; --stain-wash:#202325}
:root[data-theme="dark"].coat-neutral     {--stain:#A79BE0; --stain-soft:#C0B7EA; --stain-wash:#241F38}

/* ---------------------------------------------------------------------------
   Deck 11.

   The anchor block claims in three places that the layout never jumps: the
   element is always in the flow and only its contents appear. It reserved
   52px and then filled to between 57px and 76px, so entering the first score
   of the day moved everything under it by up to 24px, in both languages. 76px
   is the tallest the block gets at 430px on any value of any scale in either
   language, measured rather than guessed, which is what deck 11.0 means by
   sized on the longest Romanian anchor.
   --------------------------------------------------------------------------- */

/* Deck 11.0, coloured by severity and not by theme. The full sheet drew every
   band in the accent, so the one thing the inline anchor uses to catch a wrong
   pick before the sentence is read was missing from the surface somebody opens
   precisely because they are unsure. The colour is set from ancSev() in
   js/04-standing.js rather than from a second table here: the sheet and the
   anchor under the slider disagreeing about what a 6 is would be worse than
   neither of them being coloured at all. */
.anch__r[data-sev] .anch__g{
  box-sizing:border-box; align-self:flex-start; text-align:center;
  padding:4px 0; border-radius:var(--r-sm);
  background:var(--sev0); color:var(--on-sev0);
}
.anch__r[data-sev="1"] .anch__g{background:var(--sev1); color:var(--on-sev1)}
.anch__r[data-sev="2"] .anch__g{background:var(--sev2); color:var(--on-sev2)}
.anch__r[data-sev="3"] .anch__g{background:var(--sev3); color:var(--on-sev3)}

/* Deck 01, J0.4. Sign out is the only action in the app whose result cannot be
   undone from inside the app, and it was the same grey as every other quiet
   button. --bad is the app's word for that, and it is spent here and nowhere
   else. The host it now lives in sits after the three settings rows, so the
   gap above it is the separator the deck draws as a rule. */
#whoOut{margin-top:var(--sp-9)}
.btn--out{color:var(--bad); border-color:var(--bad)}



/* ============================================================
   PHASE 7 REVISION - the nutrition and nutrient screens
   Everything below was found by putting the built screen next to deck pages
   10 and 15 rather than next to the source.
   ============================================================ */

/* A screen title and its one action are a single line in the deck's toolbar.
   .row carries flex-wrap:wrap for everything else in the app that needs it, so
   the long h2 on the nutrient panel pushed the Recipe button onto a second line
   where it sat left-aligned under the back arrow and read as a list item rather
   than as the action for the screen. The title wraps inside .grow instead,
   which is what it does in the deck. Only the two sub-pages that carry a
   trailing action are named; the other two would be a no-op.

   Top alignment goes with it. Once the title takes two lines, centring drops
   the back arrow beside the second line and leaves the action pill floating at
   a different height, so the two controls that belong to the same header stop
   looking like a pair. */
#v-nutri > .stack > .row,
#v-foodlib > .stack > .row{flex-wrap:nowrap; align-items:flex-start}

/* A labelled segmented control fills its pill. .seg button had no flex, so
   Neutered read as two small words at the left of a wide empty track and the
   body condition scale read as five numbers pushed into a corner, which made
   the untapped part of the pill look like a control that was missing options.
   flex-grow only, never shrink, so .seg keeps its overflow-x scroll when a
   control genuinely has more options than fit. Scoped to .field so the plate
   view menu and the other bare .seg uses are untouched.

   Basis zero, not auto. auto grows every button from its OWN word, so the free
   space is shared but the head start is not given back, and the longest label
   stays the widest button for ever. Measured on the energy screen at an 820
   fold: Quiet 194, Normal 205, Busy 189 in one rail, Yes 297 against No 295 in
   the next. Three answers to one question at three different widths, which is
   the thing .seg--even was written to stop - its comment two thousand lines up
   says a rail where one choice is wider than its neighbour reads as a
   recommendation, and that is as true of sixteen pixels as of a hundred.
   Basis zero shares the whole rail instead of the leftovers, so the three come
   out 196 each and the pair 296 each.

   The overflow-x scroll this comment promises is kept by flex-shrink 0, which
   is untouched; the basis never had anything to do with it. A rail whose
   options genuinely do not fit still overflows and still scrolls. */
.field > .seg button{flex:1 0 0; text-align:center}

/* The status mark on a food library row. The deck types a tick, an exclamation
   mark and a question mark straight into the .tag; this app cannot, because the
   build sweeps the whole document for typed glyphs and fails on exactly those.
   The mark is an icon from the registry now, so the pill has to stop reserving
   room for a line of text. */
.tag--m{padding:4px; line-height:0; align-self:center}
.tag--m > svg{display:block}

/* Where a figure came from is not the figure. The edition, the table, the page,
   the transcription date and the reference's own caveat were all set as .hint,
   at the same size and colour as the sentence that states the finding and flush
   against it, so opening a nutrient gave you nine lines of provenance with the
   reading buried in the middle. Provenance is a footnote here: ruled off,
   smaller, quieter, and gathered into one block instead of two loose
   paragraphs. */
.cite{
  /* The gap was --sp-4, which is 8, and the two paragraphs here are 11px type
     at 1.55 line height, so the space BETWEEN the citations was the same size
     as the space between two lines inside one of them. Four separate facts
     under a nutrient number read as one wall. 16 is the smallest step that
     makes them read as separate. Audited 08.08. */
  border-top:1px solid var(--line-soft); padding-top:var(--sp-6);
  display:flex; flex-direction:column; gap:var(--s4);
  font-size:11px; line-height:1.55; color:var(--ink-3);
}

/* Ovidiu has said twice that things sit too close together, and the two
   nutrition sheets are where it shows worst. #nutBody and #foodBody are plain
   blocks and p carries margin:0 app-wide, so a finding, its provenance and the
   next micro-heading all stacked with nothing between them. List rows keep
   their own rhythm, because a list that breathes stops reading as a list;
   everything else gets air, and a rule gets the most. */
#nutBody > * + *,
#foodBody > * + *{margin-top:var(--sp-7)}
#nutBody > .orow + .orow,
#foodBody > .orow + .orow{margin-top:0}
#nutBody > .hr,
#foodBody > .hr{margin-top:var(--sp-12)}
#nutBody > .lbl,
#foodBody > .lbl{margin-top:var(--sp-8)}

/* ======================================================================
   NAVIGATION, TOUCH AND THE DIRECTORY                        06.08.2026
   ====================================================================== */

/* ---- one way back ------------------------------------------------------ */
/* Nine screens drew their own back arrow in the page while the toolbar drew
   another one above it, and the body map drew neither. Deck 05 puts the way
   back in the bar and nowhere else, so TBBACK now carries all of them and
   these are hidden rather than removed: the markup still declares the parent
   each page believes it has, which is where TBBACK's entries came from.
   Settings joined them on 07.08: it had no TBBACK row, so it was the last
   screen still relying on a button in the page, and that button always went to
   the clinic hub even when you had arrived from the household. */
.nutback, .wkback, .hback, #admBack{ display:none }

/* ---- saved, said at the field ------------------------------------------ */
/* The day writes itself half a second after every change and used to say so in
   a line at the bottom of the screen, under a Save button. Both are gone. This
   is what replaces them: one node, moved to whichever row was just edited and
   positioned from a measured rectangle, so no row has to reserve space for a
   mark that lasts a second and nothing shifts when it appears. Placed by
   okTick() in js/08-day.js and shown only for a write the server confirmed.

   Above the sheets is wrong and under the nav is wrong: it belongs to a field
   on the page, so it sits just above the toolbar's layer and no higher. */
.svtick{
  position:fixed; z-index:30; transform:translate(-100%, -50%);
  width:24px; height:24px; border-radius:var(--r-pill);
  display:flex; align-items:center; justify-content:center;
  background:var(--ok); color:var(--on-ok);
  pointer-events:none; opacity:0; transition:opacity .18s ease;
}
.svtick[data-on="1"]{opacity:1}
/* Nothing that only reports a success may be the reason somebody feels ill. */
@media (prefers-reduced-motion:reduce){ .svtick{transition:none} }

/* ---- the directory ----------------------------------------------------- */
.srch__h{
  font:600 .72rem/1 var(--f-body); letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); margin:var(--s5) 0 var(--s2);
}
.srch__h:first-of-type{ margin-top:var(--s3) }
.srch__grp{ display:flex; flex-direction:column; gap:var(--s1) }
.sres--scr .sres__t{ font-weight:600 }
.sres--scr .sres__s{ color:var(--ink-3) }

/* ---- the live walk ------------------------------------------------------
   Read outdoors, in daylight, with one hand, at arm's length, on a lead that
   is pulling. Every decision below follows from that sentence: the clock is
   the largest type in the app, the nine actions are square targets rather
   than a list of rows, and the map is grey so that six coloured marks on top
   of it are still six distinguishable marks in the sun. */
.lv__hd{
  display:flex; align-items:baseline; gap:var(--s4); flex-wrap:wrap;
  margin-bottom:var(--s4);
}
.lv__clock{
  font:400 2.5rem/1 var(--f-data); color:var(--ink);
  font-variant-numeric:tabular-nums; letter-spacing:-.02em;
}
/* The name, the undo button and the status line stack with the same 8px the
   rest of the app puts between a line and its control; they used to touch
   (Ovidiu, 02.09.2026: "pare ca iti este lene sa lasi spatiu"). */
.lv__where{display:flex; flex-direction:column; align-items:flex-start; gap:var(--s2); min-width:0}
.lv__where > .btn{align-self:flex-start}
.lv__where b{font:600 .95rem/1.3 var(--f-body); color:var(--ink)}

/* ---- the map ---- */
.lv__map{
  position:relative; overflow:hidden;
  /* 240, not 280: on a 932px phone the third row of event buttons sat under
     the tab bar with 280 (Ovidiu's screenshot, 02.09.2026); at 240 the nine
     buttons clear it and the map still shows two blocks either side. */
  height:240px; max-height:40dvh;
  border:1px solid var(--line); border-radius:var(--r);
  background:var(--paper);
  margin-bottom:var(--s4);
  /* it is a picture of where you are, not a control. Nothing on it is tappable
     and a stray drag while walking must not move it away from him. */
  touch-action:none; user-select:none;
}
.lv__pan{position:absolute; left:0; top:0; width:0; height:0; will-change:transform}
/* The tiles sit in one layer and the layer carries the filter. A filter on
   every tile gave each one its own composited edge, and the antialiasing at
   the edges drew a thin light seam at every tile boundary on the phone
   (Ovidiu's screenshot, 02.09.2026). One filtered layer has no inner edges. */
.lv__tl{position:absolute; left:0; top:0; width:0; height:0; z-index:1; filter:var(--tile-f)}
.lv__t{
  position:absolute; z-index:1; width:256px; height:256px;
  /* Grey, as asked, and done with a filter rather than a second style server:
     one basemap to fetch, one bucket to keep, and the same tile serves both
     themes. In dark theme it is also inverted, or a walk at 22:00 is a white
     rectangle held up to somebody's face. The filter is on .lv__tl now. */
}
.lv__ink{
  /* z-index, and it is load-bearing. The overlay is appended to the pan once,
     at build time, and every tile that arrives after it is appended after it.
     Two positioned siblings with no z-index are painted in document order, so
     without this the first tile to load buries the whole trace under a picture
     of the street it was walked on. */
  position:absolute; z-index:2; left:-4096px; top:-4096px; width:8192px; height:8192px;
  overflow:visible; pointer-events:none;
}
.lv__trh{fill:none; stroke:var(--card); stroke-width:12; stroke-linecap:round; stroke-linejoin:round}
.lv__tr{fill:none; stroke:var(--stain); stroke-width:6; stroke-linecap:round; stroke-linejoin:round}
/* A line drawn through a widened gate. It is a claim about a neighbourhood and
   not about a pavement, and it has to be told apart from one that is not at a
   glance, without reading the caption. Dashed is this app's word for provisional
   everywhere else, so it is dashed here. */
.lv__tr--co{stroke-dasharray:10 9; opacity:.85}
.lv__ev{fill:var(--eosin); stroke:var(--card); stroke-width:3}
/* A note, told apart from a tap. Hollow and ringed, because it is the only pin
   that has something written behind it and the only one worth going back to. */
.lv__ev--n{fill:var(--card); stroke:var(--eosin); stroke-width:4}
.lv__st{fill:var(--card); stroke:var(--stain); stroke-width:4}
/* Every mark carries a ring in the card colour, so it is a shape against the
   grey underneath it and not only a hue. Six hues on a photograph is exactly
   the failure the greyscale filter above exists to prevent. */
.lv__am{stroke:var(--card); stroke-width:3; opacity:.92}
/* Water was --stain, which is the colour of the trace, the start ring and the
   accuracy ring, and dog park was --eosin, which is the colour of every event
   marked on the walk. Two of the six amenities were therefore drawn in inks
   that already mean something else on the same map. An amenity is a thing that
   happens to be nearby and is not part of the record, so both move to the two
   remaining semantic inks that nothing on this map claims. Audited 08.08. */
.lv__am--water{fill:var(--sev1)}
.lv__am--dog{fill:var(--sev2)}
.lv__am--vet{fill:var(--bad)}
.lv__am--shop{fill:var(--warn)}
.lv__am--park{fill:var(--ok)}
.lv__am--grass{fill:var(--sev0)}

/* How good the fix is, drawn true to scale in metres by lvInk(). This is the
   only thing on the screen that tells a twelve-metre fix from a fourteen-
   hundred-metre one without reading a number, and a ring that fills the whole
   window is the correct and useful picture of a position nobody should act on.
   Deliberately not clamped at the large end for that reason. */
.lv__acc{
  fill:color-mix(in srgb, var(--stain) 12%, transparent);
  stroke:color-mix(in srgb, var(--stain) 46%, transparent);
  stroke-width:2;
}

/* Where he is, pinned to the middle, because the plane moves and he does not.
   A dot that wandered would be a dot somebody looks for. */
.lv__me{
  position:absolute; z-index:3; left:50%; top:50%; width:16px; height:16px;
  margin:-8px 0 0 -8px; border-radius:var(--r-pill);
  background:var(--stain); border:2px solid var(--card);
  box-shadow:var(--shadow); pointer-events:none;
}
/* Three states, set by lvDraw() on the map itself. "none" is no fix at all and
   is the only one that hides the dot; "rough" is a fix too coarse for the
   trace, which is still a position and still gets a dot, hollow and breathing
   so it reads as not settled yet. Ninety-seven minutes of "the map needs the
   phone's position" over a phone that knew perfectly well where it was is the
   bug this state exists to make impossible. */
.lv__map[data-fix="none"] .lv__me{display:none}
.lv__map[data-fix="rough"] .lv__me{
  background:var(--card); border-color:var(--stain); border-width:3px;
  animation:lvpulse 1.8s ease-in-out infinite;
}
@keyframes lvpulse{
  0%,100%{transform:scale(1); opacity:1}
  50%{transform:scale(1.35); opacity:.7}
}
@media (prefers-reduced-motion:reduce){
  .lv__map[data-fix="rough"] .lv__me{animation:none}
}
.lv__nomap{
  position:absolute; left:0; right:0; top:50%; transform:translateY(-50%);
  margin:0; padding:0 var(--s4);
  font:400 .8rem/1.45 var(--f-body); color:var(--ink-3); text-align:center;
}
/* Hidden as soon as there is any position at all, coarse or not, because from
   that moment the sentence is false. */
.lv__map:not([data-fix="none"]) .lv__nomap{display:none}

/* Ovidiu, 08.08: "I want to be able to open the map after and during the walk."
   During is this pair of rules. The strip keeps its size and its place in the
   column; the class swap lifts the same element out to the whole screen and
   lvDraw fills the bigger box from the tiles already in LVTILES. z-index 90
   clears the sheet at 80, because a walk can be opened from one. */
.lv__grow{
  position:absolute; right:var(--s3); top:var(--s3); z-index:4;
  appearance:none; -webkit-appearance:none; cursor:pointer;
  min-height:44px; padding:var(--s2) var(--s4);
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink);
  font-family:var(--f-body); font-size:13px; font-weight:600;
  letter-spacing:.01em;
}
.lv__map--full{
  position:fixed; inset:0; z-index:90;
  height:auto; max-height:none; margin:0;
  border:0; border-radius:0;
}
/* Out of the way of the island, and out of the way of the home indicator. */
.lv__map--full .lv__grow{top:calc(var(--s3) + env(safe-area-inset-top))}

/* And after is this one: the stored track, drawn by the same lvMini, given the
   screen instead of a corner of it. Pure SVG, so it opens with no network. */
.lvfull{
  position:fixed; inset:0; z-index:90;
  background:var(--paper);
  display:flex; flex-direction:column;
  padding:env(safe-area-inset-top) var(--s4) env(safe-area-inset-bottom);
  overflow:auto;
}
.lvfull__hd{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); padding:var(--s4) 0;
}
.lvfull__hd b{font-family:var(--f-display); font-weight:400; font-size:18px}
/* The drawing already carries an explicit height, so letting it grow as well
   left a band of empty paper between the route and the shelf that reads about
   it. It takes the room it asked for and no more. */
.lvfull .lvm{flex:0 0 auto; min-height:0}
.lvfull__slot{flex:0 0 auto}
.lvfull__cap{margin:var(--s2) 0 0}
/* The saved route over real tiles. Same tile layer the live walk uses, so the
   basemap, its grey filter and its dark-theme inversion are all one rule. */
.lvrt{
  position:relative; overflow:hidden;
  border:1px solid var(--line); border-radius:var(--r);
  background:var(--paper);
  /* The map takes the gestures. Without this the browser claims the drag for
     page scrolling and the pinch for its own zoom, and a map you cannot pan is
     a picture. Ovidiu, 08.08: "I want to zoom in and out, pan etc... normal map
     functionality, I need to check the path against the road." */
  touch-action:none; -webkit-user-select:none; user-select:none;
  cursor:grab;
}
.lvrt:active{cursor:grabbing}
/* Moved with a transform during a gesture and reset when the finger lifts, at
   which point the view is committed and the tiles are rebuilt once. */
.lvrt__pl{position:absolute; inset:0; will-change:transform}
.lvrt__pan{position:absolute; inset:0}
.lvrt__ink{position:absolute; inset:0; z-index:2; width:100%; height:100%}
.lvrt__z{
  position:absolute; right:var(--s3); top:var(--s3); z-index:4;
  display:flex; flex-direction:column; gap:var(--s2);
}
.lvrt__zb{
  appearance:none; -webkit-appearance:none; cursor:pointer;
  width:44px; height:44px; padding:0;
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink);
  font:600 18px/1 var(--f-body);
  display:flex; align-items:center; justify-content:center;
}
.lvrt__zb:active{background:var(--paper)}

/* What to do about it, under the map, and only when there is something to do.
   lvAdvice() decides; this is only how it looks. */
.lv__fix{
  display:flex; flex-direction:column; gap:var(--s2);
  margin:calc(var(--s4) * -1) 0 var(--s4);
}
.lv__fix p{margin:0; font:400 .78rem/1.5 var(--f-body); color:var(--ink-2)}

/* ---- the nine ---- */
.lv__acts{
  /* 12, the gap every card grid in the app uses; 8 read as a different page */
  display:grid; grid-template-columns:repeat(3, 1fr); gap:var(--s3);
  margin-bottom:var(--s4);
}
.lv__a{
  position:relative;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--s1); min-height:76px; padding:var(--s2);
  background:var(--card); color:var(--ink-2);
  border:1px solid var(--line); border-radius:var(--r);
  font:600 .72rem/1.2 var(--f-body); text-align:center; cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.lv__a:active{background:var(--stain-wash); border-color:var(--stain); color:var(--stain)}
.lv__a:hover{border-color:var(--stain)}
/* How many times it has been tapped this walk. On the button rather than in a
   list, because the list is below the fold on a small phone and the question
   being asked at 19:40 is "did I already log that". */
.lv__n{
  position:absolute; top:var(--s1); right:var(--s1);
  min-width:20px; height:20px; padding:0 var(--s1);
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-pill); background:var(--stain); color:var(--on-accent);
  font:600 .68rem/1 var(--f-data); font-style:normal;
}

/* ---- the tenth thing, deliberately not in the grid ----
   Full width, directly under the nine, and shaped like the nine so it reads as
   one of them rather than as a control that belongs to the map. It is the only
   thing on this screen that opens a keyboard, which is exactly why it is not a
   cell: a cell is a thumb on a lead, and this is not. */
.lv__note{
  display:flex; align-items:center; justify-content:center; gap:var(--s2);
  width:100%; min-height:var(--tap-min); padding:var(--s3);
  margin-top:var(--s3);
  background:var(--card); color:var(--ink-2);
  border:1px dashed var(--line); border-radius:var(--r);
  font:600 .78rem/1.2 var(--f-body); cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.lv__note:hover{border-color:var(--stain); color:var(--stain)}
.lv__note:active{background:var(--stain-wash); border-color:var(--stain); color:var(--stain)}
.lv__notew{
  display:flex; flex-direction:column; gap:var(--s2);
  margin-top:var(--s3); padding:var(--s3);
  background:var(--card); border:1px solid var(--stain); border-radius:var(--r);
}
.lv__notew textarea{width:100%}
.lv__notew .duo{margin-top:0}
.lv__notew .mut{margin:0; font:400 .74rem/1.4 var(--f-body)}

/* ---- what has been tapped so far ---- */
.lv__f{
  display:flex; align-items:center; gap:var(--s2);
  padding:var(--s2) 0; border-bottom:1px solid var(--line-soft);
  font:400 .82rem/1.3 var(--f-body); color:var(--ink-2);
}
.lv__f:last-child{border-bottom:0}
.lv__f .mut{font-family:var(--f-data); font-variant-numeric:tabular-nums}
/* A stool row is the door back into its own grade sheet. The affordance is the
   dash an ungraded one shows; the cursor and the press state are for the rest. */
.lv__regrade{cursor:pointer; -webkit-tap-highlight-color:transparent}
.lv__regrade:active{color:var(--stain)}
/* The time, which is also the control that moves it. It looks like the label it
   replaced until it is touched, because a row of nine taps with nine obvious
   buttons on it is a row nobody reads. */
.lv__when{
  padding:var(--s1) var(--s2); min-height:var(--tap-min);
  background:transparent; border:1px solid transparent; border-radius:var(--r-sm);
  color:var(--ink-3); font:400 .78rem/1 var(--f-data);
  font-variant-numeric:tabular-nums; cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.lv__when:hover{border-color:var(--line); color:var(--ink-2)}
.lv__when--on{border-color:var(--stain); color:var(--stain)}
.lv__whenw{display:flex; align-items:center; gap:var(--s1)}
.lv__whenb{
  min-width:var(--tap-min); min-height:var(--tap-min); padding:0 var(--s2);
  background:var(--card); color:var(--stain);
  border:1px solid var(--stain); border-radius:var(--r-sm);
  font:600 .78rem/1 var(--f-data); cursor:pointer;
}
.lv__whenb:disabled{color:var(--ink-3); border-color:var(--line); cursor:default}

/* ---- the way in, on the Log screen ---- */
.wk__live{
  display:flex; align-items:center; gap:var(--s3); width:100%;
  min-height:var(--tap-min); padding:var(--s3);
  margin-bottom:var(--s3);
  background:var(--card); color:var(--ink-2); text-align:left;
  border:1px solid var(--line); border-radius:var(--r); cursor:pointer;
}
.wk__live b{display:block; font:600 .9rem/1.3 var(--f-body); color:var(--ink)}
.wk__live .sub{display:block; font:400 .75rem/1.35 var(--f-body); color:var(--ink-3)}
/* A walk that is running is the only thing on the screen allowed to be loud. */
.wk__live--on{border-color:var(--stain); background:var(--stain-wash)}
.wk__live--on b{color:var(--stain)}
.wk__live--on .sub{
  color:var(--stain); font-family:var(--f-data); font-variant-numeric:tabular-nums;
  font-size:.9rem;
}

/* ---- the trace afterwards, on the outing sheet ---- */
.lvm{margin:0}
.lvm__s{
  display:block; width:100%; height:120px;
  background:var(--paper); border:1px solid var(--line-soft); border-radius:var(--r-sm);
}
.lvm__trh{fill:none; stroke:var(--card); stroke-linecap:round; stroke-linejoin:round}
.lvm__tr{fill:none; stroke:var(--stain); stroke-linecap:round; stroke-linejoin:round}
/* The same word as .lv__tr--co on the live map. The dash length itself is set
   inline, because this SVG is scaled to the walk's own bounding box and a fixed
   dash would be invisible on a long walk and a dotted mess on a short one. */
.lvm__tr--co{opacity:.85}
/* A stretch where the phone said nothing. Deliberately NOT the trace ink and
   deliberately not solid: it is the one part of the line that is not a record
   of anywhere he went, only of the two places he was seen either side of it.
   Thinner than the trace, quieter than the trace, and openly dashed. */
.lvm__gap{
  fill:none; stroke:var(--ink-3); stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:2 10; opacity:.75;
}
/* Symbology. Shape carries the class, because a shape survives being 8 across
   on a dark map and a hue does not. Colour is then free to mean exactly one
   thing: a sign is in the alert ink, everything else is in the trace ink. Any
   change here has to be made in the lvMark/LVSYM pair in js/36-live.js as well,
   or the legend will describe a map that is not being drawn. */
.lvm__ev{fill:var(--stain); stroke:var(--card); stroke-width:1}
.lvm__ev--sign{fill:var(--eosin)}
.lvm__ev--note{fill:var(--card); stroke:var(--eosin); stroke-width:2}
/* A recorded position is the quietest mark on the map and must stay that way:
   there are up to three hundred of them and they are the background against
   which the handful of events read. Drawn as a solid dot rather than as a ring,
   because stroke-width here is in user units and the drawing is scaled to fit,
   so a hairline ring came out under a pixel and the dots read as holes punched
   in the line. */
.lvm__fx{
  fill:none; stroke:var(--ink-3); stroke-width:5; stroke-linecap:round;
  vector-effect:non-scaling-stroke; opacity:.9;
}
.lvleg__s .lvm__fx{stroke-width:6}
.lvm__en{fill:var(--stain); stroke:var(--card); stroke-width:1}
/* The one selected mark, on a map where every other mark keeps its own ink. */
.lvm--sel{stroke:var(--ink); stroke-width:3; paint-order:stroke}
/* A recorded position IS its stroke, so the selection rule above was shrinking
   it from 5 to 3: picking a dot made it smaller, which is the opposite of what
   a selection is for. */
.lvm__fx.lvm--sel{stroke-width:9}
.lvfull svg [data-i],.lvfull svg [data-e],
.lvfull svg [data-s],.lvfull svg [data-x]{cursor:pointer; pointer-events:all}

/* The identify shelf and the legend, under the drawing. */
.lvid{
  flex:0 0 auto; min-height:76px;
  padding:var(--s3); margin-top:var(--s3);
  border:1px solid var(--line-soft); border-radius:var(--r);
  background:var(--card);
}
.lvid h4{margin:0 0 var(--s2)}
.lvid__l{
  display:grid; grid-template-columns:auto 1fr;
  gap:var(--s1) var(--s3); margin:0;
}
.lvid__l dt{color:var(--ink-2); font-size:13px}
.lvid__l dd{margin:0; font-size:13px}
.lvleg{
  flex:0 0 auto;
  display:flex; flex-wrap:wrap; gap:var(--s2) var(--s4);
  padding:var(--s3) 0 var(--s4);
  font-size:12px; color:var(--ink-2);
}
.lvleg__i{display:inline-flex; align-items:center; gap:var(--s2)}
.lvleg__s{width:16px; height:16px; flex:0 0 auto; overflow:visible}
.lvm__ev--n{fill:var(--card); stroke:var(--eosin); stroke-width:2}
.lvm__st{fill:var(--card); stroke:var(--stain); stroke-width:2}
.lvm figcaption{
  margin-top:var(--s1);
  font:400 .75rem/1.35 var(--f-body); color:var(--ink-3);
  font-variant-numeric:tabular-nums;
}

/* Two numbers and not one, because these are CONTAINER widths and the column
   runs about 40px narrower than the phone: a 360px phone measures 331 here and
   a 390px phone measures 349. Dropping to two columns at 340 therefore caught
   the 360s and the 375s, which is most of the phones in the house, and left
   nine buttons stacked five rows deep with the grader pushed off the screen.
   305 catches only a genuine 320px handset. The clock keeps the old 340: it is
   one line of type and shrinking it early costs nothing. */
@container view (max-width:305px){
  .lv__acts{grid-template-columns:1fr 1fr}
}
@container view (max-width:340px){
  .lv__clock{font-size:2rem}
}
/* On a folded-open phone the map is the thing worth the extra room, not the
   buttons: nine targets that are already comfortable do not get better at
   double the width, and a walk read at that size is being read for where. */
/* 600 and not 700, because the shell caps its own content column at 680px and
   a container query on .view can therefore never see 700. On a phone this
   never fires; on the unfolded half of a foldable, where the window is 820 and
   the column is the full 680, it does. */
@container view (min-width:600px){
  .lv__map{height:400px; max-height:50dvh}
  .lv__acts{grid-template-columns:repeat(5, 1fr)}
}

/* The invitation code, shown once and read off a screen into a message. Wide
   letter-spacing and the data face because the alphabet already drops the pairs
   that get misread; the spacing is so a group of five stays a group of five when
   somebody is copying it by eye. user-select is explicit: this is the one string
   in the app somebody genuinely needs to drag over. */
.codeout{font-family:var(--f-data); font-size:20px; font-weight:600;
  letter-spacing:0.14em; color:var(--ink); margin:var(--s3) 0;
  user-select:all; -webkit-user-select:all; word-break:break-all}

/* The walks journal takes the same swipe the medication rows have carried since
   19-adapt.js, and the same drawer width, so a thumb that has learned one has
   learned both. The wrapper clips and carries the corner; the card is the face
   that slides off the two actions underneath it. Ovidiu, 08.08: "to have the
   swipe behaviour on the walks as well". */
.wsw{position:relative; overflow:hidden; border-radius:var(--r)}
.wsw > .wsw__f{
  position:relative; z-index:1; background:var(--card);
  touch-action:pan-y;          /* the page keeps the vertical, we take the rest */
  transition:transform .18s ease; will-change:transform;
}
.wsw[data-sw="drag"] > .wsw__f{transition:none}

/* ---------- suggested rations (11.08.2026) ----------
   A stack of offers under the panel, not a screen of their own. The decision
   they support is "given what I just read is wrong with the bowl, is one of
   these better", and that question dies if you have to navigate away from the
   evidence to ask it. */
.rat{
  border:1px solid var(--line); border-radius:var(--r-sm);
  padding:var(--sp-8); display:flex; flex-direction:column; gap:var(--s2);
}
.rat + .rat{margin-top:var(--sp-6)}
/* The one being previewed. The panel above has already changed under the
   reader, so the card that changed it has to be identifiable at a glance or
   the screen is lying about what it is showing. */
.rat[data-on="1"]{border-color:var(--stain); background:var(--stain-wash)}
.rat__h{display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap}
.rat__h b{font:500 14px/1.3 var(--f-display); color:var(--ink)}
/* Three figures on one line, in the data face, because they are read as a set
   and compared down the column between cards rather than across one. */
.rat__s{display:flex; flex-wrap:wrap; gap:var(--s2) var(--sp-8);
  font:500 11px var(--f-data); font-variant-numeric:tabular-nums; color:var(--ink-2)}
.rat__n{font:400 12.5px/1.55 var(--f-body); color:var(--ink-2); margin:0}
.rat__g{font:400 11.5px/1.5 var(--f-body); color:var(--warn); margin:0}
.rat__d{font:500 11.5px/1.5 var(--f-body); color:var(--ink); margin:0}
.rat__b{display:flex; gap:var(--s2); flex-wrap:wrap; margin-top:var(--s1)}
.rat__b .btn{flex:1 1 auto; min-width:0}


/* ---- the dose log ------------------------------------------------------ */
/* One medication, every day of it. The Due now card is a day with medications
   on it; this is a medication with days on it. Same three states, same two
   buttons, same icons, so nothing has to be learned twice.

   Deliberately reuses `.md > button` rather than growing a second pair: the
   gesture is identical and a second pair of green-and-red buttons that behaved
   even slightly differently would be worse than none. */
.dlnow{display:flex; flex-direction:column; gap:var(--s2); margin-bottom:var(--s4)}
.dlnow__b{width:100%; min-height:52px; display:flex; align-items:center;
          justify-content:center; gap:var(--s2)}
.dlnow__b svg{width:20px; height:20px}

/* The vet's own sentence. It has been in the database since the first seed and
   on no screen until now, which is how "never stopped abruptly" ended up being
   something only the PDF knew. */
.dlnote{
  display:flex; gap:var(--s2); align-items:flex-start;
  padding:var(--s3); border-radius:var(--r);
  background:var(--stain-wash); color:var(--ink-2);
  font-size:12.5px; line-height:1.45; margin-bottom:var(--s4);
}
.dlnote svg{width:16px; height:16px; flex:none; margin-top:1px}

.dlbar{margin-bottom:var(--s3)}
.dllist{margin-bottom:var(--s4)}

.dlrow{
  display:flex; align-items:center; gap:var(--s3);
  padding:var(--sp-8) 0;
  border-top:1px solid var(--line-soft);
}
.dlrow:first-child{border-top:0}
.dlrow__d{flex:1; min-width:0; display:flex; flex-direction:column; gap:3px}
.dlrow__d b{font-weight:600; font-size:13.5px; line-height:1.25}
.dlrow__d s{text-decoration:none; font-size:11.5px; color:var(--ink-3)}
/* A day the course did not owe is still tickable, because a dose given off
   schedule is still a dose. It is quieter, so a list read at a glance still
   reads as the schedule and not as a wall of equal days. */
.dlrow[data-owed="0"] .dlrow__d b{font-weight:500; color:var(--ink-2)}
/* Skipped on purpose is a decision, not a failure. Same amber the card uses. */
.dlrow[data-st="s"] .dlrow__d b{color:var(--warn-ink)}

/* The way in, at the top of a register entry. */
.dllink{margin-bottom:var(--s4)}
.dllink .btn{width:100%; display:flex; align-items:center; justify-content:center;
             gap:var(--s2)}
.dllink .btn svg{width:16px; height:16px}

/* The course fold on the register form. A native <details>, because it holds
   four controls that most medications never need and a fold that the browser
   owns cannot get out of step with itself. */
.adm__d{margin:var(--s3) 0}
.adm__d > summary{
  cursor:pointer; font-size:13px; font-weight:600; color:var(--ink-2);
  padding:var(--s2) 0; list-style:none;
}
.adm__d > summary::-webkit-details-marker{display:none}
.adm__d > summary::before{content:"+ "; color:var(--ink-3)}
.adm__d[open] > summary::before{content:"– "}

/* ==== 260901 jmed ==== */
/* Two things from the journal's medication card, 01.09.2026. Appended, not
   edited: the .mrow block at ~4251 is left as it was and overridden from here
   with a longer selector.

   1. The name is the way to its settings. The thumbnail, the two lines of
      text and a chevron are one button (.mrow__go) inside the face; the two
      state buttons stay their own. No padding and no height of its own beyond
      the 44px floor the state buttons already set, so the row measures
      exactly what it did and the button fills only the width the text block
      had. The chevron is the same drawn one the register rows (.adm__x) and
      the clinic hub wear. */
.mrow .mrow__go{
  flex:1; min-width:0; min-height:var(--tap-min);
  display:flex; align-items:center; gap:var(--sp-9);
  background:none; border:0; padding:0; margin:0;
  text-align:left; color:inherit; cursor:pointer;
  border-radius:var(--r-sm);
}
.mrow .mrow__go:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.mrow .mrow__gx{flex:none; width:16px; display:grid; place-items:center; color:var(--ink-3)}
.mrow .mrow__gx svg{width:16px; height:16px}
/* 2. "Between the given icon and Skip there is no space." The action layer
      keeps 12px clear on its left (4pt grid), so a row dragged open shows its own
      background between the Given button and Skip: the card's colour in the
      list, the amber wash inside the late fold, because .mrow carries it in
      both places. The buttons keep their 76px and their full height; only the
      layer is wider, 162 now, which is the W in swipeRow() and must stay
      equal to it. */
.mrow .mrow__a{padding-left:12px}

/* ==== 260901 itch ==== */
/* A carried score (data-carry="1", 01.09.2026): the number the day inherited
   from the last scored day, drained so a carried 2 never reads as a scored 2.
   The slider keeps its position, which is the whole point, and loses its ink. */
#itchScale[data-carry="1"] .sld__v{opacity:.5; font-weight:400}
#itchScale[data-carry="1"] .sld__i::-webkit-slider-runnable-track{opacity:.45}
#itchScale[data-carry="1"] .sld__i::-moz-range-track{opacity:.45}
#itchScale[data-carry="1"] .sld__i::-webkit-slider-thumb{border-style:dashed; border-color:var(--ink-3); background:var(--card)}
#itchScale[data-carry="1"] .sld__i::-moz-range-thumb{border-style:dashed; border-color:var(--ink-3); background:var(--card)}
#itchScale[data-carry="1"] .sld__ticks i[data-on="1"]{background:var(--ink-3); opacity:.6}
#itchScale[data-carry="1"] .sld__ticks i[data-on="2"]{opacity:.5}
#itchPrev[data-carry="1"]{color:var(--ink-2); font-style:italic}
/* the hollow swatch in the trend legend, the same mark the canvas draws */
.legend__sw.itc-sw{background:var(--card); border:1.5px solid var(--stain); border-radius:50%}
/* calendar: a carried day wears the band of the number it inherited, at well
   under half the ink of a scored day, with a faint edge so it is not blank */
.cal__d[data-carry="1"][data-i="0"]{background:color-mix(in srgb, var(--ok) 7%, var(--card)); color:var(--ink-3)}
.cal__d[data-carry="1"][data-i="1"]{background:color-mix(in srgb, var(--warn) 10%, var(--card)); color:var(--ink-3)}
.cal__d[data-carry="1"][data-i="2"]{background:color-mix(in srgb, var(--warn) 20%, var(--card)); color:var(--ink-3)}
.cal__d[data-carry="1"][data-i="3"]{background:color-mix(in srgb, var(--bad) 22%, var(--card)); color:var(--ink-3)}
.cal__d[data-carry="1"]{box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 35%, transparent)}
/* the day record's big number, when it is a carried one */
.drbig[data-carry="1"] b{opacity:.55; font-weight:400}

/* ==== 260901 walks ==== */
/* The backfill card in the walks journal ("Numește ieșirile fără nume") and
   the "nume propus" mark beside a name the rule wrote. New names only, under
   the wbf__ prefix; nothing existing is renamed or restyled. */
.wbf__hd{display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4)}
.wbf__n{font-family:var(--f-data); font-variant-numeric:tabular-nums; font-size:.9rem; color:var(--ink-3); flex:none}
.wbf__p{margin-top:var(--sp-4); display:grid; gap:0}
.wbf__g{padding:var(--s2) 0; border-top:1px solid var(--line-soft)}
.wbf__g--no b{color:var(--ink-3); font-weight:400}
.wbf__gh{display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-4)}
.wbf__p > .duo{margin-top:var(--sp-4)}
.wnm .wbf__sg{display:inline-block; margin-left:8px; font:400 .7rem/1.4 var(--f-body); color:var(--ink-3); letter-spacing:.02em; vertical-align:middle; white-space:nowrap}

/* ==== 260901 search ==== */
/* The answer cards over the grouped hit list, js/41-search.js. A card is a
   button; the rows inside it that carry data-act jump on their own. */
.srx-card{display:block; width:100%; text-align:left; cursor:pointer; appearance:none; color:var(--ink); font:inherit;
  background:var(--card); border:1px solid var(--stain); border-radius:var(--r); padding:12px 12px 8px; margin:0 0 8px}
.srx-card__k{display:flex; align-items:center; gap:8px; font-family:var(--f-data); font-size:.62rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--stain)}
.srx-card__k .ic{width:16px; height:16px}
.srx-card__t{font-size:1rem; font-weight:600; margin-top:4px; line-height:1.25}
.srx-card__s{font-size:.8rem; color:var(--ink-2); margin-top:4px}
.srx-card__n{font-size:.76rem; color:var(--ink-3); margin-top:8px}
.srx-card__go{font-size:.74rem; color:var(--stain); margin-top:8px; font-weight:600}
.srx-big{display:flex; align-items:baseline; gap:8px; margin-top:8px; flex-wrap:wrap}
.srx-big b{font-family:var(--f-data); font-size:1.7rem; font-weight:600; line-height:1; color:var(--ink)}
.srx-big span{font-size:.8rem; color:var(--ink-3)}
.srx-sparkbox{margin-top:8px; line-height:0}
.srx-spark{display:block; width:100%; height:36px}
.srx-kv{display:flex; gap:16px; margin-top:8px; flex-wrap:wrap}
.srx-kv__i{display:flex; flex-direction:column; min-width:0}
.srx-kv__i b{font-family:var(--f-data); font-size:.9rem; font-weight:600}
.srx-kv__i span{font-size:.68rem; color:var(--ink-3)}
.srx-rows{margin-top:8px; border-top:1px solid var(--line-soft)}
.srx-rows__h{font-family:var(--f-data); font-size:.62rem; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); padding:8px 0 2px}
.srx-row{display:flex; gap:8px; align-items:baseline; padding:8px 0; border-bottom:1px solid var(--line-soft); font-size:.8rem; min-width:0}
.srx-row:last-child{border-bottom:0}
.srx-row[data-act]{cursor:pointer}
.srx-row[data-act]:hover .srx-row__v{color:var(--stain)}
.srx-row__d{font-family:var(--f-data); font-size:.7rem; color:var(--ink-3); flex:none; width:72px}
.srx-row__v{font-weight:600; display:flex; flex-direction:column; gap:2px; min-width:0; flex:1 1 auto}
.srx-row__v .ic{width:12px; height:12px; vertical-align:middle; margin-left:3px}
.srx-row__b{font-family:var(--f-data); font-size:.68rem; color:var(--ink-3); flex:none}
.srx-row__s{font-size:.72rem; color:var(--ink-3); font-weight:400; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:38%}
.srx-row__v .srx-row__s{max-width:none; white-space:normal}
.srx-row--hi .srx-row__v{color:var(--sev3)}
.srx-row--lo .srx-row__v{color:var(--eosin)}
.srx-row--say .srx-row__v{font-weight:400; font-style:italic}
.srx-grp{margin-top:4px}
.srx-gh{display:flex; align-items:baseline; gap:8px; padding:12px 0 8px}
.srx-gh__n{font-family:var(--f-data); font-size:.68rem; color:var(--ink-3)}
.srx-more{appearance:none; background:none; border:1px dashed var(--line); border-radius:var(--r); color:var(--stain);
  font:600 .78rem var(--f-body); padding:8px 12px; width:100%; margin:0 0 8px; cursor:pointer}

/* ==== 260901 integration ==== */
/* The calendar legend swatch for a carried day, beside the itch block's own
   cell tint (039). Appended here because the itch block is no longer last. */
.calsw.calsw--c{background:color-mix(in srgb, var(--warn) 10%, var(--card)); box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 35%, transparent)}

/* ==== 260901 audit ==== */
/* The 02.09.2026 spacing audit. Five block hosts on the Journal (#firstRun,
   #uplCard, #firstRun2, #conflictHost, #saveState) sit empty most of the time
   and each still took the stack's 12px gap, so the itch card and the flare
   button had 24 and 48px around them instead of 12. An empty host draws
   nothing and takes no gap; the moment it is filled it is back. */
#v-today > .stack > :empty{display:none}
/* Two cards inside a block host were flush against each other. */
#nutriCard{display:grid; gap:var(--s3)}
#calibBox{display:grid; gap:var(--s3)}
/* The group expander is a control and a control is 44px tall in this app. */
.srx-more{min-height:var(--tap-min)}

/* ==== 260902 metric tap ==== */
/* A tile that leads somewhere is a button and reads as one: same box, the
   card's own chrome, a pressed state, a focus ring. Text-align keeps the
   stack left-aligned as the div version was. */
.met.met--tap{appearance:none; font:inherit; text-align:left; cursor:pointer; color:inherit}
.met.met--tap:active{transform:scale(.985)}
.met.met--tap:focus-visible{outline:2px solid var(--stain); outline-offset:2px}

/* ==== 260902 dayswipe ====
   The Journal turns pages. 08-day.js owns the gesture and the arrows; this
   block is only their shape. Every name is prefixed dsw-. */

/* The stack is the page. pan-y tells the browser the vertical pan is its and
   the horizontal one is ours, exactly as .mrow__f and .wsw__f already say for
   their rows; nested rails that scroll sideways are their own scroll
   containers and keep their gesture. */
.dsw-host{touch-action:pan-y}
.dsw-host.dsw-drag{transition:none}
.dsw-host.dsw-anim{transition:transform .22s ease, opacity .22s ease}
@keyframes dsw-bounce{
  0%{transform:none} 40%{transform:translateX(-16px)} 100%{transform:none}
}
.dsw-host.dsw-bounce{animation:dsw-bounce .3s ease}

/* The arrows sit on the toolbar's date line, one each side of the sentence.
   20px of ink and a 44px target drawn by ::after, the same pattern as
   .icobtn--q, so the bar does not grow: the negative vertical margin keeps
   the row at the height of the 11px text it already had. The h1 clips its
   overflow for the title's ellipsis, so it is given the toolbar's own bottom
   padding to clip inside instead; the negative margin returns the space. */
#tbSub[data-dsw="1"]{
  display:flex; align-items:center; gap:var(--s1);
  min-width:0;
}
#tbSub[data-dsw="1"] .dsw-sub{
  flex:0 1 auto; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.tb__t:has(> #tbSub[data-dsw="1"]){
  padding-bottom:var(--s4); margin-bottom:calc(var(--s4) * -1);
  /* and the left, or the first arrow's target is cut at the h1's edge:
     the hit-test found .tb 20px left of its centre */
  padding-left:var(--s3); margin-left:calc(var(--s3) * -1);
}
.dsw-arr{
  appearance:none; -webkit-appearance:none;
  width:var(--s5); height:var(--s5); min-width:var(--s5);
  margin:calc(var(--s1) * -1) 0;
  padding:0; border:0; border-radius:var(--r-pill);
  background:none; color:var(--stain);
  display:inline-flex; align-items:center; justify-content:center;
  position:relative; cursor:pointer; flex:none;
}
.dsw-arr::after{
  content:""; position:absolute; top:50%; left:50%;
  width:var(--tap-min); height:var(--tap-min); transform:translate(-50%,-50%);
  border-radius:var(--r-pill);
}
.dsw-arr svg{width:16px; height:16px}
.dsw-arr:active{background:var(--line-soft)}
.dsw-arr:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.dsw-arr:disabled{color:var(--ink-3); opacity:.45; cursor:default}
/* the date line's back-to-today tint stays on the words, never on the arrows */
.tb__t s[data-back="1"] .dsw-arr:disabled{color:var(--ink-3)}

/* The strip's note line. app.css:4220 prefixes it with " \00b7 " and the
   spaces collapse at the start of a flex item, so "1 overdue" printed glued
   to the dot the first time a note was set. A no-break space does not. */
#todayStrip .met__n:not(:empty)::before{content:"\00b7\00a0"}

/* A sentence or a count that opens its source. Same ink, a pointer, and a
   focus ring: the words already say where they lead. */
.dsw-live{cursor:pointer}
.dsw-live:focus-visible{outline:2px solid var(--stain); outline-offset:2px; border-radius:var(--r)}
#stList .stf, #sysNow .sgnow__i, #mealBox .c__h em{cursor:pointer}

/* The card a tile landed on. One breath of the accent, then gone. */
@keyframes dsw-hi{
  0%{box-shadow:0 0 0 2px var(--stain)} 100%{box-shadow:0 0 0 0 transparent}
}
.dsw-hi{animation:dsw-hi 1.4s ease-out}
@media (prefers-reduced-motion: reduce){
  .dsw-host.dsw-anim{transition:none}
  .dsw-host.dsw-bounce, .dsw-hi{animation:none}
}

/* ==== 260902 daynutri ==== */
/* The day card on the nutrition screen and the Review sub-tab. The head is a
   button to the day it describes: Ovidiu, 02.09.2026, "nicio chestie pe care
   o văd să nu pot interacționa cu ea". Every length is a token; the head
   reaches --tap-min because it is a tap target. */
.dnu-hd{
  appearance:none; background:transparent; border:0; margin:0; padding:0; width:100%;
  min-height:var(--tap-min); display:flex; align-items:center; gap:var(--sp-5);
  font:inherit; color:inherit; text-align:left; cursor:pointer; border-radius:var(--r-sm);
}
.dnu-hd:active{background:var(--stain-wash)}
.dnu-hd:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.dnu-hd__k{display:block}
.dnu-hd__m{
  display:block; margin-top:var(--sp-2);
  font-family:var(--f-display); font-weight:400; font-size:15px; line-height:1.25; color:var(--ink);
}
.dnu-met{display:flex; gap:var(--sp-5); flex-wrap:wrap}
.dnu-met .met{flex:1 1 var(--maxw-tile, 96px)}
.dnu-bag__g{
  display:grid; grid-template-columns:1fr auto; column-gap:var(--sp-7); row-gap:var(--sp-2);
  font-size:.86rem; align-items:baseline;
}
.dnu-bag__g b{font-weight:500; color:var(--ink-2); min-width:0}
.dnu-bag__g var{
  font-style:normal; font-family:var(--f-data); font-size:.78rem; color:var(--ink);
  font-variant-numeric:tabular-nums; text-align:right;
}
.dnu-link{margin-top:0}

/* ==== 260902 interact ==== */
/* Ovidiu, 02.09.2026: "Nu vreau nicio chestie pe care o văd să nu pot
   interacționa cu ea." Every number, name, date, legend line and history row
   that summarises something kept elsewhere is now a way to that place
   (itxTap, js/09-photos.js). The element keeps its tag and its classes so
   nothing restyles; it gains a pointer, the row pressed state the app already
   uses, and a focus ring. The small inline ones grow the same invisible 44px
   target .icobtn--q carries, so a date under a thumbnail is as easy to hit
   as the thumbnail. */
.itx-tap{cursor:pointer; -webkit-tap-highlight-color:transparent}
.itx-tap:active{background:var(--stain-wash)}
.itx-tap:focus-visible{outline:2px solid var(--stain); outline-offset:2px}
.itx-tap--sm{position:relative}
.itx-tap--sm::after{content:""; position:absolute; top:50%; left:0; right:0; height:var(--tap-min); transform:translateY(-50%)}
.itx-tap--sm:active{background:none; opacity:.7}
/* a chart is a box, not a row: the press reads as a dim, not a wash */
svg.itx-tap:active{background:none; opacity:.7}
@media (prefers-reduced-motion:reduce){ .itx-tap:active, .itx-tap--sm:active, svg.itx-tap:active{opacity:1; background:none; outline:2px solid var(--stain)} }

/* ==== 260902 outing sheet ==== */
/* Start and end as two equal fields, label above, input full width of its
   column, the same shape as every other field on the sheet. */
.wk__when .meal__t{width:100%; min-width:0}
.wk__duo{margin-top:0}
.wk__tf{display:flex; flex-direction:column; gap:var(--s2); min-width:0}
.wk__tf > .mut{font-size:.78rem}
/* The finish sheet's answer line: normally the duration the two fields add up
   to, and when they do not add up to one, the reason. --bad-ink and not --bad,
   the same way #saveState does it: this is text on a surface, --bad is a fill. */
#wendSay{margin-top:var(--s3); font-variant-numeric:tabular-nums}
#wendSay[data-bad="1"]{color:var(--bad-ink); font-weight:600}
/* An odd last chip in a two-column set takes the whole row rather than
   standing alone on the left (Ovidiu, 02.09.2026). Phones only: wider
   screens fit three columns, where a full-width fifth chip would be wrong.

   Bounded to SHORT sets on 03.09.2026, when Ovidiu asked for the opposite:
   "pill-urile sunt inegale ca latime, lungime ... ar trebui standardizate".
   Both instructions are right about the case each was looking at. A lone chip
   at the end of a three-chip row reads as abandoned, which is what 02.09 was
   about. A lone chip stretched to 398 at the end of a twelve-row grid of
   twenty-three lesion terms reads as the important one, and puts two widths
   back into a grid whose whole point is one - measured 03.09 at 195 and 398 on
   a 430 screen, which is exactly the raggedness being complained about.

   Five is the boundary, and it is where the two readings stop agreeing: up to
   five, the row is the block and a hole in it is conspicuous; past five, the
   grid is the block and its last cell is a calendar's last week. */
@media (max-width:599px){
  .chips--set > .chip:nth-child(-n+5):last-child:nth-child(odd){grid-column:1 / -1}
}

/* ==== 260902 gallery, batch import, filing, lesion map over time ====
   Ovidiu, 02.09.2026: a gallery by day with filters, a batch upload from the
   phone, and saying where on the animal each photograph belongs afterwards.
   Everything here reuses the app's own boxes: .card, .chips, .segs, .sheet.
   The only new geometry is the tile grid and the two run bars, because
   neither exists anywhere else. */

/* One filter row: its name, then its chips. A label above rather than beside,
   because eight rows of label-plus-chips side by side is a form and this is a
   set of switches. */
.gal__frow{display:flex; flex-direction:column; gap:var(--s1); margin-top:var(--s3)}
/* A chip whose count is zero, which is not the one that is on. It stays
   tappable and stops claiming there is something behind it. */
.chip[data-empty="1"]{opacity:.45}
.chip[data-on="1"]{border-color:var(--stain); color:var(--stain)}

.gal__rail{position:sticky; top:0; z-index:2; background:var(--paper); padding-top:var(--s1)}

.gal__day{margin-top:var(--s5)}
/* The date sticks while its own photographs scroll past, which is the whole
   reason the gallery is sectioned by day rather than run as one grid. */
.gal__hd{
  position:sticky; top:var(--s8); z-index:1;
  display:flex; align-items:baseline; gap:var(--s2);
  padding:var(--s2) 0; background:var(--paper);
  border-bottom:1px solid var(--line-soft);
}
.gal__hdd{font-weight:600; color:var(--ink)}
.gal__hds{font-size:.78rem; color:var(--ink-2)}

/* Three across on a phone, two on the narrowest, four from 600. Square, because
   a mixed-orientation grid of portrait and landscape frames reads as broken
   rather than as varied. */
.gal__grid{display:grid; grid-template-columns:repeat(3, 1fr); gap:var(--s2); margin-top:var(--s2)}
@media (max-width:359px){ .gal__grid{grid-template-columns:repeat(2, 1fr)} }
@media (min-width:600px){ .gal__grid{grid-template-columns:repeat(4, 1fr)} }

.gal__c{
  position:relative; aspect-ratio:1; border-radius:var(--r-sm);
  overflow:hidden; background:var(--line-soft); cursor:pointer;
}
.gal__c img{width:100%; height:100%; object-fit:cover; display:block}
.gal__c img[data-broke="1"]{visibility:hidden}
.gal__c:active{opacity:.8}
.gal__c[data-sel="1"]{outline:3px solid var(--stain); outline-offset:-3px}

.gal__tags{position:absolute; left:var(--s1); bottom:var(--s1); right:var(--s1); display:flex; flex-wrap:wrap; gap:2px}
.gal__t{
  font-size:.62rem; line-height:1.4; padding:0 var(--s1);
  border-radius:var(--r-xs); background:var(--card); color:var(--ink-2);
  max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.gal__t--pend{background:var(--stain); color:var(--card)}
.gal__t--walk{background:var(--sev0); color:var(--card)}
.gal__t--ann{background:var(--eosin); color:var(--card)}
/* The day's grade for that site, as a stripe down the left edge. A badge would
   compete with the site code; a stripe is read without being looked at. */
.gal__g{position:absolute; left:0; top:0; bottom:0; width:var(--s1)}
.gal__g[data-sev="0"]{background:var(--sev0)}
.gal__g[data-sev="1"]{background:var(--sev1)}
.gal__g[data-sev="2"]{background:var(--sev2)}
.gal__g[data-sev="3"]{background:var(--sev3)}
.gal__tick{
  position:absolute; right:var(--s1); top:var(--s1);
  width:var(--s5); height:var(--s5); border-radius:var(--r-pill);
  background:var(--card); color:var(--stain);
  display:grid; place-items:center; font-weight:700; font-size:.8rem;
}
/* Taking one photograph out of a batch before it is sent. Full tap target
   around a small mark, the way the rest of the app grows its targets. */
.gal__x{
  position:absolute; right:0; top:0;
  width:var(--tap-min); height:var(--tap-min);
  border:0; background:transparent; color:var(--card);
  font-size:1.2rem; line-height:1; cursor:pointer;
  text-shadow:0 1px 3px rgba(0,0,0,.6);
}

.gal__sel{display:flex; flex-direction:column; gap:var(--s2); margin-top:var(--s3)}

/* One site over time: a bar a graded day, oldest on the left. Deliberately not
   a chart. It answers "has this paw got better" at a glance and hands the real
   question to the day record. */
.gal__run{display:flex; gap:2px; align-items:flex-end; margin-top:var(--s2); flex-wrap:wrap}
.gal__runb{
  width:var(--s3); height:var(--s5); border-radius:var(--r-xs);
  background:var(--line); position:relative; cursor:pointer;
}
.gal__runb[data-sev="0"]{background:var(--sev0)}
.gal__runb[data-sev="1"]{background:var(--sev1)}
.gal__runb[data-sev="2"]{background:var(--sev2)}
.gal__runb[data-sev="3"]{background:var(--sev3)}
.gal__runb[data-shot="1"]::after{
  content:""; position:absolute; left:50%; bottom:calc(var(--s2) * -1);
  width:3px; height:3px; border-radius:var(--r-pill);
  background:var(--eosin); transform:translateX(-50%);
}

/* ---- filing one photograph at a time ---- */
/* Big enough to recognise the site in, capped so the picker is on the same
   screen. A full-width square on a 430px phone is 430px of picture before the
   first answer, on a sheet that repeats once per photograph. */
.tri__pic{
  width:100%; aspect-ratio:1; max-height:320px; border-radius:var(--r);
  overflow:hidden; background:var(--line-soft); margin-bottom:var(--s3);
}
.tri__pic img{width:100%; height:100%; object-fit:contain; display:block}
.tri__reg{margin-top:var(--s3)}

/* ---- the lesion map over time ---- */
.lm__stage{
  position:relative; overflow:hidden; touch-action:none;
  border-radius:var(--r); background:var(--card);
  border:1px solid var(--line); margin-top:var(--s3);
}
.lm__stage svg{width:100%; height:auto; display:block}
.lm__run{display:flex; gap:2px; align-items:flex-end; margin-top:var(--s2); flex-wrap:wrap}
.lm__runb{width:var(--s2); background:var(--stain-soft); border-radius:var(--r-xs); cursor:pointer}
.lm__runb[data-on="1"]{background:var(--eosin)}

/* ---- then and now, rebuilt 02.09.2026 ----
   The photographs are the screen now, not 72px tiles, and the two zoom as one.
   .cmp__box is the window, .cmp__z is what bindZoomPan transforms. */
.cmp__box{
  position:relative; overflow:hidden; touch-action:none;
  border-radius:var(--r); background:var(--line-soft);
  aspect-ratio:1; width:100%;
}
.cmp__z{width:100%; height:100%; transform-origin:50% 50%}
/* cover, not contain: the photograph fills its square and the edges are masked,
   the file itself is untouched. Ovidiu, 05.09.2026: "vreau ca poza sa umple
   patratul in care se afla, zoom in cu masca, nu crop". A tap opens the whole
   frame in the photo sheet, so nothing masked here is lost. */
.cmp__box img{width:100%; height:100%; object-fit:cover; display:block}
.cmp__box img[data-broke="1"]{visibility:hidden}
.cmp__c[data-now="1"] .cmp__box{outline:3px solid var(--eosin); outline-offset:-3px}
.cmp__c[data-now="0"] .cmp__box{outline:3px solid var(--ink-3); outline-offset:-3px}
/* Side by side on every width, including the phone. Stacking them was my
   reading of "bigger, not thumbnails" and it was the wrong half of the trade:
   Ovidiu, 02.09.2026, on the stacked version - "imi placea partea de comparat
   mai mult cand erau una langa cealalta, chiar daca erau mai mici". A
   comparison you have to scroll between is not a comparison; the eye has to
   hold both at once. At 430 that is two columns of about 190px, which is
   nearly three times the 64px tile this rebuild replaced, and the linked
   pinch is what gets you closer than that. The gap tightens on a phone so
   both frames keep as much of the width as possible. */
@media (max-width:599px){ .cmp{gap:var(--s2)} }

/* Photographs on this day nobody has placed on the animal yet. */
.hint--warn{color:var(--eosin); font-weight:600}

/* ==== 260902 the Photos tab's own doors, with the pictures in them ====
   Ovidiu: "Galeria, Atunci si acum, ar trebui sa aiba cateva poze ca preview,
   ultimele... de ce este UX UI ul atat de plictisitor? atunci si acum are
   pill-urile ingramadite." All three answered here and in js/09-photos.js. */

/* A door row that has content of its own underneath it does not close with a
   rule: the picture below belongs to the row, and a line between the two says
   it does not. */
.test--flat{border-bottom:0; padding-bottom:var(--s1)}

/* The last few frames, cut off at the edge so the strip says "there is more"
   by being cut off rather than by carrying a label that says so. */
.pstrip{
  display:flex; gap:var(--s2); overflow-x:auto;
  padding-bottom:var(--s3); margin-bottom:var(--s2);
  scrollbar-width:none;
}
.pstrip::-webkit-scrollbar{display:none}
.pstrip .thumb{flex:none; width:76px; height:76px}
.pstrip__more{
  flex:none; width:76px; height:76px; border-radius:var(--r-sm);
  border:1px dashed var(--line); background:none; color:var(--ink-2);
  font-family:var(--f-data); font-size:.85rem; font-weight:600;
  display:grid; place-items:center; cursor:pointer;
}
.pstrip__more:active{background:var(--stain-wash)}

/* The pair itself, at the width of the card rather than as two 64px tiles.
   Same two rings the compare sheet uses, so the earlier and the later frame
   are named the same way wherever they are drawn. */
.ppair{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s3);
  margin-bottom:var(--s2);
}
.ppair__c{min-width:0}
.ppair__c .thumb{width:100%; height:auto; aspect-ratio:1; border-radius:var(--r)}
.ppair__c[data-now="1"] .thumb{outline:2px solid var(--eosin); outline-offset:-2px}
.ppair__c[data-now="0"] .thumb{outline:2px solid var(--ink-3); outline-offset:-2px}
.ppair__d{
  font-family:var(--f-data); font-size:.75rem; font-weight:600;
  color:var(--ink-2); margin-top:var(--s1);
}
.ppair__g{font-size:.75rem; color:var(--ink-3)}

/* The fact the two photographs are evidence for, under the frame it belongs
   to. Kept apart from the diet line under it: at 190px a column the two ran
   together into four wrapped rows of grey and neither could be read. */
.cmp__f{font-size:.78rem; font-weight:600; color:var(--ink-2); margin-top:var(--s1)}
.cmp__c .mut{font-size:.72rem}

/* Where the photograph is filed, on the sheet that shows it. Sits between the
   name and the picture, so it is read on the way in rather than hunted for. */
.phs__file{margin-bottom:var(--s3)}

/* The site picker's plate. Same stage the lesion map over time uses, capped so
   the list by name underneath is on the same screen rather than a scroll away. */
.pick__stage{max-height:340px}
.pick__stage svg{width:100%; height:auto; display:block}
