/* mobile/css/mobile.css — 042026.2.00 — [Batch 23] Speak-Easy Mobile Facade Phase 4.
   Phase 4 adds (on top of Phase 3c):
     • 6-zone rail (Phase 3 used 4) — zone 5 = Family/Friends placeholder
       (temporary "coming soon" card until the F&F pack lands), zone 6 = Personal
       (12-tile emergency/medical grid owned by mobile-personal-zone.js).
     • .mobile-zone--coming-soon — centred card used by Zone 5 to show the
       "Family & Friends — coming soon" placeholder.
     • .mobile-quick-empty — empty-state block shown in Quick zones when the
       usage ranker returns zero entries (no phrases learned yet). Includes a
       primary "Browse phrases" button that pops the new Browse overlay.
     • .mobile-quick-browse-btn — secondary fallback button appended to every
       Quick zone so users can always get to the Browse overlay, not only when
       the zone is empty.
     • Ranker-aware rerender is keyed off window event 'jhacal.ranker.updated';
       no CSS changes needed for that path beyond the empty-state styling.
   --- Phase 3b (Batch 21 g79, unchanged) ---
   Phase 3b adds (on top of Phase 3a):
   Phase 3b adds (on top of Phase 3a):
     • .mobile-tile-settings-* — per-tile long-press settings modal (overlay,
       panel, fieldset, slider, inherit checkbox, tri-state radios, danger/
       primary/secondary buttons, actions row).
     • .mobile-tile--hidden-shown — dimmed state for tiles the user has hidden
       (visible only when Edit mode is on or the "Show hidden" toggle is on).
     • .mobile-edit-modal-* — 3-field (subject / statement / detail) in-DOM
       editor replacing the Phase 1 window.prompt(). Layout mirrors the tile-
       settings panel for visual consistency.
     • Reduced-motion overrides — transitions + backdrop blur disabled for all
       new modals so motion-sensitive users see an instant appear/disappear.
   Phase 3a foundations retained below:
   New .mobile-tile--layered modifier — adds padding for dot affordance.
   New .tile-layer-dots + .tile-layer-dot — 1 or 2 small dots anchored at the
   tile's bottom-center to signal "statement" and/or "detail" layers exist.
   Dots are always visible (Pete 2026-04-22 Q1) — discoverability over subtlety.
   New .mobile-tile.is-awaiting-next — border+glow pulse animation during the
   1s awaiting-window between layer taps. Respects prefers-reduced-motion. */

/* =========================================================================
   Custom properties — mirror speak-simply-mini's token scheme for consistency
   ========================================================================= */
:root {
  --header-h: 52px;
  --indicator-h: 22px;
  --compose-h: 120px;            /* input row ~56 + mini-preds ~42 + padding */
  --tile-gap: 8px;
  --tile-radius: 14px;
  --tile-font-scale: 1;          /* controlled via Settings later */
  --bg: #0c1116;
  --bg-elev: #161c25;
  --bg-tile: #1e2630;
  --bg-tile-active: #2a3342;
  --fg: #e7ecf3;
  --fg-muted: #9099a8;
  --accent: #6ea2ff;
  --accent-dim: #3b5a94;
  --border: rgba(255,255,255,0.08);
  --border-strong: rgba(255,255,255,0.18);
  --zone-transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
  --zone-count: 6;               /* [Phase 4] zones 1-6 — 1=hdr-placeholder 2=Quick-A 3=Quick-B 4=Spell-Check 5=F&F-coming-soon 6=Personal */
  /* Safe-area insets — iOS PWA respects these for home-bar / notch */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

/* =========================================================================
   Base reset — minimal, coherent with other JHACAL apps
   ========================================================================= */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; padding: 0; overflow: hidden; height: 100dvh; }
html { background: var(--bg); }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: calc(15px * var(--tile-font-scale));
  color: var(--fg);
  background: var(--bg);
  position: fixed;               /* lock to viewport — swipe gestures don't scroll page */
  inset: 0;
  touch-action: pan-y;           /* allow vertical scroll inside zones, we handle horizontal */
  user-select: none;
  -webkit-user-select: none;
  padding: var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left);
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
  touch-action: manipulation;
}

input[type="text"] {
  font: inherit;
  color: inherit;
  background: transparent;
  border: none;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}

.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
  border: 0;
}

/* =========================================================================
   HEADER — Zone 1 content, rendered as a persistent top bar in Phase 1
   (Design plan §3 permits the header to be visible during landing zone
   rather than hidden behind a right-swipe, for discoverability. Phase 1
   ships both: header is visible AND the RIGHT-swipe gesture still routes
   focus to the header's first button for switch-access users.)
   ========================================================================= */
.mobile-header {
  position: fixed;
  top: var(--safe-top);
  left: var(--safe-left);
  right: var(--safe-right);
  height: var(--header-h);
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  gap: 4px;
  padding: 0 8px;
  z-index: 20;
}

.mobile-hdr-btn {
  flex: 1 1 auto;
  min-height: 44px;
  max-width: 72px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg-tile);
  color: var(--fg);
  font-size: 13px;
  padding: 6px 4px;
  transition: background 0.15s, border-color 0.15s;
}
.mobile-hdr-btn:active,
.mobile-hdr-btn[aria-pressed="true"] {
  background: var(--bg-tile-active);
  border-color: var(--border-strong);
}
.mobile-hdr-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* =========================================================================
   RAIL — the sliding carriage containing all zones side-by-side
   Each zone is 100vw wide; rail is zone-count × 100vw wide;
   transform on the rail shifts which zone is on-screen.
   ========================================================================= */
.mobile-rail {
  position: fixed;
  top: calc(var(--header-h) + var(--safe-top));
  left: 0;
  right: 0;
  bottom: calc(var(--compose-h) + var(--indicator-h) + var(--safe-bottom));
  overflow: hidden;
  display: flex;
  /* Rail is wider than viewport — zones sit side-by-side */
  width: calc(100vw * var(--zone-count));
  transform: translate3d(0, 0, 0);
  transition: var(--zone-transition);
  will-change: transform;
}

/* Positional offsets based on body[data-zone] — swipe-nav flips the attr.
   The rail holds a zone-1 placeholder as its first child so the
   "zone N → translate -(N-1)*100vw" math works uniformly. The header is a
   separate persistent top-bar; zone-1 in the rail is an empty panel that
   remains reachable by a right-swipe and serves as a switch-access parking
   spot (Phase 3 may repurpose it for quick-actions). */
body[data-zone="1"] .mobile-rail { transform: translate3d(0vw, 0, 0); }
body[data-zone="2"] .mobile-rail { transform: translate3d(-100vw, 0, 0); }
body[data-zone="3"] .mobile-rail { transform: translate3d(-200vw, 0, 0); }
body[data-zone="4"] .mobile-rail { transform: translate3d(-300vw, 0, 0); }  /* Phase 2: spell-check */
body[data-zone="5"] .mobile-rail { transform: translate3d(-400vw, 0, 0); }  /* [Phase 4] F&F-coming-soon */
body[data-zone="6"] .mobile-rail { transform: translate3d(-500vw, 0, 0); }  /* [Phase 4] Personal zone */

/* During an in-flight drag, swipe-nav adds .is-dragging to freeze the
   transition so the transform tracks the finger in real time. */
.mobile-rail.is-dragging { transition: none; }

.mobile-zone {
  flex: 0 0 100vw;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 12px;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
}

/* =========================================================================
   TILE GRID
   3 cols × 4 rows on phone; 4 cols × 3 rows on wider viewports.
   Height auto-fits — tiles expand to fill the zone minus padding.
   ========================================================================= */
.mobile-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(72px, 1fr);
  gap: var(--tile-gap);
  height: 100%;
  min-height: 320px;
}
@media (min-width: 600px) {
  .mobile-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.mobile-tile {
  background: var(--bg-tile);
  border: 1px solid var(--border);
  border-radius: var(--tile-radius);
  color: var(--fg);
  font-size: calc(15px * var(--tile-font-scale));
  line-height: 1.25;
  padding: 8px 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 44px;
  word-break: break-word;
  hyphens: auto;
  transition: background 0.12s, border-color 0.12s, transform 0.08s;
  /* [Phase 3b] position:relative so .mobile-tile--hidden-shown::after (the
     "hidden" badge) and .mobile-tile--layered's .tile-layer-dots anchor inside
     the tile rather than escaping to the nearest positioned ancestor. */
  position: relative;
}
.mobile-tile:active {
  background: var(--bg-tile-active);
  border-color: var(--accent-dim);
  transform: scale(0.97);
}
.mobile-tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.mobile-tile.is-speaking {
  border-color: var(--accent);
  background: var(--bg-tile-active);
}
.mobile-tile--empty {
  background: transparent;
  border-style: dashed;
  border-color: var(--border);
  color: var(--fg-muted);
  cursor: default;
}

/* =========================================================================
   [Phase 3a] LAYERED TILES — 2-tap / 3-tap reveal affordance
   A tile with a statement gets 1 dot; a tile with statement + detail gets 2.
   Dots sit in a flex row anchored to the tile's bottom-center. Always visible.
   The .is-awaiting-next modifier pulses the tile border between layer taps
   so the user has unmistakable feedback that the tap sequence is open.
   ========================================================================= */
.mobile-tile--layered {
  /* Reserve a little extra vertical space so the dots don't crowd the
     subject text. position: relative so .tile-layer-dots can absolute-anchor. */
  position: relative;
  padding-bottom: 16px;
}

.tile-layer-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
  pointer-events: none;   /* dots are decoration; taps go to the tile button */
}

.tile-layer-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--fg-muted);
  transition: background 0.2s, transform 0.2s;
}

/* The active tile during its awaiting-window highlights itself with a
   distinct accent border and the dots brighten to show "waiting for you". */
.mobile-tile.is-awaiting-next {
  border-color: var(--accent);
  background: var(--bg-tile-active);
  animation: mobile-tile-await-pulse 1s ease-in-out infinite;
}
.mobile-tile.is-awaiting-next .tile-layer-dot {
  background: var(--accent);
  transform: scale(1.15);
}

@keyframes mobile-tile-await-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(110,162,255, 0.35); }
  50%      { box-shadow: 0 0 0 6px rgba(110,162,255, 0.00); }
}

/* =========================================================================
   ZONE 1 PLACEHOLDER — blank rail panel reached by right-swipe from landing.
   Header is rendered as a persistent top bar, so this panel is intentionally
   empty. Kept as a swipe target so the rail math stays uniform.
   ========================================================================= */
.mobile-zone--placeholder {
  /* Visually neutral, non-interactive. */
  background: transparent;
}

/* =========================================================================
   ZONE 4 — SPELL-CHECK layout (Phase 2)
   Stack: mirror input at the top, then three horizontally-scrollable
   suggestion rows (Corrections, Phrases, Frequent). Vertical overflow inside
   the zone is allowed so the whole block scrolls gracefully on small devices.
   ========================================================================= */
.mobile-zone--spell {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
}

.spell-mirror-wrap {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
}

.spell-mirror-input {
  /* Reuses .mobile-input base styles — width 100% + 16px font prevents iOS zoom. */
  width: 100%;
  font-size: 16px;
}

.spell-rows {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
}

.spell-row-block {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.spell-row-label {
  font-size: 12px;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding-left: 2px;
}

.spell-row {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 2px 0 4px;
  min-height: 44px;
  align-items: center;
}
.spell-row::-webkit-scrollbar { display: none; }

.spell-row-item {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 8px 14px;
  background: var(--bg-tile);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 18px;
  font-size: 14px;
  white-space: nowrap;
  max-width: 80vw;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.12s, border-color 0.12s;
}
.spell-row-item:active {
  background: var(--bg-tile-active);
  border-color: var(--accent-dim);
}
.spell-row-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.spell-row-empty {
  flex: 0 0 auto;
  padding: 8px 2px;
  color: var(--fg-muted);
  font-size: 13px;
  font-style: italic;
}

/* =========================================================================
   ZONE INDICATOR — three small dots above the compose bar
   ========================================================================= */
.mobile-zone-indicator {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--compose-h) + var(--safe-bottom));
  height: var(--indicator-h);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--bg);
  z-index: 15;
  pointer-events: none;
}
.zone-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border-strong);
  transition: background 0.2s, transform 0.2s;
}
.zone-dot--active {
  background: var(--accent);
  transform: scale(1.4);
}

/* =========================================================================
   COMPOSE BAR — fixed bottom, input + speak + clear + mini-predictions
   ========================================================================= */
.mobile-compose {
  position: fixed;
  left: var(--safe-left);
  right: var(--safe-right);
  bottom: var(--safe-bottom);
  height: var(--compose-h);
  background: var(--bg-elev);
  border-top: 1px solid var(--border);
  padding: 8px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  z-index: 25;
}

.mobile-preds {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  overflow-y: hidden;
  height: 42px;
  align-items: center;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.mobile-preds::-webkit-scrollbar { display: none; }

.mobile-pred-btn {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 6px 12px;
  background: var(--bg-tile);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 18px;
  font-size: 14px;
  white-space: nowrap;
}
.mobile-pred-btn:active {
  background: var(--bg-tile-active);
  border-color: var(--accent-dim);
}

.mobile-compose-row {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 56px;
}

.mobile-input {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 8px 12px;
  background: var(--bg-tile);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--fg);
  font-size: 16px;               /* 16+ px prevents iOS zoom-on-focus */
}
.mobile-input:focus {
  border-color: var(--accent);
}
.mobile-input::placeholder {
  color: var(--fg-muted);
}

.mobile-speak-btn,
.mobile-clear-btn {
  flex: 0 0 auto;
  min-width: 64px;
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 10px;
  border: 1px solid var(--border-strong);
  font-size: 14px;
  font-weight: 600;
}
.mobile-speak-btn {
  background: var(--accent-dim);
  color: #fff;
}
.mobile-speak-btn:active { background: var(--accent); }
.mobile-clear-btn {
  background: var(--bg-tile);
  color: var(--fg-muted);
  min-width: 56px;
}
.mobile-clear-btn:active {
  background: var(--bg-tile-active);
  color: var(--fg);
}

/* =========================================================================
   EDIT MODE — visual hint that tapping a tile opens the editor
   not the speaker. Toggled by #editBtn via aria-pressed="true".
   ========================================================================= */
body[data-mode="edit"] .mobile-tile {
  border-style: dashed;
  cursor: text;
}

/* =========================================================================
   Tap-only mode compatibility (L12-7 Deploy C) — when jhacal.se.tapOnly is
   '1', #composeInput gets inputmode="none" applied programmatically so the
   soft keyboard stays hidden. Nothing to style here — just a hook for
   future accessibility polish.
   ========================================================================= */
body.jhacal-tap-only .mobile-input { caret-color: transparent; }

/* =========================================================================
   Reduced motion — respect user preference
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  .mobile-rail { transition: none; }
  .mobile-tile,
  .mobile-hdr-btn,
  .zone-dot { transition: none; }
  /* [Phase 3a] Skip the awaiting pulse entirely for reduced-motion users —
     the border + background still change so the state is still visible. */
  .mobile-tile.is-awaiting-next { animation: none; }
  /* [Phase 3b] Skip modal fade-ins for motion-sensitive users. */
  .mobile-tile-settings-overlay,
  .mobile-tile-settings-panel,
  .mobile-edit-modal-overlay,
  .mobile-edit-modal-panel { animation: none !important; transition: none !important; }
}

/* =========================================================================
   PHASE 3b — Hidden-tile dimmed state (only visible when Edit mode is on
   or the "Show hidden" toggle is set). Stays clickable so long-press can
   open the settings modal to un-hide it.
   ========================================================================= */
.mobile-tile--hidden-shown {
  opacity: 0.35;
  border-style: dashed;
  border-color: var(--fg-muted);
  filter: saturate(0.5);
}
.mobile-tile--hidden-shown::after {
  content: "hidden";
  position: absolute;
  top: 4px;
  right: 6px;
  font-size: 10px;
  color: var(--fg-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  pointer-events: none;
}

/* =========================================================================
   PHASE 3b — Tile-settings modal (long-press → per-tile settings)
   Built by /mobile/js/mobile-tile-settings.js. D36-safe (no innerHTML).
   ========================================================================= */
.mobile-tile-settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 19000;                  /* below system settings modal z=20000 */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  animation: mobileModalFadeIn 120ms ease-out;
}
@keyframes mobileModalFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.mobile-tile-settings-panel {
  width: min(520px, 100%);
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  padding: 18px 18px 14px;
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.55);
  animation: mobileModalSlideIn 160ms ease-out;
}
@keyframes mobileModalSlideIn {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0);     opacity: 1; }
}
.mobile-tile-settings-title {
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 600;
}
.mobile-tile-settings-subtitle {
  margin: 0 0 14px;
  color: var(--fg-muted);
  font-size: 13px;
}
.mobile-tile-settings-fieldset {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px 12px;
  margin: 0 0 12px;
}
.mobile-tile-settings-fieldset > legend {
  padding: 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg);
}
.mobile-tile-settings-inherit-lbl {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 8px;
  font-size: 14px;
  cursor: pointer;
}
.mobile-tile-settings-inherit-lbl input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}
.mobile-tile-settings-slider-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
}
.mobile-tile-settings-slider-wrap input[type="range"] {
  flex: 1 1 auto;
  accent-color: var(--accent);
}
.mobile-tile-settings-slider-val {
  flex: 0 0 auto;
  min-width: 68px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--fg-muted);
}
.mobile-tile-settings-radio-lbl {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: 14px;
  cursor: pointer;
}
.mobile-tile-settings-radio-lbl input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}
.mobile-tile-settings-danger {
  margin-top: 6px;
  padding: 10px 12px;
  border: 1px solid rgba(255, 120, 120, 0.35);
  border-radius: 10px;
  background: rgba(255, 70, 70, 0.08);
}
.mobile-tile-settings-danger-note {
  margin: 0 0 8px;
  font-size: 13px;
  color: var(--fg-muted);
}
.mobile-tile-settings-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}
.mobile-tile-settings-btn-primary,
.mobile-tile-settings-btn-secondary,
.mobile-tile-settings-btn-danger {
  min-width: 88px;
  min-height: 44px;
  padding: 8px 16px;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border-strong);
}
.mobile-tile-settings-btn-primary {
  background: var(--accent-dim);
  color: #fff;
  border-color: var(--accent);
}
.mobile-tile-settings-btn-primary:active { background: var(--accent); }
.mobile-tile-settings-btn-secondary {
  background: var(--bg-tile);
  color: var(--fg);
}
.mobile-tile-settings-btn-secondary:active { background: var(--bg-tile-active); }
.mobile-tile-settings-btn-danger {
  background: transparent;
  color: #ff8a8a;
  border-color: rgba(255, 120, 120, 0.5);
}
.mobile-tile-settings-btn-danger:active {
  background: rgba(255, 70, 70, 0.15);
}

/* =========================================================================
   PHASE 3b — Edit-mode modal (3-field: subject / statement / detail)
   Built by /mobile/js/mobile-swipe-nav.js openInlineEditor().
   Shares look with tile-settings panel for visual consistency.
   ========================================================================= */
.mobile-edit-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 19000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  animation: mobileModalFadeIn 120ms ease-out;
}
.mobile-edit-modal-panel {
  width: min(520px, 100%);
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: var(--bg-elev);
  color: var(--fg);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  padding: 18px;
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.55);
  animation: mobileModalSlideIn 160ms ease-out;
}
.mobile-edit-modal-title {
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 600;
}
.mobile-edit-modal-hint {
  margin: 0 0 14px;
  color: var(--fg-muted);
  font-size: 13px;
}
.mobile-edit-modal-field {
  display: block;
  margin: 0 0 10px;
}
.mobile-edit-modal-field-lbl {
  display: block;
  margin-bottom: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg);
}
.mobile-edit-modal-input {
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  background: var(--bg-tile);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 16px;          /* 16+ px prevents iOS zoom-on-focus */
  box-sizing: border-box;
}
.mobile-edit-modal-input:focus {
  outline: none;
  border-color: var(--accent);
}
.mobile-edit-modal-input::placeholder {
  color: var(--fg-muted);
}
.mobile-edit-modal-note {
  margin: 6px 0 12px;
  color: var(--fg-muted);
  font-size: 12px;
  font-style: italic;
}
.mobile-edit-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}
.mobile-edit-modal-btn {
  min-width: 88px;
  min-height: 44px;
  padding: 8px 16px;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border-strong);
}
.mobile-edit-modal-btn-primary {
  background: var(--accent-dim);
  color: #fff;
  border-color: var(--accent);
}
.mobile-edit-modal-btn-primary:active { background: var(--accent); }
.mobile-edit-modal-btn-secondary {
  background: var(--bg-tile);
  color: var(--fg);
}
.mobile-edit-modal-btn-secondary:active { background: var(--bg-tile-active); }

/* (Note: .mobile-tile itself sets position: relative in its main block above —
   the "hidden" badge on .mobile-tile--hidden-shown anchors there.) */

/* =========================================================================
   [Phase 4] ZONE 5 — FAMILY / FRIENDS COMING SOON
   A single centred card stands in until the F&F phrase pack lands. Styling
   mirrors the ambient mobile palette so it doesn't look broken — just "not
   yet filled in". The card is non-interactive (no tap handler) in Phase 4.
   ========================================================================= */
.mobile-zone--coming-soon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  gap: 14px;
}

.mobile-coming-soon-card {
  max-width: 420px;
  width: 100%;
  background: var(--bg-elev);
  border: 1px dashed var(--border-strong);
  border-radius: 16px;
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: var(--fg);
}

.mobile-coming-soon-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  background: var(--bg-tile);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  line-height: 1;
  color: var(--accent);
}

.mobile-coming-soon-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
}

.mobile-coming-soon-body {
  font-size: 14px;
  line-height: 1.4;
  color: var(--fg-muted);
  margin: 0;
}

/* =========================================================================
   [Phase 4] ZONE 6 — PERSONAL (detailed rules live in mobile-personal-zone.css)
   Only the zone container base is here so the rail math works even if the
   Personal CSS file fails to load. Content is injected at zone-enter time by
   mobile-personal-zone.js — empty until first render.
   ========================================================================= */
.mobile-zone--personal {
  /* The Personal grid provides its own scroll container; the zone itself
     keeps the shared padding for consistency with Quick zones. */
  padding: 12px;
}

/* =========================================================================
   [Phase 4] QUICK-ZONE EMPTY STATE + BROWSE BUTTON
   When JhacalUsageRanker.rankTop() returns an empty list (no phrases learned
   yet, or all decayed below the score floor) the Quick zone renders a
   centred empty-state block instead of the 12-tile grid. A secondary
   "Browse phrases" button is additionally rendered below every Quick zone's
   grid so the overlay is reachable at any time.
   ========================================================================= */
.mobile-quick-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 32px 20px;
  text-align: center;
  color: var(--fg-muted);
  border: 1px dashed var(--border);
  border-radius: 14px;
  margin-bottom: 10px;
}

.mobile-quick-empty-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--fg);
  margin: 0;
}

.mobile-quick-empty-body {
  font-size: 13px;
  line-height: 1.4;
  margin: 0;
  max-width: 36ch;
}

/* "Browse phrases" button. Primary-size inside the empty-state, secondary-
   size when appended below a populated grid. */
.mobile-quick-browse-btn {
  min-height: 44px;
  padding: 10px 18px;
  border-radius: 12px;
  background: var(--bg-tile);
  color: var(--fg);
  border: 1px solid var(--border-strong);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, transform 0.08s;
  align-self: center;
  margin-top: 10px;
}
.mobile-quick-browse-btn:active {
  background: var(--bg-tile-active);
  border-color: var(--accent-dim);
  transform: scale(0.97);
}
.mobile-quick-browse-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.mobile-quick-empty .mobile-quick-browse-btn {
  background: var(--accent-dim);
  color: #fff;
  border-color: var(--accent);
}
.mobile-quick-empty .mobile-quick-browse-btn:active {
  background: var(--accent);
}

/* =========================================================================
   [Phase 4 Session 4] HELP OVERLAY HOST
   The Help button in the header opens a sandboxed iframe of /mobile/help.html
   inside .mobile-help-overlay. Visual language matches the Browse overlay
   (bottom-sheet panel, dark backdrop, slide-up reveal). The iframe itself
   paints its own dark theme; these styles only frame the overlay.
   ========================================================================= */
.mobile-help-overlay {
  position: fixed;
  inset: 0;
  z-index: 65;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  animation: mobile-help-overlay-fade 180ms ease-out;
}
.mobile-help-panel {
  width: 100%;
  max-width: 720px;
  height: 92dvh;
  max-height: 92dvh;
  background: var(--bg-elev);
  color: var(--fg);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  border: 1px solid var(--border-strong);
  border-bottom: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: mobile-help-panel-slide 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
  box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.45);
}
.mobile-help-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
}
.mobile-help-title {
  font-size: 17px;
  font-weight: 600;
  margin: 0;
  color: var(--fg);
}
.mobile-help-close {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--bg-tile);
  color: var(--fg);
  font-size: 18px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 1px solid var(--border);
}
.mobile-help-close:active {
  background: var(--bg-tile-active);
  border-color: var(--accent-dim);
}
.mobile-help-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.mobile-help-frame {
  flex: 1 1 auto;
  width: 100%;
  border: 0;
  background: var(--bg);
}
@keyframes mobile-help-overlay-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes mobile-help-panel-slide {
  from { transform: translateY(24px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .mobile-help-overlay,
  .mobile-help-panel { animation: none; }
}
