/* =========================================================================
   GRADE BIO — gradebio.com
   site.css — page composition only.

   ⛔ This file defines NO ink token. Every value below is composed from the
   seven inks locked in 00_SYSTEM/grade.css, the way a press builds a tint
   from a percentage of one ink. No third hue, no radius, no shadow, no
   gradient, no card.

   The one place this file OVERRIDES the reference sheets is contrast on the
   orange field. The reference small type on the field is mixed at
   black 55-62%, which measures 2.6:1 to 3.0:1 against #E2551A and fails
   WCAG AA for text under 18px. --field-quiet is the same ink at 90%, which
   measures 4.58:1 and passes. Same two inks, correct density.
   ========================================================================= */

:root {
  /* a screen of black on the orange field, at a density that passes AA */
  --field-quiet: color-mix(in srgb, var(--black) 90%, var(--orange));
  /* a lighter screen, only ever used on rules and hairlines, never on type */
  --field-hair:  color-mix(in srgb, var(--black) 26%, var(--orange));
}

/* ---- 0. GLOBAL SAFETY --------------------------------------------------
   Identifiers, addresses and long mono strings must never push a sheet
   wider than the viewport. A register that scrolls sideways inside its own
   block is correct; a page that scrolls sideways is a defect.            */
body { overflow-wrap: break-word; }
/* The system sets display on .btn, .state and .pcell, and a class-level
   display beats the user-agent [hidden] rule. Anything the page hides in JS
   has to actually disappear. */
[hidden] { display: none !important; }
.tblwrap { overflow-x: auto; min-width: 0; }
.tblwrap table.reg { margin: 0; }

/* the skip link is the first thing in the tab order on every sheet */
.skip {
  position: absolute; left: -9999px; top: 0;
  font-family: var(--data); font-size: 11px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
}
/* ⛔ :focus-visible, NEVER :focus. gate.js line 113 moves focus to this link
   after the entry gate is dismissed — correct a11y, it returns the reader to
   the top of the document. But under a plain `:focus` rule that programmatic
   move painted a fixed black slab across the wordmark on EVERY sheet, at every
   width, for every visitor who came in by mouse. Measured 2026-09-01 by
   exercising the real gate-accept path: activeElement === .skip, rect
   24,24,193x40, background rgb(18,16,13). :focus-visible reveals it for the
   keyboard reader who needs it and stays hidden for the pointer reader who
   never asked for it. Do not relax this back to :focus. */
.skip:focus-visible {
  position: fixed; left: var(--s5); top: var(--s5); z-index: 90;
  background: var(--strong); color: var(--ground);
  padding: 11px 16px; outline: 2px solid var(--field-deep); outline-offset: 2px;
}

/* ---- 1. RUNNING HEAD ---------------------------------------------------
   The head carries the mark, four document routes and the sheet's own
   control block. It is a running head, not a navigation bar: the routes
   are set as document names, and the sheet number travels with them.     */
.rh-nav { display: flex; align-items: center; gap: var(--s5); margin-left: auto; }
.rh-nav a {
  font-family: var(--data); font-size: 11px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--quiet);
  border-bottom: var(--w-hair) solid transparent; padding-bottom: 2px;
  white-space: nowrap;
  transition: color 120ms linear, border-color 120ms linear;
}
.rh-nav a:hover { color: var(--strong); border-bottom-color: var(--field-deep); }
.rh-nav a[aria-current="page"] { color: var(--strong); border-bottom-color: var(--strong); }
.runhead__doc { flex: none; }
@media (max-width: 1120px) { .rh-desc { display: none; } }
@media (max-width: 900px)  { .rh-nav { margin-left: 0; gap: var(--s4); } }
@media (max-width: 480px)  { .rh-nav { gap: 14px; } .rh-nav a { font-size: 10px; letter-spacing: .08em; } }
/* A printed running head does not follow the reader down the page, and on a
   narrow sheet the head wraps to two rows. Below 640 it releases the top. */
@media (max-width: 640px) { .runhead { position: static; } }

/* ---- 2. THE SHEET INDEX ------------------------------------------------
   This replaces the navigation bar. Fourteen numbered documents, listed the
   way the contents of a technical binder are listed, on the field.        */
.sheets { border-top: var(--w-slug) solid var(--strong); padding-top: var(--s4); }
.sheets__h {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--s4);
  font-family: var(--data); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--quiet); margin-bottom: var(--s3);
  flex-wrap: wrap;
}
.sheets__l { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0 var(--gut); }
.sheets a {
  display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 10px; align-items: baseline;
  padding: 8px 0; border-bottom: var(--w-hair) solid var(--hair-soft); color: var(--strong);
  transition: background 110ms linear;
}
.sheets a:hover { background: var(--ground-alt); }
.sheets a[aria-current="page"] { background: var(--ground-alt); }
.sheets a .n { font-family: var(--data); font-size: 10.5px; font-weight: 600; letter-spacing: .04em; color: var(--field-deep); }
.sheets a .t { font-size: 14.5px; line-height: 1.25; }

.on-field .sheets { border-top-color: var(--black); }
.on-field .sheets__h { color: var(--field-quiet); }
.on-field .sheets a { color: var(--black); border-bottom-color: var(--field-hair); }
.on-field .sheets a .n { color: var(--field-quiet); }
/* The tint that marks the current row has to run TOWARD the stock, not toward
   the black. Screening the orange down with 9% black put near black type on it
   at 4.33:1 and the folio number at 3.94:1. Screening it up with the stock
   lifts both over 4.5:1 and still reads as one ink on one ground. */
.on-field .sheets a:hover,
.on-field .sheets a[aria-current="page"] { background: color-mix(in srgb, var(--ground) 20%, var(--orange)); }
.on-field .sheets a[aria-current="page"] .n,
.on-field .sheets a:hover .n { color: var(--black); }

@media (max-width: 1000px) { .sheets__l { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .sheets__l { grid-template-columns: 1fr; } }

/* ---- 3. FIELD CONTRAST CORRECTIONS -------------------------------------
   Every small type role the reference sheets set at black 55-62% on the
   field is reset to --field-quiet. Same inks, AA density.                 */
.field .quiet,
.on-field .toc__h,
.on-field .toc a .n,
.on-field .toc a .p,
.crumb,
.legend div,
.idblock__ids span,
.mast__facts span,
.field .searchbar input.f::placeholder { color: var(--field-quiet); }
.crumb a:hover { color: var(--black); }

/* ---- 3b. MASTHEAD FOOTING ----------------------------------------------
   The deck and the three control figures, set as the footing of the plate.
   Not a stat row, not cards: a rule, three figures, three legends.        */
.mast__two { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: var(--gut);
             margin-top: var(--s5); align-items: start; }
.mast__facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4);
               border-top: var(--w-bar) solid var(--black); padding-top: var(--s4); }
.mast__facts b { display: block; font-family: var(--disp); font-weight: 700;
                 font-size: clamp(27px, 3.2vw, 38px); line-height: .94;
                 letter-spacing: -.018em; color: var(--black); }
.mast__facts span { display: block; font-family: var(--data); font-size: 9.5px;
                    letter-spacing: .12em; text-transform: uppercase; line-height: 1.55;
                    margin-top: 8px; color: var(--field-quiet); }
@media (max-width: 1000px) { .mast__two { grid-template-columns: 1fr; } }
@media (max-width: 520px)  { .mast__facts { grid-template-columns: 1fr; gap: var(--s3); } }

/* ---- 4. PLATES ---------------------------------------------------------
   ⛔ THIS SECTION'S PREMISE WAS "photography is one black separation
   multiplied onto the orange field," and that premise is retired. The field is
   gone; see grade.css §12 for the measurement that retired it. A plate is a
   box with a ground, a hairline, a caption and a number, and the image inside
   it is a photograph.

   Every plate still reserves its own height, so a sheet holds its layout
   whether or not a given image has landed.                                 */
/* Until an image has landed, the plate still has to behave like a plate. The
   box holds its own height from aspect-ratio, and the alt text sets small
   inside it and is clipped by it, so a file that has not landed yet never
   disturbs the sheet around it.
   ⛔ The color was --field-quiet — 90% black, mixed for legibility ON the
   orange field. With the field gone that is near-black type on a near-black
   sheet in dark mode, i.e. invisible. It is --quiet now, which is the token
   that flips. */
.plate img {
  overflow: hidden;
  font-family: var(--data); font-size: 9.5px; line-height: 1.6;
  letter-spacing: .05em; text-transform: uppercase; color: var(--quiet);
}

/* ⛔ THE GUTTER IS --black, NOT --strong. --strong flips to the stock on the
   dark sheet, and the twelve specimen plates ARE stock-ground, so a --strong
   gutter would have gone cream between cream cells and the register would have
   lost every one of its rules in dark mode. The gutter is one fixed ink in
   both sheets: on stock it is unchanged (--strong resolves to --black there
   anyway), on the dark sheet it reads as the sheet showing between plates. */
.pgrid {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 2px; background: var(--black); border: var(--w-rule) solid var(--black);
}
/* A plate whose separation has not been made yet still has to behave like a
   plate. The entry sets in mono in the bottom of the box.                  */
.plate[data-plate]::before {
  content: attr(data-plate);
  position: absolute; inset: 0; display: flex; align-items: flex-end;
  padding: 12px; font-family: var(--data); font-size: 9.5px; line-height: 1.55;
  letter-spacing: .12em; text-transform: uppercase; color: var(--quiet);
}
/* This rule used to read "when the photograph lands it multiplies down on top
   of it and covers it," and that is the one thing multiply cannot do: it only
   darkens, so the caption survived wherever the separation was light. With the
   twelve vial plates landed, every cell in the register carried PLATE 01 ·
   SERMORELIN stamped across the glass. A placeholder is for an empty box, so
   it is withdrawn the moment the box is not empty. The [data-plate] is
   repeated rather than dropped: :has() takes the specificity of its argument,
   so a bare .plate:has(img)::before scores below the rule above it and loses
   silently — measured, the caption stayed on screen with the override loaded
   and matching.                                                            */
.plate[data-plate]:has(img)::before { content: none; }
.plate img { position: relative; z-index: 1; }

.pcell { background: var(--ground); min-width: 0; display: flex; flex-direction: column; }
.pcell[hidden] { display: none; }
.pcell .plate { aspect-ratio: 4 / 5; }
.pcell__c {
  padding: 9px 11px 12px; border-top: var(--w-hair) solid var(--hair-soft);
  font-family: var(--data); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--quiet); line-height: 1.6;
}
.pcell__c b { display: block; font-family: var(--disp); font-weight: 700; font-size: 15px;
  letter-spacing: 0; color: var(--strong); line-height: 1.1; margin-bottom: 5px; }
.pcell a.pcell__c:hover { background: var(--ground-alt); }
.pcell a.pcell__c { display: block; }
@media (max-width: 1180px) { .pgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 820px)  { .pgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .pgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* an editorial band of plates, run to the full measure of the sheet */
.band { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px;
        background: var(--strong); border-top: var(--w-bar) solid var(--strong);
        border-bottom: var(--w-bar) solid var(--strong); }
.band .plate { aspect-ratio: 5 / 4; }
.band--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.band--2 .plate { aspect-ratio: 16 / 10; }
@media (max-width: 860px) { .band, .band--2 { grid-template-columns: 1fr; } }

/* a single subject plate beside its reading column */
.figcol { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: var(--gut); align-items: start; }
.figcol--l { grid-template-columns: 380px minmax(0, 1fr); }
@media (max-width: 900px) { .figcol, .figcol--l { grid-template-columns: 1fr; } }

/* ⛔ THE LAST RULE IN THIS BLOCK IS DELETED, NOT MOVED, AND IT MUST NOT COME
   BACK. It read:

       :root[data-theme="dark"] .plate--inv img { filter: invert(1) hue-rotate(180deg); }

   Two things were wrong with it. It keyed on [data-theme="dark"], which
   nothing on this site ever writes, so it had never once rendered — a rule
   that cannot fire is indistinguishable from a rule that is correct. And when
   it was finally made to fire and measured, it did not do what its comment
   claimed: the paper inverted cleanly to rgb(20,15,6) but the orange label
   spine came out rgb(255,164,126), a pale salmon within a few points of the
   banned #fa673e, and every attempt to pull it back removed the spine from the
   frame entirely. The full table is in grade.css §12.

   The class name .plate--inv stays because 27 elements carry it and removing
   it would move markup. It now marks a stock-ground specimen plate and carries
   no styling of its own; the exposure step that the dark sheet DOES apply is
   --plate-exposure, and it lives on .plate img in grade.css §12 so that every
   plate is treated the same way. */

/* ---- 5. TWO-COLUMN READING SETTING ------------------------------------- */
.two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gut); }
.three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gut); }
@media (max-width: 900px) { .two, .three { grid-template-columns: 1fr; } }

/* ---- 6. A NUMBERED REGISTER OF QUESTIONS ------------------------------- */
.qa { border-top: var(--w-rule) solid var(--strong); }
.qa__i {
  display: grid; grid-template-columns: 62px minmax(0, 1fr); gap: var(--s5);
  padding: var(--s5) 0; border-bottom: var(--w-hair) solid var(--hair-soft);
}
.qa__n { font-family: var(--data); font-size: 11px; font-weight: 600; letter-spacing: .06em;
         color: var(--field-deep); padding-top: 6px; }
.qa h3 { font-size: 20px; line-height: 1.08; margin-bottom: 9px; }
.qa p { font-size: 15.5px; margin-bottom: var(--s4); }
.qa p:last-child { margin-bottom: 0; }
@media (max-width: 640px) { .qa__i { grid-template-columns: 44px minmax(0, 1fr); gap: var(--s4); } }
/* Stacked, the answer holds its 64ch measure and leaves the right third of the
   sheet empty for fourteen rows running. Set the question against the answer
   instead: the row then spans the page without the prose exceeding its measure,
   and it reads the way the .step rows on the other sheets do. */
@media (min-width: 1000px) {
  .qa__i > div:last-child {
    display: grid; grid-template-columns: 340px minmax(0, 1fr);
    gap: var(--s4) var(--gut); align-items: start;
  }
  .qa__i > div:last-child > h3 { grid-column: 1; margin-bottom: 0; }
  .qa__i > div:last-child > p  { grid-column: 2; }
}

/* ---- 7. NUMBERED CLAUSES (privacy, terms) ------------------------------ */
.leg > section {
  display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: var(--s5);
  padding: var(--s5) 0; border-bottom: var(--w-hair) solid var(--hair-soft);
}
.leg > section:first-child { border-top: var(--w-rule) solid var(--strong); }
.leg .cl { font-family: var(--data); font-size: 11px; font-weight: 600; letter-spacing: .06em;
           color: var(--field-deep); padding-top: 6px; }
.leg h2 { font-size: 21px; line-height: 1.06; margin-bottom: 10px; }
.leg p { font-size: 15.5px; }
.leg ul { margin: 0 0 var(--s5); padding-left: 1.1em; max-width: 66ch; }
.leg li { font-size: 15.5px; line-height: 1.55; padding: 3px 0; }
@media (max-width: 640px) { .leg > section { grid-template-columns: 60px minmax(0, 1fr); gap: var(--s4); } }
/* Same correction as the question register: a stacked clause leaves most of the
   sheet blank for ten clauses running. The heading sits against its clause. */
@media (min-width: 1000px) {
  .leg > section > div:last-child {
    display: grid; grid-template-columns: 300px minmax(0, 1fr);
    gap: var(--s4) var(--gut); align-items: start;
  }
  .leg > section > div:last-child > h2 { grid-column: 1; margin-bottom: 0; }
  .leg > section > div:last-child > :is(p, ul, ol, div, table) { grid-column: 2; }
}

/* ---- 8. FORMS ----------------------------------------------------------
   Labels sit above inputs. Validation is shown on the field. Submit stays
   disabled while a field is unresolved and says which one.                */
label.f > span .req { color: var(--field-deep); }
textarea.f {
  width: 100%; font-family: var(--data); font-size: 14px; letter-spacing: .02em;
  padding: 9px 10px; color: var(--strong); background: var(--ground);
  border: var(--w-rule) solid var(--strong); outline-offset: 2px;
  line-height: 1.55; resize: vertical; min-height: 132px;
}
textarea.f::placeholder { color: var(--quiet); opacity: 1; }
textarea.f:focus-visible { outline: 2px solid var(--field-deep); outline-offset: 2px; }
textarea.f[aria-invalid="true"] { border-color: var(--field-deep); outline: var(--w-rule) solid var(--field-deep); outline-offset: -3px; }
select.f[aria-invalid="true"] { border-color: var(--field-deep); outline: var(--w-rule) solid var(--field-deep); outline-offset: -3px; }
input[type="file"].f { padding: 8px 10px; font-size: 12.5px; }
input[type="date"].f { min-height: 38px; }

.fset { border: 0; padding: 0; margin: 0 0 var(--s7); min-width: 0; }
.fset > legend {
  display: flex; align-items: baseline; gap: var(--s4); width: 100%;
  border-bottom: var(--w-bar) solid var(--strong); padding: 0 0 var(--s2); margin-bottom: var(--s5);
}
.fset > legend .n { font-family: var(--data); font-size: 12px; font-weight: 600;
                    letter-spacing: .06em; color: var(--field-deep); }
.fset > legend .t { font-family: var(--disp); font-weight: 700; text-transform: uppercase;
                    font-size: 26px; letter-spacing: -.008em; color: var(--strong); line-height: 1; }
.fset > legend .meta { margin-left: auto; font-family: var(--data); font-size: 10.5px;
                       letter-spacing: .12em; text-transform: uppercase; color: var(--quiet); }
@media (max-width: 620px) { .fset > legend { flex-wrap: wrap; } .fset > legend .meta { margin-left: 0; width: 100%; } }

.fg3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4) var(--s5); }
.fg3 .span2 { grid-column: span 2; }
.fg3 .span3 { grid-column: 1 / -1; }
@media (max-width: 820px) {
  .fg3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fg3 .span2, .fg3 .span3 { grid-column: 1 / -1; }
}
@media (max-width: 560px) { .fg3 { grid-template-columns: 1fr; } }
/* The system stylesheet styles input.f and select.f. A message field and a
   file field are neither, so they are brought onto the same footing here. */
textarea.f {
  width: 100%; font-family: var(--data); font-size: 14px; letter-spacing: .02em;
  line-height: 1.6; padding: 9px 10px; color: var(--strong); background: var(--ground);
  border: var(--w-rule) solid var(--strong); outline-offset: 2px;
  min-height: 168px; resize: vertical; display: block;
}
textarea.f::placeholder { color: var(--quiet); opacity: 1; }
textarea.f:focus-visible { outline: 2px solid var(--field-deep); outline-offset: 2px; }
input[type="file"].f { padding: 8px 10px; font-size: 12.5px; }
input[type="file"].f::file-selector-button {
  font-family: var(--data); font-size: 10.5px; letter-spacing: .11em; text-transform: uppercase;
  background: var(--strong); color: var(--ground); border: 0; padding: 6px 11px;
  margin-right: 11px; cursor: pointer;
}
label.f > span .req { color: var(--field-deep); }
label.f > span .opt { color: var(--quiet); }
input.f[aria-invalid="true"], select.f[aria-invalid="true"], textarea.f[aria-invalid="true"] {
  /* a doubled rule, drawn inside the box so nothing shifts. No shadow: the ink
     law allows none, and an outline is a rule rather than a glow. */
  border-color: var(--field-deep); outline: var(--w-rule) solid var(--field-deep); outline-offset: -3px;
}
/* The form runs full width and lets each fieldset own its own grid. The
   system's .fgrid splits into two columns, which halves every field on a
   twenty three field sheet. */
.gform { min-width: 0; }
.gform > * { min-width: 0; }

/* A disabled submit still has to be readable: it is the control the reason
   line underneath is talking about. The system's opacity of .42 takes the
   label under 2:1 against its own ground. */
.btn[disabled], .btn[disabled]:hover {
  opacity: 1; background: transparent; color: var(--quiet);
  border-color: var(--hair); cursor: not-allowed;
}

.fgrid { min-width: 0; }
.fgrid > * { min-width: 0; }

/* the honeypot. Present in the DOM, off the sheet, out of the tab order. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* attestations: six separate affirmations, each stored with its own wording */
.att { border-top: var(--w-rule) solid var(--strong); }
.att__i {
  display: grid; grid-template-columns: 34px 16px minmax(0, 1fr); gap: 12px;
  align-items: start; padding: 14px 0; cursor: pointer;
  border-bottom: var(--w-hair) solid var(--hair-soft);
  transition: background 110ms linear;
}
.att__i:hover { background: var(--ground-alt); }
.att__i .n { font-family: var(--data); font-size: 11px; font-weight: 600; letter-spacing: .06em;
             color: var(--field-deep); padding-top: 3px; }
.att__i input { width: 16px; height: 16px; margin: 3px 0 0; accent-color: var(--orange);
                border: var(--w-rule) solid var(--strong); flex: none; }
.att__i input:focus-visible { outline: 2px solid var(--field-deep); outline-offset: 2px; }
.att__i .w { font-size: 15px; line-height: 1.5; color: var(--body); }
.att__i[data-invalid="true"] { background: var(--wash-12); }
@media (max-width: 560px) { .att__i { grid-template-columns: 26px 16px minmax(0, 1fr); gap: 9px; } }

/* the typed signature is the signature */
.sig { border: var(--w-rule) solid var(--strong); }
.sig__h { background: var(--strong); color: var(--ground); font-family: var(--data);
          font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; padding: 9px 16px; }
.sig__b { padding: var(--s5) 16px 16px; }
.sig__line { border-bottom: var(--w-bar) solid var(--strong); }
input.f.sig__in { border: 0; border-bottom: 0; font-family: var(--text); font-size: 26px;
                  letter-spacing: .01em; padding: 6px 0 8px; background: transparent; }
input.f.sig__in::placeholder { font-size: 17px; letter-spacing: .02em; }

/* the Turnstile mount. The box is drawn so the sheet holds its height
   whether or not the widget has been keyed yet. */
.turnstile-mount {
  border: var(--w-hair) dashed var(--hair); min-height: 68px;
  display: flex; align-items: center; padding: 0 16px;
  font-family: var(--data); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--quiet);
}
.turnstile-mount:not(:empty) { border-style: none; padding: 0; }

/* submit block: the button and the one line that says what is unresolved */
.subm { border-top: var(--w-bar) solid var(--strong); padding-top: var(--s5); margin-top: var(--s7);
        display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap; }
.subm .btn { flex: none; }
.subm__why { font-family: var(--data); font-size: 11px; letter-spacing: .1em;
             text-transform: uppercase; color: var(--field-deep); line-height: 1.6; min-width: 0; }
.subm__why[data-ok="true"] { color: var(--quiet); }

/* ---- 9. FORM RESULT STATES --------------------------------------------- */
.state { border: var(--w-bar) solid var(--strong); }
.state__h { background: var(--strong); color: var(--ground); padding: 11px 18px;
            font-family: var(--data); font-size: 10.5px; letter-spacing: .14em;
            text-transform: uppercase; display: flex; justify-content: space-between;
            gap: var(--s4); flex-wrap: wrap; }
.state__b { padding: var(--s6) 18px var(--s6); }
.state__b h2 { font-size: 30px; margin-bottom: var(--s4); }
.state__b p { font-size: 16px; }
.state[hidden] { display: none; }

/* ---- 10. A KEY / VALUE CONTACT BLOCK ----------------------------------- */
.kv { border-top: var(--w-rule) solid var(--strong); }
.kv__r { display: grid; grid-template-columns: 168px minmax(0, 1fr); gap: var(--s5);
         padding: 11px 0; border-bottom: var(--w-hair) solid var(--hair-soft); }
.kv__k { font-family: var(--data); font-size: 10.5px; letter-spacing: .13em;
         text-transform: uppercase; color: var(--quiet); padding-top: 4px; }
.kv__v { font-size: 15.5px; line-height: 1.45; min-width: 0; }
.kv__v a { border-bottom: var(--w-hair) solid var(--hair); }
.kv__v a:hover { color: var(--field-deep); border-bottom-color: var(--field-deep); }
@media (max-width: 640px) { .kv__r { grid-template-columns: 1fr; gap: 2px; } }

/* ---- 11. THE 404 SHEET -------------------------------------------------- */
.nf { padding: var(--s9) 0 var(--s8); }
.nf__n { font-family: var(--disp); font-weight: 700; line-height: .82;
         font-size: clamp(96px, 26vw, 300px); letter-spacing: -.04em; color: var(--black); }

/* ---- 11b. THE SPECIFICATION SHEET --------------------------------------
   Lifted from the approved press-product.html, which carried these rules in a
   page-local <style> block rather than in the system stylesheets. Two sheets
   use them now, so they live here. The reference's .price rule is deliberately
   NOT carried across: no price appears anywhere on this site.              */
.prod { padding: var(--s6) 0; }
.crumb { font-family: var(--data); font-size: 11px; letter-spacing: .13em;
         text-transform: uppercase; }
.crumb a { color: inherit; text-decoration: none; border-bottom: 1px solid currentColor; }
.seq { font-family: var(--data); font-size: clamp(12px, 1.5vw, 15px); line-height: 2.05;
       letter-spacing: .18em; word-break: break-all; color: var(--black);
       border-top: var(--w-hair) solid var(--field-hair);
       border-bottom: var(--w-hair) solid var(--field-hair);
       padding: 12px 0; margin-top: var(--s5); }
.seq b { font-weight: 600; }
.prodgrid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--gut);
            align-items: start; }
.sticky { position: sticky; top: 74px; }
.ordbox { border: var(--w-rule) solid var(--strong); }
.ordbox__h { background: var(--strong); color: var(--ground); font-family: var(--data);
             font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
             padding: 9px 16px; }
.ordbox__b { padding: var(--s5) 16px 16px; }
.ordbox__b p { font-size: 14.5px; }
@media (max-width: 1000px) { .prodgrid { grid-template-columns: 1fr; } .sticky { position: static; } }

/* ---- 12. UTILITIES ------------------------------------------------------ */
.mt5 { margin-top: var(--s5); }
.mt4 { margin-top: var(--s4); }
.mt9 { margin-top: var(--s9); }
.wide { max-width: none; }
.measure { max-width: 66ch; }
.b-top { border-top: var(--w-bar) solid var(--strong); }
.stack-tight > * + * { margin-top: var(--s4); }
.colo__ix { columns: 2; column-gap: var(--gut); }
@media (max-width: 560px) { .colo__ix { columns: 1; } }

/* ---- 13. SECTION HEADER AIR --------------------------------------------
   The system puts a section's top spacing on .sec, which wraps only the home
   sheet's sections. Every other sheet sets its headers directly in the column,
   so a rule was landing flush against the table or panel above it. */
.sec > * + .sechead, .wrap > * + .sechead, main > * + .sechead { margin-top: var(--s8); }

/* ---- 14. THE ID BLOCK ON THE FIELD --------------------------------------
   .spec__k is set in --quiet, which is a screen of black tuned for the stock
   ground. On the orange field it measures 1.51:1 and the labels disappear.
   --field-quiet is the same role re-tuned for the field (4.58:1). */
.field .spec__k, .on-field .spec__k { color: var(--field-quiet); }

/* A block that scrolls has to be reachable from the keyboard, or its right hand
   columns are unreadable without a mouse. tabindex makes the wrapper focusable;
   this is the ring that says so. No gradient cue: the ink law allows none. */
.tblwrap:focus-visible { outline: var(--w-bar) solid var(--field-deep); outline-offset: 3px; }

/* A result panel is scrolled to as soon as it is drawn, and the running head is
   sticky, so without this the first line lands underneath it. */
.state { scroll-margin-top: 92px; }


/* =========================================================================
   15. THE SITE ENTRY GATE — moved to 00_SYSTEM/gate.css
   It is imported by grade.css so that it reaches the portal pages too, which
   do not link this sheet. Do not re-add gate rules here: a rule here covers
   14 pages, a rule there covers all 24.
   ========================================================================= */

/* ========================================================================
   14. THE CHROME REBUILD — 2026-09-01
   Six measured defects, fixed here and in the pages. The design language is
   unchanged: no radius, no shadow, no gradient, hairline rules, seven inks.
   What changed is the amount of furniture standing between a reader and the
   sheet. Nothing below introduces a hue or a material.
   ======================================================================== */

/* ---- 14.1 THE INDEX DISCLOSURE ----------------------------------------
   Navigation used to be printed three times on every sheet: four routes in
   the running head, a fourteen-row index inside the orange masthead, and
   twelve of those fourteen again in the colophon. The masthead copy is gone
   from all 24 pages; its content moved in here, where it is reachable from
   the head at every width instead of only at the top of the page.

   A <details> and not a hamburger, because BRAND_LOCK §5 says "an index, not
   a nav bar", and because a document's contents open in place. It needs no
   JavaScript, so it cannot break with the gate, and it is keyboard-operable
   from the user agent.                                                    */
.rh-ix { flex: none; margin-left: var(--s5); }
.rh-ix > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 9px;
  font-family: var(--data); font-size: 11px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--strong);
  padding: 6px 0 5px; border-bottom: var(--w-bar) solid var(--strong);
  transition: color 110ms linear;
}
.rh-ix > summary::-webkit-details-marker { display: none; }
.rh-ix > summary:hover { color: var(--field-deep); }
.rh-ix > summary:focus-visible { outline: 2px solid var(--field-deep); outline-offset: 3px; }

/* The glyph is the step wedge in miniature — three cuts, graduating. It is
   the mark this brand already owns, not a borrowed hamburger. Open, the
   three cuts resolve to one solid bar: the wedge closed up. */
.rh-ix__g { display: block; width: 15px; flex: none; }
.rh-ix__g i { display: block; height: 2px; background: currentColor; }
.rh-ix__g i + i { margin-top: 3px; }
.rh-ix__g i:nth-child(1) { width: 100%; }
.rh-ix__g i:nth-child(2) { width: 68%; }
.rh-ix__g i:nth-child(3) { width: 40%; }
.rh-ix[open] > summary .rh-ix__g i { width: 100%; }
.rh-ix[open] > summary .rh-ix__g i + i { margin-top: 2px; height: 2px; }
/* The label is real text in the markup, not generated content — CSS ::after
   is not reliably exposed to a screen reader, and this control is the whole
   navigation below 900 px. */
.rh-ix .ix-c, .rh-ix[open] > summary .ix-o { display: none; }
.rh-ix[open] > summary .ix-c { display: inline; }

/* The panel is a sheet of stock dropped from the head, ruled top and bottom.
   Absolute against .runhead — which is sticky, therefore a containing block —
   so it travels with the head and spans the full sheet width rather than the
   width of the control that opened it. top:100% rather than a hardcoded 46 px:
   the head's height is set in one place and this follows it. */
.rh-ix__p {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 4;
  background: var(--ground); border-bottom: var(--w-slug) solid var(--strong);
  max-height: calc(100dvh - 46px); overflow-y: auto;
  padding: var(--s5) 0 var(--s6);
}
.rh-ix__p > .wrap { padding-left: var(--s7); padding-right: var(--s7); }
.rh-ix__p .sheets { border-top: 0; padding-top: 0; }
/* 48 px of gutter each side is a third of a 390 px sheet. The index is the
   thing a phone reader came to the head for; give it the page's own gutter. */
@media (max-width: 700px) {
  .rh-ix__p > .wrap { padding-left: var(--s5); padding-right: var(--s5); }
}
@media (prefers-reduced-motion: no-preference) {
  .rh-ix[open] .rh-ix__p { animation: ixIn 130ms cubic-bezier(.23,1,.32,1); }
}
@keyframes ixIn { from { opacity: 0; transform: translateY(-4px); } }

/* ---- 14.2 ONE HEAD ROW, 320 TO 1920 ------------------------------------
   The four inline routes stood beside the wordmark down to 320 px, where they
   collided with it and clipped it. They are retired below 900; the index
   disclosure carries all fourteen sheets from there down, which is more than
   the four routes ever carried. */
@media (max-width: 900px) {
  .rh-nav { display: none; }
  .rh-ix { margin-left: auto; }
}
/* ⛔ 360, not 420. At 420 the word INDEX was suppressed on a 390 px iPhone —
   the commonest phone width there is — leaving a bare three-bar glyph beside
   the folio with no label and no name for what it opens. Measured at 390: the
   head runs wordmark 88 px, glyph + label 74 px, folio 118 px inside a 350 px
   line, so the label fits with room over. Only below 360 does it have to go. */
@media (max-width: 360px) {
  .rh-ix__lb { position: absolute; left: -9999px; }
  .rh-ix > summary { gap: 0; padding-left: 2px; }
}
/* The head no longer wraps at any width, so the reason it was released below
   640 ("on a narrow sheet the head wraps to two rows") no longer holds. A
   sticky head is what makes the index reachable without scrolling back up,
   which is the whole point of moving it there. Deliberate reversal. */
@media (max-width: 640px) { .runhead { position: sticky; } }

/* ---- 14.3 THE MASTHEAD, CUT DOWN -------------------------------------
   It opened at 1,540 px on a 390 px phone carrying a wordmark, three lines of
   sheet metadata, a headline and two paragraphs — mostly empty orange, and
   the client has twice said he dislikes the orange screens. The lockup is
   dropped from every interior sheet (the same mark is 60 px above it in the
   head) and replaced by one ruled slug line, which is how a document plate
   actually identifies itself. */
.mast { padding: var(--s6) 0 var(--s6); }
@media (max-width: 700px) { .mast { padding: var(--s5) 0 var(--s5); } }

.mast__slug {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px var(--s5);
  border-bottom: var(--w-bar) solid var(--black); padding-bottom: var(--s3);
  font-family: var(--data); font-size: 10.5px; font-weight: 500;
  letter-spacing: .13em; text-transform: uppercase; color: var(--field-quiet);
}
.mast__slug b { color: var(--black); font-weight: 600; }
.mast__slug .r { margin-left: auto; }
@media (max-width: 620px) { .mast__slug .r { margin-left: 0; } }
.mast__slug + .mast__line { margin-top: var(--s5); }

/* ---- 14.4 IMAGERY IN THE HEAD ----------------------------------------
   Asked for explicitly. The plate sits in the masthead's own grid, so at
   desktop it fills space the type was not using and costs no height at all.
   ⛔ No overlay of any kind on it. The plates are stock-ground photographs
   and they stay that way; the orange is the surround, never a wash over the
   glass. It is separated from the field by a 2 px black rule, the same
   separation the register grid uses.

   The masthead ITSELF becomes the grid, rather than a wrapper being introduced
   around its children: every sheet keeps the markup it had and gains exactly
   one figure as a last child. */
.mast--fig { display: grid; grid-template-columns: minmax(0,1fr) 340px; gap: 0 var(--gut); }
.mast--fig > * { grid-column: 1; min-width: 0; }
/* ⛔ `grid-row: 1 / -1` is wrong here and looks right. -1 resolves against the
   EXPLICIT grid, and these rows are all implicit, so it collapses to row 1
   alone: the figure's 425 px then set the height of the row holding the slug
   line, and the masthead opened with 400 px of empty orange above the
   headline. Measured at 1440 before it was caught. Span a count instead. */
.mast--fig > .mast__fig { grid-column: 2; grid-row: 1 / span 20; align-self: end; }

.mast__fig { margin: 0; border: var(--w-bar) solid var(--black); background: var(--ground); }
.mast__fig .plate { aspect-ratio: 4 / 5; margin: 0; }
.mast__fig figcaption {
  border-top: var(--w-bar) solid var(--black); background: var(--ground);
  padding: 7px 10px 8px; font-family: var(--data); font-size: 9.5px;
  letter-spacing: .11em; text-transform: uppercase; color: var(--quiet); line-height: 1.5;
}
@media (max-width: 1080px) { .mast--fig { grid-template-columns: minmax(0,1fr) 262px; } }
@media (max-width: 860px) {
  .mast--fig { grid-template-columns: 1fr; }
  .mast--fig > .mast__fig { grid-column: 1; grid-row: auto; margin-top: var(--s5); }
  /* Letterboxed on a phone: the imagery is present, and it does not cost a
     screen to say so. The crop holds the subject, never the empty ground. */
  .mast__fig .plate { aspect-ratio: 21 / 9; }
  .mast__fig .plate img { object-position: center 44%; }
}

/* The specification sheets already carry their own plate in .mast__two. It gets
   the same letterbox below 860 rather than a full 4:5 portrait of a vial, which
   was 1,522 px of masthead on a 390 px phone. */
@media (max-width: 860px) {
  .mast__two .plate--inv { aspect-ratio: 21 / 9 !important; }
  .mast__two .plate--inv img { object-position: center 44%; }
}

/* ---- 14.5 THE PLATE IS THE LINK --------------------------------------
   Every plate in the register now links to the sheet behind it. The whole
   cell is one anchor — one hit area, one focus ring, one hover — rather than
   a picture that ignores the pointer sitting above a caption that does not. */
a.pcell { display: flex; text-decoration: none; color: inherit; position: relative; }
a.pcell .plate { transition: none; }
a.pcell .pcell__c { background: var(--ground); transition: background 110ms linear, color 110ms linear; }
a.pcell:hover .pcell__c { background: var(--strong); color: var(--ground); }
a.pcell:hover .pcell__c b { color: var(--ground); }
/* the pressed state of a printed key: one pixel, and it settles immediately */
a.pcell:active { transform: translateY(1px); }
a.pcell:focus { outline: none; }
a.pcell:focus-visible { outline: var(--w-bar) solid var(--field-deep); outline-offset: 3px; z-index: 2; }
@media (prefers-reduced-motion: reduce) {
  a.pcell:active { transform: none; }
  a.pcell .pcell__c { transition: none; }
}

/* ---- 14.6 THE REGISTER READS BEFORE IT FILTERS -----------------------
   Below 1000 the two-column register stacked its rail first, which put the
   filter panel and three marginal notes — 3,076 px of them — above the first
   product. The rail is reordered under the register; a cross-reference at the
   top of the register points down to it, the way a document cites a section
   rather than reprinting it. */
@media (max-width: 1000px) {
  .cat { display: flex; flex-direction: column; }
  .cat > main { order: 1; }
  .cat > .cat__rail { order: 2; margin-top: var(--s7); }
}
.xref {
  display: none; font-family: var(--data); font-size: 10.5px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--quiet);
  border-bottom: var(--w-hair) solid var(--rule); padding-bottom: 3px;
}
.xref:hover { color: var(--field-deep); border-bottom-color: var(--field-deep); }
@media (max-width: 1000px) { .xref { display: inline-block; } }

/* ---- 14.7 THE ANNEX ---------------------------------------------------
   "I have to scroll all the way down to see the information." The answer a
   printed technical document already has is a numbered contents: on a phone a
   section from 3.0 onward arrives as its own head, and the reader opens the
   one they came for instead of scrolling past the three they did not.

   ⛔ Every annex ships with the `open` attribute set in the markup. It is
   chrome.js that closes them, and only under 761 px. So the failure direction
   is: no JavaScript, no CSS, wrong engine -> the reader gets every word. It
   can never hide content by accident, only by working. */
.ann > summary { list-style: none; cursor: pointer; }
.ann > summary::-webkit-details-marker { display: none; }
@media (min-width: 861px) {
  .ann > summary { display: none; }
  .ann--sec > summary { display: block; }
  .ann--sec > summary .sechead { margin-bottom: var(--s5); }
}
@media (max-width: 860px) {
  .ann { border-top: var(--w-bar) solid var(--strong); margin-top: var(--s6); }
  .ann > summary {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
    font-family: var(--data); font-size: 10.5px; font-weight: 600;
    letter-spacing: .13em; text-transform: uppercase; color: var(--strong);
    padding: 11px 0;
  }
  .ann > summary:focus-visible { outline: 2px solid var(--field-deep); outline-offset: 2px; }
  .ann > summary .s::after { content: "Open"; color: var(--quiet); }
  .ann[open] > summary .s::after { content: "Close"; color: var(--quiet); }
  .ann__b { padding-bottom: var(--s5); }

  /* A folded SECTION keeps its own head — the number, the title and the meta
     line are the tab. Nothing is restyled; the head is simply the control. */
  .ann--sec { border-top: 0; margin-top: 0; }
  .ann--sec > summary { display: block; padding: 0; }
  .ann--sec > summary .sechead { margin-bottom: 0; padding-bottom: var(--s3); }
  .ann--sec > summary .sechead::after {
    content: "+"; margin-left: var(--s4); font-family: var(--data);
    font-size: 15px; font-weight: 400; color: var(--field-deep); line-height: 1;
  }
  .ann--sec[open] > summary .sechead::after { content: "\2212"; }
  .ann--sec > summary .sechead .meta { margin-left: auto; }
  .ann--sec > .ann__b { padding-top: var(--s5); padding-bottom: 0; }
  .sec:has(> .ann--sec) { padding-top: var(--s6); }
}

/* ---- 14.8 PHONE RHYTHM -------------------------------------------------
   The section scale was set for a 1440 sheet and carried straight down to a
   390 one, where 64 px of air above every rule reads as a hole rather than a
   margin. The type scale is untouched; only the air moves. */
@media (max-width: 860px) {
  .sec { padding-top: var(--s7); }
  .sec > * + .sechead, .wrap > * + .sechead, main > * + .sechead { margin-top: var(--s7); }
  .colo { margin-top: var(--s7); }
  .colo__ix { columns: 2; }
  .mast__sub { font-size: 16.5px; margin-top: var(--s4); }
  .band, .band--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .band--2 .plate { aspect-ratio: 4 / 5; }
}
@media (max-width: 560px) { .colo__ix { columns: 2; } }
