/* ===================================================================
   THE DESIGN LANGUAGE — every page on this box, in one file.
   The written version of these rules lives in CLAUDE.md ("Design
   language"). This file is the implementation; that is the contract.

   Three ideas hold it together:

     1. THREE FONTS, each with one job. --font-ui is the site's voice
        and the default; the other two are exceptions you opt into.
     2. FIVE COLOURS THAT MEAN THINGS. Paper and ink carry the page;
        accent, accent-2 and danger each say one thing and nothing else.
     3. TOKENS, NOT VALUES. A page that writes `14.5px` or `#cc0066`
        has left the system. Steps exist so pages can't drift apart by
        half a pixel at a time.

   Pages own their LAYOUT (grids, widths, what sits where) and nothing
   else. If you find yourself restyling a button, a heading, an input
   or a badge in a page, the rule belongs here instead.
   =================================================================== */

:root {
  /* ---------------------------------------------------------------
     FONTS — three, each with one scenario. Anything else is a bug.
     --------------------------------------------------------------- */

  /* The site's voice. THE DEFAULT FOR EVERYTHING: body copy, headings,
     buttons, labels, tables, numbers, code, chrome. If you are not sure
     which font to use, it is this one. */
  --font-ui: ui-monospace, "SF Mono", Menlo, "Courier New", monospace;

  /* Long human writing, and only that: note bodies, agent chat text,
     goal/album descriptions, multi-paragraph help. One line of mono
     reads fine; six paragraphs of it does not. Never for UI chrome —
     a sans-serif button in a mono interface looks like a mistake. */
  --font-prose: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Sticky notes on /shared_notes. That is the entire list. It is here
     because a corkboard should not look like a terminal; it is scoped
     this tightly because everything else should.
     Ordered by legibility, not by charm: these are printed, unjoined
     faces, and the stack ends in sans-serif rather than `cursive` —
     the generic resolves to a joined script on most Linux desktops,
     which is the least readable thing a note could be set in. */
  --font-hand: "Chalkboard SE", "Comic Sans MS", "Segoe Print", "Marker Felt", sans-serif;

  /* ---------------------------------------------------------------
     TYPE SCALE — five steps, in px so nested `em` can't compound a
     page into its own scale. Two weights: 400, and 700 for emphasis.

     These are the DESKTOP values. Every step is one notch larger on a
     phone — see "SMALL SCREENS" at the foot of this file. A page that
     writes the token gets that for free; a page that writes `13px`
     does not, which is the practical reason raw sizes are banned.
     --------------------------------------------------------------- */
  --fs-xs: 11px;   /* tags, badges, timestamps, table units */
  --fs-sm: 13px;   /* secondary and meta text, small buttons, hints */
  --fs-md: 15px;   /* body — the default */
  --fs-lg: 18px;   /* section headings (h2) */
  --fs-xl: 24px;   /* page title (h1), one per page */

  --lh-tight: 1.25;  /* headings */
  --lh: 1.6;         /* body */

  /* The smallest a control may be and still be reliably hittable with a
     thumb. Desktop pointers are precise and want density, so this is
     raised — not the button heights themselves — on small screens. */
  --tap: 36px;

  /* ---------------------------------------------------------------
     COLOUR — paper and ink carry the page. Three accents each say one
     thing. Two status colours report state and never fill a button.
     --------------------------------------------------------------- */

  /* surfaces & text */
  --paper:   #faf0d4;  /* page background */
  --paper2:  #fff8e0;  /* raised surface: cards, inputs, rows, the header */
  --ink:     #2d1b00;  /* body text */
  --dim:     #7a6a4a;  /* secondary text — meta, hints, captions — and the
                          edge of a form control: --rule is the site's
                          structural divider and reads as decoration around
                          an input, where the boundary IS the affordance. */
  --rule:    #d4a017;  /* borders and strong dividers */
  --line:    #e4d5a8;  /* hairline dividers, inside a card */

  /* accents — one job each. There is exactly ONE accent hue family on
     this site, so nothing has to harmonise with anything: --accent is
     where you WANT someone to go, --accent-2 is structure. */
  --accent:   #cc0066; /* links, and THE primary action. One per view. */
  --accent-d: #a30052; /* its pressed/hover state */
  --accent-2: #6633cc; /* headings, structure, secondary interactive */

  /* Destructive and irreversible, and nothing else. A yellow rather than
     any red: red fought the hotpink from every angle we tried it — a
     bright one read as a second palette, a wine one was close enough to be
     mistaken for the primary action. Yellow is nowhere near --accent, so a
     delete can't be misread as the main action.

     THREE STEPS, because a fill this light cannot double as text:
       --danger      the fill. Takes --ink for its label, never white.
       --danger-d    its pressed/hover fill.
       --danger-ink  danger as TEXT or a hairline on paper. */
  --danger:     #f8c832;
  --danger-d:   #e3b118;
  --danger-ink: #8f5600;

  /* status — reporting only. Never a button fill. */
  --ok:   #3f7d2e;
  --warn: #b26a00;

  /* the hover wash, used everywhere something highlights */
  --hl: #ffee88;

  /* ---------------------------------------------------------------
     SHAPE & DEPTH
     --------------------------------------------------------------- */
  --r-sm: 4px;   /* tags, chips, inline bits */
  --r:    8px;   /* buttons, inputs, cards — the default */
  --r-lg: 14px;  /* sheets, dialogs, big panels */

  --shadow-1: 0 1px 2px rgba(45, 27, 0, .10);
  --shadow-2: 0 4px 14px rgba(45, 27, 0, .14);
  --shadow-3: 0 12px 40px rgba(45, 27, 0, .22);   /* overlays only */

  --ring:      0 0 0 3px rgba(204, 0, 102, .30);  /* keyboard focus on a button or link */
  --ring-soft: 0 0 0 2px rgba(204, 0, 102, .13);  /* the focused field — present, not shouting */

  /* ---------------------------------------------------------------
     SPACING — six steps. Pages compose from these.
     --------------------------------------------------------------- */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  --speed: .13s;  /* every transition on the site */

  /* ---------------------------------------------------------------
     Older names, kept pointing at the tokens above so no page can
     drift back to a second palette. Prefer the names above in new
     code; these exist so a page doesn't have to be rewritten to stop
     being wrong.
     --------------------------------------------------------------- */
  --card: var(--paper2);
  --soft: var(--paper2);
  --muted: var(--dim);
  --hotpink: var(--accent);
  --purple: var(--accent-2);
  --pink: #e07ab8;
  --deep: #2a0a4a;      /* inverted surfaces: toasts, overlay chrome */
  --cyan: #66ccff;
  --gold: var(--rule);
  --yellow: var(--hl);
  --green: var(--ok);
  --green-d: #2f5f22;
  --red: var(--danger-ink);
  --danger2: var(--danger-d);
}

/* ===================================================================
   ELEMENT DEFAULTS — a page should get the house style for free.
   =================================================================== */

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

/* `hidden` is a promise the markup makes; a later `display: flex` in a page
   quietly breaks it. This makes the attribute win, so a page can lay out a
   row without also un-hiding it. */
[hidden] { display: none !important; }

/* A scoped agent console (see base.js) is written as a div whose text is the
   agent's brief. Nobody should ever read that text as page copy: not the
   collaborator it mounts for, and not the signed-in user it never mounts
   for. Hidden until base.js marks it ready is both cases at once, so a page
   can carry a console without also writing the "or not" case. */
[data-agent-scope]:not([data-agent-ready]) { display: none !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--fs-md)/var(--lh) var(--font-ui);
  /* Mono has no narrow characters, so a url, a uuid or a long compound
     word is wider here than it looks in the source. On a 360px screen an
     unbreakable one pushes a horizontal scrollbar onto the whole page. */
  overflow-wrap: break-word;
}

/* Headings. One h1 per page, and it names the page. h2 splits it into
   sections; h3 is for inside a card. All mono, all tracked out — the
   size and the letter-spacing do the work, not a second typeface. */
h1, h2, h3 {
  font-family: var(--font-ui);
  line-height: var(--lh-tight);
  margin: 0 0 var(--sp-3);
}
h1 { font-size: var(--fs-xl); font-weight: 700; letter-spacing: .02em; color: var(--accent); }
h2 {
  font-size: var(--fs-lg); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent-2);
  margin-top: var(--sp-6);
}
h3 { font-size: var(--fs-md); font-weight: 700; letter-spacing: .04em; color: var(--ink); }

a { color: var(--accent); text-underline-offset: 2px; }

/* HOVER IS FOR POINTERS THAT HAVE IT. A touch "hover" is applied on tap
   and stays applied until you touch something else, so an unguarded
   :hover leaves a trail of highlighted links behind a thumb — and on a
   .btn it reads as a button still mid-press. Every hover rule in this
   file is inside one of these; keep it that way, and never write a
   :hover in a page that isn't. */
@media (hover: hover) {
  a:hover { background: var(--hl); }
}

code, pre, kbd { font-family: var(--font-ui); }

hr { border: 0; border-top: 1px solid var(--line); margin: var(--sp-5) 0; }

/* Long human writing opts into the reading font. Applied to a container:
   `<div class="prose">`. */
.prose { font-family: var(--font-prose); line-height: 1.55; }

/* Secondary text. `.meta` for facts (timestamps, counts, who did what),
   `.hint` for guidance under a control. Same colour, different intent —
   keep them distinct so a page can restyle one without the other. */
.meta, .hint { color: var(--dim); font-size: var(--fs-sm); }

/* The standard empty state: centred, quiet, says what to do next. */
.empty {
  color: var(--dim);
  font-size: var(--fs-sm);
  text-align: center;
  padding: var(--sp-6) var(--sp-4);
}

/* Work is happening and you can't see it yet. Sized in `em` so it matches
   whatever text it sits beside; `.big` is the standalone, centred case. */
.spinner {
  display: inline-block;
  width: 1em; height: 1em;
  vertical-align: -.15em;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
.spinner.big { width: 1.8em; height: 1.8em; border-width: 3px; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Still say "working" without the motion. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

/* ===================================================================
   BUTTONS — the variant carries the meaning. Pick by what the action
   DOES, never by what colour looks good in that corner.

     .btn            neutral   safe and reversible; the common case
     .btn.primary    accent    THE main action of the view. One.
     .btn.danger     dark yel  destructive or irreversible. Confirm first.
     .btn.ghost      bare      tertiary — cancel, dismiss, inline links
     .btn.toggle     state     on/off; drive it with aria-pressed

   Sizes: .small (dense rows, toolbars), default, .block (full width).
   =================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper2);
  color: var(--ink);
  font: 700 var(--fs-sm)/1 var(--font-ui);
  letter-spacing: .03em;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--shadow-1);
  /* No double-tap-to-zoom delay, and no grey flash box around the label
     when a thumb lands on it — the :active press below is the feedback. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--speed) ease, border-color var(--speed) ease,
              color var(--speed) ease, box-shadow var(--speed) ease,
              transform var(--speed) ease;
}
@media (hover: hover) {
  .btn:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--hl);
    border-color: var(--accent);
  }
}
.btn:active:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(1px);
  box-shadow: none;
}
/* Keyboard focus is visible; mouse focus is not. */
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; box-shadow: none;
}

.btn.primary {
  background: var(--accent); border-color: var(--accent); color: #fff;
}

.btn.danger {
  background: var(--danger); border-color: var(--danger); color: var(--ink);
}

.btn.ghost {
  background: transparent; border-color: transparent;
  color: var(--accent-2); box-shadow: none;
}

@media (hover: hover) {
  .btn.primary:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--accent-d); border-color: var(--accent-d); color: #fff;
  }
  .btn.danger:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--danger-d); border-color: var(--danger-d); color: var(--ink);
  }
  .btn.ghost:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--hl); border-color: transparent;
  }
}

/* A toggle looks pressed when it is. The page sets aria-pressed; the
   CSS follows it, so the visible state can't disagree with the a11y
   state — they are the same fact. */
.btn.toggle[aria-pressed="true"] {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: none;
}
@media (hover: hover) {
  .btn.toggle[aria-pressed="true"]:hover:not(:disabled) {
    background: var(--accent-d); border-color: var(--accent-d);
  }
}

/* Dense rows and toolbars. Small is a DESKTOP density: on a phone it grows
   back to a thumb-sized target (see "SMALL SCREENS"), because a 30px
   control is a mis-tap whatever the row it's in. */
.btn.small { min-height: 30px; padding: var(--sp-1) var(--sp-3); font-size: var(--fs-xs); }
/* For a call to action that is the reason a whole section exists, and has to
   be obvious from across the room. Rare by design — a page with two of these
   has no main action. */
.btn.big { min-height: 48px; padding: var(--sp-3) var(--sp-5); font-size: var(--fs-md); }
.btn.block { display: flex; width: 100%; }

/* ===================================================================
   FORM CONTROLS — one look for every input on the site.
   =================================================================== */
input[type=text], input[type=password], input[type=search], input[type=number],
input[type=email], input[type=date], input[type=url], select, textarea {
  font: var(--fs-md)/1.5 var(--font-ui);
  color: var(--ink);
  background: var(--paper2);
  border: 1px solid var(--dim);
  border-radius: var(--r);
  padding: var(--sp-2) var(--sp-3);
  width: 100%;
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease;
}
textarea { line-height: var(--lh); resize: vertical; }
/* Focus is the accent edge; the ring behind it is a whisper. A field is
   focused for as long as you're typing into it, so the strong --ring that
   suits a momentary button press reads as a glow you can't get rid of. */
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring-soft);
}
input::placeholder, textarea::placeholder { color: var(--dim); opacity: .75; }

/* Ticks and radios are drawn by the OS. `accent-color` is the one hook
   into that, and it is the difference between a control that belongs to
   this site and one that belongs to the browser. They also default to
   about 13px square, which is under half a fingertip — so they are sized
   here rather than left to each page to notice. */
input[type=checkbox], input[type=radio] {
  accent-color: var(--accent);
  width: 18px; height: 18px;
  margin: 0;
  flex: none;
  touch-action: manipulation;
}
/* A slider is drawn by the OS too, and `accent-color` is the same one hook
   — so it takes the site's accent without a single vendor pseudo-element.
   Height rather than min-height: a range with a 36px box has a track
   floating in the middle of dead space, and the thumb is already a
   thumb-sized target on a touchscreen without any help. */
input[type=range] {
  accent-color: var(--accent);
  width: 100%;
  height: var(--tap);
  margin: 0;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* A tick you can only hit by hitting the tick is a bad tick. Labelling it
   this way gives the pair one target of a sensible size. */
label.check {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
input:disabled, select:disabled, textarea:disabled {
  opacity: .55; cursor: not-allowed;
}

/* A labelled control. The label is small, tracked, and above the input. */
.field { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-3); }
.field > label, label.field-label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dim);
}
.field .hint { margin-top: var(--sp-1); }

/* ===================================================================
   SURFACES
   =================================================================== */
.card {
  background: var(--paper2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: var(--sp-4);
  box-shadow: var(--shadow-1);
}
/* A card you can click through to. */
.card.tappable {
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: box-shadow var(--speed) ease, border-color var(--speed) ease;
}
@media (hover: hover) {
  .card.tappable:hover { border-color: var(--accent); box-shadow: var(--shadow-2); }
}
/* A thumb gets no hover, so the press itself has to answer. */
.card.tappable:active { border-color: var(--accent); box-shadow: none; }

/* A framed aside — the page's own commentary, not a piece of data. */
.panel {
  background: var(--paper2);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent-2);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4);
}
.panel.warn { border-left-color: var(--warn); }
/* The panel's accent edge becomes a strip of the tape. Painted as a
   background rather than a border because a border-box gradient stripes
   all four sides, and only the left edge should carry it. */
.panel.danger { border-left-color: var(--danger); }

/* ===================================================================
   TAGS — a small fact about a thing. Not a button; never clickable.
   =================================================================== */
.tag {
  display: inline-block;
  font: 700 var(--fs-xs)/1.7 var(--font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-2);
  white-space: nowrap;
}
.tag.ok     { color: var(--ok);       border-color: var(--ok); }
.tag.warn   { color: var(--warn);     border-color: var(--warn); }
.tag.danger { color: var(--ink); background: var(--danger); border-color: var(--danger); }
.tag.accent { color: var(--accent-2); border-color: var(--accent-2); }

/* ===================================================================
   NO ACCESS — the refusal, drawn by renderNoAccess() in base.js.
   One implementation for both routes to it: Caddy serving 403.html
   after its gate said no, and the in-page guard on <html
   data-requires> catching a session that lapsed while the tab sat
   open. Same words either way.
   =================================================================== */
.noaccess { max-width: 560px; margin: var(--sp-6) auto 0; padding: 0 var(--sp-4) var(--sp-6); }
.noaccess h1 { margin: 0 0 var(--sp-1); }
.noaccess .code {
  color: var(--dim); font-size: var(--fs-sm);
  letter-spacing: .18em; text-transform: uppercase;
}
.noaccess p { margin: var(--sp-4) 0; }
.noaccess .who {
  border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4); margin: var(--sp-5) 0;
  background: var(--paper2); color: var(--dim); font-size: var(--fs-sm);
}
.noaccess .who b { color: var(--ink); }
.noaccess .acts { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-5); }

/* A page that declares `data-requires` is held blank until base.js has an
   answer, so a guarded page can't flash its app shell on the way to the
   login page. base.js only ever REMOVES this — if the check can't run, the
   page appears. A permanently blank page is a worse failure than the flash
   this prevents, and Caddy is the gate that actually matters. */
html[data-auth-pending] body { visibility: hidden; }

/* ===================================================================
   SITE HEADER — injected by base.js on every page (renderSiteHeader).
   Sticky at z-index 20: above ordinary content (pages use 1–9), below
   every overlay (toasts, the photo lightbox, the upload sheet: 50–100).
   =================================================================== */
#site-head {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  /* Top/left/right are overwritten by fitHeaderToBody() in base.js, which
     cancels the page's own body padding. The bottom gap is ours to keep. */
  margin: 0 0 var(--sp-5);
  border-bottom: 1px solid var(--rule);
  background: var(--paper2);
  font: var(--fs-sm)/1.5 var(--font-ui);
}

#site-head .crumbs { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-1); }
#site-head .crumbs i { color: var(--rule); font-style: normal; }
#site-head .crumbs a { color: var(--accent); text-decoration: none; border-bottom: 1px solid rgba(204,0,102,.35); }
@media (hover: hover) {
  #site-head .crumbs a:hover { background: var(--hl); border-bottom-color: var(--accent); }
}
/* The level you're on: present, but not offering to take you nowhere. */
#site-head .crumbs .here { color: var(--ink); font-weight: 700; }

#site-head .ident { margin-left: auto; display: flex; align-items: baseline; gap: var(--sp-2); color: var(--dim); }
#site-head .ident .name { color: var(--ink); font-weight: 700; }
#site-head .ident .role {
  font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase;
  color: #fff; background: var(--accent-2); border-radius: var(--r-sm); padding: 0 var(--sp-1);
}
#site-head .ident a { color: var(--accent-2); text-decoration: none; border-bottom: 1px dotted var(--accent-2); }
@media (hover: hover) {
  #site-head .ident a:hover { background: var(--hl); }
}
#site-head .ident .dim { color: var(--dim); }
/* The bar is baseline-aligned, and a button's baseline is its label — which
   would hang the whole control below the text beside it. */
#site-head .ident .btn { align-self: center; }

@media (max-width: 560px) {
  #site-head { padding: var(--sp-2) var(--sp-3); font-size: 12px; }
  /* Identity drops to its own line rather than squeezing the trail out. */
  #site-head .ident { margin-left: 0; width: 100%; }
  /* One line, however long the last label is. A session title or an album
     name can be a sentence, and letting it wrap the trail onto three lines
     costs the same screen on every page. The links are the navigation and
     stay whole; the level you're already on is a label, not a target, so
     it's the one that gives up its width. */
  #site-head .crumbs { flex-wrap: nowrap; min-width: 0; overflow: hidden; }
  #site-head .crumbs a, #site-head .crumbs i { flex: none; }
  #site-head .crumbs .here {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Toast: one element, one class, one implementation (see toast() in
   base.js). Each page still sizes and positions its own #toast — only
   the visibility contract and the error colour are shared. */
.toast.err, #toast.err { border-color: var(--danger); }

/* ===================================================================
   SMALL SCREENS — the same design language, sized for a thumb.

   This block is the whole mobile story for the site, and it is written
   as TOKEN CHANGES rather than as per-component patches: a page that
   composes from --fs-* and .btn gets a phone layout without knowing
   this block exists. That is what pays for the "tokens, not values"
   rule elsewhere in this file — a page that hard-codes `13px` opts
   itself out of every line below.

   Two triggers, deliberately:
     max-width  — a narrow window, including a desktop one dragged small.
     pointer: coarse — a touch screen at any width. A tablet in landscape
                is 1024px wide and still driven by a thumb.
   =================================================================== */
@media (max-width: 720px), (pointer: coarse) {
  :root {
    /* Every step up one notch. Mono at 13px is legible on a monitor a
       forearm away and squinting-distance on a phone; the meta and hint
       text that carries most of this site's detail was the worst of it. */
    --fs-xs: 12px;
    --fs-sm: 14px;
    --fs-md: 16px;
    --fs-lg: 19px;
    /* --fs-xl stays 24px: an h1 is already ~24 mono characters wide at
       360px, and growing it starts wrapping page titles onto two lines. */

    /* A fingertip is about 9mm. Everything hittable grows to meet it. */
    --tap: 44px;
  }

  /* .small is a desktop density, not a smaller kind of button. It keeps
     its tighter padding and quieter label so a toolbar still reads as a
     toolbar, but it stops being a 30px mis-tap. */
  .btn.small { min-height: 40px; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); }
  .btn.big   { min-height: 52px; }

  /* Fields are targets too — and a short input is harder to hit than a
     short button, because there is no label to aim at. */
  input[type=text], input[type=password], input[type=search], input[type=number],
  input[type=email], input[type=date], input[type=url], select {
    min-height: var(--tap);
  }

  /* iOS Safari zooms the page in when you focus an input under 16px and
     leaves you zoomed. --fs-md is already 16px here; this is the floor
     for a page that sets a field's size itself. */
  input[type=text], input[type=password], input[type=search], input[type=number],
  input[type=email], input[type=date], input[type=url], select, textarea {
    font-size: max(16px, var(--fs-md));
  }

  input[type=checkbox], input[type=radio] { width: 22px; height: 22px; }

  /* Chrome should cost a phone as little height as it can and still be
     the same header. */
  #site-head { margin-bottom: var(--sp-4); }
  /* The identity line is two links in running text; give them a target
     without turning them into buttons. */
  #site-head .ident { min-height: 32px; align-items: center; }
  #site-head .ident a { padding: var(--sp-1) 0; }
}
