/* ═══════════════════════════════════════════════════════════════════════════
 * responsive.css — THE ONE PLACE THAT MAKES XSCN FIT EVERY SCREEN.
 *
 * Loaded FIRST on every surface, before each page's own <style> block, so any
 * page can still override anything here. Nothing in this file changes a
 * design decision; every rule below stops a specific physical failure that a
 * real person hits on a real device.
 *
 * The rule for adding to this file: it must fix a class of breakage, not one
 * screen. Screen-specific tweaks belong in that screen. Anything added here
 * gets a comment saying what breaks without it — otherwise nobody will dare
 * delete it later and the file rots into a junk drawer.
 *
 * Checked by:  node tools/responsive-audit.js
 * Looked at :  /admin/responsive-lab  (every breakpoint, side by side, live)
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. NOTHING MAY BE WIDER THAN WHAT HOLDS IT ────────────────────────────
 * Media carries its own intrinsic width. A 1200px-wide photo dropped into a
 * card on a 360px phone drags the entire page sideways, and every OTHER screen
 * on that page then looks broken too, because the horizontal scroll is on the
 * document. This one rule is the single highest-yield line in the file. */
img, video, canvas, svg, iframe, embed, object {
  max-width: 100%;
}
/* Height must follow, or a constrained image is squashed instead of scaled.
 * Scoped to img only — canvases and videos often need an explicit height. */
img { height: auto; }

/* ── 2. LONG UNBREAKABLE STRINGS ───────────────────────────────────────────
 * This is a MONEY app. It is full of things that are one long "word" with no
 * space to break at: wallet addresses, xsk- API keys, transaction hashes,
 * device ids, node fingerprints, pasted URLs. On a phone each one is a
 * horizontal scrollbar waiting to happen. overflow-wrap is inherited, so
 * setting it once on the root covers every element that doesn't opt out —
 * and it only ever activates on a line that would otherwise overflow, so it
 * cannot change the look of text that already fits. */
html {
  overflow-wrap: break-word;
}

/* ── 3. WIDE CONTENT SCROLLS ITSELF, NOT THE PAGE ──────────────────────────
 * Code blocks, logs and receipts are legitimately wide. They should carry
 * their own sideways scroll so the page underneath stays still. */
pre, code, samp, kbd {
  max-width: 100%;
}
pre {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ── 4. FORM CONTROLS ──────────────────────────────────────────────────────
 * A textarea's default resize handle lets a user drag it wider than the phone.
 * And iOS zooms the whole page in — permanently, with no way back on some
 * versions — whenever it focuses an input whose font is under 16px. Both are
 * "the app broke" moments with a one-line cause. */
textarea { resize: vertical; max-width: 100%; }
@media (max-width: 820px) {
  input:not([type=checkbox]):not([type=radio]):not([type=range]),
  select, textarea {
    font-size: max(16px, 1em);
  }
}

/* ── 5. TAP TARGETS ────────────────────────────────────────────────────────
 * Apple HIG says 44pt, Material says 48dp. Below that, thumbs miss — and a
 * missed tap does not read as "I missed", it reads as "this app is broken".
 *
 * These are the controls the audit measured under 44px on a real 360px
 * viewport. They are listed EXPLICITLY rather than as a blanket `button {}`
 * rule on purpose: a blanket min-height reflows every dense toolbar in the
 * app, and an unverifiable change to every button on 37 pages is exactly the
 * kind of "fix" that produces ten broken things in thirty seconds.
 *
 * Only on coarse pointers — a mouse is precise and desktop density is a
 * feature there, so nothing changes on a laptop.
 *
 * !important is deliberate and load-bearing here. This stylesheet is loaded
 * BEFORE each page's own <style> block so that pages can freely override the
 * defensive base rules above. That ordering means these floors would otherwise
 * LOSE to the page rule they exist to correct. A minimum hit size is not a
 * suggestion the page gets to argue with, so it is marked as such — and it is
 * confined to this short, explicitly-listed block, never applied blanket. */
@media (pointer: coarse) {
  /* dashboard: home category pills (measured 100x16 — barely a line of text) */
  .hcat { min-height: 40px !important; display: inline-flex !important; align-items: center !important; }
  /* dashboard: the ✕ that closes a modal. Missing this one traps people. */
  .modal-close { min-width: 44px !important; min-height: 44px !important; }
  /* dashboard: floating nav pills (42px — 2px short of the line) */
  #appnav button { width: 44px !important; height: 44px !important; }
  /* dashboard: Twin action row */
  .twin-hero .twin-acts button { min-width: 44px !important; min-height: 44px !important; }
  /* admin: zoom stepper + floating-window chrome, both mouse-sized (30/24px) */
  .zoomctl button { min-width: 40px !important; min-height: 40px !important; }
  .adm-win-btns button { min-width: 40px !important; min-height: 40px !important; }
  /* the public WeedTok page — a stranger's first impression, on a phone */
  .icon-btn { min-width: 40px !important; min-height: 40px !important; }

  /* ── measured live, not from the CSS ──────────────────────────────────
   * These are sized by PADDING rather than by a declared width/height, so a
   * static read of the stylesheet cannot see how big they end up. They were
   * measured in a real 360x780 viewport with the demo tenant loaded, and the
   * number after each one is what was actually on screen. The worst by far is
   * .easy-x: a fourteen-pixel-wide close button, in Easy Mode — the mode built
   * for the people least able to hit a fourteen-pixel target. */
  .easy-x            { min-width: 44px !important; min-height: 44px !important; }  /* was 14x24 */
  #cur-chip          { min-height: 36px !important; }                              /* was 49x18 */
  .vault-guide-btn   { min-height: 36px !important; }                              /* was 64x19 */
  #av-reval-all      { min-height: 40px !important; }                              /* was 149x29 */
  /* Top-bar pills: 36px, not 44px, on purpose — the bar itself is 52px tall,
   * so 44 would leave four pixels of breathing room and the bar would look
   * stuffed. 36 is a real improvement that does not wreck the header. */
  .credits-pill, .user-pill, .admin-pill { min-height: 36px !important;
    display: inline-flex !important; align-items: center !important; }
}

/* Utility for new code: put .tap44 on anything small and clickable and it
 * grows its HIT area on touch without changing how it looks, because the
 * growth happens in a pseudo-element rather than in layout. */
.tap44 { position: relative; }
@media (pointer: coarse) {
  .tap44::after {
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 100%; height: 100%;
    min-width: 44px; min-height: 44px;
  }
}

/* ── 6. SHORT SCREENS AND LANDSCAPE PHONES ─────────────────────────────────
 * A phone held sideways is ~360px TALL. Floating action buttons stacked up
 * the right edge, a 52px top bar and a bottom bar can between them leave
 * almost no room for the actual content. Nothing here hides anything — it
 * tightens the furniture so the content still gets the middle of the screen.
 * Also fires on a laptop with the dev-tools drawer open, which is where this
 * gets noticed. */
@media (max-height: 460px) and (orientation: landscape) {
  .topbar { height: calc(44px + env(safe-area-inset-top)) !important; }
  #appnav { transform: scale(.86); transform-origin: left bottom; }
  /* Modals must never be taller than the screen they open on. */
  .overlay > *, .modal, .sheet { max-height: 92vh; max-height: 92dvh; overflow-y: auto; }
}

/* ── 7. THE DYNAMIC VIEWPORT ───────────────────────────────────────────────
 * 100vh on a phone means "the screen with the URL bar HIDDEN". Any full-height
 * layout sized with it puts its last ~60px — usually the primary button —
 * underneath the browser chrome, unreachable until the user scrolls, which a
 * full-height layout by definition does not do. dvh is the fix and every
 * browser XSCN targets has had it since 2023. Pages that already declare dvh
 * are unaffected; this only upgrades the ones that did not. */
@supports (height: 100dvh) {
  .full-h, .screen-h { height: 100dvh; }
}

/* ── 8. ULTRAWIDE (2560px and beyond) ──────────────────────────────────────
 * The opposite failure, and the one nobody tests for: on a 34" monitor an
 * unconstrained column of text becomes a 2400px-wide ribbon that is physically
 * unreadable — the eye loses the line. Content gets a ceiling; full-bleed
 * canvases (the honeycomb, the galaxy, reels, live cams) opt out by not
 * carrying this class. */
.wrap-readable { max-width: 1440px; margin-inline: auto; }
@media (min-width: 2200px) {
  .wrap-readable { max-width: 1600px; }
}

/* ── 9. SAFE AREAS ─────────────────────────────────────────────────────────
 * Utilities so new code stops re-deriving the notch/home-indicator maths and
 * getting it wrong. Requires viewport-fit=cover in the page's viewport meta. */
.safe-t { padding-top: env(safe-area-inset-top); }
.safe-b { padding-bottom: env(safe-area-inset-bottom); }
.safe-x { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }

/* ── 10. MOTION ────────────────────────────────────────────────────────────
 * Honoured app-wide rather than per-page. Someone with vestibular sensitivity
 * should not have to find a setting inside the app to stop the room moving. */
@media (prefers-reduced-motion: reduce) {
  *:not(.keep-motion):not(.keep-motion *), *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
/* .keep-motion is the escape hatch for animation that CARRIES INFORMATION —
 * a loading spinner, a live "recording" pulse, a progress bar. Freezing those
 * does not calm the interface, it makes it look hung. Put .keep-motion on the
 * spinner, not on the decoration. */
