/* ============================================================
   wuld.ink — mobile + touch accessibility layer      (Phase 1)
   ------------------------------------------------------------
   PURELY ADDITIVE. Every rule lives inside a mobile media query
   (max-width) or a touch media query (pointer: coarse / hover:
   none). On a fine-pointer desktop viewport NONE of these rules
   match — desktop is byte-inert by construction, and the
   desktop visual-regression gate proves it.

   Loaded site-wide AFTER base.css. base.css / tokens.css are
   never edited (they carry no ?v; this file self-versions).
   ============================================================ */

/* ---------- Overflow safety — no horizontal scroll on phones ---------- */
@media (max-width: 640px) {
  /* Long unbreakable tokens (URLs, error codes, registry paths, inline
     code) wrap instead of pushing the page wider than the phone. */
  p, li, dd, dt, blockquote, figcaption,
  .entry-body, .essay-body, .page-intro {
    overflow-wrap: break-word;
  }
  code {
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* Block code + wide tables scroll inside their own box, never the page. */
  pre {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Media never exceeds the column (base caps most; belt-and-braces). */
  img, video, canvas, svg, iframe {
    max-width: 100%;
  }
}

/* ---------- Touch targets — form fields + submit + disclosure ----------
   Only elements that are NEVER inside a dense chrome bar. Component
   icon-bars (ambient, wrong-hour, console, gallery) are Phase 2. */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]),
  select,
  textarea {
    min-height: 44px;
  }
  button[type="submit"],
  input[type="submit"],
  input[type="button"],
  input[type="reset"] {
    min-height: 44px;
    padding-block: var(--s-2);
  }
  summary {
    min-height: 44px;
    padding-block: var(--s-2);
  }
  /* Bordered pill / chip links (glossary "see also", etc.) — enlarge the
     tap box without disturbing the row's wrap. */
  .entry-see-also a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* ============================================================
   K275 — Phase 2 site-wide mobile layer. Every rule is scoped
   ≤640 (layout) or coarse-pointer (touch); desktop stays byte-
   inert, proven by the layout-fingerprint gate, not eyeballed.
   ============================================================ */

/* A3 — fluid type: comfortable phone reading without touching the
   desktop root (html{font-size} stays frozen). Class-based heroes
   (e.g. homepage .cover-mark) keep their own sizing by specificity. */
@media (max-width: 640px) {
  h1 { font-size: clamp(1.9rem, 7vw, 2.6rem); line-height: 1.12; overflow-wrap: break-word; }
  h2 { font-size: clamp(1.5rem, 5.5vw, 2rem); line-height: 1.16; overflow-wrap: break-word; }
  h3 { font-size: clamp(1.25rem, 4.5vw, 1.6rem); line-height: 1.2; overflow-wrap: break-word; }
  main p, main li, main blockquote { line-height: 1.65; }
}

/* A4 — safe-area insets for notched phones (viewport-fit=cover is
   swept onto every viewport meta). Guarded so unsupported browsers
   ignore it. The body-bottom inset lives in ambient-player.css. */
@supports (padding: env(safe-area-inset-top)) {
  @media (max-width: 640px) {
    .site-header {
      padding-inline-start: max(var(--s-3, 12px), env(safe-area-inset-left));
      padding-inline-end: max(var(--s-3, 12px), env(safe-area-inset-right));
    }
    .nav-toggle { margin-inline-end: env(safe-area-inset-right); }
  }
}

/* A2 — void-engine, touch-fixes-only (session fork): every
   instrument control ≥44px; the triptych/panels/rows wrap and
   never overflow. A full instrument reflow is deferred to its own
   session. Void-unique classes bare; generic .btn/.chip row-scoped. */
@media (pointer: coarse) {
  .cat-btn, .mbtn, .tbtn, .pbtn, .nqbtn, .gen-btn, .read-btn,
  .corp-mode-btn, .stab,
  .chips-inner .chip, .chips-inner .chip-x,
  .pre-row .btn, .slider-row .btn, .filter-row .btn,
  .search-row .btn, .corp-controls .btn { min-height: 44px; }
  .corp-input, .search-row input { min-height: 44px; }
}
@media (max-width: 640px) {
  .chips-inner, .sub-tabs, .corp-controls, .corp-mode-row,
  .filter-row, .pre-row, .slider-row, .search-row { flex-wrap: wrap; }
  .panel, .chips-panel, .corpus-panel, .lib-panel, .gen-panel,
  .neg-panel, .archive-panel, .ds-panel { max-width: 100%; }
}

/* A2 — notes editor: keyboard-aware height on phones (dvh tracks
   the visual viewport as the soft keyboard opens). JS byte-frozen. */
@supports (height: 100dvh) {
  @media (max-width: 640px) {
    #editor { min-height: 50dvh; }
  }
}

/* K275 — mobile touch (2nd pass): donation-platform CTAs + the
   footer changelog link reach the 44px floor. */
@media (pointer: coarse) {
  .donations-platform-link { min-height: 44px; }
  .changelog-link { min-width: 44px; }
}

/* ============================================================
   K287 — void-engine FULL mobile reflow: the half K275 deferred.
   Every rule is scoped @media ≤640 / pointer:coarse / hover:none,
   so desktop is byte-inert by construction (proven: layout
   fingerprint, 0 deltas at 1280×900 and 900×800).

   TWO THINGS THE NEXT SESSION SHOULD NOT REDISCOVER:

   (1) SPECIFICITY, NOT PLACEMENT. This file is linked BEFORE the
       void-engine page's own inline <style>, so an equal-
       specificity rule here LOSES to the engine's (hazard ccl).
       Every selector below therefore carries a real ancestor or a
       tag qualifier. No !important, and no html.mnav-on — that
       class is JS-set, and these rules must hold without JS.

   (2) THE INLINE <style> IS NOT FIXABLE. It is one of the two
       regions wholesale-substituted from the void-engine-suite's
       DUAL_ENGINE source on every engine sync (see CLAUDE.md's
       src/void-engine/index.html row). Anything patched there dies
       at the next paste-relay. Engine mobile rules live HERE.

   Void-unique classes are used bare where they are genuinely
   unique site-wide (grep-verified: .nadd .cfoot .pb .ctrl .tl
   .tcb .neg-hdr .corp-hdr .arch-hdr .chips-hdr .filter-clear
   appear in src/void-engine/index.html and nowhere else).
   ============================================================ */

/* B1 — the sticky engine toggle was the page's ONLY horizontal
   overflow: three tabs measure 406px inside a 390px strip, and
   html{overflow-x:hidden} CLIPS rather than scrolls it, so
   "Transmission" was unreachable, not merely off-screen. The tab
   labels also wrapped to two lines, giving the strip a 90px sticky
   height on every scroll. Equalise the three tabs and tighten the
   strip's own padding/tracking: 406→390 at 390px, 90px→45px tall,
   one line, all three reachable. Not flex-wrap as the primary fix
   — a second row would cost 45px of every scroll on a 103,000px
   page — but kept as the safety net below ~340px. */
@media (max-width: 640px) {
  body #engine-toggle { flex-wrap: wrap; }
  body #engine-toggle button {
    min-height: 44px;
    padding: 12px 8px;
    font-size: 10px;
    letter-spacing: .06em;
    flex: 1 1 0;
    min-width: 0;
  }
}

/* B2 — THE REAL VOID-ENGINE MOBILE DEFECT, and it was never a
   sizing problem. `.nadd` (the per-card NEG control, one per
   lexicon entry, 397 of them) is `opacity:0` until `.card:hover`.
   A phone cannot hover — so on touch the negation set was not a
   small target, it was UNREACHABLE. 370 of K275's 388 residual
   "sub-44 targets" were this invisible button; sizing them without
   revealing them would have produced 370 invisible 44px tap zones,
   which is worse than the bug. Reveal where hover cannot happen,
   then give the 44px box. The negative block margin spends the
   card's own 18px padding instead of growing .cfoot, so the
   397-card list does not get taller — measured document height at
   390px actually FELL, 103,002 → 102,852. */
@media (hover: none) {
  .cfoot .nadd { opacity: 1; }
}
@media (pointer: coarse) {
  .cfoot .nadd {
    min-height: 44px;
    margin-block: -8px;
    padding-inline: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* B3 — the instrument's button family. K275's A2 reached only the
   row-scoped .btn (.pre-row .slider-row .filter-row .search-row
   .corp-controls); the panel-body and control-strip buttons and
   the clickable panel headers (div[onclick], so no A2 selector saw
   them) were missed. */
@media (pointer: coarse) {
  .pb .btn, .ctrl .btn,
  .neg-hdr .btn, .corp-hdr .btn, .arch-hdr .btn, .chips-hdr .btn,
  .filter-clear,
  .neg-hdr, .corp-hdr, .arch-hdr { min-height: 44px; }
}

/* B4 — checkbox rows: the 13px box is the wrong thing to grow. The
   LABEL is the target (it wraps the input, so a tap anywhere on it
   toggles), and it gets the 44px box; the box itself goes 13→22px,
   as far as the mono register tolerates. The two .tcb inputs stay
   the accepted residual — a 44px checkbox would be a different
   design, not an accessible one. */
@media (pointer: coarse) {
  label.tl {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
  }
  label.tl .tcb { width: 22px; height: 22px; }
}

/* B5 — the Transmission canvas' drifting ghost words are absolutely
   positioned, white-space:nowrap and JS-placed: on a 390px pane a
   long word reaches ~508px. Nothing scrolls today only because
   html{overflow-x:hidden} catches it, which is exactly the guard
   that is unreliable on iOS Safari. Clip at the layer — it sits
   inset:0 on a full-width pane, so this is visually identical to
   the clipping already happening, but structural rather than
   incidental. (This one is intermittent: the words are animated,
   so a single render can miss it.) */
@media (max-width: 640px) {
  #trans-engine-wrap #t-text-layer { overflow: hidden; }
}

/* ============================================================
   K287b — void-engine MOBILE REACH. Josiah, on his own phone,
   after K287: "I have to scroll almost all the way down to
   finally get to the selections."

   MEASURED, and he was right: at 390px the first lexicon card
   sat at y=3,965 — **4.7 phone screens of chrome before the
   content**. The stack: header 397 · stats 168 · compiler panel
   776 · negative filter 393 · presets 554 · chips 76 · the
   sidebar's 26-category list 1,147.

   THE ORDER IS NOT THE BUG, so it is not reordered. Compiler,
   negative filter and presets are all legitimately before-you-
   browse tools, and moving them below a 98,690px card list would
   strand COPY POSITIVE and the presets somewhere no thumb will
   ever reach. The bug is HEIGHT: four blocks that are the right
   size for a desktop column are absurd stacked on a phone.

   So each one scrolls inside itself instead of pushing the
   lexicon down, and two desktop-shaped stacks become rows.
   Nothing is hidden, nothing is reordered, every control stays
   exactly where it was. First card 3,965 → 2,162 (4.7 → 2.6
   screens). `overscroll-behavior: contain` keeps a capped box
   from chaining its scroll into the page at the boundary.

   The remaining floor is the header (397) plus the stats — both
   content, not padding. Getting below ~2 screens means either
   hiding the lede (which advertises "RIGHT-CLICK ANY CARD", an
   affordance a phone does not have) or capping the card grid
   itself into a ~500px porthole. Both are Josiah's calls, not
   build calls; recorded in the priorities file, not done here.
   ============================================================ */
@media (max-width: 640px) {
  /* Two stacks that are desktop-column shapes: the status pills are
     an align-items:flex-end right rail, and the sidebar/perm stat
     lines are set at line-height 2.1-2.4 for a wide column. */
  .hdr .stat-pills            { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 6px; }
  #void-engine-wrap .perm-bar { gap: 4px 14px; line-height: 1.5; }
  #void-engine-wrap .sb-stat  { line-height: 1.7; }

  /* The four tall control blocks. Each is a single element on this
     page (verified: 1 match apiece), so the caps cannot leak into
     another surface even though mobile-a11y.css is site-wide. */
  .panel .pb           { max-height: 38vh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  .app > .pre-row      { max-height: 22vh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; align-content: flex-start; }
  /* the ONE rule here that needs an id to win: the engine's own
     `.neg-panel.exp{max-height:600px}` is the SAME specificity (0,2,0)
     and sits later in source order, so a bare `.neg-panel.exp` here
     silently loses — hazard cclii, caught by the real-bytes gate after
     the injected-CSS probe had passed. Measure what you ship. */
  #void-engine-wrap .neg-panel.exp { max-height: 30vh; }
  .sidebar .cat-scroll { max-height: 36vh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
}
