/* Minimal element defaults so specimen cards and kits inherit the brand without
   a utility framework. Components carry their own styles inline.

   EVERYTHING HERE IS LAYERED, and that is the most important line in the file.
   A rule outside a cascade layer beats a rule inside one no matter how specific
   the loser is — so an unlayered `a{color:...}` here outranked EVERY Tailwind
   text utility on every anchor, on every surface that imports these tokens. A
   white-on-white primary button is what that looks like: `text-neutral-950` on
   an anchor, silently overridden to --text-primary, invisible on its own white
   fill, and reading as a rendering glitch rather than a cascade bug.

   The same trap already bit `text-decoration` here once (see the a:hover note
   below). It was fixed by deleting that one declaration, which left the identical
   defect in `color` untouched. Layering fixes the whole class: these are
   DEFAULTS, and a default must lose to anything an app states deliberately.

   The rules added for controls, media and touch go one step further and wrap
   their selectors in :where(), which has ZERO specificity. @layer decides who
   wins BETWEEN layers; :where() decides who wins INSIDE this one. An app that
   ships its own reset — `input { … }`, `img { … }` — very often lands in the
   same base layer, and these defaults have to lose that collision too. */
@layer base {
  *{box-sizing:border-box;border-color:var(--border)}
  html{
    -webkit-font-smoothing:antialiased;
    -moz-osx-font-smoothing:grayscale;
    text-rendering:optimizeLegibility;
    /* The page never scrolls sideways. On the root box as well as <body>,
       because the scrollport is the documentElement; `clip` rather than
       `hidden`, so neither becomes a scroll container and `sticky` still
       holds. */
    overflow-x:clip;
    scroll-behavior:smooth;
    /* iOS inflates text when a phone is rotated to landscape unless this is
       pinned. It is the single most common reason a mobile layout that was
       verified in portrait comes apart on its side. */
    -webkit-text-size-adjust:100%;
    /* A scroll the browser makes for the page — an anchor jump, focus moving,
       scrollIntoView — lands clear of a fixed header or dock. A surface that
       has one sets --header / --dock; one that has none scrolls to the edge. */
    scroll-padding-block:var(--header,0px) var(--dock,0px);
  }
  /* A slide settles where it begins: a page whose sections state
     `scroll-snap-align` is the scroller's half here. `proximity`, never
     `mandatory`, so a long section never holds a reader; a page with no snap
     points is untouched. */
  @media (min-width:1024px) and (prefers-reduced-motion:no-preference){
    :where(html){scroll-snap-type:y proximity}
  }
  /* Each font-family carries the stack as a literal fallback. --font-sans lives in
     tokens/fonts.css, which a surface may legitimately import separately; without
     the fallback an unresolved var() makes font-family invalid and the UA drops to
     its SERIF default — the whole console silently rendered in Times. */
  /* The insets keep content out of the notch and the home indicator on a page
     drawn under them (viewport-fit=cover); env() is 0 on a flat screen. The
     top one is the header's to pay. */
  body{margin:0;padding:0 var(--safe-right,0px) var(--safe-bottom,0px) var(--safe-left,0px);overflow-x:clip;background:var(--background);color:var(--foreground);font-family:var(--font-sans,ui-sans-serif,system-ui,sans-serif);font-size:var(--text-base);line-height:var(--leading-base);font-feature-settings:var(--font-feature-settings);overflow-wrap:break-word}
  h1,h2,h3,h4{margin:0;font-family:var(--font-display,var(--font-sans,ui-sans-serif,system-ui,sans-serif));letter-spacing:var(--tracking-tight);color:var(--text-primary)}
  p{margin:0;text-wrap:pretty}
  code,pre,kbd,samp{font-family:var(--font-mono,ui-monospace,SFMono-Regular,monospace)}
  /* No `transition` here: the shared interactive rule below states one, and an
     `a` selector (0,0,1) would outrank that :where() (0,0,0) and replace the
     whole shorthand — anchors would transition colour and nothing else. */
  a{color:var(--text-primary);text-decoration:none;text-underline-offset:4px}
  /* Underline is the accessible affordance for a link in RUNNING TEXT and a visual
     bug everywhere else: nav items, cards and anchor-buttons are all <a> too, so a
     blanket `a:hover` underlined every one of them on every surface that imports
     these tokens. Scope it to the elements that actually carry prose; a link that
     is a component states its own hover. The old rule also re-set `color` to the
     value `a` already has — a no-op that only served to outrank a component's own
     hover colour. */
  /* A list or a cell inside chrome is not running text: nav, aside, header and
     the shell's slots carry links that never underline. */
  :is(p,li,blockquote,dd,dt,td,th,figcaption) a:hover:not(:is(nav,aside,header,[role=navigation],[data-hanzo-shell],[data-slot=rail],[data-slot=bar]) a){text-decoration:underline}

  /* ——— the smooth ———
     Every interactive element in the reference transitions its colour, its fill
     and its EDGE. That is most of what separates "classy and smooth" from a
     correct set of colours: a hairline that steps from .10 to .16 instantly
     reads as a flicker, and over 150ms reads as a response. Granted once, here,
     rather than restated on every component. tokens/motion.css already zeroes
     all of it under prefers-reduced-motion. */
  :where(a,button,[role=button],input,select,textarea,summary,[tabindex]){
    transition:color var(--duration-fast) var(--ease-out),
               background-color var(--duration-fast) var(--ease-out),
               border-color var(--duration-fast) var(--ease-out),
               box-shadow var(--duration-fast) var(--ease-out),
               opacity var(--duration-fast) var(--ease-out),
               transform var(--duration-press) var(--ease-out);
  }

  /* ——— the press ———
     A button that moves under the finger is the cheapest "this is a real
     control" signal there is, and its absence is most of why a correct-looking
     button still feels like a picture of a button. 2% over 90ms — small enough
     that it reads as pressure rather than as animation, and it is the
     reference's own value (.auth-signin-btn:active). Inputs are excluded: a
     text field does not depress, and scaling one mid-caret is a glitch. */
  :where(button,[role=button],a[role=button],summary):active:not(:disabled){
    transform:scale(.98);
  }

  /* ——— the tap ———
     No grey flash where a finger lands, and no wait for a double-tap zoom on
     a control. A dialog or drawer scrolls itself and stops at its end rather
     than carrying the page behind it along. */
  :where(a,button,[role=button],input,select,textarea,summary,label){
    -webkit-tap-highlight-color:transparent;
    touch-action:manipulation;
  }
  :where(dialog,[role=dialog],[data-drawer]){overscroll-behavior:contain}

  /* ——— controls ———
     A bare <input> renders in the UA's face (Arial) at the UA's size with the
     UA's border, which puts two typefaces and a foreign blue focus ring inside
     Hanzo cards on every surface that has not styled its own fields yet. The
     rules below are what a control looks like here. --border-control, NOT
     --border: a field's edge is its own duty and its own rung, so a surface can
     retune fields without touching every divider on the page. It owes no
     contrast ratio — the whole budget goes to --ring, below. */
  :where(input,select,textarea,button){font:inherit;color:inherit}
  :where(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),select,textarea){
    background:var(--surface-2);
    border:1px solid var(--border-control);
    /* --radius-md (8px), not --radius-sm (6px). On a 36px control the two are
       one step apart and it is the step between "boxy" and "considered";
       --radius-sm keeps its job on genuinely small parts — badges, chips, menu
       rows — where 8px would look bubbly. The reference draws its composer and
       its buttons at 8-10px and nothing at 6px except 22px chips. */
    border-radius:var(--radius-md);
    padding:0 var(--space-3);
    /* A field's ring is drawn INSIDE its own box. The one focus rule below
       reads this; nothing else about the indicator changes.

       A field lives in containers a button does not — composers, palettes and
       panels that round their corners with `overflow:hidden`. An outline at a
       POSITIVE offset is painted outside the border box, so any such ancestor
       clips it, and the computed style still says `2px solid` while nothing is
       drawn: a chat composer measured byte-identical screenshots focused and
       unfocused, and a search pill kept only its left and right edges. A
       negative offset cannot be clipped by anything, so a field needs to know
       nothing about what contains it.

       -2 against a 2px ring lays the outline over the outermost 2px of the box,
       so it REPLACES the 1px edge for as long as focus is there rather than
       drawing a second line a pixel inside the first. */
    --ring-offset:-2px;
  }
  :where(textarea){padding:var(--space-2) var(--space-3);resize:vertical}
  /* A field has NO focus rule of its own — see the ring at the bottom of this
     layer, which is the one focus indicator for everything.

     There used to be one here: `outline:none` plus a brightened edge (.15 ->
     .22) plus a soft halo, the composer look. It was removed because it could
     not do the job in either of the two ways that matter.

     It was never VISIBLE ENOUGH. Composited on --background the brightened edge
     measures 1.91:1 and the halo 1.25:1, against the 3:1 that WCAG 1.4.11 asks
     of a focus indicator and that this package already gates --ring on. The
     budget was documented as being spent entirely on --ring "because that is
     what a keyboard user navigates by" — true for a button, and false for a
     field for exactly as long as this rule told fields not to use it.

     And it was SUPPRESSIBLE. It carried the indicator on `border-color`, so any
     app that states `border` on its own fields overrode it — @hanzo/id does,
     unlayered, which beats this layer whatever its specificity, and its focused
     fields sat at the resting .15 while both files read as correct. An outline
     is not a border: nothing in an app's field styling reaches it, so the ring
     paints whether or not the app has opinions about edges. */
  :where(input,select,textarea):hover:not(:focus-visible):not(:disabled){background:var(--surface-3)}
  :where(input,textarea)::placeholder{color:var(--text-disabled)}
  :where(input,select,textarea,button):disabled{opacity:.5;cursor:not-allowed}
  :where(button,[role=button],summary,label,select){cursor:pointer}
  /* A bare <button> loses the UA's outset bevel and grey fill. A button that
     wants an edge or a ground states one, and any rule at all outranks this. */
  :where(button){border:0;background:none}

  /* ——— the pointer floor ———
     WCAG 2.5.8 AA's 24px, for every pointer; the thumb's 44px is below, under
     pointer:coarse. A min-height cannot size an inline box, so a header or
     footer link is drawn as a box first. An icon-only control has no text to
     widen it, so it is a centred box too.

     `:only-child` counts ELEMENTS: `<a>Docs <svg/></a>` matches as well, and a
     label+icon control states its own `justify-content`. */
  :where(button,[role=button],header a[href],footer a[href]){min-height:24px}
  :where(header a[href],footer a[href]){display:inline-flex;align-items:center}
  :where(:is(a[href],button,[role=button]):has(> svg:only-child)){display:inline-flex;align-items:center;justify-content:center}

  /* MONO KEEPS ITS CELLS. Zen Mono fuses `--` into one glyph, and a brand's
     --font-feature-settings rides down into it from <body>, so a command meant
     to be retyped draws one column short. Wherever the mono face is asked for —
     these elements, a gui Text on the `$mono` face (gui names that face's scope
     `font_mono`), or a style naming the token — every character keeps its cell.
     After the controls' `font:inherit`, which would otherwise hand a mono
     button its parent's ligatures back. */
  :where(code,pre,kbd,samp,.font_mono,[style*='--font-mono']){font-variant-ligatures:none;font-feature-settings:normal}

  /* ——— media ———
     An image, a video or an embed at its intrinsic width is the usual cause of a
     390px page that scrolls sideways. */
  :where(img,svg,video,canvas,iframe,picture,object){max-width:100%}
  /* `auto` keeps the aspect when max-width narrows the box. An element given a
     height and no width keeps the height it was given: an SVG mark has no
     intrinsic size, and `auto` collapses it to 0x0. */
  :where(:is(img,video):is(:not([height]),[width])){height:auto}
  /* A code block scrolls ITSELF rather than widening the document. */
  :where(pre){overflow-x:auto}

  /* ——— touch ———
     Everything a finger aims at clears 44px, and every field renders at 16px so
     iOS does not zoom the viewport on focus. Both are keyed on pointer:coarse —
     the actual signal — so a desktop mouse still gets compact controls. This is
     the whole of "mobile-first without per-app work": an app that never thought
     about phones still has hittable buttons and a viewport that stays put. */
  @media (pointer:coarse){
    :where(button,[role=button],a[role=button],summary,select,textarea,
           input:not([type=checkbox]):not([type=radio]):not([type=hidden])){
      min-height:var(--tap-target);
    }
    /* @hanzo/gui writes a control's size INLINE, and only an important
       declaration outranks an inline one, so these are the floor that holds
       on a gui control. A switch or checkbox keeps its own box: the button IS
       the track, and the pseudo-element below widens its target. A radio drawn
       as a button — a segment of a toggle group — is a control like any other. */
    :where(button:not([role=switch],[role=checkbox]),[role=button],select,header a[href],footer a[href]){min-height:var(--tap-target) !important}
    :where(:is(a[href],button,[role=button]):has(> svg:only-child)){min-width:var(--tap-target) !important}
    :where(input,select,textarea){font-size:var(--text-control)}
    /* A checkbox, radio or switch must NOT grow to 44px — the box is 16px
       because that is what a checkbox looks like. What has to reach 44px is the
       area a finger may land in, so the target is expanded with a centred
       pseudo-element that changes nothing about layout or paint. WCAG 2.5.8 puts
       the floor at 24px; a 16px box misses it, and every one of these ships at
       16px. */
    :where([role=checkbox],[role=radio],[role=switch],input[type=checkbox],input[type=radio]){position:relative}
    :where([role=checkbox],[role=radio],[role=switch],input[type=checkbox],input[type=radio])::after{
      content:'';position:absolute;top:50%;left:50%;
      width:var(--tap-target);height:var(--tap-target);
      transform:translate(-50%,-50%);
    }
  }

  /* THE focus indicator. One rule, every focusable thing, no exceptions — a
     button, a link, a summary, a field. 2px at --ring is 3.77:1 on the darkest
     canvas and clears the 2px perimeter WCAG 2.4.13 asks for; the gate in
     check-tokens holds --ring to that and nothing else here may weaken it.

     WHERE the ring sits is the one thing that varies, and it varies as a VALUE
     the element carries, never as a second rule: `--ring-offset` defaults to 2
     and a field sets it to -2 (see the field block above). Two rules is what
     0.4.9 removed, and a second one here would be the same defect again.

     There were two rules until 0.4.9, and they collided invisibly. Both computed
     to (0,1,0) — :where() zeroes whatever it wraps, leaving one pseudo-class on
     each side — so the cascade fell through to SOURCE ORDER inside this layer,
     this rule was written later, and it overrode the `outline:none` the field
     rule stated expressly to prevent it. Every focused input on every consumer
     drew BOTH the ring and the edge+halo. Each rule read as correct alone, which
     is why it survived review in both files; the defect existed only in their
     order. One rule cannot disagree with itself. */
  :focus-visible{outline:2px solid var(--ring);outline-offset:var(--ring-offset,2px)}
  /* The ring's colour at REST too, so a control that transitions colours has
     no currentColor -> ring interpolation to flash when focus lands. */
  :where(*){outline-color:var(--ring)}
  /* --white-20 is white-on-white in the light theme, so selection reads through
     --selection, which BOTH themes define. */
  ::selection{background:var(--selection);color:var(--text-primary)}

  /* ——— the scrollbar ———
     6px, no track, no stepper buttons, the thumb on the same hairline ladder as
     everything else. ONE treatment per engine. Chromium 121+ reads the standard
     `scrollbar-color`/`scrollbar-width` as well as the `::-webkit-*` pseudos,
     and an element with either standard property set ignores its pseudos —
     `scrollbar-color` inherits, so stated on html it gives every scroller the
     native bar, which on Linux/GTK draws a boxed arrow at each end. So the
     standard pair goes only to an engine without the pseudos (Firefox), and the
     pseudos serve the rest. Check 1e holds that.
     The thumb is a --white-* rung, so it does not invert; the `.light`
     restatements sit here rather than in colors.css because a scrollbar is an
     element default and this file is where element defaults live. Everything
     stays INSIDE the layer — an unlayered rule here would outrank an app's own
     utilities, which is the defect check 1c exists to catch. */
  @supports not selector(::-webkit-scrollbar){
    :where(html){scrollbar-color:var(--white-15) transparent;scrollbar-width:thin}
    .light{scrollbar-color:rgb(0 0 0 / .18) transparent}
  }
  :where(*)::-webkit-scrollbar{width:6px;height:6px}
  :where(*)::-webkit-scrollbar-track{background:transparent}
  :where(*)::-webkit-scrollbar-thumb{background:var(--white-15);border-radius:var(--radius-full)}
  :where(*)::-webkit-scrollbar-thumb:hover{background:var(--white-30)}
  :where(*)::-webkit-scrollbar-corner{background:transparent}
  /* The steppers. Styling the other pseudos does not remove them; only this
     does. Zero width AND height because each axis reads one of them. */
  :where(*)::-webkit-scrollbar-button{display:none;width:0;height:0}
  /* `.light` itself as well as its subtree: with the theme class on <html>,
     html's own pseudos draw the page's scrollbar. */
  .light::-webkit-scrollbar-thumb,.light :where(*)::-webkit-scrollbar-thumb{background:rgb(0 0 0 / .18)}
  .light::-webkit-scrollbar-thumb:hover,.light :where(*)::-webkit-scrollbar-thumb:hover{background:rgb(0 0 0 / .32)}
}
