/* ============================================================================
   Genemap — design tokens (DT.1)
   ----------------------------------------------------------------------------
   DERIVED FROM THE CONSTRAINT, NOT FROM TASTE.
   A producer using this on a phone, in a yard, in sunlight, possibly wearing
   gloves. Every number below follows from that sentence:

     gloves     -> touch targets 56px, not the 44px WCAG 2.5.5 AAA minimum.
                   A gloved fingertip contact patch is ~15-20mm; 56px at a
                   typical 2x DPR is ~14mm of glass, so 56 is the floor and
                   primary actions get 64.
     sunlight   -> contrast at least 7:1 (WCAG AAA) for body text, not 4.5:1.
                   Ink #14231c on paper #ffffff is 15.4:1. Nothing carries
                   meaning by hue alone -- a colour-only state is unreadable
                   in glare.
     arm's      -> body 17px minimum. iOS default is 17; 16 and below is the
     length        first thing to fail at distance with a bright screen.
     one hand   -> primary actions in the lower half; nothing that must be
                   reached at the top of a 6.7" phone.
     few        -> a screen states one job. The type scale has five steps, not
     choices       nine, so there is no room to build a dense hierarchy.
     minimal    -> inputs are 56px tall with 17px text (below 16px iOS zooms
     typing        the viewport on focus, which loses the producer's place).

   EVERY NAME IS PREFIXED --gm-. PHASE 100 FOUND WHY, THE HARD WAY.
   The first version used bare names: --ink, --accent, --paper, --radius. Those
   ALREADY EXIST in beef/core/css/core.css, where --ink is used 18 times and
   --accent 26. This file is linked AFTER core.css, so its :root won -- and
   Phase 98 silently changed the brand accent on 12 production pages from teal
   #0D9488 to dark green #14563a. Shipped, unnoticed, and not visible in any
   test. A token set that shares a namespace with the stylesheet it sits beside
   is not a token set, it is an override.

   Applied in Phase 98 to the critical path ONLY. Scope is deliberate: measure
   how it reads before spending it on 89 pages.
   ============================================================================ */

:root {
  /* ── spacing: a 4px base, six steps ─────────────────────────────────── */
  --gm-sp-1: 4px;  --gm-sp-2: 8px;  --gm-sp-3: 12px;
  --gm-sp-4: 16px; --gm-sp-5: 24px; --gm-sp-6: 32px;

  /* ── type: five steps. 17px body is the floor, not the default ──────── */
  --gm-fs-sm:   15px;   /* secondary only -- never the sole carrier of meaning */
  --gm-fs-body: 17px;
  --gm-fs-lg:   20px;   /* primary actions, numbers a producer acts on */
  --gm-fs-xl:   26px;
  --gm-fs-2xl:  34px;
  --gm-lh-tight: 1.25;
  --gm-lh-body:  1.5;

  /* ── touch: gloves ──────────────────────────────────────────────────── */
  --gm-tap:      56px;   /* every interactive element */
  --gm-tap-lg:   64px;   /* the primary action on a screen */
  --gm-tap-gap:  12px;   /* so a mis-hit lands on nothing, not on the wrong thing */

  /* ── colour: contrast first, hue never load-bearing ─────────────────── */
  --gm-paper:      #ffffff;
  --gm-ink:        #14231c;   /* 15.4:1 on paper */
  --gm-ink-soft:   #45564e;   /* 7.6:1 -- still AAA for body */
  --gm-line:       #c9d2cc;
  --gm-line-strong:#8a998f;
  --gm-accent:     #14563a;   /* 8.9:1 on paper */
  --gm-accent-ink: #ffffff;
  --gm-done:       #1c6b3f;
  --gm-warn-bg:    #fff6e5;
  --gm-warn-ink:   #6b4a05;   /* 7.4:1 on warn-bg */
  --gm-radius:     10px;
  --gm-focus:      0 0 0 3px #14563a, 0 0 0 6px #ffffff;
}

/* Sunlight: honour the producer's own contrast setting rather than overriding */
@media (prefers-contrast: more) {
  :root { --gm-ink-soft: #14231c; --gm-line: #45564e; }
}

/* ── THE FLOOR, NOT A REPAINT ───────────────────────────────────────────
   Phase 100. The first version of these rules also set colour, border and
   background, which would have repainted 89 pages that already have a visual
   identity. THE CONSTRAINT IS DIMENSIONAL -- gloves, sunlight, iOS zoom -- so
   the floor is dimensional. Each page keeps its own colours; what it cannot
   keep is a 38px target or 13.5px text, which is what was measured live on
   beef/phenotypes.html before this phase.

   Colour tokens above stay defined for pages that opt in explicitly; nothing
   here consumes them, so adding class="gm" cannot change a single colour. */

.gm a, .gm button, .gm input, .gm select, .gm textarea,
.gm [role="button"], .gm .btn {
  font-size: max(var(--gm-fs-body), 1em);
}
.gm button, .gm .btn, .gm input[type="submit"], .gm input[type="button"],
.gm [role="button"] {
  min-height: var(--gm-tap);
  min-width: var(--gm-tap);
  /* PHASE 102: min-height has NO EFFECT on a display:inline element. Measured
     on the ten signed-out pages that load no stylesheet at all, where an
     `a.btn` stayed 20px tall while every other control reached 56px. Pages with
     core.css already make .btn inline-block, which is why this never showed up
     on the application pages. Setting it here means the floor does not depend
     on another stylesheet having been loaded first. */
  box-sizing: border-box;
}
/* display lives in a ZERO-specificity rule (:where) so a page's own `.x { display: none }` on a
   button still wins. At `.gm button` weight it un-hid every button a page hides with a plain class
   rule — e.g. the header's desktop-hidden hamburger, which then pushed the header 85px past the
   screen (2026-09-25). */
:where(.gm) :where(button, .btn, input[type="submit"], input[type="button"], [role="button"]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* The desktop header menu (shown only from 1300px, where it is used with a pointer) keeps its own
   compact size; at 56px per trigger its eight groups wrapped onto two lines and overflowed. Narrower,
   it is replaced by the hamburger drawer, whose rows keep the full tap size. */
@media (min-width: 1300px) {
  .gm .gm-tn-menu .gm-tn-trigger { min-height: 40px; min-width: 0; font-size: 14px; white-space: nowrap; }
  .gm .gm-tn-menu button + button { margin-left: 0; }
}
.gm input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
.gm select, .gm textarea {
  min-height: var(--gm-tap);
  font-size: var(--gm-fs-body);   /* below 16px iOS zooms the viewport on focus */
}
.gm input[type="checkbox"], .gm input[type="radio"] {
  min-width: 28px; min-height: 28px;      /* the control itself, not its row */
}
/* a mis-hit should land on nothing, not on the wrong thing */
.gm .btn + .btn, .gm button + button { margin-left: var(--gm-tap-gap); }

/* body copy floor -- does not touch headings, which pages size themselves */
.gm { font-size: max(var(--gm-fs-body), 100%); }

/* one-hand reach: a page may opt a bar into the lower half on small screens */
@media (max-width: 640px) {
  .gm .action-bar {
    position: sticky; bottom: 0; padding: var(--gm-sp-3);
    display: flex; gap: var(--gm-tap-gap);
  }
  .gm .action-bar .btn-primary { flex: 1; }
}

/* The tools menu (core/js/nav.js) is closed until its Menu button opens it. This rule holds the
   static list closed from the first paint, before nav.js has run — without it the whole list
   flashed open above the page. The links stay in the markup (Phase 97: real anchors). */
.gm-nav:not([data-open="1"]) .gm-nav-panel { display: none; }
