/* norhi-base.css — the behaviour tier: how a page sizes, scrolls, zooms and loads on any device.
   norhi-ui 1.1.2. No class names of its own (htmx's two indicator classes excepted), so it cannot
   collide with a product's CSS.

   1.1.1: this file is BEHAVIOUR ONLY. The element defaults that 1.1.0 carried here (box-sizing, body,
   the heading ladder, control inheritance, table figures, wrapping, media sizing, code) moved to
   base/norhi-elements.css, which a product links when it deletes its own reset — found on the
   first adoption (central-scribe): a default is only invisible where the product already sets the
   whole property, and `field-sizing: content` fought the JS that sizes scribe's textareas, the
   heading shorthand leaked its line-height under a product's own font-size, `overflow-wrap:
   anywhere` changes table min-content, and `height: auto` on canvas and svg overrides attributes.
   So linking THIS file changes what a user sees in exactly three ways: Inter's distinct I / l / 1
   (ss04 — a PLAIN zero since 1.1.3; in text boxes, selects and buttons too since 2.0.3; tabular figures in <time> and numeric controls only, 1.1.2), a focus ring where the product had none, and controls of at least 16px on a touch device.

   Load it as its OWN stylesheet, FIRST — before the product's CSS and before the Tailwind bundle —
   and never @import it into a bundle: the @font-face URLs are relative to this file (the manifest
   static storage rewrites them), and everything below depends on `norhi-base` being the FIRST
   cascade layer the page declares:

     1. DEFAULTS are normal declarations in `@layer norhi-base`. First-declared = lowest priority
        among normal declarations: Tailwind's layers and any product CSS override them without a fight.
     2. GUARANTEES are `!important` declarations in the SAME layer. For important declarations CSS
        inverts the layer order — the first-declared layer outranks every later layer AND every
        unlayered `!important` rule, at any specificity. That is the only position in the author
        cascade from which "a control is never under 16px on a touch device" is actually true: the
        fleet carries typographic `!important` today (norhi-portal 47, boreal 37, central-scribe 37,
        NUCP_FCP 9 — norhi-portal's static/css/base-layout.css re-pins `.text-sm` to 12px !important
        under 768px), and an unlayered normal rule, which 1.1.0's first draft used, loses to every
        one of them. Only an inline `style="… !important"` outranks these. */

/* ── the touch floor's own size (2.0.4) ─────────────────────────────────────────────────────────
   Registered so a value that is not a length (a unitless 20, `large`, a typo) computes to 0px, and the
   floor below still reads 16px: unregistered, it would void the floor's declaration, and the control
   would inherit its parent's size, under 16px on most pages. Not inherited: a control names its own
   size, a wrapper's does not leak into every field inside it. See the guarantee, below.
   A browser without @property (Safari and iOS Safari 15.4-16.3, Firefox 97-127: under the browser
   floor in ui-conventions) never registers it, so the floor's var() falls back to 0px: there too a
   control that names nothing computes 16px, and a named px, rem or token size holds (tested with this
   line deleted, Chromium and WebKit). What is lost there is only what registration adds: a name that is
   not a length voids the declaration, and a wrapper's name reaches the fields inside it. */
@property --norhi-field-font-size { syntax: "<length>"; inherits: false; initial-value: 0px; }

/* ── fonts: declared once, here ─────────────────────────────────────────────────────────── */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/Inter.var.52ad712490c4.woff2") format("woff2"); }
@font-face { font-family: 'Inter'; font-style: italic; font-weight: 100 900; font-display: swap;
  src: url("../fonts/Inter-Italic.var.ec1e7b88affd.woff2") format("woff2"); }   /* fetched only by a page that sets italic text */
@font-face { font-family: 'JetBrains Mono NL'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/JetBrainsMonoNL-Regular.673220337e04.woff2") format("woff2"); }
@font-face { font-family: 'JetBrains Mono NL'; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/JetBrainsMonoNL-Bold.a38fb8b203b6.woff2") format("woff2"); }
/* The metric-matched fallback: Arial resized to Inter's measurements, so the swap from fallback to
   Inter does not move the layout. Inter 4.1: unitsPerEm 2048, ascent 1984, descent 494, no line gap;
   size-adjust is Inter's letter-frequency-weighted average advance over Arial's. */
@font-face { font-family: 'Inter Fallback'; src: local("Arial");
  size-adjust: 107.12%; ascent-override: 90.44%; descent-override: 22.52%; line-gap-override: 0%; }

@layer norhi-base {
  :root {
    color-scheme: light;                       /* white is the only ground: a phone's auto-dark must not invent a palette */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    font-feature-settings: var(--norhi-features);   /* ss04: distinct I / l / 1, plain zero — the one change step 1 is MEANT to show. NOT tnum, NOT a slashed zero: see the token */
    font-optical-sizing: auto;                 /* Inter's opsz axis: display cut on headings, text cut on small type */
    font-synthesis: none;                      /* a missing weight shows as a bug, not as a smeared fake */
    scroll-padding-top: var(--norhi-header-h); /* a focused element is not hidden under the sticky header (WCAG 2.4.11) */
    -webkit-tap-highlight-color: transparent;  /* the focus ring does this job */
    --norhi-safe-top: env(safe-area-inset-top, 0px);       --norhi-safe-bottom: env(safe-area-inset-bottom, 0px);
    --norhi-safe-left: env(safe-area-inset-left, 0px);     --norhi-safe-right: env(safe-area-inset-right, 0px);
  }
  /* ss04 reaches the controls too (2.0.3). The UA stylesheet gives input, textarea, select and button a `font` of their own, and in
     Chromium and WebKit that shorthand resets font-feature-settings to `normal`: a notes box lost the I / l / 1 distinction of the page
     around it (found in boreal's review). `inherit`, not the token, so a control follows its context. Any product rule still wins (this
     is the first layer), and the numeric controls below keep tabular figures on specificity (0,1,1 over 0,0,1). */
  input, textarea, select, button { font-feature-settings: inherit; }
  /* Tabular figures where figures line up, and nowhere else: a timestamp, a numeric or date control. A numeric COLUMN opts in by class
     (.nui-num, Tailwind's tabular-nums, print's td.num) — not every table, because a table cell is often prose. */
  time, input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"],
  input[inputmode="numeric"], input[inputmode="decimal"] { font-feature-settings: var(--norhi-features-tabular); }

  h1[tabindex="-1"]:focus { outline: none; }   /* the page-like swap's focus target: announced, not ringed */
  :focus-visible { outline: var(--norhi-focus); outline-offset: var(--norhi-focus-offset); }

  /* Native dialog: the browser gives the focus trap, Esc, the inert background and focus return. */
  dialog { color: inherit; border: 1px solid var(--norhi-border); border-radius: 0; padding: var(--norhi-space-6);
           max-width: min(40rem, calc(100vw - 2rem)); }
  dialog::backdrop { background: var(--norhi-scrim); }

  /* htmx's indicator classes: the fleet's htmx-config sets includeIndicatorCSS:false (2.0.0; htmx 2
     called it includeIndicatorStyles), so htmx injects no stylesheet of its own and these two
     rules are the only definition. */
  .htmx-indicator { opacity: 0; transition: opacity var(--norhi-motion-state) var(--norhi-ease); }
  .htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
}

/* ── guarantees: !important in the first-declared layer — see the header ─────────────────── */
@layer norhi-base {
  /* iOS Safari zooms the viewport when a focused control is under 16px, and does not zoom back —
     in a standalone PWA too. The test is the POINTER, not the width: a large iPhone in landscape is
     932px wide. Fixed with the font size, never with maximum-scale=1 / user-scalable=no.
     AT LEAST 16px, not exactly (2.0.4). Through 2.0.3 this read `max(16px, 1rem)`, which is 16px, so a
     control styled larger shrank on a touch screen (measured 2026-10-07: the Transcribe title 20 -> 16 and
     notes 17 -> 16 in Scribe and Boreal, Boreal's five two-factor code boxes 20-30 -> 16; the dev side's
     Partners code boxes 20-30 -> 16), and a product could only undo it with an inline !important or by
     writing into this layer (NUAMP's reading surfaces do). CSS cannot read a control's own size from
     here, so a control meant to be larger NAMES it, beside its font-size:
       .code-input { font-size: var(--type-display); --norhi-field-font-size: var(--type-display); }
     A normal declaration, no !important, in any layer or none. The floor holds whatever absolute size is
     named: a size under 16px or an undefined var() computes 16, and where @property registers the name
     (above) so does a unitless number or a keyword. A font-relative name breaks it: a registered length
     that font-size reads cannot be font-relative, so an em size (or a calc() with an em in it) drops the
     whole declaration, and the control inherits its parent's size, under 16px on most pages (14px in a
     test). Name it in px, rem or a --type-* token, never em. */
  @media (pointer: coarse) {
    input, select, textarea, [contenteditable], [contenteditable="true"], .ProseMirror {
      font-size: max(16px, 1rem, var(--norhi-field-font-size, 0px)) !important; }
  }

  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
                             transition-duration: .01ms !important; scroll-behavior: auto !important; }
  }
}

/* Hover is an enhancement: a phone must not keep a stuck hover state after a tap. A product writes
   its hover rules inside `@media (hover: hover)`. */
@media (hover: none) { a:hover { text-decoration: inherit; } }

/* Windows high contrast: respected, not fought. Borders are real borders; icons follow the text. */
@media (forced-colors: active) { svg { forced-color-adjust: auto; } }
