/* Delivery Mate — one stylesheet, no framework, no build step.

   THE CONTENT-SECURITY-POLICY IS THE FIRST DESIGN CONSTRAINT, NOT A FOOTNOTE.
   `src/deliverymate_web/app.py` sends `default-src 'self'; style-src 'self';
   img-src 'self' data:` with no `unsafe-inline`. So:

     * no Google Fonts, no CDN, no remote stylesheet, no remote image — every
       one of those is blocked with no visible error, and the page silently
       falls back to whatever the device had. A font pulled from a generator
       would look correct on the machine that wrote it and generic everywhere
       else, which is the failure this file exists to fix.
     * no inline `style=` attribute anywhere in the templates. A spacing tweak
       is a class in this file. The only trace of an inline style would be a
       console error nobody reads.

   The typeface is therefore SELF-HOSTED from this origin: IBM Plex Sans Arabic,
   SIL Open Font License 1.1 (`/static/fonts/LICENSE-IBM-Plex.txt`), which is the
   family TechMate's identity page sets its lockups in, and which carries BOTH
   scripts in one design. That last part is still the reason it was chosen over a
   Latin-only face, though the reason has changed shape: this product no longer
   renders the two scripts on one line — it serves English at `/` and Arabic at
   `/ar/`, one language per page (docs/DECISIONS.md §39) — but the SAME design
   has to carry a whole Arabic interface and a whole English one, and the
   switcher puts one script's word inside the other's page on every screen.
   A crisp Latin beside a default system Arabic is exactly the mismatch that
   reads as unfinished. Six subsetted files, 193 KB total, `font-display: swap`,
   and `unicode-range` so a device downloads only the scripts it draws.

   ONE LANGUAGE PER PAGE, and `dir` is on the `<html>` element. Everything below
   is written in LOGICAL properties — `margin-inline-end`, `border-inline-start`,
   `text-align: start` — so both directions come out of one rule.
   `[dir="rtl"]` appears only where a property has no logical form, such as a
   background-position or a glyph in a `content:`. `scripts/capture_surfaces.py
   --lang ar` renders the real Arabic pages and walks them for overflow.

   THE PALETTE IS TECHMATE'S, APPLIED, NOT INVENTED. Indigo #16304A, sand
   #D9C7A7, paper #F4F2ED and one flat ink are Ahmed's, settled 2026-09-13.
   Every neutral below is a point on the indigo -> paper ramp, so there is no
   second hue anywhere; the only colours that are not on that ramp are the three
   functional states (accepted / warning / blocking), because a logistics
   surface has to be able to say "this one is refused" without relying on a
   reader remembering which shade of indigo meant what.

   Sized for a phone first: the founder reviews applications on his, a carrier
   fills the application form in an office, and a dispatcher reads the queue in
   a van. Nothing here is wider than its container and nothing relies on a
   horizontal scroll to be readable — `scripts/capture_surfaces.py` asserts
   that at 375 px by walking for a real clipping container, because
   `overflow-x: hidden` on a wrapper produces a false pass.
   Contrast is measured, never eyeballed: `scripts/contrast_audit.py`. */

/* ===================================================================== font */
/* Latin and Arabic are the same family at the same weights, so a bilingual
   pair has one voice. The ranges are fontsource's own, kept verbatim. */
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/plex-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/fonts/plex-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 600; font-display: swap;
  src: url("/static/fonts/plex-latin-600.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/plex-arabic-400.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF,
    U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/fonts/plex-arabic-500.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF,
    U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: "Plex Sans Arabic";
  font-style: normal; font-weight: 600; font-display: swap;
  src: url("/static/fonts/plex-arabic-600.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF,
    U+FE70-FE74, U+FE76-FEFC;
}

/* =================================================================== tokens */
*, *::before, *::after { box-sizing: border-box; }

:root {
  /* -- brand, exactly as delivered ------------------------------------- */
  --indigo: #16304A;
  --indigo-hover: #1E4266;
  --indigo-press: #0F2033;
  --sand: #D9C7A7;
  --paper: #F4F2ED;

  /* -- neutrals: every one a point on the indigo -> paper ramp ---------- */
  --ink: #16304A;          /* 13.50:1 on white, 12.06:1 on paper           */
  --ink-2: #42576B;        /*  7.48:1 / 6.69:1  — secondary copy           */
  --ink-3: #596A7B;        /*  5.57:1 / 4.98:1  — muted, and the Arabic
                              companion line, which is TEXT and therefore
                              held to 4.5:1 on every ground it sits on     */
  --control: #7C8995;      /*  3.58:1 / 3.20:1  — the border of anything
                              you can type in or press (WCAG 1.4.11)       */
  --line: #D0D3D3;         /*  card and section hairline — decorative, and
                              deliberately NOT claimed as AA: it separates
                              two surfaces, it does not identify a control  */
  --line-soft: #E2E2E0;
  --surface: #FFFFFF;
  --ground: var(--paper);
  --on-indigo: #FFFFFF;    /* 13.50:1 on indigo                            */
  --on-indigo-2: #AFC0D2;  /*  6.55:1 on indigo — the Arabic line on a
                              primary button, still real text              */

  /* -- the three functional states ------------------------------------- */
  --ok: #1A5C3C;      --ok-bg: #E8F1EB;    --ok-line: #A9C8B7;
  --warn: #7A521A;    --warn-bg: #F7EEDF;  --warn-line: var(--sand);
  --block: #8E2222;   --block-bg: #F7E9E7; --block-line: #DDB3AE;
  --info-bg: #EAEEF3; --info-line: #BFCBD8;

  /* -- 4 px spacing scale ---------------------------------------------- */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;

  /* -- radii ----------------------------------------------------------- */
  --r-xs: 4px; --r-sm: 6px; --r-md: 8px; --r-lg: 12px; --r-pill: 999px;

  /* -- elevation: the shadow is indigo, not black, so it belongs -------- */
  --lift-1: 0 1px 2px rgba(22, 48, 74, .05), 0 1px 3px rgba(22, 48, 74, .04);
  --lift-2: 0 2px 6px rgba(22, 48, 74, .06), 0 8px 24px rgba(22, 48, 74, .07);

  --font: "Plex Sans Arabic", ui-sans-serif, system-ui, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans Arabic", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
  --measure: 62ch;
}

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: -.003em;
  -webkit-font-smoothing: antialiased;
  /* `zero` is IBM Plex's slashed zero. A reference is read aloud down a phone
     line and typed back in; 0 and O must not be the same shape. */
  font-feature-settings: "kern" 1;
  /* A body that is shorter than the viewport still puts the footer at the
     bottom, so a two-line page does not look like a broken one. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.wrap { width: 100%; max-width: 62rem; margin: 0 auto; padding: 0 var(--s4); }
/* A form and a document are read down a single column; a queue is scanned
   across one. 62rem of two-column form on a desk monitor is the shape that
   makes a product look like a database front end, so the pages that are a
   single task get a narrower measure — `base.html` block `main_class`. */
.wrap.narrow { max-width: 46rem; }
/* A registration step is three fields and a sentence. Measured, not chosen:
   `scripts/measure_column.py` reported the old form's widest text run at 45%
   of a 1280px window — the rest of the screen was a margin, which is what "the
   ui/ux is bad from all aspects" looks like on a desk monitor. 34rem puts the
   column at a real reading measure and the card carries its own edge, so the
   emptiness reads as focus instead of as something missing. */
.wrap.form { max-width: 34rem; }
/* Step one of registration, which carries a column beside the form. See
   `.split` below: the form column stays at the reading measure and the window
   is filled with something worth reading rather than with a wider form. */
.wrap.wide-form { max-width: 58rem; }
/* A sign-in screen is two fields. At the form measure it is a wide empty card
   with a pair of inputs stretched across it. */
.wrap.slim { max-width: 27rem; }

/* The skip link is the first thing in the tab order and the only thing that
   moves when it is focused. It is hidden by CLIPPING rather than by being
   parked at left:-9999px: an off-canvas element is still laid out, and the
   overflow walk in `scripts/capture_surfaces.py` would have to be taught to
   forgive it — a gate with an exception in it is a gate one exception away from
   forgiving a real bug. */
.skip {
  position: absolute; top: 0; left: 0; z-index: 20;
  width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
  background: var(--indigo); color: var(--on-indigo);
  padding: var(--s3) var(--s4); border-radius: 0 0 var(--r-md) 0;
  font-size: .875rem; font-weight: 500; text-decoration: none;
}
.skip:focus {
  width: auto; height: auto; overflow: visible; clip-path: none;
}
   sits on the brand ink it takes the measured light tint instead. */

/* =================================================================== header */
/* A 3 px indigo rule across the top: the cheapest way to say a page belongs to
   somebody, and the only place on a light surface where the brand ink is used
   as a field rather than as text. */
header.site {
  background: var(--surface);
  border-top: 3px solid var(--indigo);
  border-bottom: 1px solid var(--line);
  flex: none;
}
header.site .wrap {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  align-items: center; justify-content: space-between;
  padding-top: var(--s3); padding-bottom: var(--s3);
}

/* THE MARK.

   CLEAR SPACE is 0.30 H of the 80-unit ARTWORK. The supplied 96-unit file
   already carries 0.1 H of it, so what has to be ADDED is 0.1667 x the rendered
   file size — at the 30 px drawn here, 5 px on every side. Clear space is a
   rule about other ELEMENTS, not about the edge of the page, so it is met by
   whatever is actually next to the file box, and each neighbour is named rather
   than guessed at:

     wordmark   8 px margin + the 10 px flex gap = 18 px   >= 5 ✓
     page edge  the wrap's own 16 px padding                >= 5 ✓
     above      the header's 12 px padding                  >= 5 ✓
     below      the header's 12 px padding                  >= 5 ✓

   That is why there is no left margin: the mark sits on the same line as the
   page's text column instead of being pushed 8 px inside it, and the rule is
   still satisfied with three times the margin it asks for.

   MINIMUM SIZE is 16 px for the mark alone — measured, not calculated: at 16 px
   the 12-unit channel is 2 px of clean paper. It is drawn at 30 px.

   The mark is never redrawn, never recoloured, never mirrored and never put
   inside a second rounded container — the file is already a rounded tile and
   wrapping it halves its size and leaves two competing radii. */
.brand {
  display: inline-flex; align-items: center; gap: 10px;
  text-decoration: none; color: inherit;
}
.brand .mark {
  display: block; width: 30px; height: 30px; flex: none;
  margin-inline-end: 8px;      /* logical, so the gap follows the mark in RTL */
}
/* ONE wordmark, in the language of the page. It used to stack the English over
   the Arabic on every page — a bilingual sign, in the corner of a page whose
   reader has already told us which language they want.

   The identity's lockup floor is a 42 px mark, and what sets it is the Arabic
   line landing at 12.18 px. That constraint bound the STACKED lockup; a single
   line at the wordmark's own size clears it with room, and the mark is drawn at
   30 px which is well over its own 16 px minimum. */
.brand .name {
  display: block; font-weight: 600; font-size: 1rem; line-height: 1.2;
  letter-spacing: -.012em; color: var(--ink);
}
html[lang="ar"] .brand .name { font-size: 1.0625rem; letter-spacing: 0; }

/* The nav, the language switcher and sign-out, as one group at the trailing
   edge. Logical `gap` and flex order mean the whole strip mirrors in Arabic
   without a single `[dir="rtl"]` rule. */
header.site .chrome {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4);
}
header.site nav {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s2) var(--s4);
}

/* THE SWITCHER.

   It is the one control on the page whose label is in the OTHER script, and it
   has to be findable by somebody who cannot read the rest of the page — so it
   is a bordered target rather than a text link, at a real tap size, and it
   carries its own `dir` so the two scripts do not fight over the box. */
.lang {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 32px; padding: var(--s1) var(--s3);
  border: 1px solid var(--control); border-radius: var(--r-pill);
  color: var(--ink-2); text-decoration: none;
  font-size: .8125rem; font-weight: 500; line-height: 1.3;
  background: var(--surface);
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.lang:hover { background: var(--paper); border-color: var(--ink-3); color: var(--ink); }
@media (max-width: 30rem) { .lang { min-height: 36px; padding: var(--s1) var(--s4); } }

footer.site .wrap a { color: var(--ink-2); }
header.site nav > a {
  color: var(--ink-2); text-decoration: none; font-size: .8125rem;
  font-weight: 500; padding: var(--s2) 0; border-bottom: 2px solid transparent;
  line-height: 1.25;
}
header.site nav > a:hover { color: var(--ink); border-bottom-color: var(--sand); }
header.site nav form { margin: 0; }
header.site nav form button { min-height: 34px; padding: var(--s1) var(--s3); font-size: .8125rem; }

/* ==================================================================== main */
main.wrap { flex: 1 0 auto; padding-top: var(--s6); padding-bottom: var(--s7); }
@media (max-width: 36rem) { main.wrap { padding-top: var(--s5); } }

footer.site {
  flex: none; border-top: 1px solid var(--line); background: var(--surface);
}
footer.site .wrap {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
  align-items: baseline; justify-content: space-between;
  color: var(--ink-3); font-size: .8125rem;
  padding-top: var(--s4); padding-bottom: var(--s4);
}
footer.site p { margin: 0; }

/* ====================================== one language, and the other direction */
/* WHAT THIS REPLACED.

   There was a `.bi` idiom here: every label rendered its English and its Arabic,
   stacked, on every page, from one helper. It is gone, and with it the reason
   nothing about this product could be fixed by restyling — every heading, hint,
   button and error was two lines in two scripts, so every component was twice
   as tall and twice as noisy as its design.

   A page is now `dir="ltr"` or `dir="rtl"` on the `<html>` element, from the
   path, and everything below is written in LOGICAL properties so both come out
   of the same rules. `[dir="rtl"]` appears only where a property has no logical
   form (a background-position, a glyph in a `content:`).

   THE RUNS THAT GO THE OTHER WAY. A reference, an error code, an IBAN, an email,
   a version stamp and a state name are Latin wherever they land, and an
   undeclared Latin run inside an Arabic paragraph has its trailing punctuation
   moved to the front by the bidi algorithm: "not_found." renders as
   ".not_found". HTML's own `[dir]` rule already isolates an element that
   declares one, and the templates declare `dir="ltr"` on every such run; this
   is the belt to that pair of braces, scoped to the machine-value classes so a
   translated label inside a pill is not forced the wrong way. */
[dir="rtl"] .mono,
[dir="rtl"] .num,
[dir="rtl"] .token,
[dir="rtl"] code,
[dir="rtl"] time {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
}

/* The one-sentence introduction under a heading. It is the page's own voice, so
   it is a step up from body copy in size and a step down in weight. */
p.lede {
  max-width: var(--measure);
  margin: 0 0 var(--s5);
  color: var(--ink-2);
  font-size: 1.0625rem;
  line-height: 1.55;
}
@media (max-width: 30rem) { p.lede { font-size: 1rem; } }

/* Arabic body copy sits taller than Latin at the same size and looks cramped at
   the Latin value; and Plex's Arabic optical size runs a little smaller, so it
   takes a fraction more. One rule, on the document, rather than a modifier on
   every element that might hold Arabic. */
html[lang="ar"] body { line-height: 1.7; letter-spacing: 0; }
html[lang="ar"] h1, html[lang="ar"] h2, html[lang="ar"] h3 { letter-spacing: 0; }
html[lang="ar"] h1 { line-height: 1.35; }
html[lang="ar"] p.lede { line-height: 1.75; }

/* =============================================================== typography */
h1, h2, h3 { margin: 0; color: var(--ink); font-weight: 600; }

h1 {
  font-size: 1.5rem; line-height: 1.2; letter-spacing: -.022em;
  margin-bottom: var(--s3);
}

h2 {
  font-size: 1.0625rem; line-height: 1.3; letter-spacing: -.012em;
  margin: var(--s6) 0 var(--s3);
}

h3 {
  font-size: .9375rem; line-height: 1.35; letter-spacing: -.008em;
  margin: 0 0 var(--s2);
}

/* A heading that opens a card should not push the card's own padding down. */
.card > h2:first-child, .card > h3:first-child,
fieldset > h2:first-child, fieldset > h3:first-child { margin-top: 0; }

p { margin: 0 0 var(--s3); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

.muted { color: var(--ink-3); }
.small { font-size: .8125rem; line-height: 1.6; }

a { color: var(--indigo); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--indigo-hover); }

strong { font-weight: 600; }

/* ==================================================================== cards */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s5);
  margin: var(--s4) 0;
  box-shadow: var(--lift-1);
}
@media (max-width: 36rem) { .card { padding: var(--s4); border-radius: var(--r-md); } }
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

/* Three doors on the front page. Equal weight, because they are three
   different people and none of them is the default. */
.choices { display: grid; gap: var(--s4); grid-template-columns: 1fr; margin-top: var(--s5); }
@media (min-width: 46rem) { .choices { grid-template-columns: repeat(3, 1fr); } }
.choices .card { margin: 0; display: flex; flex-direction: column; }
.choices .card .actions { margin-top: auto; padding-top: var(--s4); }

/* ==================================================================== forms */
/* A `fieldset` with its default border and a floating `legend` is the single
   most recognisable "raw HTML form" shape there is. The element stays — it is
   what groups the fields for a screen reader — and it is drawn as a card with a
   ruled header instead. `float` on the legend is the one technique every
   browser renders as a block; the clearfix below is what that costs. */
fieldset {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--lift-1);
  padding: var(--s4) var(--s5) var(--s5);
  margin: var(--s5) 0;
  min-width: 0;                 /* a fieldset otherwise refuses to shrink */
}
@media (max-width: 36rem) { fieldset { padding: var(--s3) var(--s4) var(--s4); border-radius: var(--r-md); } }
fieldset::after { content: ""; display: block; clear: both; }
fieldset > legend {
  float: left; width: 100%; padding: 0 0 var(--s3); margin: 0 0 var(--s4);
  border-bottom: 1px solid var(--line-soft);
  font-size: .8125rem; font-weight: 600; letter-spacing: .02em; color: var(--ink);
}
fieldset > legend + * { clear: both; }

.field { margin: 0 0 var(--s5); }
.field:last-child { margin-bottom: 0; }
.field > label,
.field > .label {
  display: block; margin-bottom: var(--s2);
  font-size: .875rem; font-weight: 500; color: var(--ink); line-height: 1.35;
}
.field .small { margin-top: var(--s1); color: var(--ink-3); }

/* "Optional" belongs ON the label, not implied by the absence of an asterisk.
   Every field on a registration form is required until proven otherwise, and a
   person filling one in on a phone should not have to count marks to find out
   which one they can skip. */
.field > label .opt {
  font-weight: 400; color: var(--ink-3); font-size: .9em;
}

/* THE HINT COMES BEFORE THE INPUT.

   It used to come after, which is the arrangement that makes it decorative:
   somebody types, gets it wrong, and only then reads the sentence that would
   have told them. "As it is written on your commercial registration" has to be
   readable while the cursor is still in the field. */
.field > .hint {
  margin: 0 0 var(--s2); max-width: var(--measure);
  font-size: .8125rem; line-height: 1.5; color: var(--ink-3);
}

/* An error sits directly under the input it is about, with the same accent as
   the border that turned. Several messages is the normal case, not the
   exception — the password policy raises all of its reasons together. */
.field > .err { margin-top: var(--s2); }
.field > .err p { margin: 0 0 var(--s1); }
.field > .err p:last-child { margin-bottom: 0; }

.grid2 { display: grid; gap: 0 var(--s5); grid-template-columns: 1fr; }
@media (min-width: 38rem) { .grid2 { grid-template-columns: 1fr 1fr; } }

/* MATCHED BY EXCLUSION, AND THE EXCLUSION COSTS NOTHING.

   Two lessons are folded into this one selector.

   **Exclusion, not enumeration.** The rule used to list the input types it knew
   about, and `admin_carriers.html`'s reason-code field carries no `type`
   attribute at all — so it matched nothing and rendered at the browser's
   default `size` while every field beside it was full width. An allow-list of
   input types silently drops whichever one nobody remembered.

   **`:not(:where(…))`, not a chain of `:not()`s.** The obvious way to write the
   exclusion is eight `:not([type=…])`s, and that scores (0,8,1) — which beats
   `input:focus-visible` (0,1,1), `input[aria-invalid=true]` (0,2,1),
   `input:hover:not(:disabled)` (0,2,1) and `input[list]` (0,1,1). Every state
   below it silently lost: a field with a validation error kept the ordinary
   grey border, a focused field never turned indigo, and the datalist chevron
   never painted. Nothing errors — the declarations are simply outranked.
   `:where()` contributes zero specificity and `:not()` takes the specificity of
   its argument, so `:not(:where(…))` excludes exactly the same types at a
   specificity of nothing, and this whole rule is (0,0,1). Every state rule
   below now wins on its own merits.
   `background-color` rather than the `background` shorthand for the same
   family of reason: the shorthand resets `background-image` to none. */
input:not(:where([type=checkbox], [type=radio], [type=submit], [type=button],
                 [type=image], [type=reset], [type=range], [type=color])),
select, textarea {
  width: 100%; max-width: 100%;
  min-height: 42px;
  padding: 9px var(--s3);
  /* 16 px, deliberately: iOS zooms the whole page when a field under 16 px
     takes focus, and the page never zooms back. */
  font-family: inherit; font-size: 1rem; line-height: 1.4;
  color: var(--ink);
  background-color: var(--surface);
  border: 1px solid var(--control);
  border-radius: var(--r-md);
  transition: border-color .12s ease, box-shadow .12s ease;
}
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: 1; }

/* AN INPUT WHOSE CONTENT IS LATIN BY DEFINITION STAYS LEFT-TO-RIGHT.

   Measured, on the Arabic carrier form: the phone field's `+968` placeholder
   rendered as `968+`. That is the bidi algorithm doing exactly what it is
   supposed to — a `+` is a neutral character, and in a right-to-left field it
   goes to the right-hand end of the run — and it is the same defect the old
   `bi()` docstring warned would "appear everywhere at once the day somebody
   adds an Arabic-primary mode". An email, a phone number, a password, a URL and
   a number are Latin whatever the page around them is; typed into a
   right-to-left field they show their punctuation on the wrong end and read as
   a typo the person then "corrects".

   `text-align: start` resolves against the element's own `direction`, which
   this rule has just made `ltr` — so the content sits at the left edge of the
   box while the label above it stays on the right, which is the arrangement
   every Arabic sign-in form in the world uses. Text fields that hold an
   identifier (IBAN, CR number, a reference) carry `dir="ltr"` in the template
   instead: they are `type="text"` and cannot be told apart from a name here. */
[dir="rtl"] input[type="email"],
[dir="rtl"] input[type="tel"],
[dir="rtl"] input[type="url"],
[dir="rtl"] input[type="password"],
[dir="rtl"] input[type="number"],
[dir="rtl"] input[type="date"],
[dir="rtl"] input[dir="ltr"] {
  direction: ltr;
  text-align: start;
}
textarea { min-height: 5.5rem; resize: vertical; padding-top: var(--s2); }

select {
  appearance: none;
  /* The chevron is a data: URI, which `img-src 'self' data:` permits and a
     remote asset would not. Indigo at the ink-3 value so it reads as a hint. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23596A7B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s3) center;
  background-size: 16px 16px;
  padding-right: var(--s6);
}
[dir="rtl"] select { background-position: left var(--s3) center; padding-right: var(--s3); padding-left: var(--s6); }

/* An `<input list=…>` gets a browser-drawn picker triangle that owes nothing to
   this stylesheet — a solid black wedge sitting next to our own chevron on the
   select beside it. The native control keeps its click target; only its paint
   is replaced. (Scoped to `[list]` on purpose: the date fields' calendar
   button is a real affordance and is left alone.) */
input[list] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23596A7B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s3) center;
  background-size: 16px 16px;
  padding-right: var(--s6);
}
[dir="rtl"] input[list] { background-position: left var(--s3) center; padding-right: var(--s3); padding-left: var(--s6); }
input[list]::-webkit-calendar-picker-indicator { opacity: 0; cursor: pointer; }

input[type=file] { padding: var(--s2); font-size: .875rem; }
input[type=file]::file-selector-button {
  font: inherit; font-size: .8125rem; font-weight: 500;
  margin-inline-end: var(--s3); padding: 6px var(--s3);
  border: 1px solid var(--control); border-radius: var(--r-sm);
  background: var(--paper); color: var(--ink); cursor: pointer;
}

input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
  border-color: var(--ink-3);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--indigo);
  box-shadow: 0 0 0 3px rgba(22, 48, 74, .14);
}
button:focus-visible, a:focus-visible, .btn:focus-visible, summary:focus-visible {
  outline: 2px solid var(--indigo); outline-offset: 2px; border-radius: var(--r-sm);
}

.checks { display: grid; gap: var(--s2); grid-template-columns: 1fr; }
@media (min-width: 32rem) { .checks { grid-template-columns: 1fr 1fr; } }
.check {
  display: flex; gap: 10px; align-items: flex-start;
  padding: var(--s2) 10px; margin: 0 -10px;
  border-radius: var(--r-sm); cursor: pointer;
  font-size: .875rem; color: var(--ink-2); line-height: 1.45;
}
.check + .check { margin-top: var(--s1); }
.check:hover { background: var(--paper); }
.check input { margin: 2px 0 0; flex: none; width: 16px; height: 16px; accent-color: var(--indigo); }
/* A version stamp trailing the label reads as part of the sentence. It gets its
   own line. (It used to land after the Arabic half of a stacked pair, which was
   worse; the reason it needs its own line survived the pair.) */
.check .mono { display: block; margin-top: 2px; color: var(--ink-3); }

.err {
  color: var(--block); font-size: .8125rem; line-height: 1.5;
  margin: var(--s1) 0 0; font-weight: 500;
}
input[aria-invalid=true], select[aria-invalid=true], textarea[aria-invalid=true] {
  border-color: var(--block);
}
input[aria-invalid=true]:focus-visible { box-shadow: 0 0 0 3px rgba(142, 34, 34, .14); }

/* ================================================================== buttons */
/* The old rule was `.actions > * { flex: 1 1 auto }`, which made a lone button
   span the whole card — so the queue's "Open" link was a 900 px slab of accent
   blue. A button is now the width of its label, and only goes full width on a
   phone, where a thumb target that spans the column is the right answer. */
button, .btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px;
  padding: var(--s2) var(--s4);
  border-radius: var(--r-md);
  border: 1px solid var(--control);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit; font-size: .875rem; font-weight: 500; line-height: 1.25;
  text-align: center; text-decoration: none; cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease, box-shadow .12s ease;
}
button:hover, .btn:hover { background: var(--paper); border-color: var(--ink-3); color: var(--ink); }
button:active, .btn:active { background: var(--line-soft); }

button.primary, .btn.primary {
  background: var(--indigo); border-color: var(--indigo); color: var(--on-indigo);
  box-shadow: var(--lift-1);
}
button.primary:hover, .btn.primary:hover {
  background: var(--indigo-hover); border-color: var(--indigo-hover); color: var(--on-indigo);
}
button.primary:active, .btn.primary:active { background: var(--indigo-press); }

/* Two suspensions, two buttons, and they must not look alike. The kind is
   submitted by which one was pressed (docs/DECISIONS.md §21), so the only
   protection against the wrong click is that the wrong click looks wrong. */
button.danger {
  border-color: var(--block); color: var(--block); background: var(--surface);
}
button.danger:hover { background: var(--block-bg); border-color: var(--block); color: var(--block); }

button.quiet, .btn.quiet { border-color: transparent; background: transparent; }
button.quiet:hover, .btn.quiet:hover { background: var(--paper); border-color: var(--line); }

button:disabled { opacity: .55; cursor: not-allowed; }

.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: var(--s5); align-items: stretch; }
.actions > * { flex: 0 0 auto; }
.actions > form { display: contents; }
.card > .actions, fieldset > .actions { margin-top: var(--s4); }
/* Copy that explains what the button above it does — the two suspension
   buttons each carry one — must not touch the button. */
.actions + p, .actions + .small { margin-top: var(--s3); }
@media (max-width: 30rem) {
  .actions > * { flex: 1 1 100%; }
  button, .btn { min-height: 44px; }   /* thumb target */
}

/* A filter bar is not a form to fill in; it is a control strip. Given the
   ordinary form treatment it becomes a tall card with a lone button stranded
   at the bottom, which is most of what made the queue look unfinished. */
.toolbar { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s4); align-items: flex-end; }
.toolbar .field { flex: 1 1 12rem; margin: 0; }
.toolbar .actions { margin: 0; flex: 0 0 auto; }
@media (max-width: 30rem) { .toolbar .actions > * { flex: 1 1 auto; } }

/* ================================================================ data view */
dl.kv { margin: 0; }
dl.kv > div {
  display: grid; gap: 2px var(--s5);
  grid-template-columns: 1fr;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 38rem) { dl.kv > div { grid-template-columns: 15rem 1fr; align-items: baseline; } }
dl.kv > div:first-child { padding-top: 0; }
dl.kv > div:last-child { border-bottom: 0; padding-bottom: 0; }
dl.kv dt { color: var(--ink-3); font-size: .8125rem; line-height: 1.45; }
dl.kv dd { margin: 0; overflow-wrap: anywhere; font-size: .9375rem; color: var(--ink); }

/* `.mono` used to be Consolas, and it is on the identifiers — the reference,
   the CR number, the error code — which are the things the eye lands on first
   on every screen in this product. That is where "it reads like a developer
   tool" actually came from, and a monospaced face is not what makes a reference
   legible: tabular figures and a slashed zero are. So `.mono` is now the
   product's own face with `tnum` and `zero` turned on, and real monospace is
   kept for `.token`, where somebody is transcribing 26 characters by hand and
   the column alignment is the point. */
code, .mono {
  font-family: inherit;
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "tnum" 1, "zero" 1;
  letter-spacing: .012em;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

.token {
  display: block;
  font-family: var(--font-mono);
  font-size: .9375rem;
  background: var(--paper);
  border: 1px dashed var(--control);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  color: var(--ink);
  letter-spacing: .06em; word-spacing: .2em;
  overflow-wrap: anywhere; word-break: break-word;
}

.scroll-x { overflow-x: auto; }

/* ============================================================ queue & lists */
.tiles { display: grid; gap: 10px; grid-template-columns: repeat(2, 1fr); margin: var(--s5) 0; }
@media (min-width: 34rem) { .tiles { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 52rem) { .tiles { grid-template-columns: repeat(5, 1fr); } }
.tile {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--s3) var(--s4) 10px;
  display: flex; flex-direction: column; gap: 2px;
}
.tile .n {
  font-size: 1.75rem; font-weight: 600; line-height: 1.1; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.tile .n .small { font-size: .5em; font-weight: 500; color: var(--ink-3); margin-inline-start: 1px; }
/* `align-self: start` because a tile is a COLUMN flex container, and a flex item
   in one stretches to the full width — which sent the Arabic companion line to
   the far edge of the tile instead of sitting under the English it belongs to.
   Shrink-wrapping the pair puts the two lines back into one block. */
.tile .cap { font-size: .75rem; color: var(--ink-2); font-weight: 500; line-height: 1.35; }

.rows { display: grid; gap: 10px; }
.row {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--s4) var(--s5);
  box-shadow: var(--lift-1);
  transition: border-color .12s ease, box-shadow .12s ease;
}
@media (max-width: 36rem) { .row { padding: var(--s4); } }
.row:hover { border-color: var(--control); box-shadow: var(--lift-2); }
.row > h3 { margin: 0 0 var(--s2); font-size: 1rem; letter-spacing: -.014em; }
.row > h3 .small { font-weight: 500; color: var(--ink-3); }
.row .actions { margin-top: var(--s4); }
/* A card inside a row is a box inside a box. The nested panel keeps its
   separation but loses its frame: a rule and some air say the same thing
   without two competing radii. */
.row > .card, .row > form.card {
  margin: var(--s4) 0 0; padding: var(--s4) 0 0; background: transparent;
  border: 0; border-top: 1px solid var(--line-soft); border-radius: 0;
  box-shadow: none;
}
.row > .card.ok, .row > .card.warn, .row > .card.error {
  padding: var(--s3) var(--s4); border: 1px solid var(--line);
  border-inline-start-width: 3px; border-radius: var(--r-md);
}
.row > .card.ok    { border-color: var(--ok-line);    border-inline-start-color: var(--ok); }
.row > .card.warn  { border-color: var(--warn-line);  border-inline-start-color: var(--warn); }
.row > .card.error { border-color: var(--block-line); border-inline-start-color: var(--block); }
.row fieldset { margin: var(--s4) 0 0; box-shadow: none; }

.meta {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s2) var(--s3); font-size: .8125rem; color: var(--ink-3);
  margin: 0 0 var(--s2);
}
.meta:last-child { margin-bottom: 0; }
.meta > span { overflow-wrap: anywhere; }
/* A reference is read aloud on a support call, so it must not break mid-token.
   It is short enough never to be the thing that widens the page. */
.meta .mono { white-space: nowrap; overflow-wrap: normal; word-break: normal; }

.pill {
  display: inline-block; padding: 3px var(--s2); border-radius: var(--r-pill);
  font-size: .6875rem; font-weight: 500; line-height: 1.35; letter-spacing: .015em;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-2);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* A pill holds a machine value or a short label, on one line, in one language. */
.pill.block { background: var(--block-bg); border-color: var(--block-line); color: var(--block); }
.pill.warn  { background: var(--warn-bg);  border-color: var(--warn-line);  color: var(--warn); }
.pill.info  { background: var(--info-bg);  border-color: var(--info-line);  color: var(--indigo); }
.pill.ok    { background: var(--ok-bg);    border-color: var(--ok-line);    color: var(--ok); }

/* ================================================================== notices */
/* The bar down the leading edge is what makes a refusal legible at a glance on
   a phone held at arm's length in a van. It uses the logical inline-start edge
   so it is on the right in a right-to-left block without a second rule. */
.notice {
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--control);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  margin: var(--s4) 0;
  background: var(--surface);
  font-size: .9375rem;
}
.notice p { margin: 0 0 var(--s1); overflow-wrap: anywhere; max-width: var(--measure); }
.notice p:last-child { margin-bottom: 0; }
/* A refusal that names something on ANOTHER step lists it here, so choosing
   which step to render can never be a way of hiding a message. */
.notice ul.plain { margin-top: var(--s2); }
.notice ul.plain li { padding: var(--s1) 0; border-bottom: 0; }
.notice.ok    { background: var(--ok-bg);    border-color: var(--ok-line);    border-inline-start-color: var(--ok); }
.notice.error { background: var(--block-bg); border-color: var(--block-line); border-inline-start-color: var(--block); }
.notice.warn  { background: var(--warn-bg);  border-color: var(--warn-line);  border-inline-start-color: var(--warn); }
.notice.info  { background: var(--info-bg);  border-color: var(--info-line);  border-inline-start-color: var(--indigo); }

/* `admin_carriers.html` renders the result of a suspension as a card with a
   state class rather than a notice; give it the same reading. */
.card.ok    { background: var(--ok-bg);    border-color: var(--ok-line); box-shadow: none; }
.card.error { background: var(--block-bg); border-color: var(--block-line); box-shadow: none; }
.card.warn  { background: var(--warn-bg);  border-color: var(--warn-line); box-shadow: none; }

.flag { padding: var(--s3) 0; border-bottom: 1px solid var(--line-soft); }
.flag:first-child { padding-top: 0; }
.flag:last-child { border-bottom: 0; padding-bottom: 0; }
.flag strong { font-weight: 600; margin-inline-start: var(--s1); }
.flag .detail { font-size: .875rem; color: var(--ink-2); overflow-wrap: anywhere; margin-top: var(--s1); }

ul.plain { list-style: none; padding: 0; margin: 0; }
ul.plain li {
  padding: 10px 0; border-bottom: 1px solid var(--line-soft);
  font-size: .9375rem;
}
ul.plain li:first-child { padding-top: 0; }
ul.plain li:last-child { border-bottom: 0; padding-bottom: 0; }

.docs li { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3); align-items: center; }
.docs .name { font-weight: 500; overflow-wrap: anywhere; color: var(--ink); }
.docs .btn { min-height: 30px; padding: 2px 10px; font-size: .75rem; }

.check-list li { display: flex; gap: 10px; align-items: baseline; border-bottom: 0; padding: var(--s1) 0; }
.check-list .mark { flex: none; width: 1.1rem; color: var(--ok); font-weight: 600; }
.check-list .muted .mark, .check-list li .muted { color: var(--ink-3); }

/* A way back out of a detail page. It is navigation, not a heading, so it is
   the size of navigation — at body size with its Arabic companion beneath it,
   it was the largest thing above the applicant's own name. */
.backlink { margin: 0 0 var(--s4); }
.backlink a {
  /* `flex-start`, not `center`: the label can wrap to two lines, and a centred
     arrow then lands in the gap between them. */
  display: inline-flex; align-items: flex-start; gap: 6px;
  font-size: .8125rem; font-weight: 500; color: var(--ink-3); text-decoration: none;
}
.backlink a::before { content: "\2190"; font-size: 1em; line-height: 1.6; }
[dir="rtl"] .backlink a::before { content: "\2192"; }
.backlink a:hover { color: var(--ink); text-decoration: underline; }

/* The claim strip on an approval. A card whose only content is one button is a
   large empty box with a button in the corner of it; this is the same control
   as a bar. */
.claimbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s2) var(--s4); padding: var(--s3) var(--s4);
}
.claimbar > p { margin: 0; flex: 1 1 14rem; }
.claimbar > form { margin: 0; }
.claimbar .actions { margin: 0; }

/* ===================================================== registration progress */
/* THREE STEPS, AND WHICH ONE YOU ARE ON.

   A wall of nine fields does not tell somebody how much is left, so they guess,
   and on a phone they guess "a lot" and close the tab. This is the whole of the
   answer: a numbered strip that says how far in they are, how far is left, and
   what each remaining step is about — before they commit to the first field.

   It is an `<ol>` because it IS an ordered list, `aria-current="step"` marks
   the one in hand, and the numbers are `aria-hidden` because the list order
   already carries them for a screen reader. */
.steps {
  display: flex; gap: var(--s1);
  list-style: none; margin: 0 0 var(--s5); padding: 0;
  counter-reset: none;
}
.steps li {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) 0 0;
  border-top: 3px solid var(--line);
  font-size: .75rem; line-height: 1.3; color: var(--ink-3);
}
/* `--ink-2`, not `--ink-3`. `scripts/contrast_audit.py --pages` measured
   #596A7B on #E2E2E0 at 4.29:1 and failed the run: `--ink-3` is designed
   against white (5.57:1) and paper (4.98:1), and the step pill is neither.
   #42576B on the same ground is 5.76:1. The number in an unreached step is
   still text, and 11px text does not get the 3:1 large-text allowance. */
.steps .n {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: var(--r-pill);
  background: var(--line-soft); color: var(--ink-2);
  font-size: .6875rem; font-weight: 600; font-variant-numeric: tabular-nums;
}
.steps .s { overflow-wrap: anywhere; }
.steps .done { border-top-color: var(--indigo); color: var(--ink-2); }
.steps .done .n { background: var(--indigo); color: var(--on-indigo); }
.steps .now { border-top-color: var(--indigo); color: var(--ink); font-weight: 600; }
.steps .now .n { background: var(--indigo); color: var(--on-indigo); }
/* On a phone the three labels do not fit beside three numbers. The bar and the
   numbers stay — they are the "how far is left" part — and only the step being
   worked on keeps its name, which is the only one that has to be read. */
@media (max-width: 30rem) {
  .steps li { flex-direction: column; align-items: flex-start; gap: var(--s1); }
  /* Clipped, not `display: none` and not parked off-canvas. It stays in the
     accessibility tree, so a screen reader still reads all three step names —
     and it stays IN FLOW at 1px, so it cannot become the element that widens
     the page, which an absolutely positioned one can. */
  .steps .todo .s, .steps .done .s {
    display: block; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}

.reg > fieldset:first-of-type { margin-top: 0; }
.reg > .actions { margin-top: var(--s5); }

/* THE FORM AND THE REASON TO FILL IT IN, SIDE BY SIDE ON A DESK SCREEN.
   Measured: the registration form used 43% of a 1280px window and the rest was
   paper. Widening the form would have been the wrong repair — a three-field
   form at 900px is worse, not better — so the form keeps its measure and the
   space beside it earns its place.

   Grid, with `minmax(0, …)` on both tracks: a grid item's default `min-width:
   auto` refuses to shrink below its content, and one long unbroken string in
   the form (an email a person pasted) would then push the whole layout past
   the viewport. Below the breakpoint it is one centred column and the aside
   simply follows the form, which is the right order on a phone: decide, then
   read the reassurance if you want it. */
.split { display: grid; gap: var(--s6); grid-template-columns: 1fr; justify-items: center; }
.split > * { width: 100%; min-width: 0; max-width: 34rem; }
@media (min-width: 62rem) {
  .split {
    grid-template-columns: minmax(0, 34rem) minmax(0, 19rem);
    gap: var(--s7); justify-items: stretch; align-items: start;
  }
  .split > * { max-width: none; }
}

.why { color: var(--ink-2); }
.why > h2 {
  margin: 0 0 var(--s4); font-size: .6875rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3);
}
html[lang="ar"] .why > h2 { text-transform: none; letter-spacing: 0; font-size: .8125rem; }
.why-item { padding: var(--s4) 0; border-top: 1px solid var(--line); }
.why-item:first-of-type { padding-top: 0; border-top: 0; }
.why-item:last-child { padding-bottom: 0; }
.why-item h3 { font-size: .9375rem; margin-bottom: var(--s1); color: var(--ink); }
.why-item p { margin: 0; font-size: .875rem; line-height: 1.6; color: var(--ink-2); }
/* On a phone it is the thing AFTER the form, so it gets a rule above it to say
   so rather than running straight on from the sign-in links. */
@media (max-width: 61.99rem) {
  .why { border-top: 1px solid var(--line); padding-top: var(--s5); }
}

/* The step's own heading, inside the card. The count is a caption above the
   name rather than a prefix on it, so the name stays the biggest thing. */
fieldset > legend .of {
  display: block; margin-bottom: 2px;
  font-size: .6875rem; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}
html[lang="ar"] fieldset > legend .of { text-transform: none; letter-spacing: 0; }

/* What is about to be created, on the last step. A password box on its own is a
   screen with no memory of the three answers that got somebody to it. */
.review {
  background: var(--paper); border: 1px solid var(--line-soft);
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  margin: 0 0 var(--s5);
}
.review h3 { font-size: .8125rem; color: var(--ink-3); font-weight: 500; margin-bottom: var(--s2); }
dl.kv.tight > div { padding: var(--s1) 0; border-bottom: 0; grid-template-columns: 1fr; gap: 0; }
@media (min-width: 26rem) { dl.kv.tight > div { grid-template-columns: 9rem 1fr; gap: 0 var(--s3); } }
dl.kv.tight dd { font-size: .875rem; overflow-wrap: anywhere; }

/* Sign-in and "I have a code" are still one tap away. They are no longer three
   cards of equal weight competing with the reason nearly everybody is here. */
.alt-paths {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
  margin: var(--s5) 0 0; color: var(--ink-3); max-width: none;
}
.alt-paths a { font-weight: 500; }

/* A verification code is transcribed from a phone screen, one character at a
   time. Tabular figures, a slashed zero, and space between the characters. */
input.code {
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "tnum" 1, "zero" 1;
  letter-spacing: .14em; font-size: 1.125rem;
}
.sent-to { margin-bottom: var(--s5); }
.sent-to strong { font-weight: 600; overflow-wrap: anywhere; }

/* The hosting disclosure. It is a legal sentence, it is not the point of the
   screen, and it must not look like an error. */
.disclosure {
  margin-top: var(--s4); padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
}

/* A lede inside a card is explaining that card, not the page. */
.card p.lede, fieldset p.lede {
  font-size: .9375rem; margin-bottom: var(--s3); color: var(--ink-2);
}

/* ================================================================ utilities */
.pad-t   { margin-top: var(--s3); }
.pad-t-s { margin-top: var(--s2); }
.flush-b { margin-bottom: 0; }

img { max-width: 100%; height: auto; }

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .001ms !important; animation-duration: .001ms !important; }
}

@media print {
  header.site nav, .actions, form { display: none; }
  body { background: #fff; }
  .card, .row, fieldset, .tile { box-shadow: none; }
}
